There was a bug in the way passwords were applied, and it had to do with the same key apparently being applied regardless of what was in the password box (yikes). Also, the compression level box decided to not work. I ended up fixing these via 2 days of trial and error. As part of this making things more direct fix, I ended up needing to use a better algorithm for passwords that is still compatible but now has support for Unicode in its passwords, so no need to go through UTF7 anymore. This is a MAJOR fix. Signed-off-by: stgiga <stgigamovement@yahoo.com>
9713 lines
306 KiB
HTML
9713 lines
306 KiB
HTML
<!DOCTYPE html>
|
|
<html><head>
|
|
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
|
<!--<meta name="robots" content="none, noindex, nofollow, noimageindex, noarchive, nosnippet, notranslate, nocache, noyaca, noydir, noodp">
|
|
<meta name="googlebot" content="noindex, nofollow">-->
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<script type="text/javascript">
|
|
/*
|
|
---
|
|
MooTools: the javascript framework
|
|
|
|
web build:
|
|
- http://mootools.net/core/76bf47062d6c1983d66ce47ad66aa0e0
|
|
|
|
packager build:
|
|
- packager build Core/Core Core/Array Core/String Core/Number Core/Function Core/Object Core/Event Core/Browser Core/Class Core/Class.Extras Core/Slick.Parser Core/Slick.Finder Core/Element Core/Element.Style Core/Element.Event Core/Element.Delegation Core/Element.Dimensions Core/Fx Core/Fx.CSS Core/Fx.Tween Core/Fx.Morph Core/Fx.Transitions Core/Request Core/Request.HTML Core/Request.JSON Core/Cookie Core/JSON Core/DOMReady Core/Swiff
|
|
|
|
...
|
|
*/
|
|
|
|
/*
|
|
---
|
|
|
|
name: Core
|
|
|
|
description: The heart of MooTools.
|
|
|
|
license: MIT-style license.
|
|
|
|
copyright: Copyright (c) 2006-2012 [Valerio Proietti](http://mad4milk.net/).
|
|
|
|
authors: The MooTools production team (http://mootools.net/developers/)
|
|
|
|
inspiration:
|
|
- Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
|
|
- Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
|
|
|
|
provides: [Core, MooTools, Type, typeOf, instanceOf, Native]
|
|
|
|
...
|
|
*/
|
|
|
|
(function(){
|
|
|
|
this.MooTools = {
|
|
version: '1.4.5',
|
|
build: 'ab8ea8824dc3b24b6666867a2c4ed58ebb762cf0'
|
|
};
|
|
|
|
// typeOf, instanceOf
|
|
|
|
var typeOf = this.typeOf = function(item){
|
|
if (item == null) return 'null';
|
|
if (item.$family != null) return item.$family();
|
|
|
|
if (item.nodeName){
|
|
if (item.nodeType == 1) return 'element';
|
|
if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace';
|
|
} else if (typeof item.length == 'number'){
|
|
if (item.callee) return 'arguments';
|
|
if ('item' in item) return 'collection';
|
|
}
|
|
|
|
return typeof item;
|
|
};
|
|
|
|
var instanceOf = this.instanceOf = function(item, object){
|
|
if (item == null) return false;
|
|
var constructor = item.$constructor || item.constructor;
|
|
while (constructor){
|
|
if (constructor === object) return true;
|
|
constructor = constructor.parent;
|
|
}
|
|
/*<ltIE8>*/
|
|
if (!item.hasOwnProperty) return false;
|
|
/*</ltIE8>*/
|
|
return item instanceof object;
|
|
};
|
|
|
|
// Function overloading
|
|
|
|
var Function = this.Function;
|
|
|
|
var enumerables = true;
|
|
for (var i in {toString: 1}) enumerables = null;
|
|
if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor'];
|
|
|
|
Function.prototype.overloadSetter = function(usePlural){
|
|
var self = this;
|
|
return function(a, b){
|
|
if (a == null) return this;
|
|
if (usePlural || typeof a != 'string'){
|
|
for (var k in a) self.call(this, k, a[k]);
|
|
if (enumerables) for (var i = enumerables.length; i--;){
|
|
k = enumerables[i];
|
|
if (a.hasOwnProperty(k)) self.call(this, k, a[k]);
|
|
}
|
|
} else {
|
|
self.call(this, a, b);
|
|
}
|
|
return this;
|
|
};
|
|
};
|
|
|
|
Function.prototype.overloadGetter = function(usePlural){
|
|
var self = this;
|
|
return function(a){
|
|
var args, result;
|
|
if (typeof a != 'string') args = a;
|
|
else if (arguments.length > 1) args = arguments;
|
|
else if (usePlural) args = [a];
|
|
if (args){
|
|
result = {};
|
|
for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]);
|
|
} else {
|
|
result = self.call(this, a);
|
|
}
|
|
return result;
|
|
};
|
|
};
|
|
|
|
Function.prototype.extend = function(key, value){
|
|
this[key] = value;
|
|
}.overloadSetter();
|
|
|
|
Function.prototype.implement = function(key, value){
|
|
this.prototype[key] = value;
|
|
}.overloadSetter();
|
|
|
|
// From
|
|
|
|
var slice = Array.prototype.slice;
|
|
|
|
Function.from = function(item){
|
|
return (typeOf(item) == 'function') ? item : function(){
|
|
return item;
|
|
};
|
|
};
|
|
|
|
Array.from = function(item){
|
|
if (item == null) return [];
|
|
return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item];
|
|
};
|
|
|
|
Number.from = function(item){
|
|
var number = parseFloat(item);
|
|
return isFinite(number) ? number : null;
|
|
};
|
|
|
|
String.from = function(item){
|
|
return item + '';
|
|
};
|
|
|
|
// hide, protect
|
|
|
|
Function.implement({
|
|
|
|
hide: function(){
|
|
this.$hidden = true;
|
|
return this;
|
|
},
|
|
|
|
protect: function(){
|
|
this.$protected = true;
|
|
return this;
|
|
}
|
|
|
|
});
|
|
|
|
// Type
|
|
|
|
var Type = this.Type = function(name, object){
|
|
if (name){
|
|
var lower = name.toLowerCase();
|
|
var typeCheck = function(item){
|
|
return (typeOf(item) == lower);
|
|
};
|
|
|
|
Type['is' + name] = typeCheck;
|
|
if (object != null){
|
|
object.prototype.$family = (function(){
|
|
return lower;
|
|
}).hide();
|
|
|
|
}
|
|
}
|
|
|
|
if (object == null) return null;
|
|
|
|
object.extend(this);
|
|
object.$constructor = Type;
|
|
object.prototype.$constructor = object;
|
|
|
|
return object;
|
|
};
|
|
|
|
var toString = Object.prototype.toString;
|
|
|
|
Type.isEnumerable = function(item){
|
|
return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' );
|
|
};
|
|
|
|
var hooks = {};
|
|
|
|
var hooksOf = function(object){
|
|
var type = typeOf(object.prototype);
|
|
return hooks[type] || (hooks[type] = []);
|
|
};
|
|
|
|
var implement = function(name, method){
|
|
if (method && method.$hidden) return;
|
|
|
|
var hooks = hooksOf(this);
|
|
|
|
for (var i = 0; i < hooks.length; i++){
|
|
var hook = hooks[i];
|
|
if (typeOf(hook) == 'type') implement.call(hook, name, method);
|
|
else hook.call(this, name, method);
|
|
}
|
|
|
|
var previous = this.prototype[name];
|
|
if (previous == null || !previous.$protected) this.prototype[name] = method;
|
|
|
|
if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){
|
|
return method.apply(item, slice.call(arguments, 1));
|
|
});
|
|
};
|
|
|
|
var extend = function(name, method){
|
|
if (method && method.$hidden) return;
|
|
var previous = this[name];
|
|
if (previous == null || !previous.$protected) this[name] = method;
|
|
};
|
|
|
|
Type.implement({
|
|
|
|
implement: implement.overloadSetter(),
|
|
|
|
extend: extend.overloadSetter(),
|
|
|
|
alias: function(name, existing){
|
|
implement.call(this, name, this.prototype[existing]);
|
|
}.overloadSetter(),
|
|
|
|
mirror: function(hook){
|
|
hooksOf(this).push(hook);
|
|
return this;
|
|
}
|
|
|
|
});
|
|
|
|
new Type('Type', Type);
|
|
|
|
// Default Types
|
|
|
|
var force = function(name, object, methods){
|
|
var isType = (object != Object),
|
|
prototype = object.prototype;
|
|
|
|
if (isType) object = new Type(name, object);
|
|
|
|
for (var i = 0, l = methods.length; i < l; i++){
|
|
var key = methods[i],
|
|
generic = object[key],
|
|
proto = prototype[key];
|
|
|
|
if (generic) generic.protect();
|
|
if (isType && proto) object.implement(key, proto.protect());
|
|
}
|
|
|
|
if (isType){
|
|
var methodsEnumerable = prototype.propertyIsEnumerable(methods[0]);
|
|
object.forEachMethod = function(fn){
|
|
if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){
|
|
fn.call(prototype, prototype[methods[i]], methods[i]);
|
|
}
|
|
for (var key in prototype) fn.call(prototype, prototype[key], key)
|
|
};
|
|
}
|
|
|
|
return force;
|
|
};
|
|
|
|
force('String', String, [
|
|
'charAt', 'charCodeAt', 'concat', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
|
|
'slice', 'split', 'substr', 'substring', 'trim', 'toLowerCase', 'toUpperCase'
|
|
])('Array', Array, [
|
|
'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
|
|
'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight'
|
|
])('Number', Number, [
|
|
'toExponential', 'toFixed', 'toLocaleString', 'toPrecision'
|
|
])('Function', Function, [
|
|
'apply', 'call', 'bind'
|
|
])('RegExp', RegExp, [
|
|
'exec', 'test'
|
|
])('Object', Object, [
|
|
'create', 'defineProperty', 'defineProperties', 'keys',
|
|
'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames',
|
|
'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen'
|
|
])('Date', Date, ['now']);
|
|
|
|
Object.extend = extend.overloadSetter();
|
|
|
|
Date.extend('now', function(){
|
|
return +(new Date);
|
|
});
|
|
|
|
new Type('Boolean', Boolean);
|
|
|
|
// fixes NaN returning as Number
|
|
|
|
Number.prototype.$family = function(){
|
|
return isFinite(this) ? 'number' : 'null';
|
|
}.hide();
|
|
|
|
// Number.random
|
|
|
|
Number.extend('random', function(min, max){
|
|
return Math.floor(Math.random() * (max - min + 1) + min);
|
|
});
|
|
|
|
// forEach, each
|
|
|
|
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
Object.extend('forEach', function(object, fn, bind){
|
|
for (var key in object){
|
|
if (hasOwnProperty.call(object, key)) fn.call(bind, object[key], key, object);
|
|
}
|
|
});
|
|
|
|
Object.each = Object.forEach;
|
|
|
|
Array.implement({
|
|
|
|
forEach: function(fn, bind){
|
|
for (var i = 0, l = this.length; i < l; i++){
|
|
if (i in this) fn.call(bind, this[i], i, this);
|
|
}
|
|
},
|
|
|
|
each: function(fn, bind){
|
|
Array.forEach(this, fn, bind);
|
|
return this;
|
|
}
|
|
|
|
});
|
|
|
|
// Array & Object cloning, Object merging and appending
|
|
|
|
var cloneOf = function(item){
|
|
switch (typeOf(item)){
|
|
case 'array': return item.clone();
|
|
case 'object': return Object.clone(item);
|
|
default: return item;
|
|
}
|
|
};
|
|
|
|
Array.implement('clone', function(){
|
|
var i = this.length, clone = new Array(i);
|
|
while (i--) clone[i] = cloneOf(this[i]);
|
|
return clone;
|
|
});
|
|
|
|
var mergeOne = function(source, key, current){
|
|
switch (typeOf(current)){
|
|
case 'object':
|
|
if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
|
|
else source[key] = Object.clone(current);
|
|
break;
|
|
case 'array': source[key] = current.clone(); break;
|
|
default: source[key] = current;
|
|
}
|
|
return source;
|
|
};
|
|
|
|
Object.extend({
|
|
|
|
merge: function(source, k, v){
|
|
if (typeOf(k) == 'string') return mergeOne(source, k, v);
|
|
for (var i = 1, l = arguments.length; i < l; i++){
|
|
var object = arguments[i];
|
|
for (var key in object) mergeOne(source, key, object[key]);
|
|
}
|
|
return source;
|
|
},
|
|
|
|
clone: function(object){
|
|
var clone = {};
|
|
for (var key in object) clone[key] = cloneOf(object[key]);
|
|
return clone;
|
|
},
|
|
|
|
append: function(original){
|
|
for (var i = 1, l = arguments.length; i < l; i++){
|
|
var extended = arguments[i] || {};
|
|
for (var key in extended) original[key] = extended[key];
|
|
}
|
|
return original;
|
|
}
|
|
|
|
});
|
|
|
|
// Object-less types
|
|
|
|
['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){
|
|
new Type(name);
|
|
});
|
|
|
|
// Unique ID
|
|
|
|
var UID = Date.now();
|
|
|
|
String.extend('uniqueID', function(){
|
|
return (UID++).toString(36);
|
|
});
|
|
|
|
|
|
|
|
})();
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Array
|
|
|
|
description: Contains Array Prototypes like each, contains, and erase.
|
|
|
|
license: MIT-style license.
|
|
|
|
requires: Type
|
|
|
|
provides: Array
|
|
|
|
...
|
|
*/
|
|
|
|
Array.implement({
|
|
|
|
/*<!ES5>*/
|
|
every: function(fn, bind){
|
|
for (var i = 0, l = this.length >>> 0; i < l; i++){
|
|
if ((i in this) && !fn.call(bind, this[i], i, this)) return false;
|
|
}
|
|
return true;
|
|
},
|
|
|
|
filter: function(fn, bind){
|
|
var results = [];
|
|
for (var value, i = 0, l = this.length >>> 0; i < l; i++) if (i in this){
|
|
value = this[i];
|
|
if (fn.call(bind, value, i, this)) results.push(value);
|
|
}
|
|
return results;
|
|
},
|
|
|
|
indexOf: function(item, from){
|
|
var length = this.length >>> 0;
|
|
for (var i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++){
|
|
if (this[i] === item) return i;
|
|
}
|
|
return -1;
|
|
},
|
|
|
|
map: function(fn, bind){
|
|
var length = this.length >>> 0, results = Array(length);
|
|
for (var i = 0; i < length; i++){
|
|
if (i in this) results[i] = fn.call(bind, this[i], i, this);
|
|
}
|
|
return results;
|
|
},
|
|
|
|
some: function(fn, bind){
|
|
for (var i = 0, l = this.length >>> 0; i < l; i++){
|
|
if ((i in this) && fn.call(bind, this[i], i, this)) return true;
|
|
}
|
|
return false;
|
|
},
|
|
/*</!ES5>*/
|
|
|
|
clean: function(){
|
|
return this.filter(function(item){
|
|
return item != null;
|
|
});
|
|
},
|
|
|
|
invoke: function(methodName){
|
|
var args = Array.slice(arguments, 1);
|
|
return this.map(function(item){
|
|
return item[methodName].apply(item, args);
|
|
});
|
|
},
|
|
|
|
associate: function(keys){
|
|
var obj = {}, length = Math.min(this.length, keys.length);
|
|
for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
|
|
return obj;
|
|
},
|
|
|
|
link: function(object){
|
|
var result = {};
|
|
for (var i = 0, l = this.length; i < l; i++){
|
|
for (var key in object){
|
|
if (object[key](this[i])){
|
|
result[key] = this[i];
|
|
delete object[key];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
|
|
contains: function(item, from){
|
|
return this.indexOf(item, from) != -1;
|
|
},
|
|
|
|
append: function(array){
|
|
this.push.apply(this, array);
|
|
return this;
|
|
},
|
|
|
|
getLast: function(){
|
|
return (this.length) ? this[this.length - 1] : null;
|
|
},
|
|
|
|
getRandom: function(){
|
|
return (this.length) ? this[Number.random(0, this.length - 1)] : null;
|
|
},
|
|
|
|
include: function(item){
|
|
if (!this.contains(item)) this.push(item);
|
|
return this;
|
|
},
|
|
|
|
combine: function(array){
|
|
for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
|
|
return this;
|
|
},
|
|
|
|
erase: function(item){
|
|
for (var i = this.length; i--;){
|
|
if (this[i] === item) this.splice(i, 1);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
empty: function(){
|
|
this.length = 0;
|
|
return this;
|
|
},
|
|
|
|
flatten: function(){
|
|
var array = [];
|
|
for (var i = 0, l = this.length; i < l; i++){
|
|
var type = typeOf(this[i]);
|
|
if (type == 'null') continue;
|
|
array = array.concat((type == 'array' || type == 'collection' || type == 'arguments' || instanceOf(this[i], Array)) ? Array.flatten(this[i]) : this[i]);
|
|
}
|
|
return array;
|
|
},
|
|
|
|
pick: function(){
|
|
for (var i = 0, l = this.length; i < l; i++){
|
|
if (this[i] != null) return this[i];
|
|
}
|
|
return null;
|
|
},
|
|
|
|
hexToRgb: function(array){
|
|
if (this.length != 3) return null;
|
|
var rgb = this.map(function(value){
|
|
if (value.length == 1) value += value;
|
|
return value.toInt(16);
|
|
});
|
|
return (array) ? rgb : 'rgb(' + rgb + ')';
|
|
},
|
|
|
|
rgbToHex: function(array){
|
|
if (this.length < 3) return null;
|
|
if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
|
|
var hex = [];
|
|
for (var i = 0; i < 3; i++){
|
|
var bit = (this[i] - 0).toString(16);
|
|
hex.push((bit.length == 1) ? '0' + bit : bit);
|
|
}
|
|
return (array) ? hex : '#' + hex.join('');
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: String
|
|
|
|
description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
|
|
|
|
license: MIT-style license.
|
|
|
|
requires: Type
|
|
|
|
provides: String
|
|
|
|
...
|
|
*/
|
|
|
|
String.implement({
|
|
|
|
test: function(regex, params){
|
|
return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this);
|
|
},
|
|
|
|
contains: function(string, separator){
|
|
return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : String(this).indexOf(string) > -1;
|
|
},
|
|
|
|
trim: function(){
|
|
return String(this).replace(/^\s+|\s+$/g, '');
|
|
},
|
|
|
|
clean: function(){
|
|
return String(this).replace(/\s+/g, ' ').trim();
|
|
},
|
|
|
|
camelCase: function(){
|
|
return String(this).replace(/-\D/g, function(match){
|
|
return match.charAt(1).toUpperCase();
|
|
});
|
|
},
|
|
|
|
hyphenate: function(){
|
|
return String(this).replace(/[A-Z]/g, function(match){
|
|
return ('-' + match.charAt(0).toLowerCase());
|
|
});
|
|
},
|
|
|
|
capitalize: function(){
|
|
return String(this).replace(/\b[a-z]/g, function(match){
|
|
return match.toUpperCase();
|
|
});
|
|
},
|
|
|
|
escapeRegExp: function(){
|
|
return String(this).replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
|
|
},
|
|
|
|
toInt: function(base){
|
|
return parseInt(this, base || 10);
|
|
},
|
|
|
|
toFloat: function(){
|
|
return parseFloat(this);
|
|
},
|
|
|
|
hexToRgb: function(array){
|
|
var hex = String(this).match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
|
|
return (hex) ? hex.slice(1).hexToRgb(array) : null;
|
|
},
|
|
|
|
rgbToHex: function(array){
|
|
var rgb = String(this).match(/\d{1,3}/g);
|
|
return (rgb) ? rgb.rgbToHex(array) : null;
|
|
},
|
|
|
|
substitute: function(object, regexp){
|
|
return String(this).replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
|
|
if (match.charAt(0) == '\\') return match.slice(1);
|
|
return (object[name] != null) ? object[name] : '';
|
|
});
|
|
}
|
|
|
|
});
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Number
|
|
|
|
description: Contains Number Prototypes like limit, round, times, and ceil.
|
|
|
|
license: MIT-style license.
|
|
|
|
requires: Type
|
|
|
|
provides: Number
|
|
|
|
...
|
|
*/
|
|
|
|
Number.implement({
|
|
|
|
limit: function(min, max){
|
|
return Math.min(max, Math.max(min, this));
|
|
},
|
|
|
|
round: function(precision){
|
|
precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -precision : 0);
|
|
return Math.round(this * precision) / precision;
|
|
},
|
|
|
|
times: function(fn, bind){
|
|
for (var i = 0; i < this; i++) fn.call(bind, i, this);
|
|
},
|
|
|
|
toFloat: function(){
|
|
return parseFloat(this);
|
|
},
|
|
|
|
toInt: function(base){
|
|
return parseInt(this, base || 10);
|
|
}
|
|
|
|
});
|
|
|
|
Number.alias('each', 'times');
|
|
|
|
(function(math){
|
|
var methods = {};
|
|
math.each(function(name){
|
|
if (!Number[name]) methods[name] = function(){
|
|
return Math[name].apply(null, [this].concat(Array.from(arguments)));
|
|
};
|
|
});
|
|
Number.implement(methods);
|
|
})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Function
|
|
|
|
description: Contains Function Prototypes like create, bind, pass, and delay.
|
|
|
|
license: MIT-style license.
|
|
|
|
requires: Type
|
|
|
|
provides: Function
|
|
|
|
...
|
|
*/
|
|
|
|
Function.extend({
|
|
|
|
attempt: function(){
|
|
for (var i = 0, l = arguments.length; i < l; i++){
|
|
try {
|
|
return arguments[i]();
|
|
} catch (e){}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
});
|
|
|
|
Function.implement({
|
|
|
|
attempt: function(args, bind){
|
|
try {
|
|
return this.apply(bind, Array.from(args));
|
|
} catch (e){}
|
|
|
|
return null;
|
|
},
|
|
|
|
/*<!ES5-bind>*/
|
|
bind: function(that){
|
|
var self = this,
|
|
args = arguments.length > 1 ? Array.slice(arguments, 1) : null,
|
|
F = function(){};
|
|
|
|
var bound = function(){
|
|
var context = that, length = arguments.length;
|
|
if (this instanceof bound){
|
|
F.prototype = self.prototype;
|
|
context = new F;
|
|
}
|
|
var result = (!args && !length)
|
|
? self.call(context)
|
|
: self.apply(context, args && length ? args.concat(Array.slice(arguments)) : args || arguments);
|
|
return context == that ? result : context;
|
|
};
|
|
return bound;
|
|
},
|
|
/*</!ES5-bind>*/
|
|
|
|
pass: function(args, bind){
|
|
var self = this;
|
|
if (args != null) args = Array.from(args);
|
|
return function(){
|
|
return self.apply(bind, args || arguments);
|
|
};
|
|
},
|
|
|
|
delay: function(delay, bind, args){
|
|
return setTimeout(this.pass((args == null ? [] : args), bind), delay);
|
|
},
|
|
|
|
periodical: function(periodical, bind, args){
|
|
return setInterval(this.pass((args == null ? [] : args), bind), periodical);
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Object
|
|
|
|
description: Object generic methods
|
|
|
|
license: MIT-style license.
|
|
|
|
requires: Type
|
|
|
|
provides: [Object, Hash]
|
|
|
|
...
|
|
*/
|
|
|
|
(function(){
|
|
|
|
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
|
|
Object.extend({
|
|
|
|
subset: function(object, keys){
|
|
var results = {};
|
|
for (var i = 0, l = keys.length; i < l; i++){
|
|
var k = keys[i];
|
|
if (k in object) results[k] = object[k];
|
|
}
|
|
return results;
|
|
},
|
|
|
|
map: function(object, fn, bind){
|
|
var results = {};
|
|
for (var key in object){
|
|
if (hasOwnProperty.call(object, key)) results[key] = fn.call(bind, object[key], key, object);
|
|
}
|
|
return results;
|
|
},
|
|
|
|
filter: function(object, fn, bind){
|
|
var results = {};
|
|
for (var key in object){
|
|
var value = object[key];
|
|
if (hasOwnProperty.call(object, key) && fn.call(bind, value, key, object)) results[key] = value;
|
|
}
|
|
return results;
|
|
},
|
|
|
|
every: function(object, fn, bind){
|
|
for (var key in object){
|
|
if (hasOwnProperty.call(object, key) && !fn.call(bind, object[key], key)) return false;
|
|
}
|
|
return true;
|
|
},
|
|
|
|
some: function(object, fn, bind){
|
|
for (var key in object){
|
|
if (hasOwnProperty.call(object, key) && fn.call(bind, object[key], key)) return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
keys: function(object){
|
|
var keys = [];
|
|
for (var key in object){
|
|
if (hasOwnProperty.call(object, key)) keys.push(key);
|
|
}
|
|
return keys;
|
|
},
|
|
|
|
values: function(object){
|
|
var values = [];
|
|
for (var key in object){
|
|
if (hasOwnProperty.call(object, key)) values.push(object[key]);
|
|
}
|
|
return values;
|
|
},
|
|
|
|
getLength: function(object){
|
|
return Object.keys(object).length;
|
|
},
|
|
|
|
keyOf: function(object, value){
|
|
for (var key in object){
|
|
if (hasOwnProperty.call(object, key) && object[key] === value) return key;
|
|
}
|
|
return null;
|
|
},
|
|
|
|
contains: function(object, value){
|
|
return Object.keyOf(object, value) != null;
|
|
},
|
|
|
|
toQueryString: function(object, base){
|
|
var queryString = [];
|
|
|
|
Object.each(object, function(value, key){
|
|
if (base) key = base + '[' + key + ']';
|
|
var result;
|
|
switch (typeOf(value)){
|
|
case 'object': result = Object.toQueryString(value, key); break;
|
|
case 'array':
|
|
var qs = {};
|
|
value.each(function(val, i){
|
|
qs[i] = val;
|
|
});
|
|
result = Object.toQueryString(qs, key);
|
|
break;
|
|
default: result = key + '=' + encodeURIComponent(value);
|
|
}
|
|
if (value != null) queryString.push(result);
|
|
});
|
|
|
|
return queryString.join('&');
|
|
}
|
|
|
|
});
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Browser
|
|
|
|
description: The Browser Object. Contains Browser initialization, Window and Document, and the Browser Hash.
|
|
|
|
license: MIT-style license.
|
|
|
|
requires: [Array, Function, Number, String]
|
|
|
|
provides: [Browser, Window, Document]
|
|
|
|
...
|
|
*/
|
|
|
|
(function(){
|
|
|
|
var document = this.document;
|
|
var window = document.window = this;
|
|
|
|
var ua = navigator.userAgent.toLowerCase(),
|
|
platform = navigator.platform.toLowerCase(),
|
|
UA = ua.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/) || [null, 'unknown', 0],
|
|
mode = UA[1] == 'ie' && document.documentMode;
|
|
|
|
var Browser = this.Browser = {
|
|
|
|
extend: Function.prototype.extend,
|
|
|
|
name: (UA[1] == 'version') ? UA[3] : UA[1],
|
|
|
|
version: mode || parseFloat((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]),
|
|
|
|
Platform: {
|
|
name: ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || platform.match(/mac|win|linux/) || ['other'])[0]
|
|
},
|
|
|
|
Features: {
|
|
xpath: !!(document.evaluate),
|
|
air: !!(window.runtime),
|
|
query: !!(document.querySelector),
|
|
json: !!(window.JSON)
|
|
},
|
|
|
|
Plugins: {}
|
|
|
|
};
|
|
|
|
Browser[Browser.name] = true;
|
|
Browser[Browser.name + parseInt(Browser.version, 10)] = true;
|
|
Browser.Platform[Browser.Platform.name] = true;
|
|
|
|
// Request
|
|
|
|
Browser.Request = (function(){
|
|
|
|
var XMLHTTP = function(){
|
|
return new XMLHttpRequest();
|
|
};
|
|
|
|
var MSXML2 = function(){
|
|
return new ActiveXObject('MSXML2.XMLHTTP');
|
|
};
|
|
|
|
var MSXML = function(){
|
|
return new ActiveXObject('Microsoft.XMLHTTP');
|
|
};
|
|
|
|
return Function.attempt(function(){
|
|
XMLHTTP();
|
|
return XMLHTTP;
|
|
}, function(){
|
|
MSXML2();
|
|
return MSXML2;
|
|
}, function(){
|
|
MSXML();
|
|
return MSXML;
|
|
});
|
|
|
|
})();
|
|
|
|
Browser.Features.xhr = !!(Browser.Request);
|
|
|
|
// Flash detection
|
|
|
|
var version = (Function.attempt(function(){
|
|
return navigator.plugins['Shockwave Flash'].description;
|
|
}, function(){
|
|
return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
|
|
}) || '0 r0').match(/\d+/g);
|
|
|
|
Browser.Plugins.Flash = {
|
|
version: Number(version[0] || '0.' + version[1]) || 0,
|
|
build: Number(version[2]) || 0
|
|
};
|
|
|
|
// String scripts
|
|
|
|
Browser.exec = function(text){
|
|
if (!text) return text;
|
|
if (window.execScript){
|
|
window.execScript(text);
|
|
} else {
|
|
var script = document.createElement('script');
|
|
script.setAttribute('type', 'text/javascript');
|
|
script.text = text;
|
|
document.head.appendChild(script);
|
|
document.head.removeChild(script);
|
|
}
|
|
return text;
|
|
};
|
|
|
|
String.implement('stripScripts', function(exec){
|
|
var scripts = '';
|
|
var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(all, code){
|
|
scripts += code + '\n';
|
|
return '';
|
|
});
|
|
if (exec === true) Browser.exec(scripts);
|
|
else if (typeOf(exec) == 'function') exec(scripts, text);
|
|
return text;
|
|
});
|
|
|
|
// Window, Document
|
|
|
|
Browser.extend({
|
|
Document: this.Document,
|
|
Window: this.Window,
|
|
Element: this.Element,
|
|
Event: this.Event
|
|
});
|
|
|
|
this.Window = this.$constructor = new Type('Window', function(){});
|
|
|
|
this.$family = Function.from('window').hide();
|
|
|
|
Window.mirror(function(name, method){
|
|
window[name] = method;
|
|
});
|
|
|
|
this.Document = document.$constructor = new Type('Document', function(){});
|
|
|
|
document.$family = Function.from('document').hide();
|
|
|
|
Document.mirror(function(name, method){
|
|
document[name] = method;
|
|
});
|
|
|
|
document.html = document.documentElement;
|
|
if (!document.head) document.head = document.getElementsByTagName('head')[0];
|
|
|
|
if (document.execCommand) try {
|
|
document.execCommand("BackgroundImageCache", false, true);
|
|
} catch (e){}
|
|
|
|
/*<ltIE9>*/
|
|
if (this.attachEvent && !this.addEventListener){
|
|
var unloadEvent = function(){
|
|
this.detachEvent('onunload', unloadEvent);
|
|
document.head = document.html = document.window = null;
|
|
};
|
|
this.attachEvent('onunload', unloadEvent);
|
|
}
|
|
|
|
// IE fails on collections and <select>.options (refers to <select>)
|
|
var arrayFrom = Array.from;
|
|
try {
|
|
arrayFrom(document.html.childNodes);
|
|
} catch(e){
|
|
Array.from = function(item){
|
|
if (typeof item != 'string' && Type.isEnumerable(item) && typeOf(item) != 'array'){
|
|
var i = item.length, array = new Array(i);
|
|
while (i--) array[i] = item[i];
|
|
return array;
|
|
}
|
|
return arrayFrom(item);
|
|
};
|
|
|
|
var prototype = Array.prototype,
|
|
slice = prototype.slice;
|
|
['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice'].each(function(name){
|
|
var method = prototype[name];
|
|
Array[name] = function(item){
|
|
return method.apply(Array.from(item), slice.call(arguments, 1));
|
|
};
|
|
});
|
|
}
|
|
/*</ltIE9>*/
|
|
|
|
|
|
|
|
})();
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Event
|
|
|
|
description: Contains the Event Type, to make the event object cross-browser.
|
|
|
|
license: MIT-style license.
|
|
|
|
requires: [Window, Document, Array, Function, String, Object]
|
|
|
|
provides: Event
|
|
|
|
...
|
|
*/
|
|
|
|
(function() {
|
|
|
|
var _keys = {};
|
|
|
|
var DOMEvent = this.DOMEvent = new Type('DOMEvent', function(event, win){
|
|
if (!win) win = window;
|
|
event = event || win.event;
|
|
if (event.$extended) return event;
|
|
this.event = event;
|
|
this.$extended = true;
|
|
this.shift = event.shiftKey;
|
|
this.control = event.ctrlKey;
|
|
this.alt = event.altKey;
|
|
this.meta = event.metaKey;
|
|
var type = this.type = event.type;
|
|
var target = event.target || event.srcElement;
|
|
while (target && target.nodeType == 3) target = target.parentNode;
|
|
this.target = document.id(target);
|
|
|
|
if (type.indexOf('key') == 0){
|
|
var code = this.code = (event.which || event.keyCode);
|
|
this.key = _keys[code];
|
|
if (type == 'keydown'){
|
|
if (code > 111 && code < 124) this.key = 'f' + (code - 111);
|
|
else if (code > 95 && code < 106) this.key = code - 96;
|
|
}
|
|
if (this.key == null) this.key = String.fromCharCode(code).toLowerCase();
|
|
} else if (type == 'click' || type == 'dblclick' || type == 'contextmenu' || type == 'DOMMouseScroll' || type.indexOf('mouse') == 0){
|
|
var doc = win.document;
|
|
doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
|
|
this.page = {
|
|
x: (event.pageX != null) ? event.pageX : event.clientX + doc.scrollLeft,
|
|
y: (event.pageY != null) ? event.pageY : event.clientY + doc.scrollTop
|
|
};
|
|
this.client = {
|
|
x: (event.pageX != null) ? event.pageX - win.pageXOffset : event.clientX,
|
|
y: (event.pageY != null) ? event.pageY - win.pageYOffset : event.clientY
|
|
};
|
|
if (type == 'DOMMouseScroll' || type == 'mousewheel')
|
|
this.wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
|
|
|
|
this.rightClick = (event.which == 3 || event.button == 2);
|
|
if (type == 'mouseover' || type == 'mouseout'){
|
|
var related = event.relatedTarget || event[(type == 'mouseover' ? 'from' : 'to') + 'Element'];
|
|
while (related && related.nodeType == 3) related = related.parentNode;
|
|
this.relatedTarget = document.id(related);
|
|
}
|
|
} else if (type.indexOf('touch') == 0 || type.indexOf('gesture') == 0){
|
|
this.rotation = event.rotation;
|
|
this.scale = event.scale;
|
|
this.targetTouches = event.targetTouches;
|
|
this.changedTouches = event.changedTouches;
|
|
var touches = this.touches = event.touches;
|
|
if (touches && touches[0]){
|
|
var touch = touches[0];
|
|
this.page = {x: touch.pageX, y: touch.pageY};
|
|
this.client = {x: touch.clientX, y: touch.clientY};
|
|
}
|
|
}
|
|
|
|
if (!this.client) this.client = {};
|
|
if (!this.page) this.page = {};
|
|
});
|
|
|
|
DOMEvent.implement({
|
|
|
|
stop: function(){
|
|
return this.preventDefault().stopPropagation();
|
|
},
|
|
|
|
stopPropagation: function(){
|
|
if (this.event.stopPropagation) this.event.stopPropagation();
|
|
else this.event.cancelBubble = true;
|
|
return this;
|
|
},
|
|
|
|
preventDefault: function(){
|
|
if (this.event.preventDefault) this.event.preventDefault();
|
|
else this.event.returnValue = false;
|
|
return this;
|
|
}
|
|
|
|
});
|
|
|
|
DOMEvent.defineKey = function(code, key){
|
|
_keys[code] = key;
|
|
return this;
|
|
};
|
|
|
|
DOMEvent.defineKeys = DOMEvent.defineKey.overloadSetter(true);
|
|
|
|
DOMEvent.defineKeys({
|
|
'38': 'up', '40': 'down', '37': 'left', '39': 'right',
|
|
'27': 'esc', '32': 'space', '8': 'backspace', '9': 'tab',
|
|
'46': 'delete', '13': 'enter'
|
|
});
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Class
|
|
|
|
description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.
|
|
|
|
license: MIT-style license.
|
|
|
|
requires: [Array, String, Function, Number]
|
|
|
|
provides: Class
|
|
|
|
...
|
|
*/
|
|
|
|
(function(){
|
|
|
|
var Class = this.Class = new Type('Class', function(params){
|
|
if (instanceOf(params, Function)) params = {initialize: params};
|
|
|
|
var newClass = function(){
|
|
reset(this);
|
|
if (newClass.$prototyping) return this;
|
|
this.$caller = null;
|
|
var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
|
|
this.$caller = this.caller = null;
|
|
return value;
|
|
}.extend(this).implement(params);
|
|
|
|
newClass.$constructor = Class;
|
|
newClass.prototype.$constructor = newClass;
|
|
newClass.prototype.parent = parent;
|
|
|
|
return newClass;
|
|
});
|
|
|
|
var parent = function(){
|
|
if (!this.$caller) throw new Error('The method "parent" cannot be called.');
|
|
var name = this.$caller.$name,
|
|
parent = this.$caller.$owner.parent,
|
|
previous = (parent) ? parent.prototype[name] : null;
|
|
if (!previous) throw new Error('The method "' + name + '" has no parent.');
|
|
return previous.apply(this, arguments);
|
|
};
|
|
|
|
var reset = function(object){
|
|
for (var key in object){
|
|
var value = object[key];
|
|
switch (typeOf(value)){
|
|
case 'object':
|
|
var F = function(){};
|
|
F.prototype = value;
|
|
object[key] = reset(new F);
|
|
break;
|
|
case 'array': object[key] = value.clone(); break;
|
|
}
|
|
}
|
|
return object;
|
|
};
|
|
|
|
var wrap = function(self, key, method){
|
|
if (method.$origin) method = method.$origin;
|
|
var wrapper = function(){
|
|
if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.');
|
|
var caller = this.caller, current = this.$caller;
|
|
this.caller = current; this.$caller = wrapper;
|
|
var result = method.apply(this, arguments);
|
|
this.$caller = current; this.caller = caller;
|
|
return result;
|
|
}.extend({$owner: self, $origin: method, $name: key});
|
|
return wrapper;
|
|
};
|
|
|
|
var implement = function(key, value, retain){
|
|
if (Class.Mutators.hasOwnProperty(key)){
|
|
value = Class.Mutators[key].call(this, value);
|
|
if (value == null) return this;
|
|
}
|
|
|
|
if (typeOf(value) == 'function'){
|
|
if (value.$hidden) return this;
|
|
this.prototype[key] = (retain) ? value : wrap(this, key, value);
|
|
} else {
|
|
Object.merge(this.prototype, key, value);
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
var getInstance = function(klass){
|
|
klass.$prototyping = true;
|
|
var proto = new klass;
|
|
delete klass.$prototyping;
|
|
return proto;
|
|
};
|
|
|
|
Class.implement('implement', implement.overloadSetter());
|
|
|
|
Class.Mutators = {
|
|
|
|
Extends: function(parent){
|
|
this.parent = parent;
|
|
this.prototype = getInstance(parent);
|
|
},
|
|
|
|
Implements: function(items){
|
|
Array.from(items).each(function(item){
|
|
var instance = new item;
|
|
for (var key in instance) implement.call(this, key, instance[key], true);
|
|
}, this);
|
|
}
|
|
};
|
|
|
|
})();
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Class.Extras
|
|
|
|
description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
|
|
|
|
license: MIT-style license.
|
|
|
|
requires: Class
|
|
|
|
provides: [Class.Extras, Chain, Events, Options]
|
|
|
|
...
|
|
*/
|
|
|
|
(function(){
|
|
|
|
this.Chain = new Class({
|
|
|
|
$chain: [],
|
|
|
|
chain: function(){
|
|
this.$chain.append(Array.flatten(arguments));
|
|
return this;
|
|
},
|
|
|
|
callChain: function(){
|
|
return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
|
|
},
|
|
|
|
clearChain: function(){
|
|
this.$chain.empty();
|
|
return this;
|
|
}
|
|
|
|
});
|
|
|
|
var removeOn = function(string){
|
|
return string.replace(/^on([A-Z])/, function(full, first){
|
|
return first.toLowerCase();
|
|
});
|
|
};
|
|
|
|
this.Events = new Class({
|
|
|
|
$events: {},
|
|
|
|
addEvent: function(type, fn, internal){
|
|
type = removeOn(type);
|
|
|
|
|
|
|
|
this.$events[type] = (this.$events[type] || []).include(fn);
|
|
if (internal) fn.internal = true;
|
|
return this;
|
|
},
|
|
|
|
addEvents: function(events){
|
|
for (var type in events) this.addEvent(type, events[type]);
|
|
return this;
|
|
},
|
|
|
|
fireEvent: function(type, args, delay){
|
|
type = removeOn(type);
|
|
var events = this.$events[type];
|
|
if (!events) return this;
|
|
args = Array.from(args);
|
|
events.each(function(fn){
|
|
if (delay) fn.delay(delay, this, args);
|
|
else fn.apply(this, args);
|
|
}, this);
|
|
return this;
|
|
},
|
|
|
|
removeEvent: function(type, fn){
|
|
type = removeOn(type);
|
|
var events = this.$events[type];
|
|
if (events && !fn.internal){
|
|
var index = events.indexOf(fn);
|
|
if (index != -1) delete events[index];
|
|
}
|
|
return this;
|
|
},
|
|
|
|
removeEvents: function(events){
|
|
var type;
|
|
if (typeOf(events) == 'object'){
|
|
for (type in events) this.removeEvent(type, events[type]);
|
|
return this;
|
|
}
|
|
if (events) events = removeOn(events);
|
|
for (type in this.$events){
|
|
if (events && events != type) continue;
|
|
var fns = this.$events[type];
|
|
for (var i = fns.length; i--;) if (i in fns){
|
|
this.removeEvent(type, fns[i]);
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
});
|
|
|
|
this.Options = new Class({
|
|
|
|
setOptions: function(){
|
|
var options = this.options = Object.merge.apply(null, [{}, this.options].append(arguments));
|
|
if (this.addEvent) for (var option in options){
|
|
if (typeOf(options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
|
|
this.addEvent(option, options[option]);
|
|
delete options[option];
|
|
}
|
|
return this;
|
|
}
|
|
|
|
});
|
|
|
|
})();
|
|
|
|
|
|
/*
|
|
---
|
|
name: Slick.Parser
|
|
description: Standalone CSS3 Selector parser
|
|
provides: Slick.Parser
|
|
...
|
|
*/
|
|
|
|
;(function(){
|
|
|
|
var parsed,
|
|
separatorIndex,
|
|
combinatorIndex,
|
|
reversed,
|
|
cache = {},
|
|
reverseCache = {},
|
|
reUnescape = /\\/g;
|
|
|
|
var parse = function(expression, isReversed){
|
|
if (expression == null) return null;
|
|
if (expression.Slick === true) return expression;
|
|
expression = ('' + expression).replace(/^\s+|\s+$/g, '');
|
|
reversed = !!isReversed;
|
|
var currentCache = (reversed) ? reverseCache : cache;
|
|
if (currentCache[expression]) return currentCache[expression];
|
|
parsed = {
|
|
Slick: true,
|
|
expressions: [],
|
|
raw: expression,
|
|
reverse: function(){
|
|
return parse(this.raw, true);
|
|
}
|
|
};
|
|
separatorIndex = -1;
|
|
while (expression != (expression = expression.replace(regexp, parser)));
|
|
parsed.length = parsed.expressions.length;
|
|
return currentCache[parsed.raw] = (reversed) ? reverse(parsed) : parsed;
|
|
};
|
|
|
|
var reverseCombinator = function(combinator){
|
|
if (combinator === '!') return ' ';
|
|
else if (combinator === ' ') return '!';
|
|
else if ((/^!/).test(combinator)) return combinator.replace(/^!/, '');
|
|
else return '!' + combinator;
|
|
};
|
|
|
|
var reverse = function(expression){
|
|
var expressions = expression.expressions;
|
|
for (var i = 0; i < expressions.length; i++){
|
|
var exp = expressions[i];
|
|
var last = {parts: [], tag: '*', combinator: reverseCombinator(exp[0].combinator)};
|
|
|
|
for (var j = 0; j < exp.length; j++){
|
|
var cexp = exp[j];
|
|
if (!cexp.reverseCombinator) cexp.reverseCombinator = ' ';
|
|
cexp.combinator = cexp.reverseCombinator;
|
|
delete cexp.reverseCombinator;
|
|
}
|
|
|
|
exp.reverse().push(last);
|
|
}
|
|
return expression;
|
|
};
|
|
|
|
var escapeRegExp = function(string){// Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan <http://stevenlevithan.com/regex/xregexp/> MIT License
|
|
return string.replace(/[-[\]{}()*+?.\\^$|,#\s]/g, function(match){
|
|
return '\\' + match;
|
|
});
|
|
};
|
|
|
|
var regexp = new RegExp(
|
|
/*
|
|
#!/usr/bin/env ruby
|
|
puts "\t\t" + DATA.read.gsub(/\(\?x\)|\s+#.*$|\s+|\\$|\\n/,'')
|
|
__END__
|
|
"(?x)^(?:\
|
|
\\s* ( , ) \\s* # Separator \n\
|
|
| \\s* ( <combinator>+ ) \\s* # Combinator \n\
|
|
| ( \\s+ ) # CombinatorChildren \n\
|
|
| ( <unicode>+ | \\* ) # Tag \n\
|
|
| \\# ( <unicode>+ ) # ID \n\
|
|
| \\. ( <unicode>+ ) # ClassName \n\
|
|
| # Attribute \n\
|
|
\\[ \
|
|
\\s* (<unicode1>+) (?: \
|
|
\\s* ([*^$!~|]?=) (?: \
|
|
\\s* (?:\
|
|
([\"']?)(.*?)\\9 \
|
|
)\
|
|
) \
|
|
)? \\s* \
|
|
\\](?!\\]) \n\
|
|
| :+ ( <unicode>+ )(?:\
|
|
\\( (?:\
|
|
(?:([\"'])([^\\12]*)\\12)|((?:\\([^)]+\\)|[^()]*)+)\
|
|
) \\)\
|
|
)?\
|
|
)"
|
|
*/
|
|
"^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(<unicode>+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)"
|
|
.replace(/<combinator>/, '[' + escapeRegExp(">+~`!@$%^&={}\\;</") + ']')
|
|
.replace(/<unicode>/g, '(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
|
|
.replace(/<unicode1>/g, '(?:[:\\w\00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
|
|
);
|
|
|
|
function parser(
|
|
rawMatch,
|
|
|
|
separator,
|
|
combinator,
|
|
combinatorChildren,
|
|
|
|
tagName,
|
|
id,
|
|
className,
|
|
|
|
attributeKey,
|
|
attributeOperator,
|
|
attributeQuote,
|
|
attributeValue,
|
|
|
|
pseudoMarker,
|
|
pseudoClass,
|
|
pseudoQuote,
|
|
pseudoClassQuotedValue,
|
|
pseudoClassValue
|
|
){
|
|
if (separator || separatorIndex === -1){
|
|
parsed.expressions[++separatorIndex] = [];
|
|
combinatorIndex = -1;
|
|
if (separator) return '';
|
|
}
|
|
|
|
if (combinator || combinatorChildren || combinatorIndex === -1){
|
|
combinator = combinator || ' ';
|
|
var currentSeparator = parsed.expressions[separatorIndex];
|
|
if (reversed && currentSeparator[combinatorIndex])
|
|
currentSeparator[combinatorIndex].reverseCombinator = reverseCombinator(combinator);
|
|
currentSeparator[++combinatorIndex] = {combinator: combinator, tag: '*'};
|
|
}
|
|
|
|
var currentParsed = parsed.expressions[separatorIndex][combinatorIndex];
|
|
|
|
if (tagName){
|
|
currentParsed.tag = tagName.replace(reUnescape, '');
|
|
|
|
} else if (id){
|
|
currentParsed.id = id.replace(reUnescape, '');
|
|
|
|
} else if (className){
|
|
className = className.replace(reUnescape, '');
|
|
|
|
if (!currentParsed.classList) currentParsed.classList = [];
|
|
if (!currentParsed.classes) currentParsed.classes = [];
|
|
currentParsed.classList.push(className);
|
|
currentParsed.classes.push({
|
|
value: className,
|
|
regexp: new RegExp('(^|\\s)' + escapeRegExp(className) + '(\\s|$)')
|
|
});
|
|
|
|
} else if (pseudoClass){
|
|
pseudoClassValue = pseudoClassValue || pseudoClassQuotedValue;
|
|
pseudoClassValue = pseudoClassValue ? pseudoClassValue.replace(reUnescape, '') : null;
|
|
|
|
if (!currentParsed.pseudos) currentParsed.pseudos = [];
|
|
currentParsed.pseudos.push({
|
|
key: pseudoClass.replace(reUnescape, ''),
|
|
value: pseudoClassValue,
|
|
type: pseudoMarker.length == 1 ? 'class' : 'element'
|
|
});
|
|
|
|
} else if (attributeKey){
|
|
attributeKey = attributeKey.replace(reUnescape, '');
|
|
attributeValue = (attributeValue || '').replace(reUnescape, '');
|
|
|
|
var test, regexp;
|
|
|
|
switch (attributeOperator){
|
|
case '^=' : regexp = new RegExp( '^'+ escapeRegExp(attributeValue) ); break;
|
|
case '$=' : regexp = new RegExp( escapeRegExp(attributeValue) +'$' ); break;
|
|
case '~=' : regexp = new RegExp( '(^|\\s)'+ escapeRegExp(attributeValue) +'(\\s|$)' ); break;
|
|
case '|=' : regexp = new RegExp( '^'+ escapeRegExp(attributeValue) +'(-|$)' ); break;
|
|
case '=' : test = function(value){
|
|
return attributeValue == value;
|
|
}; break;
|
|
case '*=' : test = function(value){
|
|
return value && value.indexOf(attributeValue) > -1;
|
|
}; break;
|
|
case '!=' : test = function(value){
|
|
return attributeValue != value;
|
|
}; break;
|
|
default : test = function(value){
|
|
return !!value;
|
|
};
|
|
}
|
|
|
|
if (attributeValue == '' && (/^[*$^]=$/).test(attributeOperator)) test = function(){
|
|
return false;
|
|
};
|
|
|
|
if (!test) test = function(value){
|
|
return value && regexp.test(value);
|
|
};
|
|
|
|
if (!currentParsed.attributes) currentParsed.attributes = [];
|
|
currentParsed.attributes.push({
|
|
key: attributeKey,
|
|
operator: attributeOperator,
|
|
value: attributeValue,
|
|
test: test
|
|
});
|
|
|
|
}
|
|
|
|
return '';
|
|
};
|
|
|
|
// Slick NS
|
|
|
|
var Slick = (this.Slick || {});
|
|
|
|
Slick.parse = function(expression){
|
|
return parse(expression);
|
|
};
|
|
|
|
Slick.escapeRegExp = escapeRegExp;
|
|
|
|
if (!this.Slick) this.Slick = Slick;
|
|
|
|
}).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
|
|
|
|
|
|
/*
|
|
---
|
|
name: Slick.Finder
|
|
description: The new, superfast css selector engine.
|
|
provides: Slick.Finder
|
|
requires: Slick.Parser
|
|
...
|
|
*/
|
|
|
|
;(function(){
|
|
|
|
var local = {},
|
|
featuresCache = {},
|
|
toString = Object.prototype.toString;
|
|
|
|
// Feature / Bug detection
|
|
|
|
local.isNativeCode = function(fn){
|
|
return (/\{\s*\[native code\]\s*\}/).test('' + fn);
|
|
};
|
|
|
|
local.isXML = function(document){
|
|
return (!!document.xmlVersion) || (!!document.xml) || (toString.call(document) == '[object XMLDocument]') ||
|
|
(document.nodeType == 9 && document.documentElement.nodeName != 'HTML');
|
|
};
|
|
|
|
local.setDocument = function(document){
|
|
|
|
// convert elements / window arguments to document. if document cannot be extrapolated, the function returns.
|
|
var nodeType = document.nodeType;
|
|
if (nodeType == 9); // document
|
|
else if (nodeType) document = document.ownerDocument; // node
|
|
else if (document.navigator) document = document.document; // window
|
|
else return;
|
|
|
|
// check if it's the old document
|
|
|
|
if (this.document === document) return;
|
|
this.document = document;
|
|
|
|
// check if we have done feature detection on this document before
|
|
|
|
var root = document.documentElement,
|
|
rootUid = this.getUIDXML(root),
|
|
features = featuresCache[rootUid],
|
|
feature;
|
|
|
|
if (features){
|
|
for (feature in features){
|
|
this[feature] = features[feature];
|
|
}
|
|
return;
|
|
}
|
|
|
|
features = featuresCache[rootUid] = {};
|
|
|
|
features.root = root;
|
|
features.isXMLDocument = this.isXML(document);
|
|
|
|
features.brokenStarGEBTN
|
|
= features.starSelectsClosedQSA
|
|
= features.idGetsName
|
|
= features.brokenMixedCaseQSA
|
|
= features.brokenGEBCN
|
|
= features.brokenCheckedQSA
|
|
= features.brokenEmptyAttributeQSA
|
|
= features.isHTMLDocument
|
|
= features.nativeMatchesSelector
|
|
= false;
|
|
|
|
var starSelectsClosed, starSelectsComments,
|
|
brokenSecondClassNameGEBCN, cachedGetElementsByClassName,
|
|
brokenFormAttributeGetter;
|
|
|
|
var selected, id = 'slick_uniqueid';
|
|
var testNode = document.createElement('div');
|
|
|
|
var testRoot = document.body || document.getElementsByTagName('body')[0] || root;
|
|
testRoot.appendChild(testNode);
|
|
|
|
// on non-HTML documents innerHTML and getElementsById doesnt work properly
|
|
try {
|
|
testNode.innerHTML = '<a id="'+id+'"></a>';
|
|
features.isHTMLDocument = !!document.getElementById(id);
|
|
} catch(e){};
|
|
|
|
if (features.isHTMLDocument){
|
|
|
|
testNode.style.display = 'none';
|
|
|
|
// IE returns comment nodes for getElementsByTagName('*') for some documents
|
|
testNode.appendChild(document.createComment(''));
|
|
starSelectsComments = (testNode.getElementsByTagName('*').length > 1);
|
|
|
|
// IE returns closed nodes (EG:"</foo>") for getElementsByTagName('*') for some documents
|
|
try {
|
|
testNode.innerHTML = 'foo</foo>';
|
|
selected = testNode.getElementsByTagName('*');
|
|
starSelectsClosed = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
|
|
} catch(e){};
|
|
|
|
features.brokenStarGEBTN = starSelectsComments || starSelectsClosed;
|
|
|
|
// IE returns elements with the name instead of just id for getElementsById for some documents
|
|
try {
|
|
testNode.innerHTML = '<a name="'+ id +'"></a><b id="'+ id +'"></b>';
|
|
features.idGetsName = document.getElementById(id) === testNode.firstChild;
|
|
} catch(e){};
|
|
|
|
if (testNode.getElementsByClassName){
|
|
|
|
// Safari 3.2 getElementsByClassName caches results
|
|
try {
|
|
testNode.innerHTML = '<a class="f"></a><a class="b"></a>';
|
|
testNode.getElementsByClassName('b').length;
|
|
testNode.firstChild.className = 'b';
|
|
cachedGetElementsByClassName = (testNode.getElementsByClassName('b').length != 2);
|
|
} catch(e){};
|
|
|
|
// Opera 9.6 getElementsByClassName doesnt detects the class if its not the first one
|
|
try {
|
|
testNode.innerHTML = '<a class="a"></a><a class="f b a"></a>';
|
|
brokenSecondClassNameGEBCN = (testNode.getElementsByClassName('a').length != 2);
|
|
} catch(e){};
|
|
|
|
features.brokenGEBCN = cachedGetElementsByClassName || brokenSecondClassNameGEBCN;
|
|
}
|
|
|
|
if (testNode.querySelectorAll){
|
|
// IE 8 returns closed nodes (EG:"</foo>") for querySelectorAll('*') for some documents
|
|
try {
|
|
testNode.innerHTML = 'foo</foo>';
|
|
selected = testNode.querySelectorAll('*');
|
|
features.starSelectsClosedQSA = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
|
|
} catch(e){};
|
|
|
|
// Safari 3.2 querySelectorAll doesnt work with mixedcase on quirksmode
|
|
try {
|
|
testNode.innerHTML = '<a class="MiX"></a>';
|
|
features.brokenMixedCaseQSA = !testNode.querySelectorAll('.MiX').length;
|
|
} catch(e){};
|
|
|
|
// Webkit and Opera dont return selected options on querySelectorAll
|
|
try {
|
|
testNode.innerHTML = '<select><option selected="selected">a</option></select>';
|
|
features.brokenCheckedQSA = (testNode.querySelectorAll(':checked').length == 0);
|
|
} catch(e){};
|
|
|
|
// IE returns incorrect results for attr[*^$]="" selectors on querySelectorAll
|
|
try {
|
|
testNode.innerHTML = '<a class=""></a>';
|
|
features.brokenEmptyAttributeQSA = (testNode.querySelectorAll('[class*=""]').length != 0);
|
|
} catch(e){};
|
|
|
|
}
|
|
|
|
// IE6-7, if a form has an input of id x, form.getAttribute(x) returns a reference to the input
|
|
try {
|
|
testNode.innerHTML = '<form action="s"><input id="action"/></form>';
|
|
brokenFormAttributeGetter = (testNode.firstChild.getAttribute('action') != 's');
|
|
} catch(e){};
|
|
|
|
// native matchesSelector function
|
|
|
|
features.nativeMatchesSelector = root.matchesSelector || /*root.msMatchesSelector ||*/ root.mozMatchesSelector || root.webkitMatchesSelector;
|
|
if (features.nativeMatchesSelector) try {
|
|
// if matchesSelector trows errors on incorrect sintaxes we can use it
|
|
features.nativeMatchesSelector.call(root, ':slick');
|
|
features.nativeMatchesSelector = null;
|
|
} catch(e){};
|
|
|
|
}
|
|
|
|
try {
|
|
root.slick_expando = 1;
|
|
delete root.slick_expando;
|
|
features.getUID = this.getUIDHTML;
|
|
} catch(e) {
|
|
features.getUID = this.getUIDXML;
|
|
}
|
|
|
|
testRoot.removeChild(testNode);
|
|
testNode = selected = testRoot = null;
|
|
|
|
// getAttribute
|
|
|
|
features.getAttribute = (features.isHTMLDocument && brokenFormAttributeGetter) ? function(node, name){
|
|
var method = this.attributeGetters[name];
|
|
if (method) return method.call(node);
|
|
var attributeNode = node.getAttributeNode(name);
|
|
return (attributeNode) ? attributeNode.nodeValue : null;
|
|
} : function(node, name){
|
|
var method = this.attributeGetters[name];
|
|
return (method) ? method.call(node) : node.getAttribute(name);
|
|
};
|
|
|
|
// hasAttribute
|
|
|
|
features.hasAttribute = (root && this.isNativeCode(root.hasAttribute)) ? function(node, attribute) {
|
|
return node.hasAttribute(attribute);
|
|
} : function(node, attribute) {
|
|
node = node.getAttributeNode(attribute);
|
|
return !!(node && (node.specified || node.nodeValue));
|
|
};
|
|
|
|
// contains
|
|
// FIXME: Add specs: local.contains should be different for xml and html documents?
|
|
var nativeRootContains = root && this.isNativeCode(root.contains),
|
|
nativeDocumentContains = document && this.isNativeCode(document.contains);
|
|
|
|
features.contains = (nativeRootContains && nativeDocumentContains) ? function(context, node){
|
|
return context.contains(node);
|
|
} : (nativeRootContains && !nativeDocumentContains) ? function(context, node){
|
|
// IE8 does not have .contains on document.
|
|
return context === node || ((context === document) ? document.documentElement : context).contains(node);
|
|
} : (root && root.compareDocumentPosition) ? function(context, node){
|
|
return context === node || !!(context.compareDocumentPosition(node) & 16);
|
|
} : function(context, node){
|
|
if (node) do {
|
|
if (node === context) return true;
|
|
} while ((node = node.parentNode));
|
|
return false;
|
|
};
|
|
|
|
// document order sorting
|
|
// credits to Sizzle (http://sizzlejs.com/)
|
|
|
|
features.documentSorter = (root.compareDocumentPosition) ? function(a, b){
|
|
if (!a.compareDocumentPosition || !b.compareDocumentPosition) return 0;
|
|
return a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
|
|
} : ('sourceIndex' in root) ? function(a, b){
|
|
if (!a.sourceIndex || !b.sourceIndex) return 0;
|
|
return a.sourceIndex - b.sourceIndex;
|
|
} : (document.createRange) ? function(a, b){
|
|
if (!a.ownerDocument || !b.ownerDocument) return 0;
|
|
var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
|
|
aRange.setStart(a, 0);
|
|
aRange.setEnd(a, 0);
|
|
bRange.setStart(b, 0);
|
|
bRange.setEnd(b, 0);
|
|
return aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
|
|
} : null ;
|
|
|
|
root = null;
|
|
|
|
for (feature in features){
|
|
this[feature] = features[feature];
|
|
}
|
|
};
|
|
|
|
// Main Method
|
|
|
|
var reSimpleSelector = /^([#.]?)((?:[\w-]+|\*))$/,
|
|
reEmptyAttribute = /\[.+[*$^]=(?:""|'')?\]/,
|
|
qsaFailExpCache = {};
|
|
|
|
local.search = function(context, expression, append, first){
|
|
|
|
var found = this.found = (first) ? null : (append || []);
|
|
|
|
if (!context) return found;
|
|
else if (context.navigator) context = context.document; // Convert the node from a window to a document
|
|
else if (!context.nodeType) return found;
|
|
|
|
// setup
|
|
|
|
var parsed, i,
|
|
uniques = this.uniques = {},
|
|
hasOthers = !!(append && append.length),
|
|
contextIsDocument = (context.nodeType == 9);
|
|
|
|
if (this.document !== (contextIsDocument ? context : context.ownerDocument)) this.setDocument(context);
|
|
|
|
// avoid duplicating items already in the append array
|
|
if (hasOthers) for (i = found.length; i--;) uniques[this.getUID(found[i])] = true;
|
|
|
|
// expression checks
|
|
|
|
if (typeof expression == 'string'){ // expression is a string
|
|
|
|
/*<simple-selectors-override>*/
|
|
var simpleSelector = expression.match(reSimpleSelector);
|
|
simpleSelectors: if (simpleSelector) {
|
|
|
|
var symbol = simpleSelector[1],
|
|
name = simpleSelector[2],
|
|
node, nodes;
|
|
|
|
if (!symbol){
|
|
|
|
if (name == '*' && this.brokenStarGEBTN) break simpleSelectors;
|
|
nodes = context.getElementsByTagName(name);
|
|
if (first) return nodes[0] || null;
|
|
for (i = 0; node = nodes[i++];){
|
|
if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
|
|
}
|
|
|
|
} else if (symbol == '#'){
|
|
|
|
if (!this.isHTMLDocument || !contextIsDocument) break simpleSelectors;
|
|
node = context.getElementById(name);
|
|
if (!node) return found;
|
|
if (this.idGetsName && node.getAttributeNode('id').nodeValue != name) break simpleSelectors;
|
|
if (first) return node || null;
|
|
if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
|
|
|
|
} else if (symbol == '.'){
|
|
|
|
if (!this.isHTMLDocument || ((!context.getElementsByClassName || this.brokenGEBCN) && context.querySelectorAll)) break simpleSelectors;
|
|
if (context.getElementsByClassName && !this.brokenGEBCN){
|
|
nodes = context.getElementsByClassName(name);
|
|
if (first) return nodes[0] || null;
|
|
for (i = 0; node = nodes[i++];){
|
|
if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
|
|
}
|
|
} else {
|
|
var matchClass = new RegExp('(^|\\s)'+ Slick.escapeRegExp(name) +'(\\s|$)');
|
|
nodes = context.getElementsByTagName('*');
|
|
for (i = 0; node = nodes[i++];){
|
|
className = node.className;
|
|
if (!(className && matchClass.test(className))) continue;
|
|
if (first) return node;
|
|
if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (hasOthers) this.sort(found);
|
|
return (first) ? null : found;
|
|
|
|
}
|
|
/*</simple-selectors-override>*/
|
|
|
|
/*<query-selector-override>*/
|
|
querySelector: if (context.querySelectorAll) {
|
|
|
|
if (!this.isHTMLDocument
|
|
|| qsaFailExpCache[expression]
|
|
//TODO: only skip when expression is actually mixed case
|
|
|| this.brokenMixedCaseQSA
|
|
|| (this.brokenCheckedQSA && expression.indexOf(':checked') > -1)
|
|
|| (this.brokenEmptyAttributeQSA && reEmptyAttribute.test(expression))
|
|
|| (!contextIsDocument //Abort when !contextIsDocument and...
|
|
// there are multiple expressions in the selector
|
|
// since we currently only fix non-document rooted QSA for single expression selectors
|
|
&& expression.indexOf(',') > -1
|
|
)
|
|
|| Slick.disableQSA
|
|
) break querySelector;
|
|
|
|
var _expression = expression, _context = context;
|
|
if (!contextIsDocument){
|
|
// non-document rooted QSA
|
|
// credits to Andrew Dupont
|
|
var currentId = _context.getAttribute('id'), slickid = 'slickid__';
|
|
_context.setAttribute('id', slickid);
|
|
_expression = '#' + slickid + ' ' + _expression;
|
|
context = _context.parentNode;
|
|
}
|
|
|
|
try {
|
|
if (first) return context.querySelector(_expression) || null;
|
|
else nodes = context.querySelectorAll(_expression);
|
|
} catch(e) {
|
|
qsaFailExpCache[expression] = 1;
|
|
break querySelector;
|
|
} finally {
|
|
if (!contextIsDocument){
|
|
if (currentId) _context.setAttribute('id', currentId);
|
|
else _context.removeAttribute('id');
|
|
context = _context;
|
|
}
|
|
}
|
|
|
|
if (this.starSelectsClosedQSA) for (i = 0; node = nodes[i++];){
|
|
if (node.nodeName > '@' && !(hasOthers && uniques[this.getUID(node)])) found.push(node);
|
|
} else for (i = 0; node = nodes[i++];){
|
|
if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
|
|
}
|
|
|
|
if (hasOthers) this.sort(found);
|
|
return found;
|
|
|
|
}
|
|
/*</query-selector-override>*/
|
|
|
|
parsed = this.Slick.parse(expression);
|
|
if (!parsed.length) return found;
|
|
} else if (expression == null){ // there is no expression
|
|
return found;
|
|
} else if (expression.Slick){ // expression is a parsed Slick object
|
|
parsed = expression;
|
|
} else if (this.contains(context.documentElement || context, expression)){ // expression is a node
|
|
(found) ? found.push(expression) : found = expression;
|
|
return found;
|
|
} else { // other junk
|
|
return found;
|
|
}
|
|
|
|
/*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
|
|
|
|
// cache elements for the nth selectors
|
|
|
|
this.posNTH = {};
|
|
this.posNTHLast = {};
|
|
this.posNTHType = {};
|
|
this.posNTHTypeLast = {};
|
|
|
|
/*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
|
|
|
|
// if append is null and there is only a single selector with one expression use pushArray, else use pushUID
|
|
this.push = (!hasOthers && (first || (parsed.length == 1 && parsed.expressions[0].length == 1))) ? this.pushArray : this.pushUID;
|
|
|
|
if (found == null) found = [];
|
|
|
|
// default engine
|
|
|
|
var j, m, n;
|
|
var combinator, tag, id, classList, classes, attributes, pseudos;
|
|
var currentItems, currentExpression, currentBit, lastBit, expressions = parsed.expressions;
|
|
|
|
search: for (i = 0; (currentExpression = expressions[i]); i++) for (j = 0; (currentBit = currentExpression[j]); j++){
|
|
|
|
combinator = 'combinator:' + currentBit.combinator;
|
|
if (!this[combinator]) continue search;
|
|
|
|
tag = (this.isXMLDocument) ? currentBit.tag : currentBit.tag.toUpperCase();
|
|
id = currentBit.id;
|
|
classList = currentBit.classList;
|
|
classes = currentBit.classes;
|
|
attributes = currentBit.attributes;
|
|
pseudos = currentBit.pseudos;
|
|
lastBit = (j === (currentExpression.length - 1));
|
|
|
|
this.bitUniques = {};
|
|
|
|
if (lastBit){
|
|
this.uniques = uniques;
|
|
this.found = found;
|
|
} else {
|
|
this.uniques = {};
|
|
this.found = [];
|
|
}
|
|
|
|
if (j === 0){
|
|
this[combinator](context, tag, id, classes, attributes, pseudos, classList);
|
|
if (first && lastBit && found.length) break search;
|
|
} else {
|
|
if (first && lastBit) for (m = 0, n = currentItems.length; m < n; m++){
|
|
this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
|
|
if (found.length) break search;
|
|
} else for (m = 0, n = currentItems.length; m < n; m++) this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
|
|
}
|
|
|
|
currentItems = this.found;
|
|
}
|
|
|
|
// should sort if there are nodes in append and if you pass multiple expressions.
|
|
if (hasOthers || (parsed.expressions.length > 1)) this.sort(found);
|
|
|
|
return (first) ? (found[0] || null) : found;
|
|
};
|
|
|
|
// Utils
|
|
|
|
local.uidx = 1;
|
|
local.uidk = 'slick-uniqueid';
|
|
|
|
local.getUIDXML = function(node){
|
|
var uid = node.getAttribute(this.uidk);
|
|
if (!uid){
|
|
uid = this.uidx++;
|
|
node.setAttribute(this.uidk, uid);
|
|
}
|
|
return uid;
|
|
};
|
|
|
|
local.getUIDHTML = function(node){
|
|
return node.uniqueNumber || (node.uniqueNumber = this.uidx++);
|
|
};
|
|
|
|
// sort based on the setDocument documentSorter method.
|
|
|
|
local.sort = function(results){
|
|
if (!this.documentSorter) return results;
|
|
results.sort(this.documentSorter);
|
|
return results;
|
|
};
|
|
|
|
/*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
|
|
|
|
local.cacheNTH = {};
|
|
|
|
local.matchNTH = /^([+-]?\d*)?([a-z]+)?([+-]\d+)?$/;
|
|
|
|
local.parseNTHArgument = function(argument){
|
|
var parsed = argument.match(this.matchNTH);
|
|
if (!parsed) return false;
|
|
var special = parsed[2] || false;
|
|
var a = parsed[1] || 1;
|
|
if (a == '-') a = -1;
|
|
var b = +parsed[3] || 0;
|
|
parsed =
|
|
(special == 'n') ? {a: a, b: b} :
|
|
(special == 'odd') ? {a: 2, b: 1} :
|
|
(special == 'even') ? {a: 2, b: 0} : {a: 0, b: a};
|
|
|
|
return (this.cacheNTH[argument] = parsed);
|
|
};
|
|
|
|
local.createNTHPseudo = function(child, sibling, positions, ofType){
|
|
return function(node, argument){
|
|
var uid = this.getUID(node);
|
|
if (!this[positions][uid]){
|
|
var parent = node.parentNode;
|
|
if (!parent) return false;
|
|
var el = parent[child], count = 1;
|
|
if (ofType){
|
|
var nodeName = node.nodeName;
|
|
do {
|
|
if (el.nodeName != nodeName) continue;
|
|
this[positions][this.getUID(el)] = count++;
|
|
} while ((el = el[sibling]));
|
|
} else {
|
|
do {
|
|
if (el.nodeType != 1) continue;
|
|
this[positions][this.getUID(el)] = count++;
|
|
} while ((el = el[sibling]));
|
|
}
|
|
}
|
|
argument = argument || 'n';
|
|
var parsed = this.cacheNTH[argument] || this.parseNTHArgument(argument);
|
|
if (!parsed) return false;
|
|
var a = parsed.a, b = parsed.b, pos = this[positions][uid];
|
|
if (a == 0) return b == pos;
|
|
if (a > 0){
|
|
if (pos < b) return false;
|
|
} else {
|
|
if (b < pos) return false;
|
|
}
|
|
return ((pos - b) % a) == 0;
|
|
};
|
|
};
|
|
|
|
/*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
|
|
|
|
local.pushArray = function(node, tag, id, classes, attributes, pseudos){
|
|
if (this.matchSelector(node, tag, id, classes, attributes, pseudos)) this.found.push(node);
|
|
};
|
|
|
|
local.pushUID = function(node, tag, id, classes, attributes, pseudos){
|
|
var uid = this.getUID(node);
|
|
if (!this.uniques[uid] && this.matchSelector(node, tag, id, classes, attributes, pseudos)){
|
|
this.uniques[uid] = true;
|
|
this.found.push(node);
|
|
}
|
|
};
|
|
|
|
local.matchNode = function(node, selector){
|
|
if (this.isHTMLDocument && this.nativeMatchesSelector){
|
|
try {
|
|
return this.nativeMatchesSelector.call(node, selector.replace(/\[([^=]+)=\s*([^'"\]]+?)\s*\]/g, '[$1="$2"]'));
|
|
} catch(matchError) {}
|
|
}
|
|
|
|
var parsed = this.Slick.parse(selector);
|
|
if (!parsed) return true;
|
|
|
|
// simple (single) selectors
|
|
var expressions = parsed.expressions, simpleExpCounter = 0, i;
|
|
for (i = 0; (currentExpression = expressions[i]); i++){
|
|
if (currentExpression.length == 1){
|
|
var exp = currentExpression[0];
|
|
if (this.matchSelector(node, (this.isXMLDocument) ? exp.tag : exp.tag.toUpperCase(), exp.id, exp.classes, exp.attributes, exp.pseudos)) return true;
|
|
simpleExpCounter++;
|
|
}
|
|
}
|
|
|
|
if (simpleExpCounter == parsed.length) return false;
|
|
|
|
var nodes = this.search(this.document, parsed), item;
|
|
for (i = 0; item = nodes[i++];){
|
|
if (item === node) return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
local.matchPseudo = function(node, name, argument){
|
|
var pseudoName = 'pseudo:' + name;
|
|
if (this[pseudoName]) return this[pseudoName](node, argument);
|
|
var attribute = this.getAttribute(node, name);
|
|
return (argument) ? argument == attribute : !!attribute;
|
|
};
|
|
|
|
local.matchSelector = function(node, tag, id, classes, attributes, pseudos){
|
|
if (tag){
|
|
var nodeName = (this.isXMLDocument) ? node.nodeName : node.nodeName.toUpperCase();
|
|
if (tag == '*'){
|
|
if (nodeName < '@') return false; // Fix for comment nodes and closed nodes
|
|
} else {
|
|
if (nodeName != tag) return false;
|
|
}
|
|
}
|
|
|
|
if (id && node.getAttribute('id') != id) return false;
|
|
|
|
var i, part, cls;
|
|
if (classes) for (i = classes.length; i--;){
|
|
cls = this.getAttribute(node, 'class');
|
|
if (!(cls && classes[i].regexp.test(cls))) return false;
|
|
}
|
|
if (attributes) for (i = attributes.length; i--;){
|
|
part = attributes[i];
|
|
if (part.operator ? !part.test(this.getAttribute(node, part.key)) : !this.hasAttribute(node, part.key)) return false;
|
|
}
|
|
if (pseudos) for (i = pseudos.length; i--;){
|
|
part = pseudos[i];
|
|
if (!this.matchPseudo(node, part.key, part.value)) return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
var combinators = {
|
|
|
|
' ': function(node, tag, id, classes, attributes, pseudos, classList){ // all child nodes, any level
|
|
|
|
var i, item, children;
|
|
|
|
if (this.isHTMLDocument){
|
|
getById: if (id){
|
|
item = this.document.getElementById(id);
|
|
if ((!item && node.all) || (this.idGetsName && item && item.getAttributeNode('id').nodeValue != id)){
|
|
// all[id] returns all the elements with that name or id inside node
|
|
// if theres just one it will return the element, else it will be a collection
|
|
children = node.all[id];
|
|
if (!children) return;
|
|
if (!children[0]) children = [children];
|
|
for (i = 0; item = children[i++];){
|
|
var idNode = item.getAttributeNode('id');
|
|
if (idNode && idNode.nodeValue == id){
|
|
this.push(item, tag, null, classes, attributes, pseudos);
|
|
break;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
if (!item){
|
|
// if the context is in the dom we return, else we will try GEBTN, breaking the getById label
|
|
if (this.contains(this.root, node)) return;
|
|
else break getById;
|
|
} else if (this.document !== node && !this.contains(node, item)) return;
|
|
this.push(item, tag, null, classes, attributes, pseudos);
|
|
return;
|
|
}
|
|
getByClass: if (classes && node.getElementsByClassName && !this.brokenGEBCN){
|
|
children = node.getElementsByClassName(classList.join(' '));
|
|
if (!(children && children.length)) break getByClass;
|
|
for (i = 0; item = children[i++];) this.push(item, tag, id, null, attributes, pseudos);
|
|
return;
|
|
}
|
|
}
|
|
getByTag: {
|
|
children = node.getElementsByTagName(tag);
|
|
if (!(children && children.length)) break getByTag;
|
|
if (!this.brokenStarGEBTN) tag = null;
|
|
for (i = 0; item = children[i++];) this.push(item, tag, id, classes, attributes, pseudos);
|
|
}
|
|
},
|
|
|
|
'>': function(node, tag, id, classes, attributes, pseudos){ // direct children
|
|
if ((node = node.firstChild)) do {
|
|
if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
|
|
} while ((node = node.nextSibling));
|
|
},
|
|
|
|
'+': function(node, tag, id, classes, attributes, pseudos){ // next sibling
|
|
while ((node = node.nextSibling)) if (node.nodeType == 1){
|
|
this.push(node, tag, id, classes, attributes, pseudos);
|
|
break;
|
|
}
|
|
},
|
|
|
|
'^': function(node, tag, id, classes, attributes, pseudos){ // first child
|
|
node = node.firstChild;
|
|
if (node){
|
|
if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
|
|
else this['combinator:+'](node, tag, id, classes, attributes, pseudos);
|
|
}
|
|
},
|
|
|
|
'~': function(node, tag, id, classes, attributes, pseudos){ // next siblings
|
|
while ((node = node.nextSibling)){
|
|
if (node.nodeType != 1) continue;
|
|
var uid = this.getUID(node);
|
|
if (this.bitUniques[uid]) break;
|
|
this.bitUniques[uid] = true;
|
|
this.push(node, tag, id, classes, attributes, pseudos);
|
|
}
|
|
},
|
|
|
|
'++': function(node, tag, id, classes, attributes, pseudos){ // next sibling and previous sibling
|
|
this['combinator:+'](node, tag, id, classes, attributes, pseudos);
|
|
this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
|
|
},
|
|
|
|
'~~': function(node, tag, id, classes, attributes, pseudos){ // next siblings and previous siblings
|
|
this['combinator:~'](node, tag, id, classes, attributes, pseudos);
|
|
this['combinator:!~'](node, tag, id, classes, attributes, pseudos);
|
|
},
|
|
|
|
'!': function(node, tag, id, classes, attributes, pseudos){ // all parent nodes up to document
|
|
while ((node = node.parentNode)) if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
|
|
},
|
|
|
|
'!>': function(node, tag, id, classes, attributes, pseudos){ // direct parent (one level)
|
|
node = node.parentNode;
|
|
if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
|
|
},
|
|
|
|
'!+': function(node, tag, id, classes, attributes, pseudos){ // previous sibling
|
|
while ((node = node.previousSibling)) if (node.nodeType == 1){
|
|
this.push(node, tag, id, classes, attributes, pseudos);
|
|
break;
|
|
}
|
|
},
|
|
|
|
'!^': function(node, tag, id, classes, attributes, pseudos){ // last child
|
|
node = node.lastChild;
|
|
if (node){
|
|
if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
|
|
else this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
|
|
}
|
|
},
|
|
|
|
'!~': function(node, tag, id, classes, attributes, pseudos){ // previous siblings
|
|
while ((node = node.previousSibling)){
|
|
if (node.nodeType != 1) continue;
|
|
var uid = this.getUID(node);
|
|
if (this.bitUniques[uid]) break;
|
|
this.bitUniques[uid] = true;
|
|
this.push(node, tag, id, classes, attributes, pseudos);
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
for (var c in combinators) local['combinator:' + c] = combinators[c];
|
|
|
|
var pseudos = {
|
|
|
|
/*<pseudo-selectors>*/
|
|
|
|
'empty': function(node){
|
|
var child = node.firstChild;
|
|
return !(child && child.nodeType == 1) && !(node.innerText || node.textContent || '').length;
|
|
},
|
|
|
|
'not': function(node, expression){
|
|
return !this.matchNode(node, expression);
|
|
},
|
|
|
|
'contains': function(node, text){
|
|
return (node.innerText || node.textContent || '').indexOf(text) > -1;
|
|
},
|
|
|
|
'first-child': function(node){
|
|
while ((node = node.previousSibling)) if (node.nodeType == 1) return false;
|
|
return true;
|
|
},
|
|
|
|
'last-child': function(node){
|
|
while ((node = node.nextSibling)) if (node.nodeType == 1) return false;
|
|
return true;
|
|
},
|
|
|
|
'only-child': function(node){
|
|
var prev = node;
|
|
while ((prev = prev.previousSibling)) if (prev.nodeType == 1) return false;
|
|
var next = node;
|
|
while ((next = next.nextSibling)) if (next.nodeType == 1) return false;
|
|
return true;
|
|
},
|
|
|
|
/*<nth-pseudo-selectors>*/
|
|
|
|
'nth-child': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTH'),
|
|
|
|
'nth-last-child': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHLast'),
|
|
|
|
'nth-of-type': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTHType', true),
|
|
|
|
'nth-last-of-type': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHTypeLast', true),
|
|
|
|
'index': function(node, index){
|
|
return this['pseudo:nth-child'](node, '' + (index + 1));
|
|
},
|
|
|
|
'even': function(node){
|
|
return this['pseudo:nth-child'](node, '2n');
|
|
},
|
|
|
|
'odd': function(node){
|
|
return this['pseudo:nth-child'](node, '2n+1');
|
|
},
|
|
|
|
/*</nth-pseudo-selectors>*/
|
|
|
|
/*<of-type-pseudo-selectors>*/
|
|
|
|
'first-of-type': function(node){
|
|
var nodeName = node.nodeName;
|
|
while ((node = node.previousSibling)) if (node.nodeName == nodeName) return false;
|
|
return true;
|
|
},
|
|
|
|
'last-of-type': function(node){
|
|
var nodeName = node.nodeName;
|
|
while ((node = node.nextSibling)) if (node.nodeName == nodeName) return false;
|
|
return true;
|
|
},
|
|
|
|
'only-of-type': function(node){
|
|
var prev = node, nodeName = node.nodeName;
|
|
while ((prev = prev.previousSibling)) if (prev.nodeName == nodeName) return false;
|
|
var next = node;
|
|
while ((next = next.nextSibling)) if (next.nodeName == nodeName) return false;
|
|
return true;
|
|
},
|
|
|
|
/*</of-type-pseudo-selectors>*/
|
|
|
|
// custom pseudos
|
|
|
|
'enabled': function(node){
|
|
return !node.disabled;
|
|
},
|
|
|
|
'disabled': function(node){
|
|
return node.disabled;
|
|
},
|
|
|
|
'checked': function(node){
|
|
return node.checked || node.selected;
|
|
},
|
|
|
|
'focus': function(node){
|
|
return this.isHTMLDocument && this.document.activeElement === node && (node.href || node.type || this.hasAttribute(node, 'tabindex'));
|
|
},
|
|
|
|
'root': function(node){
|
|
return (node === this.root);
|
|
},
|
|
|
|
'selected': function(node){
|
|
return node.selected;
|
|
}
|
|
|
|
/*</pseudo-selectors>*/
|
|
};
|
|
|
|
for (var p in pseudos) local['pseudo:' + p] = pseudos[p];
|
|
|
|
// attributes methods
|
|
|
|
var attributeGetters = local.attributeGetters = {
|
|
|
|
'for': function(){
|
|
return ('htmlFor' in this) ? this.htmlFor : this.getAttribute('for');
|
|
},
|
|
|
|
'href': function(){
|
|
return ('href' in this) ? this.getAttribute('href', 2) : this.getAttribute('href');
|
|
},
|
|
|
|
'style': function(){
|
|
return (this.style) ? this.style.cssText : this.getAttribute('style');
|
|
},
|
|
|
|
'tabindex': function(){
|
|
var attributeNode = this.getAttributeNode('tabindex');
|
|
return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
|
|
},
|
|
|
|
'type': function(){
|
|
return this.getAttribute('type');
|
|
},
|
|
|
|
'maxlength': function(){
|
|
var attributeNode = this.getAttributeNode('maxLength');
|
|
return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
|
|
}
|
|
|
|
};
|
|
|
|
attributeGetters.MAXLENGTH = attributeGetters.maxLength = attributeGetters.maxlength;
|
|
|
|
// Slick
|
|
|
|
var Slick = local.Slick = (this.Slick || {});
|
|
|
|
Slick.version = '1.1.7';
|
|
|
|
// Slick finder
|
|
|
|
Slick.search = function(context, expression, append){
|
|
return local.search(context, expression, append);
|
|
};
|
|
|
|
Slick.find = function(context, expression){
|
|
return local.search(context, expression, null, true);
|
|
};
|
|
|
|
// Slick containment checker
|
|
|
|
Slick.contains = function(container, node){
|
|
local.setDocument(container);
|
|
return local.contains(container, node);
|
|
};
|
|
|
|
// Slick attribute getter
|
|
|
|
Slick.getAttribute = function(node, name){
|
|
local.setDocument(node);
|
|
return local.getAttribute(node, name);
|
|
};
|
|
|
|
Slick.hasAttribute = function(node, name){
|
|
local.setDocument(node);
|
|
return local.hasAttribute(node, name);
|
|
};
|
|
|
|
// Slick matcher
|
|
|
|
Slick.match = function(node, selector){
|
|
if (!(node && selector)) return false;
|
|
if (!selector || selector === node) return true;
|
|
local.setDocument(node);
|
|
return local.matchNode(node, selector);
|
|
};
|
|
|
|
// Slick attribute accessor
|
|
|
|
Slick.defineAttributeGetter = function(name, fn){
|
|
local.attributeGetters[name] = fn;
|
|
return this;
|
|
};
|
|
|
|
Slick.lookupAttributeGetter = function(name){
|
|
return local.attributeGetters[name];
|
|
};
|
|
|
|
// Slick pseudo accessor
|
|
|
|
Slick.definePseudo = function(name, fn){
|
|
local['pseudo:' + name] = function(node, argument){
|
|
return fn.call(node, argument);
|
|
};
|
|
return this;
|
|
};
|
|
|
|
Slick.lookupPseudo = function(name){
|
|
var pseudo = local['pseudo:' + name];
|
|
if (pseudo) return function(argument){
|
|
return pseudo.call(this, argument);
|
|
};
|
|
return null;
|
|
};
|
|
|
|
// Slick overrides accessor
|
|
|
|
Slick.override = function(regexp, fn){
|
|
local.override(regexp, fn);
|
|
return this;
|
|
};
|
|
|
|
Slick.isXML = local.isXML;
|
|
|
|
Slick.uidOf = function(node){
|
|
return local.getUIDHTML(node);
|
|
};
|
|
|
|
if (!this.Slick) this.Slick = Slick;
|
|
|
|
}).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Element
|
|
|
|
description: One of the most important items in MooTools. Contains the dollar function, the dollars function, and an handful of cross-browser, time-saver methods to let you easily work with HTML Elements.
|
|
|
|
license: MIT-style license.
|
|
|
|
requires: [Window, Document, Array, String, Function, Object, Number, Slick.Parser, Slick.Finder]
|
|
|
|
provides: [Element, Elements, $, $$, Iframe, Selectors]
|
|
|
|
...
|
|
*/
|
|
|
|
var Element = function(tag, props){
|
|
var konstructor = Element.Constructors[tag];
|
|
if (konstructor) return konstructor(props);
|
|
if (typeof tag != 'string') return document.id(tag).set(props);
|
|
|
|
if (!props) props = {};
|
|
|
|
if (!(/^[\w-]+$/).test(tag)){
|
|
var parsed = Slick.parse(tag).expressions[0][0];
|
|
tag = (parsed.tag == '*') ? 'div' : parsed.tag;
|
|
if (parsed.id && props.id == null) props.id = parsed.id;
|
|
|
|
var attributes = parsed.attributes;
|
|
if (attributes) for (var attr, i = 0, l = attributes.length; i < l; i++){
|
|
attr = attributes[i];
|
|
if (props[attr.key] != null) continue;
|
|
|
|
if (attr.value != null && attr.operator == '=') props[attr.key] = attr.value;
|
|
else if (!attr.value && !attr.operator) props[attr.key] = true;
|
|
}
|
|
|
|
if (parsed.classList && props['class'] == null) props['class'] = parsed.classList.join(' ');
|
|
}
|
|
|
|
return document.newElement(tag, props);
|
|
};
|
|
|
|
|
|
if (Browser.Element){
|
|
Element.prototype = Browser.Element.prototype;
|
|
// IE8 and IE9 require the wrapping.
|
|
Element.prototype._fireEvent = (function(fireEvent){
|
|
return function(type, event){
|
|
return fireEvent.call(this, type, event);
|
|
};
|
|
})(Element.prototype.fireEvent);
|
|
}
|
|
|
|
new Type('Element', Element).mirror(function(name){
|
|
if (Array.prototype[name]) return;
|
|
|
|
var obj = {};
|
|
obj[name] = function(){
|
|
var results = [], args = arguments, elements = true;
|
|
for (var i = 0, l = this.length; i < l; i++){
|
|
var element = this[i], result = results[i] = element[name].apply(element, args);
|
|
elements = (elements && typeOf(result) == 'element');
|
|
}
|
|
return (elements) ? new Elements(results) : results;
|
|
};
|
|
|
|
Elements.implement(obj);
|
|
});
|
|
|
|
if (!Browser.Element){
|
|
Element.parent = Object;
|
|
|
|
Element.Prototype = {
|
|
'$constructor': Element,
|
|
'$family': Function.from('element').hide()
|
|
};
|
|
|
|
Element.mirror(function(name, method){
|
|
Element.Prototype[name] = method;
|
|
});
|
|
}
|
|
|
|
Element.Constructors = {};
|
|
|
|
|
|
|
|
var IFrame = new Type('IFrame', function(){
|
|
var params = Array.link(arguments, {
|
|
properties: Type.isObject,
|
|
iframe: function(obj){
|
|
return (obj != null);
|
|
}
|
|
});
|
|
|
|
var props = params.properties || {}, iframe;
|
|
if (params.iframe) iframe = document.id(params.iframe);
|
|
var onload = props.onload || function(){};
|
|
delete props.onload;
|
|
props.id = props.name = [props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + String.uniqueID()].pick();
|
|
iframe = new Element(iframe || 'iframe', props);
|
|
|
|
var onLoad = function(){
|
|
onload.call(iframe.contentWindow);
|
|
};
|
|
|
|
if (window.frames[props.id]) onLoad();
|
|
else iframe.addListener('load', onLoad);
|
|
return iframe;
|
|
});
|
|
|
|
var Elements = this.Elements = function(nodes){
|
|
if (nodes && nodes.length){
|
|
var uniques = {}, node;
|
|
for (var i = 0; node = nodes[i++];){
|
|
var uid = Slick.uidOf(node);
|
|
if (!uniques[uid]){
|
|
uniques[uid] = true;
|
|
this.push(node);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
Elements.prototype = {length: 0};
|
|
Elements.parent = Array;
|
|
|
|
new Type('Elements', Elements).implement({
|
|
|
|
filter: function(filter, bind){
|
|
if (!filter) return this;
|
|
return new Elements(Array.filter(this, (typeOf(filter) == 'string') ? function(item){
|
|
return item.match(filter);
|
|
} : filter, bind));
|
|
}.protect(),
|
|
|
|
push: function(){
|
|
var length = this.length;
|
|
for (var i = 0, l = arguments.length; i < l; i++){
|
|
var item = document.id(arguments[i]);
|
|
if (item) this[length++] = item;
|
|
}
|
|
return (this.length = length);
|
|
}.protect(),
|
|
|
|
unshift: function(){
|
|
var items = [];
|
|
for (var i = 0, l = arguments.length; i < l; i++){
|
|
var item = document.id(arguments[i]);
|
|
if (item) items.push(item);
|
|
}
|
|
return Array.prototype.unshift.apply(this, items);
|
|
}.protect(),
|
|
|
|
concat: function(){
|
|
var newElements = new Elements(this);
|
|
for (var i = 0, l = arguments.length; i < l; i++){
|
|
var item = arguments[i];
|
|
if (Type.isEnumerable(item)) newElements.append(item);
|
|
else newElements.push(item);
|
|
}
|
|
return newElements;
|
|
}.protect(),
|
|
|
|
append: function(collection){
|
|
for (var i = 0, l = collection.length; i < l; i++) this.push(collection[i]);
|
|
return this;
|
|
}.protect(),
|
|
|
|
empty: function(){
|
|
while (this.length) delete this[--this.length];
|
|
return this;
|
|
}.protect()
|
|
|
|
});
|
|
|
|
|
|
|
|
(function(){
|
|
|
|
// FF, IE
|
|
var splice = Array.prototype.splice, object = {'0': 0, '1': 1, length: 2};
|
|
|
|
splice.call(object, 1, 1);
|
|
if (object[1] == 1) Elements.implement('splice', function(){
|
|
var length = this.length;
|
|
var result = splice.apply(this, arguments);
|
|
while (length >= this.length) delete this[length--];
|
|
return result;
|
|
}.protect());
|
|
|
|
Array.forEachMethod(function(method, name){
|
|
Elements.implement(name, method);
|
|
});
|
|
|
|
Array.mirror(Elements);
|
|
|
|
/*<ltIE8>*/
|
|
var createElementAcceptsHTML;
|
|
try {
|
|
createElementAcceptsHTML = (document.createElement('<input name=x>').name == 'x');
|
|
} catch (e){}
|
|
|
|
var escapeQuotes = function(html){
|
|
return ('' + html).replace(/&/g, '&').replace(/"/g, '"');
|
|
};
|
|
/*</ltIE8>*/
|
|
|
|
Document.implement({
|
|
|
|
newElement: function(tag, props){
|
|
if (props && props.checked != null) props.defaultChecked = props.checked;
|
|
/*<ltIE8>*/// Fix for readonly name and type properties in IE < 8
|
|
if (createElementAcceptsHTML && props){
|
|
tag = '<' + tag;
|
|
if (props.name) tag += ' name="' + escapeQuotes(props.name) + '"';
|
|
if (props.type) tag += ' type="' + escapeQuotes(props.type) + '"';
|
|
tag += '>';
|
|
delete props.name;
|
|
delete props.type;
|
|
}
|
|
/*</ltIE8>*/
|
|
return this.id(this.createElement(tag)).set(props);
|
|
}
|
|
|
|
});
|
|
|
|
})();
|
|
|
|
(function(){
|
|
|
|
Slick.uidOf(window);
|
|
Slick.uidOf(document);
|
|
|
|
Document.implement({
|
|
|
|
newTextNode: function(text){
|
|
return this.createTextNode(text);
|
|
},
|
|
|
|
getDocument: function(){
|
|
return this;
|
|
},
|
|
|
|
getWindow: function(){
|
|
return this.window;
|
|
},
|
|
|
|
id: (function(){
|
|
|
|
var types = {
|
|
|
|
string: function(id, nocash, doc){
|
|
id = Slick.find(doc, '#' + id.replace(/(\W)/g, '\\$1'));
|
|
return (id) ? types.element(id, nocash) : null;
|
|
},
|
|
|
|
element: function(el, nocash){
|
|
Slick.uidOf(el);
|
|
if (!nocash && !el.$family && !(/^(?:object|embed)$/i).test(el.tagName)){
|
|
var fireEvent = el.fireEvent;
|
|
// wrapping needed in IE7, or else crash
|
|
el._fireEvent = function(type, event){
|
|
return fireEvent(type, event);
|
|
};
|
|
Object.append(el, Element.Prototype);
|
|
}
|
|
return el;
|
|
},
|
|
|
|
object: function(obj, nocash, doc){
|
|
if (obj.toElement) return types.element(obj.toElement(doc), nocash);
|
|
return null;
|
|
}
|
|
|
|
};
|
|
|
|
types.textnode = types.whitespace = types.window = types.document = function(zero){
|
|
return zero;
|
|
};
|
|
|
|
return function(el, nocash, doc){
|
|
if (el && el.$family && el.uniqueNumber) return el;
|
|
var type = typeOf(el);
|
|
return (types[type]) ? types[type](el, nocash, doc || document) : null;
|
|
};
|
|
|
|
})()
|
|
|
|
});
|
|
|
|
if (window.$ == null) Window.implement('$', function(el, nc){
|
|
return document.id(el, nc, this.document);
|
|
});
|
|
|
|
Window.implement({
|
|
|
|
getDocument: function(){
|
|
return this.document;
|
|
},
|
|
|
|
getWindow: function(){
|
|
return this;
|
|
}
|
|
|
|
});
|
|
|
|
[Document, Element].invoke('implement', {
|
|
|
|
getElements: function(expression){
|
|
return Slick.search(this, expression, new Elements);
|
|
},
|
|
|
|
getElement: function(expression){
|
|
return document.id(Slick.find(this, expression));
|
|
}
|
|
|
|
});
|
|
|
|
var contains = {contains: function(element){
|
|
return Slick.contains(this, element);
|
|
}};
|
|
|
|
if (!document.contains) Document.implement(contains);
|
|
if (!document.createElement('div').contains) Element.implement(contains);
|
|
|
|
|
|
|
|
// tree walking
|
|
|
|
var injectCombinator = function(expression, combinator){
|
|
if (!expression) return combinator;
|
|
|
|
expression = Object.clone(Slick.parse(expression));
|
|
|
|
var expressions = expression.expressions;
|
|
for (var i = expressions.length; i--;)
|
|
expressions[i][0].combinator = combinator;
|
|
|
|
return expression;
|
|
};
|
|
|
|
Object.forEach({
|
|
getNext: '~',
|
|
getPrevious: '!~',
|
|
getParent: '!'
|
|
}, function(combinator, method){
|
|
Element.implement(method, function(expression){
|
|
return this.getElement(injectCombinator(expression, combinator));
|
|
});
|
|
});
|
|
|
|
Object.forEach({
|
|
getAllNext: '~',
|
|
getAllPrevious: '!~',
|
|
getSiblings: '~~',
|
|
getChildren: '>',
|
|
getParents: '!'
|
|
}, function(combinator, method){
|
|
Element.implement(method, function(expression){
|
|
return this.getElements(injectCombinator(expression, combinator));
|
|
});
|
|
});
|
|
|
|
Element.implement({
|
|
|
|
getFirst: function(expression){
|
|
return document.id(Slick.search(this, injectCombinator(expression, '>'))[0]);
|
|
},
|
|
|
|
getLast: function(expression){
|
|
return document.id(Slick.search(this, injectCombinator(expression, '>')).getLast());
|
|
},
|
|
|
|
getWindow: function(){
|
|
return this.ownerDocument.window;
|
|
},
|
|
|
|
getDocument: function(){
|
|
return this.ownerDocument;
|
|
},
|
|
|
|
getElementById: function(id){
|
|
return document.id(Slick.find(this, '#' + ('' + id).replace(/(\W)/g, '\\$1')));
|
|
},
|
|
|
|
match: function(expression){
|
|
return !expression || Slick.match(this, expression);
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (window.$$ == null) Window.implement('$$', function(selector){
|
|
if (arguments.length == 1){
|
|
if (typeof selector == 'string') return Slick.search(this.document, selector, new Elements);
|
|
else if (Type.isEnumerable(selector)) return new Elements(selector);
|
|
}
|
|
return new Elements(arguments);
|
|
});
|
|
|
|
// Inserters
|
|
|
|
var inserters = {
|
|
|
|
before: function(context, element){
|
|
var parent = element.parentNode;
|
|
if (parent) parent.insertBefore(context, element);
|
|
},
|
|
|
|
after: function(context, element){
|
|
var parent = element.parentNode;
|
|
if (parent) parent.insertBefore(context, element.nextSibling);
|
|
},
|
|
|
|
bottom: function(context, element){
|
|
element.appendChild(context);
|
|
},
|
|
|
|
top: function(context, element){
|
|
element.insertBefore(context, element.firstChild);
|
|
}
|
|
|
|
};
|
|
|
|
inserters.inside = inserters.bottom;
|
|
|
|
|
|
|
|
// getProperty / setProperty
|
|
|
|
var propertyGetters = {}, propertySetters = {};
|
|
|
|
// properties
|
|
|
|
var properties = {};
|
|
Array.forEach([
|
|
'type', 'value', 'defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan',
|
|
'frameBorder', 'rowSpan', 'tabIndex', 'useMap'
|
|
], function(property){
|
|
properties[property.toLowerCase()] = property;
|
|
});
|
|
|
|
properties.html = 'innerHTML';
|
|
properties.text = (document.createElement('div').textContent == null) ? 'innerText': 'textContent';
|
|
|
|
Object.forEach(properties, function(real, key){
|
|
propertySetters[key] = function(node, value){
|
|
node[real] = value;
|
|
};
|
|
propertyGetters[key] = function(node){
|
|
return node[real];
|
|
};
|
|
});
|
|
|
|
// Booleans
|
|
|
|
var bools = [
|
|
'compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked',
|
|
'disabled', 'readOnly', 'multiple', 'selected', 'noresize',
|
|
'defer', 'defaultChecked', 'autofocus', 'controls', 'autoplay',
|
|
'loop'
|
|
];
|
|
|
|
var booleans = {};
|
|
Array.forEach(bools, function(bool){
|
|
var lower = bool.toLowerCase();
|
|
booleans[lower] = bool;
|
|
propertySetters[lower] = function(node, value){
|
|
node[bool] = !!value;
|
|
};
|
|
propertyGetters[lower] = function(node){
|
|
return !!node[bool];
|
|
};
|
|
});
|
|
|
|
// Special cases
|
|
|
|
Object.append(propertySetters, {
|
|
|
|
'class': function(node, value){
|
|
('className' in node) ? node.className = (value || '') : node.setAttribute('class', value);
|
|
},
|
|
|
|
'for': function(node, value){
|
|
('htmlFor' in node) ? node.htmlFor = value : node.setAttribute('for', value);
|
|
},
|
|
|
|
'style': function(node, value){
|
|
(node.style) ? node.style.cssText = value : node.setAttribute('style', value);
|
|
},
|
|
|
|
'value': function(node, value){
|
|
node.value = (value != null) ? value : '';
|
|
}
|
|
|
|
});
|
|
|
|
propertyGetters['class'] = function(node){
|
|
return ('className' in node) ? node.className || null : node.getAttribute('class');
|
|
};
|
|
|
|
/* <webkit> */
|
|
var el = document.createElement('button');
|
|
// IE sets type as readonly and throws
|
|
try { el.type = 'button'; } catch(e){}
|
|
if (el.type != 'button') propertySetters.type = function(node, value){
|
|
node.setAttribute('type', value);
|
|
};
|
|
el = null;
|
|
/* </webkit> */
|
|
|
|
/*<IE>*/
|
|
var input = document.createElement('input');
|
|
input.value = 't';
|
|
input.type = 'submit';
|
|
if (input.value != 't') propertySetters.type = function(node, type){
|
|
var value = node.value;
|
|
node.type = type;
|
|
node.value = value;
|
|
};
|
|
input = null;
|
|
/*</IE>*/
|
|
|
|
/* getProperty, setProperty */
|
|
|
|
/* <ltIE9> */
|
|
var pollutesGetAttribute = (function(div){
|
|
div.random = 'attribute';
|
|
return (div.getAttribute('random') == 'attribute');
|
|
})(document.createElement('div'));
|
|
|
|
/* <ltIE9> */
|
|
|
|
Element.implement({
|
|
|
|
setProperty: function(name, value){
|
|
var setter = propertySetters[name.toLowerCase()];
|
|
if (setter){
|
|
setter(this, value);
|
|
} else {
|
|
/* <ltIE9> */
|
|
if (pollutesGetAttribute) var attributeWhiteList = this.retrieve('$attributeWhiteList', {});
|
|
/* </ltIE9> */
|
|
|
|
if (value == null){
|
|
this.removeAttribute(name);
|
|
/* <ltIE9> */
|
|
if (pollutesGetAttribute) delete attributeWhiteList[name];
|
|
/* </ltIE9> */
|
|
} else {
|
|
this.setAttribute(name, '' + value);
|
|
/* <ltIE9> */
|
|
if (pollutesGetAttribute) attributeWhiteList[name] = true;
|
|
/* </ltIE9> */
|
|
}
|
|
}
|
|
return this;
|
|
},
|
|
|
|
setProperties: function(attributes){
|
|
for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
|
|
return this;
|
|
},
|
|
|
|
getProperty: function(name){
|
|
var getter = propertyGetters[name.toLowerCase()];
|
|
if (getter) return getter(this);
|
|
/* <ltIE9> */
|
|
if (pollutesGetAttribute){
|
|
var attr = this.getAttributeNode(name), attributeWhiteList = this.retrieve('$attributeWhiteList', {});
|
|
if (!attr) return null;
|
|
if (attr.expando && !attributeWhiteList[name]){
|
|
var outer = this.outerHTML;
|
|
// segment by the opening tag and find mention of attribute name
|
|
if (outer.substr(0, outer.search(/\/?['"]?>(?![^<]*<['"])/)).indexOf(name) < 0) return null;
|
|
attributeWhiteList[name] = true;
|
|
}
|
|
}
|
|
/* </ltIE9> */
|
|
var result = Slick.getAttribute(this, name);
|
|
return (!result && !Slick.hasAttribute(this, name)) ? null : result;
|
|
},
|
|
|
|
getProperties: function(){
|
|
var args = Array.from(arguments);
|
|
return args.map(this.getProperty, this).associate(args);
|
|
},
|
|
|
|
removeProperty: function(name){
|
|
return this.setProperty(name, null);
|
|
},
|
|
|
|
removeProperties: function(){
|
|
Array.each(arguments, this.removeProperty, this);
|
|
return this;
|
|
},
|
|
|
|
set: function(prop, value){
|
|
var property = Element.Properties[prop];
|
|
(property && property.set) ? property.set.call(this, value) : this.setProperty(prop, value);
|
|
}.overloadSetter(),
|
|
|
|
get: function(prop){
|
|
var property = Element.Properties[prop];
|
|
return (property && property.get) ? property.get.apply(this) : this.getProperty(prop);
|
|
}.overloadGetter(),
|
|
|
|
erase: function(prop){
|
|
var property = Element.Properties[prop];
|
|
(property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
|
|
return this;
|
|
},
|
|
|
|
hasClass: function(className){
|
|
return this.className.clean().contains(className, ' ');
|
|
},
|
|
|
|
addClass: function(className){
|
|
if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean();
|
|
return this;
|
|
},
|
|
|
|
removeClass: function(className){
|
|
this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1');
|
|
return this;
|
|
},
|
|
|
|
toggleClass: function(className, force){
|
|
if (force == null) force = !this.hasClass(className);
|
|
return (force) ? this.addClass(className) : this.removeClass(className);
|
|
},
|
|
|
|
adopt: function(){
|
|
var parent = this, fragment, elements = Array.flatten(arguments), length = elements.length;
|
|
if (length > 1) parent = fragment = document.createDocumentFragment();
|
|
|
|
for (var i = 0; i < length; i++){
|
|
var element = document.id(elements[i], true);
|
|
if (element) parent.appendChild(element);
|
|
}
|
|
|
|
if (fragment) this.appendChild(fragment);
|
|
|
|
return this;
|
|
},
|
|
|
|
appendText: function(text, where){
|
|
return this.grab(this.getDocument().newTextNode(text), where);
|
|
},
|
|
|
|
grab: function(el, where){
|
|
inserters[where || 'bottom'](document.id(el, true), this);
|
|
return this;
|
|
},
|
|
|
|
inject: function(el, where){
|
|
inserters[where || 'bottom'](this, document.id(el, true));
|
|
return this;
|
|
},
|
|
|
|
replaces: function(el){
|
|
el = document.id(el, true);
|
|
el.parentNode.replaceChild(this, el);
|
|
return this;
|
|
},
|
|
|
|
wraps: function(el, where){
|
|
el = document.id(el, true);
|
|
return this.replaces(el).grab(el, where);
|
|
},
|
|
|
|
getSelected: function(){
|
|
this.selectedIndex; // Safari 3.2.1
|
|
return new Elements(Array.from(this.options).filter(function(option){
|
|
return option.selected;
|
|
}));
|
|
},
|
|
|
|
toQueryString: function(){
|
|
var queryString = [];
|
|
this.getElements('input, select, textarea').each(function(el){
|
|
var type = el.type;
|
|
if (!el.name || el.disabled || type == 'submit' || type == 'reset' || type == 'file' || type == 'image') return;
|
|
|
|
var value = (el.get('tag') == 'select') ? el.getSelected().map(function(opt){
|
|
// IE
|
|
return document.id(opt).get('value');
|
|
}) : ((type == 'radio' || type == 'checkbox') && !el.checked) ? null : el.get('value');
|
|
|
|
Array.from(value).each(function(val){
|
|
if (typeof val != 'undefined') queryString.push(encodeURIComponent(el.name) + '=' + encodeURIComponent(val));
|
|
});
|
|
});
|
|
return queryString.join('&');
|
|
}
|
|
|
|
});
|
|
|
|
var collected = {}, storage = {};
|
|
|
|
var get = function(uid){
|
|
return (storage[uid] || (storage[uid] = {}));
|
|
};
|
|
|
|
var clean = function(item){
|
|
var uid = item.uniqueNumber;
|
|
if (item.removeEvents) item.removeEvents();
|
|
if (item.clearAttributes) item.clearAttributes();
|
|
if (uid != null){
|
|
delete collected[uid];
|
|
delete storage[uid];
|
|
}
|
|
return item;
|
|
};
|
|
|
|
var formProps = {input: 'checked', option: 'selected', textarea: 'value'};
|
|
|
|
Element.implement({
|
|
|
|
destroy: function(){
|
|
var children = clean(this).getElementsByTagName('*');
|
|
Array.each(children, clean);
|
|
Element.dispose(this);
|
|
return null;
|
|
},
|
|
|
|
empty: function(){
|
|
Array.from(this.childNodes).each(Element.dispose);
|
|
return this;
|
|
},
|
|
|
|
dispose: function(){
|
|
return (this.parentNode) ? this.parentNode.removeChild(this) : this;
|
|
},
|
|
|
|
clone: function(contents, keepid){
|
|
contents = contents !== false;
|
|
var clone = this.cloneNode(contents), ce = [clone], te = [this], i;
|
|
|
|
if (contents){
|
|
ce.append(Array.from(clone.getElementsByTagName('*')));
|
|
te.append(Array.from(this.getElementsByTagName('*')));
|
|
}
|
|
|
|
for (i = ce.length; i--;){
|
|
var node = ce[i], element = te[i];
|
|
if (!keepid) node.removeAttribute('id');
|
|
/*<ltIE9>*/
|
|
if (node.clearAttributes){
|
|
node.clearAttributes();
|
|
node.mergeAttributes(element);
|
|
node.removeAttribute('uniqueNumber');
|
|
if (node.options){
|
|
var no = node.options, eo = element.options;
|
|
for (var j = no.length; j--;) no[j].selected = eo[j].selected;
|
|
}
|
|
}
|
|
/*</ltIE9>*/
|
|
var prop = formProps[element.tagName.toLowerCase()];
|
|
if (prop && element[prop]) node[prop] = element[prop];
|
|
}
|
|
|
|
/*<ltIE9>*/
|
|
if (Browser.ie){
|
|
var co = clone.getElementsByTagName('object'), to = this.getElementsByTagName('object');
|
|
for (i = co.length; i--;) co[i].outerHTML = to[i].outerHTML;
|
|
}
|
|
/*</ltIE9>*/
|
|
return document.id(clone);
|
|
}
|
|
|
|
});
|
|
|
|
[Element, Window, Document].invoke('implement', {
|
|
|
|
addListener: function(type, fn){
|
|
if (type == 'unload'){
|
|
var old = fn, self = this;
|
|
fn = function(){
|
|
self.removeListener('unload', fn);
|
|
old();
|
|
};
|
|
} else {
|
|
collected[Slick.uidOf(this)] = this;
|
|
}
|
|
if (this.addEventListener) this.addEventListener(type, fn, !!arguments[2]);
|
|
else this.attachEvent('on' + type, fn);
|
|
return this;
|
|
},
|
|
|
|
removeListener: function(type, fn){
|
|
if (this.removeEventListener) this.removeEventListener(type, fn, !!arguments[2]);
|
|
else this.detachEvent('on' + type, fn);
|
|
return this;
|
|
},
|
|
|
|
retrieve: function(property, dflt){
|
|
var storage = get(Slick.uidOf(this)), prop = storage[property];
|
|
if (dflt != null && prop == null) prop = storage[property] = dflt;
|
|
return prop != null ? prop : null;
|
|
},
|
|
|
|
store: function(property, value){
|
|
var storage = get(Slick.uidOf(this));
|
|
storage[property] = value;
|
|
return this;
|
|
},
|
|
|
|
eliminate: function(property){
|
|
var storage = get(Slick.uidOf(this));
|
|
delete storage[property];
|
|
return this;
|
|
}
|
|
|
|
});
|
|
|
|
/*<ltIE9>*/
|
|
if (window.attachEvent && !window.addEventListener) window.addListener('unload', function(){
|
|
Object.each(collected, clean);
|
|
if (window.CollectGarbage) CollectGarbage();
|
|
});
|
|
/*</ltIE9>*/
|
|
|
|
Element.Properties = {};
|
|
|
|
|
|
|
|
Element.Properties.style = {
|
|
|
|
set: function(style){
|
|
this.style.cssText = style;
|
|
},
|
|
|
|
get: function(){
|
|
return this.style.cssText;
|
|
},
|
|
|
|
erase: function(){
|
|
this.style.cssText = '';
|
|
}
|
|
|
|
};
|
|
|
|
Element.Properties.tag = {
|
|
|
|
get: function(){
|
|
return this.tagName.toLowerCase();
|
|
}
|
|
|
|
};
|
|
|
|
Element.Properties.html = {
|
|
|
|
set: function(html){
|
|
if (html == null) html = '';
|
|
else if (typeOf(html) == 'array') html = html.join('');
|
|
this.innerHTML = html;
|
|
},
|
|
|
|
erase: function(){
|
|
this.innerHTML = '';
|
|
}
|
|
|
|
};
|
|
|
|
/*<ltIE9>*/
|
|
// technique by jdbarlett - http://jdbartlett.com/innershiv/
|
|
var div = document.createElement('div');
|
|
div.innerHTML = '<nav></nav>';
|
|
var supportsHTML5Elements = (div.childNodes.length == 1);
|
|
if (!supportsHTML5Elements){
|
|
var tags = 'abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video'.split(' '),
|
|
fragment = document.createDocumentFragment(), l = tags.length;
|
|
while (l--) fragment.createElement(tags[l]);
|
|
}
|
|
div = null;
|
|
/*</ltIE9>*/
|
|
|
|
/*<IE>*/
|
|
var supportsTableInnerHTML = Function.attempt(function(){
|
|
var table = document.createElement('table');
|
|
table.innerHTML = '<tr><td></td></tr>';
|
|
return true;
|
|
});
|
|
|
|
/*<ltFF4>*/
|
|
var tr = document.createElement('tr'), html = '<td></td>';
|
|
tr.innerHTML = html;
|
|
var supportsTRInnerHTML = (tr.innerHTML == html);
|
|
tr = null;
|
|
/*</ltFF4>*/
|
|
|
|
if (!supportsTableInnerHTML || !supportsTRInnerHTML || !supportsHTML5Elements){
|
|
|
|
Element.Properties.html.set = (function(set){
|
|
|
|
var translations = {
|
|
table: [1, '<table>', '</table>'],
|
|
select: [1, '<select>', '</select>'],
|
|
tbody: [2, '<table><tbody>', '</tbody></table>'],
|
|
tr: [3, '<table><tbody><tr>', '</tr></tbody></table>']
|
|
};
|
|
|
|
translations.thead = translations.tfoot = translations.tbody;
|
|
|
|
return function(html){
|
|
var wrap = translations[this.get('tag')];
|
|
if (!wrap && !supportsHTML5Elements) wrap = [0, '', ''];
|
|
if (!wrap) return set.call(this, html);
|
|
|
|
var level = wrap[0], wrapper = document.createElement('div'), target = wrapper;
|
|
if (!supportsHTML5Elements) fragment.appendChild(wrapper);
|
|
wrapper.innerHTML = [wrap[1], html, wrap[2]].flatten().join('');
|
|
while (level--) target = target.firstChild;
|
|
this.empty().adopt(target.childNodes);
|
|
if (!supportsHTML5Elements) fragment.removeChild(wrapper);
|
|
wrapper = null;
|
|
};
|
|
|
|
})(Element.Properties.html.set);
|
|
}
|
|
/*</IE>*/
|
|
|
|
/*<ltIE9>*/
|
|
var testForm = document.createElement('form');
|
|
testForm.innerHTML = '<select><option>s</option></select>';
|
|
|
|
if (testForm.firstChild.value != 's') Element.Properties.value = {
|
|
|
|
set: function(value){
|
|
var tag = this.get('tag');
|
|
if (tag != 'select') return this.setProperty('value', value);
|
|
var options = this.getElements('option');
|
|
for (var i = 0; i < options.length; i++){
|
|
var option = options[i],
|
|
attr = option.getAttributeNode('value'),
|
|
optionValue = (attr && attr.specified) ? option.value : option.get('text');
|
|
if (optionValue == value) return option.selected = true;
|
|
}
|
|
},
|
|
|
|
get: function(){
|
|
var option = this, tag = option.get('tag');
|
|
|
|
if (tag != 'select' && tag != 'option') return this.getProperty('value');
|
|
|
|
if (tag == 'select' && !(option = option.getSelected()[0])) return '';
|
|
|
|
var attr = option.getAttributeNode('value');
|
|
return (attr && attr.specified) ? option.value : option.get('text');
|
|
}
|
|
|
|
};
|
|
testForm = null;
|
|
/*</ltIE9>*/
|
|
|
|
/*<IE>*/
|
|
if (document.createElement('div').getAttributeNode('id')) Element.Properties.id = {
|
|
set: function(id){
|
|
this.id = this.getAttributeNode('id').value = id;
|
|
},
|
|
get: function(){
|
|
return this.id || null;
|
|
},
|
|
erase: function(){
|
|
this.id = this.getAttributeNode('id').value = '';
|
|
}
|
|
};
|
|
/*</IE>*/
|
|
|
|
})();
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Element.Style
|
|
|
|
description: Contains methods for interacting with the styles of Elements in a fashionable way.
|
|
|
|
license: MIT-style license.
|
|
|
|
requires: Element
|
|
|
|
provides: Element.Style
|
|
|
|
...
|
|
*/
|
|
|
|
(function(){
|
|
|
|
var html = document.html;
|
|
|
|
//<ltIE9>
|
|
// Check for oldIE, which does not remove styles when they're set to null
|
|
var el = document.createElement('div');
|
|
el.style.color = 'red';
|
|
el.style.color = null;
|
|
var doesNotRemoveStyles = el.style.color == 'red';
|
|
el = null;
|
|
//</ltIE9>
|
|
|
|
Element.Properties.styles = {set: function(styles){
|
|
this.setStyles(styles);
|
|
}};
|
|
|
|
var hasOpacity = (html.style.opacity != null),
|
|
hasFilter = (html.style.filter != null),
|
|
reAlpha = /alpha\(opacity=([\d.]+)\)/i;
|
|
|
|
var setVisibility = function(element, opacity){
|
|
element.store('$opacity', opacity);
|
|
element.style.visibility = opacity > 0 || opacity == null ? 'visible' : 'hidden';
|
|
};
|
|
|
|
var setOpacity = (hasOpacity ? function(element, opacity){
|
|
element.style.opacity = opacity;
|
|
} : (hasFilter ? function(element, opacity){
|
|
var style = element.style;
|
|
if (!element.currentStyle || !element.currentStyle.hasLayout) style.zoom = 1;
|
|
if (opacity == null || opacity == 1) opacity = '';
|
|
else opacity = 'alpha(opacity=' + (opacity * 100).limit(0, 100).round() + ')';
|
|
var filter = style.filter || element.getComputedStyle('filter') || '';
|
|
style.filter = reAlpha.test(filter) ? filter.replace(reAlpha, opacity) : filter + opacity;
|
|
if (!style.filter) style.removeAttribute('filter');
|
|
} : setVisibility));
|
|
|
|
var getOpacity = (hasOpacity ? function(element){
|
|
var opacity = element.style.opacity || element.getComputedStyle('opacity');
|
|
return (opacity == '') ? 1 : opacity.toFloat();
|
|
} : (hasFilter ? function(element){
|
|
var filter = (element.style.filter || element.getComputedStyle('filter')),
|
|
opacity;
|
|
if (filter) opacity = filter.match(reAlpha);
|
|
return (opacity == null || filter == null) ? 1 : (opacity[1] / 100);
|
|
} : function(element){
|
|
var opacity = element.retrieve('$opacity');
|
|
if (opacity == null) opacity = (element.style.visibility == 'hidden' ? 0 : 1);
|
|
return opacity;
|
|
}));
|
|
|
|
var floatName = (html.style.cssFloat == null) ? 'styleFloat' : 'cssFloat';
|
|
|
|
Element.implement({
|
|
|
|
getComputedStyle: function(property){
|
|
if (this.currentStyle) return this.currentStyle[property.camelCase()];
|
|
var defaultView = Element.getDocument(this).defaultView,
|
|
computed = defaultView ? defaultView.getComputedStyle(this, null) : null;
|
|
return (computed) ? computed.getPropertyValue((property == floatName) ? 'float' : property.hyphenate()) : null;
|
|
},
|
|
|
|
setStyle: function(property, value){
|
|
if (property == 'opacity'){
|
|
if (value != null) value = parseFloat(value);
|
|
setOpacity(this, value);
|
|
return this;
|
|
}
|
|
property = (property == 'float' ? floatName : property).camelCase();
|
|
if (typeOf(value) != 'string'){
|
|
var map = (Element.Styles[property] || '@').split(' ');
|
|
value = Array.from(value).map(function(val, i){
|
|
if (!map[i]) return '';
|
|
return (typeOf(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
|
|
}).join(' ');
|
|
} else if (value == String(Number(value))){
|
|
value = Math.round(value);
|
|
}
|
|
this.style[property] = value;
|
|
//<ltIE9>
|
|
if ((value == '' || value == null) && doesNotRemoveStyles && this.style.removeAttribute){
|
|
this.style.removeAttribute(property);
|
|
}
|
|
//</ltIE9>
|
|
return this;
|
|
},
|
|
|
|
getStyle: function(property){
|
|
if (property == 'opacity') return getOpacity(this);
|
|
property = (property == 'float' ? floatName : property).camelCase();
|
|
var result = this.style[property];
|
|
if (!result || property == 'zIndex'){
|
|
result = [];
|
|
for (var style in Element.ShortStyles){
|
|
if (property != style) continue;
|
|
for (var s in Element.ShortStyles[style]) result.push(this.getStyle(s));
|
|
return result.join(' ');
|
|
}
|
|
result = this.getComputedStyle(property);
|
|
}
|
|
if (result){
|
|
result = String(result);
|
|
var color = result.match(/rgba?\([\d\s,]+\)/);
|
|
if (color) result = result.replace(color[0], color[0].rgbToHex());
|
|
}
|
|
if (Browser.opera || Browser.ie){
|
|
if ((/^(height|width)$/).test(property) && !(/px$/.test(result))){
|
|
var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
|
|
values.each(function(value){
|
|
size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
|
|
}, this);
|
|
return this['offset' + property.capitalize()] - size + 'px';
|
|
}
|
|
if (Browser.ie && (/^border(.+)Width|margin|padding/).test(property) && isNaN(parseFloat(result))){
|
|
return '0px';
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
|
|
setStyles: function(styles){
|
|
for (var style in styles) this.setStyle(style, styles[style]);
|
|
return this;
|
|
},
|
|
|
|
getStyles: function(){
|
|
var result = {};
|
|
Array.flatten(arguments).each(function(key){
|
|
result[key] = this.getStyle(key);
|
|
}, this);
|
|
return result;
|
|
}
|
|
|
|
});
|
|
|
|
Element.Styles = {
|
|
left: '@px', top: '@px', bottom: '@px', right: '@px',
|
|
width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
|
|
backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
|
|
fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
|
|
margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
|
|
borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
|
|
zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@'
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}};
|
|
|
|
['Top', 'Right', 'Bottom', 'Left'].each(function(direction){
|
|
var Short = Element.ShortStyles;
|
|
var All = Element.Styles;
|
|
['margin', 'padding'].each(function(style){
|
|
var sd = style + direction;
|
|
Short[style][sd] = All[sd] = '@px';
|
|
});
|
|
var bd = 'border' + direction;
|
|
Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
|
|
var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
|
|
Short[bd] = {};
|
|
Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px';
|
|
Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@';
|
|
Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
|
|
});
|
|
|
|
})();
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Element.Event
|
|
|
|
description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events, if necessary.
|
|
|
|
license: MIT-style license.
|
|
|
|
requires: [Element, Event]
|
|
|
|
provides: Element.Event
|
|
|
|
...
|
|
*/
|
|
|
|
(function(){
|
|
|
|
Element.Properties.events = {set: function(events){
|
|
this.addEvents(events);
|
|
}};
|
|
|
|
[Element, Window, Document].invoke('implement', {
|
|
|
|
addEvent: function(type, fn){
|
|
var events = this.retrieve('events', {});
|
|
if (!events[type]) events[type] = {keys: [], values: []};
|
|
if (events[type].keys.contains(fn)) return this;
|
|
events[type].keys.push(fn);
|
|
var realType = type,
|
|
custom = Element.Events[type],
|
|
condition = fn,
|
|
self = this;
|
|
if (custom){
|
|
if (custom.onAdd) custom.onAdd.call(this, fn, type);
|
|
if (custom.condition){
|
|
condition = function(event){
|
|
if (custom.condition.call(this, event, type)) return fn.call(this, event);
|
|
return true;
|
|
};
|
|
}
|
|
if (custom.base) realType = Function.from(custom.base).call(this, type);
|
|
}
|
|
var defn = function(){
|
|
return fn.call(self);
|
|
};
|
|
var nativeEvent = Element.NativeEvents[realType];
|
|
if (nativeEvent){
|
|
if (nativeEvent == 2){
|
|
defn = function(event){
|
|
event = new DOMEvent(event, self.getWindow());
|
|
if (condition.call(self, event) === false) event.stop();
|
|
};
|
|
}
|
|
this.addListener(realType, defn, arguments[2]);
|
|
}
|
|
events[type].values.push(defn);
|
|
return this;
|
|
},
|
|
|
|
removeEvent: function(type, fn){
|
|
var events = this.retrieve('events');
|
|
if (!events || !events[type]) return this;
|
|
var list = events[type];
|
|
var index = list.keys.indexOf(fn);
|
|
if (index == -1) return this;
|
|
var value = list.values[index];
|
|
delete list.keys[index];
|
|
delete list.values[index];
|
|
var custom = Element.Events[type];
|
|
if (custom){
|
|
if (custom.onRemove) custom.onRemove.call(this, fn, type);
|
|
if (custom.base) type = Function.from(custom.base).call(this, type);
|
|
}
|
|
return (Element.NativeEvents[type]) ? this.removeListener(type, value, arguments[2]) : this;
|
|
},
|
|
|
|
addEvents: function(events){
|
|
for (var event in events) this.addEvent(event, events[event]);
|
|
return this;
|
|
},
|
|
|
|
removeEvents: function(events){
|
|
var type;
|
|
if (typeOf(events) == 'object'){
|
|
for (type in events) this.removeEvent(type, events[type]);
|
|
return this;
|
|
}
|
|
var attached = this.retrieve('events');
|
|
if (!attached) return this;
|
|
if (!events){
|
|
for (type in attached) this.removeEvents(type);
|
|
this.eliminate('events');
|
|
} else if (attached[events]){
|
|
attached[events].keys.each(function(fn){
|
|
this.removeEvent(events, fn);
|
|
}, this);
|
|
delete attached[events];
|
|
}
|
|
return this;
|
|
},
|
|
|
|
fireEvent: function(type, args, delay){
|
|
var events = this.retrieve('events');
|
|
if (!events || !events[type]) return this;
|
|
args = Array.from(args);
|
|
|
|
events[type].keys.each(function(fn){
|
|
if (delay) fn.delay(delay, this, args);
|
|
else fn.apply(this, args);
|
|
}, this);
|
|
return this;
|
|
},
|
|
|
|
cloneEvents: function(from, type){
|
|
from = document.id(from);
|
|
var events = from.retrieve('events');
|
|
if (!events) return this;
|
|
if (!type){
|
|
for (var eventType in events) this.cloneEvents(from, eventType);
|
|
} else if (events[type]){
|
|
events[type].keys.each(function(fn){
|
|
this.addEvent(type, fn);
|
|
}, this);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
});
|
|
|
|
Element.NativeEvents = {
|
|
click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons
|
|
mousewheel: 2, DOMMouseScroll: 2, //mouse wheel
|
|
mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement
|
|
keydown: 2, keypress: 2, keyup: 2, //keyboard
|
|
orientationchange: 2, // mobile
|
|
touchstart: 2, touchmove: 2, touchend: 2, touchcancel: 2, // touch
|
|
gesturestart: 2, gesturechange: 2, gestureend: 2, // gesture
|
|
focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, paste: 2, input: 2, //form elements
|
|
load: 2, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
|
|
error: 1, abort: 1, scroll: 1 //misc
|
|
};
|
|
|
|
Element.Events = {mousewheel: {
|
|
base: (Browser.firefox) ? 'DOMMouseScroll' : 'mousewheel'
|
|
}};
|
|
|
|
if ('onmouseenter' in document.documentElement){
|
|
Element.NativeEvents.mouseenter = Element.NativeEvents.mouseleave = 2;
|
|
} else {
|
|
var check = function(event){
|
|
var related = event.relatedTarget;
|
|
if (related == null) return true;
|
|
if (!related) return false;
|
|
return (related != this && related.prefix != 'xul' && typeOf(this) != 'document' && !this.contains(related));
|
|
};
|
|
|
|
Element.Events.mouseenter = {
|
|
base: 'mouseover',
|
|
condition: check
|
|
};
|
|
|
|
Element.Events.mouseleave = {
|
|
base: 'mouseout',
|
|
condition: check
|
|
};
|
|
}
|
|
|
|
/*<ltIE9>*/
|
|
if (!window.addEventListener){
|
|
Element.NativeEvents.propertychange = 2;
|
|
Element.Events.change = {
|
|
base: function(){
|
|
var type = this.type;
|
|
return (this.get('tag') == 'input' && (type == 'radio' || type == 'checkbox')) ? 'propertychange' : 'change'
|
|
},
|
|
condition: function(event){
|
|
return this.type != 'radio' || (event.event.propertyName == 'checked' && this.checked);
|
|
}
|
|
}
|
|
}
|
|
/*</ltIE9>*/
|
|
|
|
|
|
|
|
})();
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Element.Delegation
|
|
|
|
description: Extends the Element native object to include the delegate method for more efficient event management.
|
|
|
|
license: MIT-style license.
|
|
|
|
requires: [Element.Event]
|
|
|
|
provides: [Element.Delegation]
|
|
|
|
...
|
|
*/
|
|
|
|
(function(){
|
|
|
|
var eventListenerSupport = !!window.addEventListener;
|
|
|
|
Element.NativeEvents.focusin = Element.NativeEvents.focusout = 2;
|
|
|
|
var bubbleUp = function(self, match, fn, event, target){
|
|
while (target && target != self){
|
|
if (match(target, event)) return fn.call(target, event, target);
|
|
target = document.id(target.parentNode);
|
|
}
|
|
};
|
|
|
|
var map = {
|
|
mouseenter: {
|
|
base: 'mouseover'
|
|
},
|
|
mouseleave: {
|
|
base: 'mouseout'
|
|
},
|
|
focus: {
|
|
base: 'focus' + (eventListenerSupport ? '' : 'in'),
|
|
capture: true
|
|
},
|
|
blur: {
|
|
base: eventListenerSupport ? 'blur' : 'focusout',
|
|
capture: true
|
|
}
|
|
};
|
|
|
|
/*<ltIE9>*/
|
|
var _key = '$delegation:';
|
|
var formObserver = function(type){
|
|
|
|
return {
|
|
|
|
base: 'focusin',
|
|
|
|
remove: function(self, uid){
|
|
var list = self.retrieve(_key + type + 'listeners', {})[uid];
|
|
if (list && list.forms) for (var i = list.forms.length; i--;){
|
|
list.forms[i].removeEvent(type, list.fns[i]);
|
|
}
|
|
},
|
|
|
|
listen: function(self, match, fn, event, target, uid){
|
|
var form = (target.get('tag') == 'form') ? target : event.target.getParent('form');
|
|
if (!form) return;
|
|
|
|
var listeners = self.retrieve(_key + type + 'listeners', {}),
|
|
listener = listeners[uid] || {forms: [], fns: []},
|
|
forms = listener.forms, fns = listener.fns;
|
|
|
|
if (forms.indexOf(form) != -1) return;
|
|
forms.push(form);
|
|
|
|
var _fn = function(event){
|
|
bubbleUp(self, match, fn, event, target);
|
|
};
|
|
form.addEvent(type, _fn);
|
|
fns.push(_fn);
|
|
|
|
listeners[uid] = listener;
|
|
self.store(_key + type + 'listeners', listeners);
|
|
}
|
|
};
|
|
};
|
|
|
|
var inputObserver = function(type){
|
|
return {
|
|
base: 'focusin',
|
|
listen: function(self, match, fn, event, target){
|
|
var events = {blur: function(){
|
|
this.removeEvents(events);
|
|
}};
|
|
events[type] = function(event){
|
|
bubbleUp(self, match, fn, event, target);
|
|
};
|
|
event.target.addEvents(events);
|
|
}
|
|
};
|
|
};
|
|
|
|
if (!eventListenerSupport) Object.append(map, {
|
|
submit: formObserver('submit'),
|
|
reset: formObserver('reset'),
|
|
change: inputObserver('change'),
|
|
select: inputObserver('select')
|
|
});
|
|
/*</ltIE9>*/
|
|
|
|
var proto = Element.prototype,
|
|
addEvent = proto.addEvent,
|
|
removeEvent = proto.removeEvent;
|
|
|
|
var relay = function(old, method){
|
|
return function(type, fn, useCapture){
|
|
if (type.indexOf(':relay') == -1) return old.call(this, type, fn, useCapture);
|
|
var parsed = Slick.parse(type).expressions[0][0];
|
|
if (parsed.pseudos[0].key != 'relay') return old.call(this, type, fn, useCapture);
|
|
var newType = parsed.tag;
|
|
parsed.pseudos.slice(1).each(function(pseudo){
|
|
newType += ':' + pseudo.key + (pseudo.value ? '(' + pseudo.value + ')' : '');
|
|
});
|
|
old.call(this, type, fn);
|
|
return method.call(this, newType, parsed.pseudos[0].value, fn);
|
|
};
|
|
};
|
|
|
|
var delegation = {
|
|
|
|
addEvent: function(type, match, fn){
|
|
var storage = this.retrieve('$delegates', {}), stored = storage[type];
|
|
if (stored) for (var _uid in stored){
|
|
if (stored[_uid].fn == fn && stored[_uid].match == match) return this;
|
|
}
|
|
|
|
var _type = type, _match = match, _fn = fn, _map = map[type] || {};
|
|
type = _map.base || _type;
|
|
|
|
match = function(target){
|
|
return Slick.match(target, _match);
|
|
};
|
|
|
|
var elementEvent = Element.Events[_type];
|
|
if (elementEvent && elementEvent.condition){
|
|
var __match = match, condition = elementEvent.condition;
|
|
match = function(target, event){
|
|
return __match(target, event) && condition.call(target, event, type);
|
|
};
|
|
}
|
|
|
|
var self = this, uid = String.uniqueID();
|
|
var delegator = _map.listen ? function(event, target){
|
|
if (!target && event && event.target) target = event.target;
|
|
if (target) _map.listen(self, match, fn, event, target, uid);
|
|
} : function(event, target){
|
|
if (!target && event && event.target) target = event.target;
|
|
if (target) bubbleUp(self, match, fn, event, target);
|
|
};
|
|
|
|
if (!stored) stored = {};
|
|
stored[uid] = {
|
|
match: _match,
|
|
fn: _fn,
|
|
delegator: delegator
|
|
};
|
|
storage[_type] = stored;
|
|
return addEvent.call(this, type, delegator, _map.capture);
|
|
},
|
|
|
|
removeEvent: function(type, match, fn, _uid){
|
|
var storage = this.retrieve('$delegates', {}), stored = storage[type];
|
|
if (!stored) return this;
|
|
|
|
if (_uid){
|
|
var _type = type, delegator = stored[_uid].delegator, _map = map[type] || {};
|
|
type = _map.base || _type;
|
|
if (_map.remove) _map.remove(this, _uid);
|
|
delete stored[_uid];
|
|
storage[_type] = stored;
|
|
return removeEvent.call(this, type, delegator);
|
|
}
|
|
|
|
var __uid, s;
|
|
if (fn) for (__uid in stored){
|
|
s = stored[__uid];
|
|
if (s.match == match && s.fn == fn) return delegation.removeEvent.call(this, type, match, fn, __uid);
|
|
} else for (__uid in stored){
|
|
s = stored[__uid];
|
|
if (s.match == match) delegation.removeEvent.call(this, type, match, s.fn, __uid);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
};
|
|
|
|
[Element, Window, Document].invoke('implement', {
|
|
addEvent: relay(addEvent, delegation.addEvent),
|
|
removeEvent: relay(removeEvent, delegation.removeEvent)
|
|
});
|
|
|
|
})();
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Element.Dimensions
|
|
|
|
description: Contains methods to work with size, scroll, or positioning of Elements and the window object.
|
|
|
|
license: MIT-style license.
|
|
|
|
credits:
|
|
- Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
|
|
- Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
|
|
|
|
requires: [Element, Element.Style]
|
|
|
|
provides: [Element.Dimensions]
|
|
|
|
...
|
|
*/
|
|
|
|
(function(){
|
|
|
|
var element = document.createElement('div'),
|
|
child = document.createElement('div');
|
|
element.style.height = '0';
|
|
element.appendChild(child);
|
|
var brokenOffsetParent = (child.offsetParent === element);
|
|
element = child = null;
|
|
|
|
var isOffset = function(el){
|
|
return styleString(el, 'position') != 'static' || isBody(el);
|
|
};
|
|
|
|
var isOffsetStatic = function(el){
|
|
return isOffset(el) || (/^(?:table|td|th)$/i).test(el.tagName);
|
|
};
|
|
|
|
Element.implement({
|
|
|
|
scrollTo: function(x, y){
|
|
if (isBody(this)){
|
|
this.getWindow().scrollTo(x, y);
|
|
} else {
|
|
this.scrollLeft = x;
|
|
this.scrollTop = y;
|
|
}
|
|
return this;
|
|
},
|
|
|
|
getSize: function(){
|
|
if (isBody(this)) return this.getWindow().getSize();
|
|
return {x: this.offsetWidth, y: this.offsetHeight};
|
|
},
|
|
|
|
getScrollSize: function(){
|
|
if (isBody(this)) return this.getWindow().getScrollSize();
|
|
return {x: this.scrollWidth, y: this.scrollHeight};
|
|
},
|
|
|
|
getScroll: function(){
|
|
if (isBody(this)) return this.getWindow().getScroll();
|
|
return {x: this.scrollLeft, y: this.scrollTop};
|
|
},
|
|
|
|
getScrolls: function(){
|
|
var element = this.parentNode, position = {x: 0, y: 0};
|
|
while (element && !isBody(element)){
|
|
position.x += element.scrollLeft;
|
|
position.y += element.scrollTop;
|
|
element = element.parentNode;
|
|
}
|
|
return position;
|
|
},
|
|
|
|
getOffsetParent: brokenOffsetParent ? function(){
|
|
var element = this;
|
|
if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
|
|
|
|
var isOffsetCheck = (styleString(element, 'position') == 'static') ? isOffsetStatic : isOffset;
|
|
while ((element = element.parentNode)){
|
|
if (isOffsetCheck(element)) return element;
|
|
}
|
|
return null;
|
|
} : function(){
|
|
var element = this;
|
|
if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
|
|
|
|
try {
|
|
return element.offsetParent;
|
|
} catch(e) {}
|
|
return null;
|
|
},
|
|
|
|
getOffsets: function(){
|
|
if (this.getBoundingClientRect && !Browser.Platform.ios){
|
|
var bound = this.getBoundingClientRect(),
|
|
html = document.id(this.getDocument().documentElement),
|
|
htmlScroll = html.getScroll(),
|
|
elemScrolls = this.getScrolls(),
|
|
isFixed = (styleString(this, 'position') == 'fixed');
|
|
|
|
return {
|
|
x: bound.left.toInt() + elemScrolls.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft,
|
|
y: bound.top.toInt() + elemScrolls.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop
|
|
};
|
|
}
|
|
|
|
var element = this, position = {x: 0, y: 0};
|
|
if (isBody(this)) return position;
|
|
|
|
while (element && !isBody(element)){
|
|
position.x += element.offsetLeft;
|
|
position.y += element.offsetTop;
|
|
|
|
if (Browser.firefox){
|
|
if (!borderBox(element)){
|
|
position.x += leftBorder(element);
|
|
position.y += topBorder(element);
|
|
}
|
|
var parent = element.parentNode;
|
|
if (parent && styleString(parent, 'overflow') != 'visible'){
|
|
position.x += leftBorder(parent);
|
|
position.y += topBorder(parent);
|
|
}
|
|
} else if (element != this && Browser.safari){
|
|
position.x += leftBorder(element);
|
|
position.y += topBorder(element);
|
|
}
|
|
|
|
element = element.offsetParent;
|
|
}
|
|
if (Browser.firefox && !borderBox(this)){
|
|
position.x -= leftBorder(this);
|
|
position.y -= topBorder(this);
|
|
}
|
|
return position;
|
|
},
|
|
|
|
getPosition: function(relative){
|
|
var offset = this.getOffsets(),
|
|
scroll = this.getScrolls();
|
|
var position = {
|
|
x: offset.x - scroll.x,
|
|
y: offset.y - scroll.y
|
|
};
|
|
|
|
if (relative && (relative = document.id(relative))){
|
|
var relativePosition = relative.getPosition();
|
|
return {x: position.x - relativePosition.x - leftBorder(relative), y: position.y - relativePosition.y - topBorder(relative)};
|
|
}
|
|
return position;
|
|
},
|
|
|
|
getCoordinates: function(element){
|
|
if (isBody(this)) return this.getWindow().getCoordinates();
|
|
var position = this.getPosition(element),
|
|
size = this.getSize();
|
|
var obj = {
|
|
left: position.x,
|
|
top: position.y,
|
|
width: size.x,
|
|
height: size.y
|
|
};
|
|
obj.right = obj.left + obj.width;
|
|
obj.bottom = obj.top + obj.height;
|
|
return obj;
|
|
},
|
|
|
|
computePosition: function(obj){
|
|
return {
|
|
left: obj.x - styleNumber(this, 'margin-left'),
|
|
top: obj.y - styleNumber(this, 'margin-top')
|
|
};
|
|
},
|
|
|
|
setPosition: function(obj){
|
|
return this.setStyles(this.computePosition(obj));
|
|
}
|
|
|
|
});
|
|
|
|
|
|
[Document, Window].invoke('implement', {
|
|
|
|
getSize: function(){
|
|
var doc = getCompatElement(this);
|
|
return {x: doc.clientWidth, y: doc.clientHeight};
|
|
},
|
|
|
|
getScroll: function(){
|
|
var win = this.getWindow(), doc = getCompatElement(this);
|
|
return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
|
|
},
|
|
|
|
getScrollSize: function(){
|
|
var doc = getCompatElement(this),
|
|
min = this.getSize(),
|
|
body = this.getDocument().body;
|
|
|
|
return {x: Math.max(doc.scrollWidth, body.scrollWidth, min.x), y: Math.max(doc.scrollHeight, body.scrollHeight, min.y)};
|
|
},
|
|
|
|
getPosition: function(){
|
|
return {x: 0, y: 0};
|
|
},
|
|
|
|
getCoordinates: function(){
|
|
var size = this.getSize();
|
|
return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x};
|
|
}
|
|
|
|
});
|
|
|
|
// private methods
|
|
|
|
var styleString = Element.getComputedStyle;
|
|
|
|
function styleNumber(element, style){
|
|
return styleString(element, style).toInt() || 0;
|
|
}
|
|
|
|
function borderBox(element){
|
|
return styleString(element, '-moz-box-sizing') == 'border-box';
|
|
}
|
|
|
|
function topBorder(element){
|
|
return styleNumber(element, 'border-top-width');
|
|
}
|
|
|
|
function leftBorder(element){
|
|
return styleNumber(element, 'border-left-width');
|
|
}
|
|
|
|
function isBody(element){
|
|
return (/^(?:body|html)$/i).test(element.tagName);
|
|
}
|
|
|
|
function getCompatElement(element){
|
|
var doc = element.getDocument();
|
|
return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
|
|
}
|
|
|
|
})();
|
|
|
|
//aliases
|
|
Element.alias({position: 'setPosition'}); //compatability
|
|
|
|
[Window, Document, Element].invoke('implement', {
|
|
|
|
getHeight: function(){
|
|
return this.getSize().y;
|
|
},
|
|
|
|
getWidth: function(){
|
|
return this.getSize().x;
|
|
},
|
|
|
|
getScrollTop: function(){
|
|
return this.getScroll().y;
|
|
},
|
|
|
|
getScrollLeft: function(){
|
|
return this.getScroll().x;
|
|
},
|
|
|
|
getScrollHeight: function(){
|
|
return this.getScrollSize().y;
|
|
},
|
|
|
|
getScrollWidth: function(){
|
|
return this.getScrollSize().x;
|
|
},
|
|
|
|
getTop: function(){
|
|
return this.getPosition().y;
|
|
},
|
|
|
|
getLeft: function(){
|
|
return this.getPosition().x;
|
|
}
|
|
|
|
});
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Fx
|
|
|
|
description: Contains the basic animation logic to be extended by all other Fx Classes.
|
|
|
|
license: MIT-style license.
|
|
|
|
requires: [Chain, Events, Options]
|
|
|
|
provides: Fx
|
|
|
|
...
|
|
*/
|
|
|
|
(function(){
|
|
|
|
var Fx = this.Fx = new Class({
|
|
|
|
Implements: [Chain, Events, Options],
|
|
|
|
options: {
|
|
/*
|
|
onStart: nil,
|
|
onCancel: nil,
|
|
onComplete: nil,
|
|
*/
|
|
fps: 60,
|
|
unit: false,
|
|
duration: 500,
|
|
frames: null,
|
|
frameSkip: true,
|
|
link: 'ignore'
|
|
},
|
|
|
|
initialize: function(options){
|
|
this.subject = this.subject || this;
|
|
this.setOptions(options);
|
|
},
|
|
|
|
getTransition: function(){
|
|
return function(p){
|
|
return -(Math.cos(Math.PI * p) - 1) / 2;
|
|
};
|
|
},
|
|
|
|
step: function(now){
|
|
if (this.options.frameSkip){
|
|
var diff = (this.time != null) ? (now - this.time) : 0, frames = diff / this.frameInterval;
|
|
this.time = now;
|
|
this.frame += frames;
|
|
} else {
|
|
this.frame++;
|
|
}
|
|
|
|
if (this.frame < this.frames){
|
|
var delta = this.transition(this.frame / this.frames);
|
|
this.set(this.compute(this.from, this.to, delta));
|
|
} else {
|
|
this.frame = this.frames;
|
|
this.set(this.compute(this.from, this.to, 1));
|
|
this.stop();
|
|
}
|
|
},
|
|
|
|
set: function(now){
|
|
return now;
|
|
},
|
|
|
|
compute: function(from, to, delta){
|
|
return Fx.compute(from, to, delta);
|
|
},
|
|
|
|
check: function(){
|
|
if (!this.isRunning()) return true;
|
|
switch (this.options.link){
|
|
case 'cancel': this.cancel(); return true;
|
|
case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
start: function(from, to){
|
|
if (!this.check(from, to)) return this;
|
|
this.from = from;
|
|
this.to = to;
|
|
this.frame = (this.options.frameSkip) ? 0 : -1;
|
|
this.time = null;
|
|
this.transition = this.getTransition();
|
|
var frames = this.options.frames, fps = this.options.fps, duration = this.options.duration;
|
|
this.duration = Fx.Durations[duration] || duration.toInt();
|
|
this.frameInterval = 1000 / fps;
|
|
this.frames = frames || Math.round(this.duration / this.frameInterval);
|
|
this.fireEvent('start', this.subject);
|
|
pushInstance.call(this, fps);
|
|
return this;
|
|
},
|
|
|
|
stop: function(){
|
|
if (this.isRunning()){
|
|
this.time = null;
|
|
pullInstance.call(this, this.options.fps);
|
|
if (this.frames == this.frame){
|
|
this.fireEvent('complete', this.subject);
|
|
if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
|
|
} else {
|
|
this.fireEvent('stop', this.subject);
|
|
}
|
|
}
|
|
return this;
|
|
},
|
|
|
|
cancel: function(){
|
|
if (this.isRunning()){
|
|
this.time = null;
|
|
pullInstance.call(this, this.options.fps);
|
|
this.frame = this.frames;
|
|
this.fireEvent('cancel', this.subject).clearChain();
|
|
}
|
|
return this;
|
|
},
|
|
|
|
pause: function(){
|
|
if (this.isRunning()){
|
|
this.time = null;
|
|
pullInstance.call(this, this.options.fps);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
resume: function(){
|
|
if ((this.frame < this.frames) && !this.isRunning()) pushInstance.call(this, this.options.fps);
|
|
return this;
|
|
},
|
|
|
|
isRunning: function(){
|
|
var list = instances[this.options.fps];
|
|
return list && list.contains(this);
|
|
}
|
|
|
|
});
|
|
|
|
Fx.compute = function(from, to, delta){
|
|
return (to - from) * delta + from;
|
|
};
|
|
|
|
Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};
|
|
|
|
// global timers
|
|
|
|
var instances = {}, timers = {};
|
|
|
|
var loop = function(){
|
|
var now = Date.now();
|
|
for (var i = this.length; i--;){
|
|
var instance = this[i];
|
|
if (instance) instance.step(now);
|
|
}
|
|
};
|
|
|
|
var pushInstance = function(fps){
|
|
var list = instances[fps] || (instances[fps] = []);
|
|
list.push(this);
|
|
if (!timers[fps]) timers[fps] = loop.periodical(Math.round(1000 / fps), list);
|
|
};
|
|
|
|
var pullInstance = function(fps){
|
|
var list = instances[fps];
|
|
if (list){
|
|
list.erase(this);
|
|
if (!list.length && timers[fps]){
|
|
delete instances[fps];
|
|
timers[fps] = clearInterval(timers[fps]);
|
|
}
|
|
}
|
|
};
|
|
|
|
})();
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Fx.CSS
|
|
|
|
description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
|
|
|
|
license: MIT-style license.
|
|
|
|
requires: [Fx, Element.Style]
|
|
|
|
provides: Fx.CSS
|
|
|
|
...
|
|
*/
|
|
|
|
Fx.CSS = new Class({
|
|
|
|
Extends: Fx,
|
|
|
|
//prepares the base from/to object
|
|
|
|
prepare: function(element, property, values){
|
|
values = Array.from(values);
|
|
var from = values[0], to = values[1];
|
|
if (to == null){
|
|
to = from;
|
|
from = element.getStyle(property);
|
|
var unit = this.options.unit;
|
|
// adapted from: https://github.com/ryanmorr/fx/blob/master/fx.js#L299
|
|
if (unit && from.slice(-unit.length) != unit && parseFloat(from) != 0){
|
|
element.setStyle(property, to + unit);
|
|
var value = element.getComputedStyle(property);
|
|
// IE and Opera support pixelLeft or pixelWidth
|
|
if (!(/px$/.test(value))){
|
|
value = element.style[('pixel-' + property).camelCase()];
|
|
if (value == null){
|
|
// adapted from Dean Edwards' http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
|
|
var left = element.style.left;
|
|
element.style.left = to + unit;
|
|
value = element.style.pixelLeft;
|
|
element.style.left = left;
|
|
}
|
|
}
|
|
from = (to || 1) / (parseFloat(value) || 1) * (parseFloat(from) || 0);
|
|
element.setStyle(property, from + unit);
|
|
}
|
|
}
|
|
return {from: this.parse(from), to: this.parse(to)};
|
|
},
|
|
|
|
//parses a value into an array
|
|
|
|
parse: function(value){
|
|
value = Function.from(value)();
|
|
value = (typeof value == 'string') ? value.split(' ') : Array.from(value);
|
|
return value.map(function(val){
|
|
val = String(val);
|
|
var found = false;
|
|
Object.each(Fx.CSS.Parsers, function(parser, key){
|
|
if (found) return;
|
|
var parsed = parser.parse(val);
|
|
if (parsed || parsed === 0) found = {value: parsed, parser: parser};
|
|
});
|
|
found = found || {value: val, parser: Fx.CSS.Parsers.String};
|
|
return found;
|
|
});
|
|
},
|
|
|
|
//computes by a from and to prepared objects, using their parsers.
|
|
|
|
compute: function(from, to, delta){
|
|
var computed = [];
|
|
(Math.min(from.length, to.length)).times(function(i){
|
|
computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser});
|
|
});
|
|
computed.$family = Function.from('fx:css:value');
|
|
return computed;
|
|
},
|
|
|
|
//serves the value as settable
|
|
|
|
serve: function(value, unit){
|
|
if (typeOf(value) != 'fx:css:value') value = this.parse(value);
|
|
var returned = [];
|
|
value.each(function(bit){
|
|
returned = returned.concat(bit.parser.serve(bit.value, unit));
|
|
});
|
|
return returned;
|
|
},
|
|
|
|
//renders the change to an element
|
|
|
|
render: function(element, property, value, unit){
|
|
element.setStyle(property, this.serve(value, unit));
|
|
},
|
|
|
|
//searches inside the page css to find the values for a selector
|
|
|
|
search: function(selector){
|
|
if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
|
|
var to = {}, selectorTest = new RegExp('^' + selector.escapeRegExp() + '$');
|
|
Array.each(document.styleSheets, function(sheet, j){
|
|
var href = sheet.href;
|
|
if (href && href.contains('://') && !href.contains(document.domain)) return;
|
|
var rules = sheet.rules || sheet.cssRules;
|
|
Array.each(rules, function(rule, i){
|
|
if (!rule.style) return;
|
|
var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
|
|
return m.toLowerCase();
|
|
}) : null;
|
|
if (!selectorText || !selectorTest.test(selectorText)) return;
|
|
Object.each(Element.Styles, function(value, style){
|
|
if (!rule.style[style] || Element.ShortStyles[style]) return;
|
|
value = String(rule.style[style]);
|
|
to[style] = ((/^rgb/).test(value)) ? value.rgbToHex() : value;
|
|
});
|
|
});
|
|
});
|
|
return Fx.CSS.Cache[selector] = to;
|
|
}
|
|
|
|
});
|
|
|
|
Fx.CSS.Cache = {};
|
|
|
|
Fx.CSS.Parsers = {
|
|
|
|
Color: {
|
|
parse: function(value){
|
|
if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true);
|
|
return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false;
|
|
},
|
|
compute: function(from, to, delta){
|
|
return from.map(function(value, i){
|
|
return Math.round(Fx.compute(from[i], to[i], delta));
|
|
});
|
|
},
|
|
serve: function(value){
|
|
return value.map(Number);
|
|
}
|
|
},
|
|
|
|
Number: {
|
|
parse: parseFloat,
|
|
compute: Fx.compute,
|
|
serve: function(value, unit){
|
|
return (unit) ? value + unit : value;
|
|
}
|
|
},
|
|
|
|
String: {
|
|
parse: Function.from(false),
|
|
compute: function(zero, one){
|
|
return one;
|
|
},
|
|
serve: function(zero){
|
|
return zero;
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Fx.Tween
|
|
|
|
description: Formerly Fx.Style, effect to transition any CSS property for an element.
|
|
|
|
license: MIT-style license.
|
|
|
|
requires: Fx.CSS
|
|
|
|
provides: [Fx.Tween, Element.fade, Element.highlight]
|
|
|
|
...
|
|
*/
|
|
|
|
Fx.Tween = new Class({
|
|
|
|
Extends: Fx.CSS,
|
|
|
|
initialize: function(element, options){
|
|
this.element = this.subject = document.id(element);
|
|
this.parent(options);
|
|
},
|
|
|
|
set: function(property, now){
|
|
if (arguments.length == 1){
|
|
now = property;
|
|
property = this.property || this.options.property;
|
|
}
|
|
this.render(this.element, property, now, this.options.unit);
|
|
return this;
|
|
},
|
|
|
|
start: function(property, from, to){
|
|
if (!this.check(property, from, to)) return this;
|
|
var args = Array.flatten(arguments);
|
|
this.property = this.options.property || args.shift();
|
|
var parsed = this.prepare(this.element, this.property, args);
|
|
return this.parent(parsed.from, parsed.to);
|
|
}
|
|
|
|
});
|
|
|
|
Element.Properties.tween = {
|
|
|
|
set: function(options){
|
|
this.get('tween').cancel().setOptions(options);
|
|
return this;
|
|
},
|
|
|
|
get: function(){
|
|
var tween = this.retrieve('tween');
|
|
if (!tween){
|
|
tween = new Fx.Tween(this, {link: 'cancel'});
|
|
this.store('tween', tween);
|
|
}
|
|
return tween;
|
|
}
|
|
|
|
};
|
|
|
|
Element.implement({
|
|
|
|
tween: function(property, from, to){
|
|
this.get('tween').start(property, from, to);
|
|
return this;
|
|
},
|
|
|
|
fade: function(how){
|
|
var fade = this.get('tween'), method, args = ['opacity'].append(arguments), toggle;
|
|
if (args[1] == null) args[1] = 'toggle';
|
|
switch (args[1]){
|
|
case 'in': method = 'start'; args[1] = 1; break;
|
|
case 'out': method = 'start'; args[1] = 0; break;
|
|
case 'show': method = 'set'; args[1] = 1; break;
|
|
case 'hide': method = 'set'; args[1] = 0; break;
|
|
case 'toggle':
|
|
var flag = this.retrieve('fade:flag', this.getStyle('opacity') == 1);
|
|
method = 'start';
|
|
args[1] = flag ? 0 : 1;
|
|
this.store('fade:flag', !flag);
|
|
toggle = true;
|
|
break;
|
|
default: method = 'start';
|
|
}
|
|
if (!toggle) this.eliminate('fade:flag');
|
|
fade[method].apply(fade, args);
|
|
var to = args[args.length - 1];
|
|
if (method == 'set' || to != 0) this.setStyle('visibility', to == 0 ? 'hidden' : 'visible');
|
|
else fade.chain(function(){
|
|
this.element.setStyle('visibility', 'hidden');
|
|
this.callChain();
|
|
});
|
|
return this;
|
|
},
|
|
|
|
highlight: function(start, end){
|
|
if (!end){
|
|
end = this.retrieve('highlight:original', this.getStyle('background-color'));
|
|
end = (end == 'transparent') ? '#fff' : end;
|
|
}
|
|
var tween = this.get('tween');
|
|
tween.start('background-color', start || '#ffff88', end).chain(function(){
|
|
this.setStyle('background-color', this.retrieve('highlight:original'));
|
|
tween.callChain();
|
|
}.bind(this));
|
|
return this;
|
|
}
|
|
|
|
});
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Fx.Morph
|
|
|
|
description: Formerly Fx.Styles, effect to transition any number of CSS properties for an element using an object of rules, or CSS based selector rules.
|
|
|
|
license: MIT-style license.
|
|
|
|
requires: Fx.CSS
|
|
|
|
provides: Fx.Morph
|
|
|
|
...
|
|
*/
|
|
|
|
Fx.Morph = new Class({
|
|
|
|
Extends: Fx.CSS,
|
|
|
|
initialize: function(element, options){
|
|
this.element = this.subject = document.id(element);
|
|
this.parent(options);
|
|
},
|
|
|
|
set: function(now){
|
|
if (typeof now == 'string') now = this.search(now);
|
|
for (var p in now) this.render(this.element, p, now[p], this.options.unit);
|
|
return this;
|
|
},
|
|
|
|
compute: function(from, to, delta){
|
|
var now = {};
|
|
for (var p in from) now[p] = this.parent(from[p], to[p], delta);
|
|
return now;
|
|
},
|
|
|
|
start: function(properties){
|
|
if (!this.check(properties)) return this;
|
|
if (typeof properties == 'string') properties = this.search(properties);
|
|
var from = {}, to = {};
|
|
for (var p in properties){
|
|
var parsed = this.prepare(this.element, p, properties[p]);
|
|
from[p] = parsed.from;
|
|
to[p] = parsed.to;
|
|
}
|
|
return this.parent(from, to);
|
|
}
|
|
|
|
});
|
|
|
|
Element.Properties.morph = {
|
|
|
|
set: function(options){
|
|
this.get('morph').cancel().setOptions(options);
|
|
return this;
|
|
},
|
|
|
|
get: function(){
|
|
var morph = this.retrieve('morph');
|
|
if (!morph){
|
|
morph = new Fx.Morph(this, {link: 'cancel'});
|
|
this.store('morph', morph);
|
|
}
|
|
return morph;
|
|
}
|
|
|
|
};
|
|
|
|
Element.implement({
|
|
|
|
morph: function(props){
|
|
this.get('morph').start(props);
|
|
return this;
|
|
}
|
|
|
|
});
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Fx.Transitions
|
|
|
|
description: Contains a set of advanced transitions to be used with any of the Fx Classes.
|
|
|
|
license: MIT-style license.
|
|
|
|
credits:
|
|
- Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
|
|
|
|
requires: Fx
|
|
|
|
provides: Fx.Transitions
|
|
|
|
...
|
|
*/
|
|
|
|
Fx.implement({
|
|
|
|
getTransition: function(){
|
|
var trans = this.options.transition || Fx.Transitions.Sine.easeInOut;
|
|
if (typeof trans == 'string'){
|
|
var data = trans.split(':');
|
|
trans = Fx.Transitions;
|
|
trans = trans[data[0]] || trans[data[0].capitalize()];
|
|
if (data[1]) trans = trans['ease' + data[1].capitalize() + (data[2] ? data[2].capitalize() : '')];
|
|
}
|
|
return trans;
|
|
}
|
|
|
|
});
|
|
|
|
Fx.Transition = function(transition, params){
|
|
params = Array.from(params);
|
|
var easeIn = function(pos){
|
|
return transition(pos, params);
|
|
};
|
|
return Object.append(easeIn, {
|
|
easeIn: easeIn,
|
|
easeOut: function(pos){
|
|
return 1 - transition(1 - pos, params);
|
|
},
|
|
easeInOut: function(pos){
|
|
return (pos <= 0.5 ? transition(2 * pos, params) : (2 - transition(2 * (1 - pos), params))) / 2;
|
|
}
|
|
});
|
|
};
|
|
|
|
Fx.Transitions = {
|
|
|
|
linear: function(zero){
|
|
return zero;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Fx.Transitions.extend = function(transitions){
|
|
for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]);
|
|
};
|
|
|
|
Fx.Transitions.extend({
|
|
|
|
Pow: function(p, x){
|
|
return Math.pow(p, x && x[0] || 6);
|
|
},
|
|
|
|
Expo: function(p){
|
|
return Math.pow(2, 8 * (p - 1));
|
|
},
|
|
|
|
Circ: function(p){
|
|
return 1 - Math.sin(Math.acos(p));
|
|
},
|
|
|
|
Sine: function(p){
|
|
return 1 - Math.cos(p * Math.PI / 2);
|
|
},
|
|
|
|
Back: function(p, x){
|
|
x = x && x[0] || 1.618;
|
|
return Math.pow(p, 2) * ((x + 1) * p - x);
|
|
},
|
|
|
|
Bounce: function(p){
|
|
var value;
|
|
for (var a = 0, b = 1; 1; a += b, b /= 2){
|
|
if (p >= (7 - 4 * a) / 11){
|
|
value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
|
|
break;
|
|
}
|
|
}
|
|
return value;
|
|
},
|
|
|
|
Elastic: function(p, x){
|
|
return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x && x[0] || 1) / 3);
|
|
}
|
|
|
|
});
|
|
|
|
['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){
|
|
Fx.Transitions[transition] = new Fx.Transition(function(p){
|
|
return Math.pow(p, i + 2);
|
|
});
|
|
});
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Request
|
|
|
|
description: Powerful all purpose Request Class. Uses XMLHTTPRequest.
|
|
|
|
license: MIT-style license.
|
|
|
|
requires: [Object, Element, Chain, Events, Options, Browser]
|
|
|
|
provides: Request
|
|
|
|
...
|
|
*/
|
|
|
|
(function(){
|
|
|
|
var empty = function(){},
|
|
progressSupport = ('onprogress' in new Browser.Request);
|
|
|
|
var Request = this.Request = new Class({
|
|
|
|
Implements: [Chain, Events, Options],
|
|
|
|
options: {/*
|
|
onRequest: function(){},
|
|
onLoadstart: function(event, xhr){},
|
|
onProgress: function(event, xhr){},
|
|
onComplete: function(){},
|
|
onCancel: function(){},
|
|
onSuccess: function(responseText, responseXML){},
|
|
onFailure: function(xhr){},
|
|
onException: function(headerName, value){},
|
|
onTimeout: function(){},
|
|
user: '',
|
|
password: '',*/
|
|
url: '',
|
|
data: '',
|
|
headers: {
|
|
'X-Requested-With': 'XMLHttpRequest',
|
|
'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
|
|
},
|
|
async: true,
|
|
format: false,
|
|
method: 'post',
|
|
link: 'ignore',
|
|
isSuccess: null,
|
|
emulation: true,
|
|
urlEncoded: true,
|
|
encoding: 'utf-8',
|
|
evalScripts: false,
|
|
evalResponse: false,
|
|
timeout: 0,
|
|
noCache: false
|
|
},
|
|
|
|
initialize: function(options){
|
|
this.xhr = new Browser.Request();
|
|
this.setOptions(options);
|
|
this.headers = this.options.headers;
|
|
},
|
|
|
|
onStateChange: function(){
|
|
var xhr = this.xhr;
|
|
if (xhr.readyState != 4 || !this.running) return;
|
|
this.running = false;
|
|
this.status = 0;
|
|
Function.attempt(function(){
|
|
var status = xhr.status;
|
|
this.status = (status == 1223) ? 204 : status;
|
|
}.bind(this));
|
|
xhr.onreadystatechange = empty;
|
|
if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
|
|
clearTimeout(this.timer);
|
|
|
|
this.response = {text: this.xhr.responseText || '', xml: this.xhr.responseXML};
|
|
if (this.options.isSuccess.call(this, this.status))
|
|
this.success(this.response.text, this.response.xml);
|
|
else
|
|
this.failure();
|
|
},
|
|
|
|
isSuccess: function(){
|
|
var status = this.status;
|
|
return (status >= 200 && status < 300);
|
|
},
|
|
|
|
isRunning: function(){
|
|
return !!this.running;
|
|
},
|
|
|
|
processScripts: function(text){
|
|
if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return Browser.exec(text);
|
|
return text.stripScripts(this.options.evalScripts);
|
|
},
|
|
|
|
success: function(text, xml){
|
|
this.onSuccess(this.processScripts(text), xml);
|
|
},
|
|
|
|
onSuccess: function(){
|
|
this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
|
|
},
|
|
|
|
failure: function(){
|
|
this.onFailure();
|
|
},
|
|
|
|
onFailure: function(){
|
|
this.fireEvent('complete').fireEvent('failure', this.xhr);
|
|
},
|
|
|
|
loadstart: function(event){
|
|
this.fireEvent('loadstart', [event, this.xhr]);
|
|
},
|
|
|
|
progress: function(event){
|
|
this.fireEvent('progress', [event, this.xhr]);
|
|
},
|
|
|
|
timeout: function(){
|
|
this.fireEvent('timeout', this.xhr);
|
|
},
|
|
|
|
setHeader: function(name, value){
|
|
this.headers[name] = value;
|
|
return this;
|
|
},
|
|
|
|
getHeader: function(name){
|
|
return Function.attempt(function(){
|
|
return this.xhr.getResponseHeader(name);
|
|
}.bind(this));
|
|
},
|
|
|
|
check: function(){
|
|
if (!this.running) return true;
|
|
switch (this.options.link){
|
|
case 'cancel': this.cancel(); return true;
|
|
case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
send: function(options){
|
|
if (!this.check(options)) return this;
|
|
|
|
this.options.isSuccess = this.options.isSuccess || this.isSuccess;
|
|
this.running = true;
|
|
|
|
var type = typeOf(options);
|
|
if (type == 'string' || type == 'element') options = {data: options};
|
|
|
|
var old = this.options;
|
|
options = Object.append({data: old.data, url: old.url, method: old.method}, options);
|
|
var data = options.data, url = String(options.url), method = options.method.toLowerCase();
|
|
|
|
switch (typeOf(data)){
|
|
case 'element': data = document.id(data).toQueryString(); break;
|
|
case 'object': case 'hash': data = Object.toQueryString(data);
|
|
}
|
|
|
|
if (this.options.format){
|
|
var format = 'format=' + this.options.format;
|
|
data = (data) ? format + '&' + data : format;
|
|
}
|
|
|
|
if (this.options.emulation && !['get', 'post'].contains(method)){
|
|
var _method = '_method=' + method;
|
|
data = (data) ? _method + '&' + data : _method;
|
|
method = 'post';
|
|
}
|
|
|
|
if (this.options.urlEncoded && ['post', 'put'].contains(method)){
|
|
var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
|
|
this.headers['Content-type'] = 'application/x-www-form-urlencoded' + encoding;
|
|
}
|
|
|
|
if (!url) url = document.location.pathname;
|
|
|
|
var trimPosition = url.lastIndexOf('/');
|
|
if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition);
|
|
|
|
if (this.options.noCache)
|
|
url += (url.contains('?') ? '&' : '?') + String.uniqueID();
|
|
|
|
if (data && method == 'get'){
|
|
url += (url.contains('?') ? '&' : '?') + data;
|
|
data = null;
|
|
}
|
|
|
|
var xhr = this.xhr;
|
|
if (progressSupport){
|
|
xhr.onloadstart = this.loadstart.bind(this);
|
|
xhr.onprogress = this.progress.bind(this);
|
|
}
|
|
|
|
xhr.open(method.toUpperCase(), url, this.options.async, this.options.user, this.options.password);
|
|
if (this.options.user && 'withCredentials' in xhr) xhr.withCredentials = true;
|
|
|
|
xhr.onreadystatechange = this.onStateChange.bind(this);
|
|
|
|
Object.each(this.headers, function(value, key){
|
|
try {
|
|
xhr.setRequestHeader(key, value);
|
|
} catch (e){
|
|
this.fireEvent('exception', [key, value]);
|
|
}
|
|
}, this);
|
|
|
|
this.fireEvent('request');
|
|
xhr.send(data);
|
|
if (!this.options.async) this.onStateChange();
|
|
else if (this.options.timeout) this.timer = this.timeout.delay(this.options.timeout, this);
|
|
return this;
|
|
},
|
|
|
|
cancel: function(){
|
|
if (!this.running) return this;
|
|
this.running = false;
|
|
var xhr = this.xhr;
|
|
xhr.abort();
|
|
clearTimeout(this.timer);
|
|
xhr.onreadystatechange = empty;
|
|
if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
|
|
this.xhr = new Browser.Request();
|
|
this.fireEvent('cancel');
|
|
return this;
|
|
}
|
|
|
|
});
|
|
|
|
var methods = {};
|
|
['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){
|
|
methods[method] = function(data){
|
|
var object = {
|
|
method: method
|
|
};
|
|
if (data != null) object.data = data;
|
|
return this.send(object);
|
|
};
|
|
});
|
|
|
|
Request.implement(methods);
|
|
|
|
Element.Properties.send = {
|
|
|
|
set: function(options){
|
|
var send = this.get('send').cancel();
|
|
send.setOptions(options);
|
|
return this;
|
|
},
|
|
|
|
get: function(){
|
|
var send = this.retrieve('send');
|
|
if (!send){
|
|
send = new Request({
|
|
data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
|
|
});
|
|
this.store('send', send);
|
|
}
|
|
return send;
|
|
}
|
|
|
|
};
|
|
|
|
Element.implement({
|
|
|
|
send: function(url){
|
|
var sender = this.get('send');
|
|
sender.send({data: this, url: url || sender.options.url});
|
|
return this;
|
|
}
|
|
|
|
});
|
|
|
|
})();
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Request.HTML
|
|
|
|
description: Extends the basic Request Class with additional methods for interacting with HTML responses.
|
|
|
|
license: MIT-style license.
|
|
|
|
requires: [Element, Request]
|
|
|
|
provides: Request.HTML
|
|
|
|
...
|
|
*/
|
|
|
|
Request.HTML = new Class({
|
|
|
|
Extends: Request,
|
|
|
|
options: {
|
|
update: false,
|
|
append: false,
|
|
evalScripts: true,
|
|
filter: false,
|
|
headers: {
|
|
Accept: 'text/html, application/xml, text/xml, */*'
|
|
}
|
|
},
|
|
|
|
success: function(text){
|
|
var options = this.options, response = this.response;
|
|
|
|
response.html = text.stripScripts(function(script){
|
|
response.javascript = script;
|
|
});
|
|
|
|
var match = response.html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
|
|
if (match) response.html = match[1];
|
|
var temp = new Element('div').set('html', response.html);
|
|
|
|
response.tree = temp.childNodes;
|
|
response.elements = temp.getElements(options.filter || '*');
|
|
|
|
if (options.filter) response.tree = response.elements;
|
|
if (options.update){
|
|
var update = document.id(options.update).empty();
|
|
if (options.filter) update.adopt(response.elements);
|
|
else update.set('html', response.html);
|
|
} else if (options.append){
|
|
var append = document.id(options.append);
|
|
if (options.filter) response.elements.reverse().inject(append);
|
|
else append.adopt(temp.getChildren());
|
|
}
|
|
if (options.evalScripts) Browser.exec(response.javascript);
|
|
|
|
this.onSuccess(response.tree, response.elements, response.html, response.javascript);
|
|
}
|
|
|
|
});
|
|
|
|
Element.Properties.load = {
|
|
|
|
set: function(options){
|
|
var load = this.get('load').cancel();
|
|
load.setOptions(options);
|
|
return this;
|
|
},
|
|
|
|
get: function(){
|
|
var load = this.retrieve('load');
|
|
if (!load){
|
|
load = new Request.HTML({data: this, link: 'cancel', update: this, method: 'get'});
|
|
this.store('load', load);
|
|
}
|
|
return load;
|
|
}
|
|
|
|
};
|
|
|
|
Element.implement({
|
|
|
|
load: function(){
|
|
this.get('load').send(Array.link(arguments, {data: Type.isObject, url: Type.isString}));
|
|
return this;
|
|
}
|
|
|
|
});
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: JSON
|
|
|
|
description: JSON encoder and decoder.
|
|
|
|
license: MIT-style license.
|
|
|
|
SeeAlso: <http://www.json.org/>
|
|
|
|
requires: [Array, String, Number, Function]
|
|
|
|
provides: JSON
|
|
|
|
...
|
|
*/
|
|
|
|
if (typeof JSON == 'undefined') this.JSON = {};
|
|
|
|
|
|
|
|
(function(){
|
|
|
|
var special = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'};
|
|
|
|
var escape = function(chr){
|
|
return special[chr] || '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4);
|
|
};
|
|
|
|
JSON.validate = function(string){
|
|
string = string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
|
|
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
|
|
replace(/(?:^|:|,)(?:\s*\[)+/g, '');
|
|
|
|
return (/^[\],:{}\s]*$/).test(string);
|
|
};
|
|
|
|
JSON.encode = JSON.stringify ? function(obj){
|
|
return JSON.stringify(obj);
|
|
} : function(obj){
|
|
if (obj && obj.toJSON) obj = obj.toJSON();
|
|
|
|
switch (typeOf(obj)){
|
|
case 'string':
|
|
return '"' + obj.replace(/[\x00-\x1f\\"]/g, escape) + '"';
|
|
case 'array':
|
|
return '[' + obj.map(JSON.encode).clean() + ']';
|
|
case 'object': case 'hash':
|
|
var string = [];
|
|
Object.each(obj, function(value, key){
|
|
var json = JSON.encode(value);
|
|
if (json) string.push(JSON.encode(key) + ':' + json);
|
|
});
|
|
return '{' + string + '}';
|
|
case 'number': case 'boolean': return '' + obj;
|
|
case 'null': return 'null';
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
JSON.decode = function(string, secure){
|
|
if (!string || typeOf(string) != 'string') return null;
|
|
|
|
if (secure || JSON.secure){
|
|
if (JSON.parse) return JSON.parse(string);
|
|
if (!JSON.validate(string)) throw new Error('JSON could not decode the input; security is enabled and the value is not secure.');
|
|
}
|
|
|
|
return eval('(' + string + ')');
|
|
};
|
|
|
|
})();
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Request.JSON
|
|
|
|
description: Extends the basic Request Class with additional methods for sending and receiving JSON data.
|
|
|
|
license: MIT-style license.
|
|
|
|
requires: [Request, JSON]
|
|
|
|
provides: Request.JSON
|
|
|
|
...
|
|
*/
|
|
|
|
Request.JSON = new Class({
|
|
|
|
Extends: Request,
|
|
|
|
options: {
|
|
/*onError: function(text, error){},*/
|
|
secure: true
|
|
},
|
|
|
|
initialize: function(options){
|
|
this.parent(options);
|
|
Object.append(this.headers, {
|
|
'Accept': 'application/json',
|
|
'X-Request': 'JSON'
|
|
});
|
|
},
|
|
|
|
success: function(text){
|
|
var json;
|
|
try {
|
|
json = this.response.json = JSON.decode(text, this.options.secure);
|
|
} catch (error){
|
|
this.fireEvent('error', [text, error]);
|
|
return;
|
|
}
|
|
if (json == null) this.onFailure();
|
|
else this.onSuccess(json, text);
|
|
}
|
|
|
|
});
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Cookie
|
|
|
|
description: Class for creating, reading, and deleting browser Cookies.
|
|
|
|
license: MIT-style license.
|
|
|
|
credits:
|
|
- Based on the functions by Peter-Paul Koch (http://quirksmode.org).
|
|
|
|
requires: [Options, Browser]
|
|
|
|
provides: Cookie
|
|
|
|
...
|
|
*/
|
|
|
|
var Cookie = new Class({
|
|
|
|
Implements: Options,
|
|
|
|
options: {
|
|
path: '/',
|
|
domain: false,
|
|
duration: false,
|
|
secure: false,
|
|
document: document,
|
|
encode: true
|
|
},
|
|
|
|
initialize: function(key, options){
|
|
this.key = key;
|
|
this.setOptions(options);
|
|
},
|
|
|
|
write: function(value){
|
|
if (this.options.encode) value = encodeURIComponent(value);
|
|
if (this.options.domain) value += '; domain=' + this.options.domain;
|
|
if (this.options.path) value += '; path=' + this.options.path;
|
|
if (this.options.duration){
|
|
var date = new Date();
|
|
date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
|
|
value += '; expires=' + date.toGMTString();
|
|
}
|
|
if (this.options.secure) value += '; secure';
|
|
this.options.document.cookie = this.key + '=' + value;
|
|
return this;
|
|
},
|
|
|
|
read: function(){
|
|
var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)');
|
|
return (value) ? decodeURIComponent(value[1]) : null;
|
|
},
|
|
|
|
dispose: function(){
|
|
new Cookie(this.key, Object.merge({}, this.options, {duration: -1})).write('');
|
|
return this;
|
|
}
|
|
|
|
});
|
|
|
|
Cookie.write = function(key, value, options){
|
|
return new Cookie(key, options).write(value);
|
|
};
|
|
|
|
Cookie.read = function(key){
|
|
return new Cookie(key).read();
|
|
};
|
|
|
|
Cookie.dispose = function(key, options){
|
|
return new Cookie(key, options).dispose();
|
|
};
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: DOMReady
|
|
|
|
description: Contains the custom event domready.
|
|
|
|
license: MIT-style license.
|
|
|
|
requires: [Browser, Element, Element.Event]
|
|
|
|
provides: [DOMReady, DomReady]
|
|
|
|
...
|
|
*/
|
|
|
|
(function(window, document){
|
|
|
|
var ready,
|
|
loaded,
|
|
checks = [],
|
|
shouldPoll,
|
|
timer,
|
|
testElement = document.createElement('div');
|
|
|
|
var domready = function(){
|
|
clearTimeout(timer);
|
|
if (ready) return;
|
|
Browser.loaded = ready = true;
|
|
document.removeListener('DOMContentLoaded', domready).removeListener('readystatechange', check);
|
|
|
|
document.fireEvent('domready');
|
|
window.fireEvent('domready');
|
|
};
|
|
|
|
var check = function(){
|
|
for (var i = checks.length; i--;) if (checks[i]()){
|
|
domready();
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
var poll = function(){
|
|
clearTimeout(timer);
|
|
if (!check()) timer = setTimeout(poll, 10);
|
|
};
|
|
|
|
document.addListener('DOMContentLoaded', domready);
|
|
|
|
/*<ltIE8>*/
|
|
// doScroll technique by Diego Perini http://javascript.nwbox.com/IEContentLoaded/
|
|
// testElement.doScroll() throws when the DOM is not ready, only in the top window
|
|
var doScrollWorks = function(){
|
|
try {
|
|
testElement.doScroll();
|
|
return true;
|
|
} catch (e){}
|
|
return false;
|
|
};
|
|
// If doScroll works already, it can't be used to determine domready
|
|
// e.g. in an iframe
|
|
if (testElement.doScroll && !doScrollWorks()){
|
|
checks.push(doScrollWorks);
|
|
shouldPoll = true;
|
|
}
|
|
/*</ltIE8>*/
|
|
|
|
if (document.readyState) checks.push(function(){
|
|
var state = document.readyState;
|
|
return (state == 'loaded' || state == 'complete');
|
|
});
|
|
|
|
if ('onreadystatechange' in document) document.addListener('readystatechange', check);
|
|
else shouldPoll = true;
|
|
|
|
if (shouldPoll) poll();
|
|
|
|
Element.Events.domready = {
|
|
onAdd: function(fn){
|
|
if (ready) fn.call(this);
|
|
}
|
|
};
|
|
|
|
// Make sure that domready fires before load
|
|
Element.Events.load = {
|
|
base: 'load',
|
|
onAdd: function(fn){
|
|
if (loaded && this == window) fn.call(this);
|
|
},
|
|
condition: function(){
|
|
if (this == window){
|
|
domready();
|
|
delete Element.Events.load;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// This is based on the custom load event
|
|
window.addEvent('load', function(){
|
|
loaded = true;
|
|
});
|
|
|
|
})(window, document);
|
|
|
|
|
|
/*
|
|
---
|
|
|
|
name: Swiff
|
|
|
|
description: Wrapper for embedding SWF movies. Supports External Interface Communication.
|
|
|
|
license: MIT-style license.
|
|
|
|
credits:
|
|
- Flash detection & Internet Explorer + Flash Player 9 fix inspired by SWFObject.
|
|
|
|
requires: [Options, Object, Element]
|
|
|
|
provides: Swiff
|
|
|
|
...
|
|
*/
|
|
|
|
(function(){
|
|
|
|
var Swiff = this.Swiff = new Class({
|
|
|
|
Implements: Options,
|
|
|
|
options: {
|
|
id: null,
|
|
height: 1,
|
|
width: 1,
|
|
container: null,
|
|
properties: {},
|
|
params: {
|
|
quality: 'high',
|
|
allowScriptAccess: 'always',
|
|
wMode: 'window',
|
|
swLiveConnect: true
|
|
},
|
|
callBacks: {},
|
|
vars: {}
|
|
},
|
|
|
|
toElement: function(){
|
|
return this.object;
|
|
},
|
|
|
|
initialize: function(path, options){
|
|
this.instance = 'Swiff_' + String.uniqueID();
|
|
|
|
this.setOptions(options);
|
|
options = this.options;
|
|
var id = this.id = options.id || this.instance;
|
|
var container = document.id(options.container);
|
|
|
|
Swiff.CallBacks[this.instance] = {};
|
|
|
|
var params = options.params, vars = options.vars, callBacks = options.callBacks;
|
|
var properties = Object.append({height: options.height, width: options.width}, options.properties);
|
|
|
|
var self = this;
|
|
|
|
for (var callBack in callBacks){
|
|
Swiff.CallBacks[this.instance][callBack] = (function(option){
|
|
return function(){
|
|
return option.apply(self.object, arguments);
|
|
};
|
|
})(callBacks[callBack]);
|
|
vars[callBack] = 'Swiff.CallBacks.' + this.instance + '.' + callBack;
|
|
}
|
|
|
|
params.flashVars = Object.toQueryString(vars);
|
|
if (Browser.ie){
|
|
properties.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
|
|
params.movie = path;
|
|
} else {
|
|
properties.type = 'application/x-shockwave-flash';
|
|
}
|
|
properties.data = path;
|
|
|
|
var build = '<object id="' + id + '"';
|
|
for (var property in properties) build += ' ' + property + '="' + properties[property] + '"';
|
|
build += '>';
|
|
for (var param in params){
|
|
if (params[param]) build += '<param name="' + param + '" value="' + params[param] + '" />';
|
|
}
|
|
build += '</object>';
|
|
this.object = ((container) ? container.empty() : new Element('div')).set('html', build).firstChild;
|
|
},
|
|
|
|
replaces: function(element){
|
|
element = document.id(element, true);
|
|
element.parentNode.replaceChild(this.toElement(), element);
|
|
return this;
|
|
},
|
|
|
|
inject: function(element){
|
|
document.id(element, true).appendChild(this.toElement());
|
|
return this;
|
|
},
|
|
|
|
remote: function(){
|
|
return Swiff.remote.apply(Swiff, [this.toElement()].append(arguments));
|
|
}
|
|
|
|
});
|
|
|
|
Swiff.CallBacks = {};
|
|
|
|
Swiff.remote = function(obj, fn){
|
|
var rs = obj.CallFunction('<invoke name="' + fn + '" returntype="javascript">' + __flash__argumentsToXML(arguments, 2) + '</invoke>');
|
|
return eval(rs);
|
|
};
|
|
|
|
})();
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style type="text/css">
|
|
*, body, button, input, textarea, select {
|
|
text-rendering: optimizeLegibility;
|
|
-moz-osx-font-smoothing: grayscale;
|
|
}
|
|
|
|
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td {
|
|
margin:0;
|
|
padding:0;
|
|
}
|
|
table {
|
|
border-collapse:collapse;
|
|
border-spacing:0;
|
|
}
|
|
fieldset,img {
|
|
border:0;
|
|
}
|
|
address,caption,cite,code,dfn,em,strong,th,var {
|
|
font-style:normal;
|
|
font-weight:normal;
|
|
}
|
|
ol,ul {
|
|
list-style:none;
|
|
}
|
|
caption,th {
|
|
text-align:left;
|
|
}
|
|
h1,h2,h3,h4,h5,h6 {
|
|
font-size:100%;
|
|
font-weight:normal;
|
|
}
|
|
q:before,q:after {
|
|
content:'';
|
|
}
|
|
abbr,acronym { border:0;}
|
|
|
|
</style>
|
|
|
|
|
|
|
|
|
|
|
|
<!--<link rel="stylesheet" type="text/css" href="Base64%20encode%20a%20file_files/result-light.css">-->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<title>BWTC32Key encode/decode a file</title>
|
|
|
|
|
|
|
|
</head>
|
|
|
|
<body>
|
|
<div>
|
|
<div>
|
|
<label for="filePicker">Choose or drag a (tar) file to encode:</label>
|
|
<br>
|
|
</div>
|
|
<input id="filePicker" type="file"><textarea id="ascii85password" name="ascii85password" oninput="">Enter a password here before uploading.</textarea>
|
|
<br>
|
|
<p>Compression Level (1-9): <input type="number" min="1" max="9" value="9"id="complevel"></input></p>
|
|
<div>
|
|
<h1>BWTC32Key-encoded version</h1>
|
|
<textarea id="base64textarea" placeholder="Base32768 will appear here" cols="50" rows="15"></textarea><br>
|
|
<p>B3K filename:</p>
|
|
|
|
<textarea id="B3Kfilename">myFile.B3K</textarea>
|
|
<button id="download">Download .B3K of above textbox</button>
|
|
</div>
|
|
</div>
|
|
<h3>Decoding:</h3>
|
|
<p>Upload a B3K file here:</p>
|
|
<input type="file" id="fileToRead">
|
|
<br>
|
|
<textarea id="textbox" cols="25" >Type BWTC32Key-encoded text here to decode</textarea><button id="create">Click to decode file</button><br />
|
|
<p>Output's Filename & MIMEtype</p>
|
|
<textarea id="filename">output.bin</textarea>
|
|
<textarea id="filetype">application/octet-stream</textarea><br />
|
|
<a download="output.bin" id="downloadlink" style="display: none">Download decoded file</a>
|
|
|
|
<script>
|
|
// tell the embed parent frame the height of the content
|
|
if (window.parent && window.parent.parent){
|
|
window.parent.parent.postMessage(["resultsFrame", {
|
|
height: document.body.getBoundingClientRect().height,
|
|
slug: "None"
|
|
}], "*")
|
|
}
|
|
|
|
</script>
|
|
|
|
<script>
|
|
var compLevel = document.getElementById("complevel").value;
|
|
/**
|
|
* [js-sha256]{@link https://github.com/emn178/js-sha256}
|
|
*
|
|
* @version 0.9.0
|
|
* @author Chen, Yi-Cyuan [emn178@gmail.com]
|
|
* @copyright Chen, Yi-Cyuan 2014-2017
|
|
* @license MIT
|
|
*/
|
|
/*jslint bitwise: true */
|
|
(function () {
|
|
'use strict';
|
|
|
|
var ERROR = 'input is invalid type';
|
|
var WINDOW = typeof window === 'object';
|
|
var root = WINDOW ? window : {};
|
|
if (root.JS_SHA256_NO_WINDOW) {
|
|
WINDOW = false;
|
|
}
|
|
var WEB_WORKER = !WINDOW && typeof self === 'object';
|
|
var NODE_JS = !root.JS_SHA256_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node;
|
|
if (NODE_JS) {
|
|
root = global;
|
|
} else if (WEB_WORKER) {
|
|
root = self;
|
|
}
|
|
var COMMON_JS = !root.JS_SHA256_NO_COMMON_JS && typeof module === 'object' && module.exports;
|
|
var AMD = typeof define === 'function' && define.amd;
|
|
var ARRAY_BUFFER = !root.JS_SHA256_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined';
|
|
var HEX_CHARS = '0123456789abcdef'.split('');
|
|
var EXTRA = [-2147483648, 8388608, 32768, 128];
|
|
var SHIFT = [24, 16, 8, 0];
|
|
var K = [
|
|
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
|
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
|
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
|
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
|
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
|
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
|
];
|
|
var OUTPUT_TYPES = ['hex', 'array', 'digest', 'arrayBuffer'];
|
|
|
|
var blocks = [];
|
|
|
|
if (root.JS_SHA256_NO_NODE_JS || !Array.isArray) {
|
|
Array.isArray = function (obj) {
|
|
return Object.prototype.toString.call(obj) === '[object Array]';
|
|
};
|
|
}
|
|
|
|
if (ARRAY_BUFFER && (root.JS_SHA256_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) {
|
|
ArrayBuffer.isView = function (obj) {
|
|
return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer;
|
|
};
|
|
}
|
|
|
|
var createOutputMethod = function (outputType, is224) {
|
|
return function (message) {
|
|
return new Sha256(is224, true).update(message)[outputType]();
|
|
};
|
|
};
|
|
|
|
var createMethod = function (is224) {
|
|
var method = createOutputMethod('hex', is224);
|
|
if (NODE_JS) {
|
|
method = nodeWrap(method, is224);
|
|
}
|
|
method.create = function () {
|
|
return new Sha256(is224);
|
|
};
|
|
method.update = function (message) {
|
|
return method.create().update(message);
|
|
};
|
|
for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
|
|
var type = OUTPUT_TYPES[i];
|
|
method[type] = createOutputMethod(type, is224);
|
|
}
|
|
return method;
|
|
};
|
|
|
|
var nodeWrap = function (method, is224) {
|
|
var crypto = eval("require('crypto')");
|
|
var Buffer = eval("require('buffer').Buffer");
|
|
var algorithm = is224 ? 'sha224' : 'sha256';
|
|
var nodeMethod = function (message) {
|
|
if (typeof message === 'string') {
|
|
return crypto.createHash(algorithm).update(message, 'utf8').digest('hex');
|
|
} else {
|
|
if (message === null || message === undefined) {
|
|
throw new Error(ERROR);
|
|
} else if (message.constructor === ArrayBuffer) {
|
|
message = new Uint8Array(message);
|
|
}
|
|
}
|
|
if (Array.isArray(message) || ArrayBuffer.isView(message) ||
|
|
message.constructor === Buffer) {
|
|
return crypto.createHash(algorithm).update(new Buffer(message)).digest('hex');
|
|
} else {
|
|
return method(message);
|
|
}
|
|
};
|
|
return nodeMethod;
|
|
};
|
|
|
|
var createHmacOutputMethod = function (outputType, is224) {
|
|
return function (key, message) {
|
|
return new HmacSha256(key, is224, true).update(message)[outputType]();
|
|
};
|
|
};
|
|
|
|
var createHmacMethod = function (is224) {
|
|
var method = createHmacOutputMethod('hex', is224);
|
|
method.create = function (key) {
|
|
return new HmacSha256(key, is224);
|
|
};
|
|
method.update = function (key, message) {
|
|
return method.create(key).update(message);
|
|
};
|
|
for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
|
|
var type = OUTPUT_TYPES[i];
|
|
method[type] = createHmacOutputMethod(type, is224);
|
|
}
|
|
return method;
|
|
};
|
|
|
|
function Sha256(is224, sharedMemory) {
|
|
if (sharedMemory) {
|
|
blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] =
|
|
blocks[4] = blocks[5] = blocks[6] = blocks[7] =
|
|
blocks[8] = blocks[9] = blocks[10] = blocks[11] =
|
|
blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
|
|
this.blocks = blocks;
|
|
} else {
|
|
this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
|
}
|
|
|
|
if (is224) {
|
|
this.h0 = 0xc1059ed8;
|
|
this.h1 = 0x367cd507;
|
|
this.h2 = 0x3070dd17;
|
|
this.h3 = 0xf70e5939;
|
|
this.h4 = 0xffc00b31;
|
|
this.h5 = 0x68581511;
|
|
this.h6 = 0x64f98fa7;
|
|
this.h7 = 0xbefa4fa4;
|
|
} else { // 256
|
|
this.h0 = 0x6a09e667;
|
|
this.h1 = 0xbb67ae85;
|
|
this.h2 = 0x3c6ef372;
|
|
this.h3 = 0xa54ff53a;
|
|
this.h4 = 0x510e527f;
|
|
this.h5 = 0x9b05688c;
|
|
this.h6 = 0x1f83d9ab;
|
|
this.h7 = 0x5be0cd19;
|
|
}
|
|
|
|
this.block = this.start = this.bytes = this.hBytes = 0;
|
|
this.finalized = this.hashed = false;
|
|
this.first = true;
|
|
this.is224 = is224;
|
|
}
|
|
|
|
Sha256.prototype.update = function (message) {
|
|
if (this.finalized) {
|
|
return;
|
|
}
|
|
var notString, type = typeof message;
|
|
if (type !== 'string') {
|
|
if (type === 'object') {
|
|
if (message === null) {
|
|
throw new Error(ERROR);
|
|
} else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) {
|
|
message = new Uint8Array(message);
|
|
} else if (!Array.isArray(message)) {
|
|
if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) {
|
|
throw new Error(ERROR);
|
|
}
|
|
}
|
|
} else {
|
|
throw new Error(ERROR);
|
|
}
|
|
notString = true;
|
|
}
|
|
var code, index = 0, i, length = message.length, blocks = this.blocks;
|
|
|
|
while (index < length) {
|
|
if (this.hashed) {
|
|
this.hashed = false;
|
|
blocks[0] = this.block;
|
|
blocks[16] = blocks[1] = blocks[2] = blocks[3] =
|
|
blocks[4] = blocks[5] = blocks[6] = blocks[7] =
|
|
blocks[8] = blocks[9] = blocks[10] = blocks[11] =
|
|
blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
|
|
}
|
|
|
|
if (notString) {
|
|
for (i = this.start; index < length && i < 64; ++index) {
|
|
blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];
|
|
}
|
|
} else {
|
|
for (i = this.start; index < length && i < 64; ++index) {
|
|
code = message.charCodeAt(index);
|
|
if (code < 0x80) {
|
|
blocks[i >> 2] |= code << SHIFT[i++ & 3];
|
|
} else if (code < 0x800) {
|
|
blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3];
|
|
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
|
|
} else if (code < 0xd800 || code >= 0xe000) {
|
|
blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3];
|
|
blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
|
|
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
|
|
} else {
|
|
code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
|
|
blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3];
|
|
blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3];
|
|
blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
|
|
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
|
|
}
|
|
}
|
|
}
|
|
|
|
this.lastByteIndex = i;
|
|
this.bytes += i - this.start;
|
|
if (i >= 64) {
|
|
this.block = blocks[16];
|
|
this.start = i - 64;
|
|
this.hash();
|
|
this.hashed = true;
|
|
} else {
|
|
this.start = i;
|
|
}
|
|
}
|
|
if (this.bytes > 4294967295) {
|
|
this.hBytes += this.bytes / 4294967296 << 0;
|
|
this.bytes = this.bytes % 4294967296;
|
|
}
|
|
return this;
|
|
};
|
|
|
|
Sha256.prototype.finalize = function () {
|
|
if (this.finalized) {
|
|
return;
|
|
}
|
|
this.finalized = true;
|
|
var blocks = this.blocks, i = this.lastByteIndex;
|
|
blocks[16] = this.block;
|
|
blocks[i >> 2] |= EXTRA[i & 3];
|
|
this.block = blocks[16];
|
|
if (i >= 56) {
|
|
if (!this.hashed) {
|
|
this.hash();
|
|
}
|
|
blocks[0] = this.block;
|
|
blocks[16] = blocks[1] = blocks[2] = blocks[3] =
|
|
blocks[4] = blocks[5] = blocks[6] = blocks[7] =
|
|
blocks[8] = blocks[9] = blocks[10] = blocks[11] =
|
|
blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
|
|
}
|
|
blocks[14] = this.hBytes << 3 | this.bytes >>> 29;
|
|
blocks[15] = this.bytes << 3;
|
|
this.hash();
|
|
};
|
|
|
|
Sha256.prototype.hash = function () {
|
|
var a = this.h0, b = this.h1, c = this.h2, d = this.h3, e = this.h4, f = this.h5, g = this.h6,
|
|
h = this.h7, blocks = this.blocks, j, s0, s1, maj, t1, t2, ch, ab, da, cd, bc;
|
|
|
|
for (j = 16; j < 64; ++j) {
|
|
// rightrotate
|
|
t1 = blocks[j - 15];
|
|
s0 = ((t1 >>> 7) | (t1 << 25)) ^ ((t1 >>> 18) | (t1 << 14)) ^ (t1 >>> 3);
|
|
t1 = blocks[j - 2];
|
|
s1 = ((t1 >>> 17) | (t1 << 15)) ^ ((t1 >>> 19) | (t1 << 13)) ^ (t1 >>> 10);
|
|
blocks[j] = blocks[j - 16] + s0 + blocks[j - 7] + s1 << 0;
|
|
}
|
|
|
|
bc = b & c;
|
|
for (j = 0; j < 64; j += 4) {
|
|
if (this.first) {
|
|
if (this.is224) {
|
|
ab = 300032;
|
|
t1 = blocks[0] - 1413257819;
|
|
h = t1 - 150054599 << 0;
|
|
d = t1 + 24177077 << 0;
|
|
} else {
|
|
ab = 704751109;
|
|
t1 = blocks[0] - 210244248;
|
|
h = t1 - 1521486534 << 0;
|
|
d = t1 + 143694565 << 0;
|
|
}
|
|
this.first = false;
|
|
} else {
|
|
s0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10));
|
|
s1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7));
|
|
ab = a & b;
|
|
maj = ab ^ (a & c) ^ bc;
|
|
ch = (e & f) ^ (~e & g);
|
|
t1 = h + s1 + ch + K[j] + blocks[j];
|
|
t2 = s0 + maj;
|
|
h = d + t1 << 0;
|
|
d = t1 + t2 << 0;
|
|
}
|
|
s0 = ((d >>> 2) | (d << 30)) ^ ((d >>> 13) | (d << 19)) ^ ((d >>> 22) | (d << 10));
|
|
s1 = ((h >>> 6) | (h << 26)) ^ ((h >>> 11) | (h << 21)) ^ ((h >>> 25) | (h << 7));
|
|
da = d & a;
|
|
maj = da ^ (d & b) ^ ab;
|
|
ch = (h & e) ^ (~h & f);
|
|
t1 = g + s1 + ch + K[j + 1] + blocks[j + 1];
|
|
t2 = s0 + maj;
|
|
g = c + t1 << 0;
|
|
c = t1 + t2 << 0;
|
|
s0 = ((c >>> 2) | (c << 30)) ^ ((c >>> 13) | (c << 19)) ^ ((c >>> 22) | (c << 10));
|
|
s1 = ((g >>> 6) | (g << 26)) ^ ((g >>> 11) | (g << 21)) ^ ((g >>> 25) | (g << 7));
|
|
cd = c & d;
|
|
maj = cd ^ (c & a) ^ da;
|
|
ch = (g & h) ^ (~g & e);
|
|
t1 = f + s1 + ch + K[j + 2] + blocks[j + 2];
|
|
t2 = s0 + maj;
|
|
f = b + t1 << 0;
|
|
b = t1 + t2 << 0;
|
|
s0 = ((b >>> 2) | (b << 30)) ^ ((b >>> 13) | (b << 19)) ^ ((b >>> 22) | (b << 10));
|
|
s1 = ((f >>> 6) | (f << 26)) ^ ((f >>> 11) | (f << 21)) ^ ((f >>> 25) | (f << 7));
|
|
bc = b & c;
|
|
maj = bc ^ (b & d) ^ cd;
|
|
ch = (f & g) ^ (~f & h);
|
|
t1 = e + s1 + ch + K[j + 3] + blocks[j + 3];
|
|
t2 = s0 + maj;
|
|
e = a + t1 << 0;
|
|
a = t1 + t2 << 0;
|
|
}
|
|
|
|
this.h0 = this.h0 + a << 0;
|
|
this.h1 = this.h1 + b << 0;
|
|
this.h2 = this.h2 + c << 0;
|
|
this.h3 = this.h3 + d << 0;
|
|
this.h4 = this.h4 + e << 0;
|
|
this.h5 = this.h5 + f << 0;
|
|
this.h6 = this.h6 + g << 0;
|
|
this.h7 = this.h7 + h << 0;
|
|
};
|
|
|
|
Sha256.prototype.hex = function () {
|
|
this.finalize();
|
|
|
|
var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4, h5 = this.h5,
|
|
h6 = this.h6, h7 = this.h7;
|
|
|
|
var hex = HEX_CHARS[(h0 >> 28) & 0x0F] + HEX_CHARS[(h0 >> 24) & 0x0F] +
|
|
HEX_CHARS[(h0 >> 20) & 0x0F] + HEX_CHARS[(h0 >> 16) & 0x0F] +
|
|
HEX_CHARS[(h0 >> 12) & 0x0F] + HEX_CHARS[(h0 >> 8) & 0x0F] +
|
|
HEX_CHARS[(h0 >> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] +
|
|
HEX_CHARS[(h1 >> 28) & 0x0F] + HEX_CHARS[(h1 >> 24) & 0x0F] +
|
|
HEX_CHARS[(h1 >> 20) & 0x0F] + HEX_CHARS[(h1 >> 16) & 0x0F] +
|
|
HEX_CHARS[(h1 >> 12) & 0x0F] + HEX_CHARS[(h1 >> 8) & 0x0F] +
|
|
HEX_CHARS[(h1 >> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] +
|
|
HEX_CHARS[(h2 >> 28) & 0x0F] + HEX_CHARS[(h2 >> 24) & 0x0F] +
|
|
HEX_CHARS[(h2 >> 20) & 0x0F] + HEX_CHARS[(h2 >> 16) & 0x0F] +
|
|
HEX_CHARS[(h2 >> 12) & 0x0F] + HEX_CHARS[(h2 >> 8) & 0x0F] +
|
|
HEX_CHARS[(h2 >> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] +
|
|
HEX_CHARS[(h3 >> 28) & 0x0F] + HEX_CHARS[(h3 >> 24) & 0x0F] +
|
|
HEX_CHARS[(h3 >> 20) & 0x0F] + HEX_CHARS[(h3 >> 16) & 0x0F] +
|
|
HEX_CHARS[(h3 >> 12) & 0x0F] + HEX_CHARS[(h3 >> 8) & 0x0F] +
|
|
HEX_CHARS[(h3 >> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] +
|
|
HEX_CHARS[(h4 >> 28) & 0x0F] + HEX_CHARS[(h4 >> 24) & 0x0F] +
|
|
HEX_CHARS[(h4 >> 20) & 0x0F] + HEX_CHARS[(h4 >> 16) & 0x0F] +
|
|
HEX_CHARS[(h4 >> 12) & 0x0F] + HEX_CHARS[(h4 >> 8) & 0x0F] +
|
|
HEX_CHARS[(h4 >> 4) & 0x0F] + HEX_CHARS[h4 & 0x0F] +
|
|
HEX_CHARS[(h5 >> 28) & 0x0F] + HEX_CHARS[(h5 >> 24) & 0x0F] +
|
|
HEX_CHARS[(h5 >> 20) & 0x0F] + HEX_CHARS[(h5 >> 16) & 0x0F] +
|
|
HEX_CHARS[(h5 >> 12) & 0x0F] + HEX_CHARS[(h5 >> 8) & 0x0F] +
|
|
HEX_CHARS[(h5 >> 4) & 0x0F] + HEX_CHARS[h5 & 0x0F] +
|
|
HEX_CHARS[(h6 >> 28) & 0x0F] + HEX_CHARS[(h6 >> 24) & 0x0F] +
|
|
HEX_CHARS[(h6 >> 20) & 0x0F] + HEX_CHARS[(h6 >> 16) & 0x0F] +
|
|
HEX_CHARS[(h6 >> 12) & 0x0F] + HEX_CHARS[(h6 >> 8) & 0x0F] +
|
|
HEX_CHARS[(h6 >> 4) & 0x0F] + HEX_CHARS[h6 & 0x0F];
|
|
if (!this.is224) {
|
|
hex += HEX_CHARS[(h7 >> 28) & 0x0F] + HEX_CHARS[(h7 >> 24) & 0x0F] +
|
|
HEX_CHARS[(h7 >> 20) & 0x0F] + HEX_CHARS[(h7 >> 16) & 0x0F] +
|
|
HEX_CHARS[(h7 >> 12) & 0x0F] + HEX_CHARS[(h7 >> 8) & 0x0F] +
|
|
HEX_CHARS[(h7 >> 4) & 0x0F] + HEX_CHARS[h7 & 0x0F];
|
|
}
|
|
return hex;
|
|
};
|
|
|
|
Sha256.prototype.toString = Sha256.prototype.hex;
|
|
|
|
Sha256.prototype.digest = function () {
|
|
this.finalize();
|
|
|
|
var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4, h5 = this.h5,
|
|
h6 = this.h6, h7 = this.h7;
|
|
|
|
var arr = [
|
|
(h0 >> 24) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 8) & 0xFF, h0 & 0xFF,
|
|
(h1 >> 24) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 8) & 0xFF, h1 & 0xFF,
|
|
(h2 >> 24) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 8) & 0xFF, h2 & 0xFF,
|
|
(h3 >> 24) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 8) & 0xFF, h3 & 0xFF,
|
|
(h4 >> 24) & 0xFF, (h4 >> 16) & 0xFF, (h4 >> 8) & 0xFF, h4 & 0xFF,
|
|
(h5 >> 24) & 0xFF, (h5 >> 16) & 0xFF, (h5 >> 8) & 0xFF, h5 & 0xFF,
|
|
(h6 >> 24) & 0xFF, (h6 >> 16) & 0xFF, (h6 >> 8) & 0xFF, h6 & 0xFF
|
|
];
|
|
if (!this.is224) {
|
|
arr.push((h7 >> 24) & 0xFF, (h7 >> 16) & 0xFF, (h7 >> 8) & 0xFF, h7 & 0xFF);
|
|
}
|
|
return arr;
|
|
};
|
|
|
|
Sha256.prototype.array = Sha256.prototype.digest;
|
|
|
|
Sha256.prototype.arrayBuffer = function () {
|
|
this.finalize();
|
|
|
|
var buffer = new ArrayBuffer(this.is224 ? 28 : 32);
|
|
var dataView = new DataView(buffer);
|
|
dataView.setUint32(0, this.h0);
|
|
dataView.setUint32(4, this.h1);
|
|
dataView.setUint32(8, this.h2);
|
|
dataView.setUint32(12, this.h3);
|
|
dataView.setUint32(16, this.h4);
|
|
dataView.setUint32(20, this.h5);
|
|
dataView.setUint32(24, this.h6);
|
|
if (!this.is224) {
|
|
dataView.setUint32(28, this.h7);
|
|
}
|
|
return buffer;
|
|
};
|
|
|
|
function HmacSha256(key, is224, sharedMemory) {
|
|
var i, type = typeof key;
|
|
if (type === 'string') {
|
|
var bytes = [], length = key.length, index = 0, code;
|
|
for (i = 0; i < length; ++i) {
|
|
code = key.charCodeAt(i);
|
|
if (code < 0x80) {
|
|
bytes[index++] = code;
|
|
} else if (code < 0x800) {
|
|
bytes[index++] = (0xc0 | (code >> 6));
|
|
bytes[index++] = (0x80 | (code & 0x3f));
|
|
} else if (code < 0xd800 || code >= 0xe000) {
|
|
bytes[index++] = (0xe0 | (code >> 12));
|
|
bytes[index++] = (0x80 | ((code >> 6) & 0x3f));
|
|
bytes[index++] = (0x80 | (code & 0x3f));
|
|
} else {
|
|
code = 0x10000 + (((code & 0x3ff) << 10) | (key.charCodeAt(++i) & 0x3ff));
|
|
bytes[index++] = (0xf0 | (code >> 18));
|
|
bytes[index++] = (0x80 | ((code >> 12) & 0x3f));
|
|
bytes[index++] = (0x80 | ((code >> 6) & 0x3f));
|
|
bytes[index++] = (0x80 | (code & 0x3f));
|
|
}
|
|
}
|
|
key = bytes;
|
|
} else {
|
|
if (type === 'object') {
|
|
if (key === null) {
|
|
throw new Error(ERROR);
|
|
} else if (ARRAY_BUFFER && key.constructor === ArrayBuffer) {
|
|
key = new Uint8Array(key);
|
|
} else if (!Array.isArray(key)) {
|
|
if (!ARRAY_BUFFER || !ArrayBuffer.isView(key)) {
|
|
throw new Error(ERROR);
|
|
}
|
|
}
|
|
} else {
|
|
throw new Error(ERROR);
|
|
}
|
|
}
|
|
|
|
if (key.length > 64) {
|
|
key = (new Sha256(is224, true)).update(key).array();
|
|
}
|
|
|
|
var oKeyPad = [], iKeyPad = [];
|
|
for (i = 0; i < 64; ++i) {
|
|
var b = key[i] || 0;
|
|
oKeyPad[i] = 0x5c ^ b;
|
|
iKeyPad[i] = 0x36 ^ b;
|
|
}
|
|
|
|
Sha256.call(this, is224, sharedMemory);
|
|
|
|
this.update(iKeyPad);
|
|
this.oKeyPad = oKeyPad;
|
|
this.inner = true;
|
|
this.sharedMemory = sharedMemory;
|
|
}
|
|
HmacSha256.prototype = new Sha256();
|
|
|
|
HmacSha256.prototype.finalize = function () {
|
|
Sha256.prototype.finalize.call(this);
|
|
if (this.inner) {
|
|
this.inner = false;
|
|
var innerHash = this.array();
|
|
Sha256.call(this, this.is224, this.sharedMemory);
|
|
this.update(this.oKeyPad);
|
|
this.update(innerHash);
|
|
Sha256.prototype.finalize.call(this);
|
|
}
|
|
};
|
|
|
|
var exports = createMethod();
|
|
exports.sha256 = exports;
|
|
exports.sha224 = createMethod(true);
|
|
exports.sha256.hmac = createHmacMethod();
|
|
exports.sha224.hmac = createHmacMethod(true);
|
|
|
|
if (COMMON_JS) {
|
|
module.exports = exports;
|
|
} else {
|
|
root.sha256 = exports.sha256;
|
|
root.sha224 = exports.sha224;
|
|
if (AMD) {
|
|
define(function () {
|
|
return exports;
|
|
});
|
|
}
|
|
}
|
|
})();
|
|
|
|
|
|
/*! MIT License. Copyright 2015-2018 Richard Moore <me@ricmoo.com>. See LICENSE.txt. */
|
|
(function(root) {
|
|
"use strict";
|
|
|
|
function checkInt(value) {
|
|
return (parseInt(value) === value);
|
|
}
|
|
|
|
function checkInts(arrayish) {
|
|
if (!checkInt(arrayish.length)) { return false; }
|
|
|
|
for (var i = 0; i < arrayish.length; i++) {
|
|
if (!checkInt(arrayish[i]) || arrayish[i] < 0 || arrayish[i] > 255) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function coerceArray(arg, copy) {
|
|
|
|
// ArrayBuffer view
|
|
if (arg.buffer && arg.name === 'Uint8Array') {
|
|
|
|
if (copy) {
|
|
if (arg.slice) {
|
|
arg = arg.slice();
|
|
} else {
|
|
arg = Array.prototype.slice.call(arg);
|
|
}
|
|
}
|
|
|
|
return arg;
|
|
}
|
|
|
|
// It's an array; check it is a valid representation of a byte
|
|
if (Array.isArray(arg)) {
|
|
if (!checkInts(arg)) {
|
|
throw new Error('Array contains invalid value: ' + arg);
|
|
}
|
|
|
|
return new Uint8Array(arg);
|
|
}
|
|
|
|
// Something else, but behaves like an array (maybe a Buffer? Arguments?)
|
|
if (checkInt(arg.length) && checkInts(arg)) {
|
|
return new Uint8Array(arg);
|
|
}
|
|
|
|
throw new Error('unsupported array-like object');
|
|
}
|
|
|
|
function createArray(length) {
|
|
return new Uint8Array(length);
|
|
}
|
|
|
|
function copyArray(sourceArray, targetArray, targetStart, sourceStart, sourceEnd) {
|
|
if (sourceStart != null || sourceEnd != null) {
|
|
if (sourceArray.slice) {
|
|
sourceArray = sourceArray.slice(sourceStart, sourceEnd);
|
|
} else {
|
|
sourceArray = Array.prototype.slice.call(sourceArray, sourceStart, sourceEnd);
|
|
}
|
|
}
|
|
targetArray.set(sourceArray, targetStart);
|
|
}
|
|
|
|
|
|
|
|
var convertUtf8 = (function() {
|
|
function toBytes(text) {
|
|
var result = [], i = 0;
|
|
text = encodeURI(text);
|
|
while (i < text.length) {
|
|
var c = text.charCodeAt(i++);
|
|
|
|
// if it is a % sign, encode the following 2 bytes as a hex value
|
|
if (c === 37) {
|
|
result.push(parseInt(text.substr(i, 2), 16))
|
|
i += 2;
|
|
|
|
// otherwise, just the actual byte
|
|
} else {
|
|
result.push(c)
|
|
}
|
|
}
|
|
|
|
return coerceArray(result);
|
|
}
|
|
|
|
function fromBytes(bytes) {
|
|
var result = [], i = 0;
|
|
|
|
while (i < bytes.length) {
|
|
var c = bytes[i];
|
|
|
|
if (c < 128) {
|
|
result.push(String.fromCharCode(c));
|
|
i++;
|
|
} else if (c > 191 && c < 224) {
|
|
result.push(String.fromCharCode(((c & 0x1f) << 6) | (bytes[i + 1] & 0x3f)));
|
|
i += 2;
|
|
} else {
|
|
result.push(String.fromCharCode(((c & 0x0f) << 12) | ((bytes[i + 1] & 0x3f) << 6) | (bytes[i + 2] & 0x3f)));
|
|
i += 3;
|
|
}
|
|
}
|
|
|
|
return result.join('');
|
|
}
|
|
|
|
return {
|
|
toBytes: toBytes,
|
|
fromBytes: fromBytes,
|
|
}
|
|
})();
|
|
|
|
var convertHex = (function() {
|
|
function toBytes(text) {
|
|
var result = [];
|
|
for (var i = 0; i < text.length; i += 2) {
|
|
result.push(parseInt(text.substr(i, 2), 16));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// http://ixti.net/development/javascript/2011/11/11/base64-encodedecode-of-utf8-in-browser-with-js.html
|
|
var Hex = '0123456789abcdef';
|
|
|
|
function fromBytes(bytes) {
|
|
var result = [];
|
|
for (var i = 0; i < bytes.length; i++) {
|
|
var v = bytes[i];
|
|
result.push(Hex[(v & 0xf0) >> 4] + Hex[v & 0x0f]);
|
|
}
|
|
return result.join('');
|
|
}
|
|
|
|
return {
|
|
toBytes: toBytes,
|
|
fromBytes: fromBytes,
|
|
}
|
|
})();
|
|
|
|
|
|
// Number of rounds by keysize
|
|
var numberOfRounds = {16: 10, 24: 12, 32: 14}
|
|
|
|
// Round constant words
|
|
var rcon = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91];
|
|
|
|
// S-box and Inverse S-box (S is for Substitution)
|
|
var S = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16];
|
|
var Si =[0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d];
|
|
|
|
// Transformations for encryption
|
|
var T1 = [0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a];
|
|
var T2 = [0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616];
|
|
var T3 = [0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16];
|
|
var T4 = [0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c];
|
|
|
|
// Transformations for decryption
|
|
var T5 = [0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742];
|
|
var T6 = [0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857];
|
|
var T7 = [0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8];
|
|
var T8 = [0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0];
|
|
|
|
// Transformations for decryption key expansion
|
|
var U1 = [0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3];
|
|
var U2 = [0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697];
|
|
var U3 = [0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46];
|
|
var U4 = [0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d];
|
|
|
|
function convertToInt32(bytes) {
|
|
var result = [];
|
|
for (var i = 0; i < bytes.length; i += 4) {
|
|
result.push(
|
|
(bytes[i ] << 24) |
|
|
(bytes[i + 1] << 16) |
|
|
(bytes[i + 2] << 8) |
|
|
bytes[i + 3]
|
|
);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
var AES = function(key) {
|
|
if (!(this instanceof AES)) {
|
|
throw Error('AES must be instanitated with `new`');
|
|
}
|
|
|
|
Object.defineProperty(this, 'key', {
|
|
value: coerceArray(key, true)
|
|
});
|
|
|
|
this._prepare();
|
|
}
|
|
|
|
|
|
AES.prototype._prepare = function() {
|
|
|
|
var rounds = numberOfRounds[this.key.length];
|
|
if (rounds == null) {
|
|
throw new Error('invalid key size (must be 16, 24 or 32 bytes)');
|
|
}
|
|
|
|
// encryption round keys
|
|
this._Ke = [];
|
|
|
|
// decryption round keys
|
|
this._Kd = [];
|
|
|
|
for (var i = 0; i <= rounds; i++) {
|
|
this._Ke.push([0, 0, 0, 0]);
|
|
this._Kd.push([0, 0, 0, 0]);
|
|
}
|
|
|
|
var roundKeyCount = (rounds + 1) * 4;
|
|
var KC = this.key.length / 4;
|
|
|
|
// convert the key into ints
|
|
var tk = convertToInt32(this.key);
|
|
|
|
// copy values into round key arrays
|
|
var index;
|
|
for (var i = 0; i < KC; i++) {
|
|
index = i >> 2;
|
|
this._Ke[index][i % 4] = tk[i];
|
|
this._Kd[rounds - index][i % 4] = tk[i];
|
|
}
|
|
|
|
// key expansion (fips-197 section 5.2)
|
|
var rconpointer = 0;
|
|
var t = KC, tt;
|
|
while (t < roundKeyCount) {
|
|
tt = tk[KC - 1];
|
|
tk[0] ^= ((S[(tt >> 16) & 0xFF] << 24) ^
|
|
(S[(tt >> 8) & 0xFF] << 16) ^
|
|
(S[ tt & 0xFF] << 8) ^
|
|
S[(tt >> 24) & 0xFF] ^
|
|
(rcon[rconpointer] << 24));
|
|
rconpointer += 1;
|
|
|
|
// key expansion (for non-256 bit)
|
|
if (KC != 8) {
|
|
for (var i = 1; i < KC; i++) {
|
|
tk[i] ^= tk[i - 1];
|
|
}
|
|
|
|
// key expansion for 256-bit keys is "slightly different" (fips-197)
|
|
} else {
|
|
for (var i = 1; i < (KC / 2); i++) {
|
|
tk[i] ^= tk[i - 1];
|
|
}
|
|
tt = tk[(KC / 2) - 1];
|
|
|
|
tk[KC / 2] ^= (S[ tt & 0xFF] ^
|
|
(S[(tt >> 8) & 0xFF] << 8) ^
|
|
(S[(tt >> 16) & 0xFF] << 16) ^
|
|
(S[(tt >> 24) & 0xFF] << 24));
|
|
|
|
for (var i = (KC / 2) + 1; i < KC; i++) {
|
|
tk[i] ^= tk[i - 1];
|
|
}
|
|
}
|
|
|
|
// copy values into round key arrays
|
|
var i = 0, r, c;
|
|
while (i < KC && t < roundKeyCount) {
|
|
r = t >> 2;
|
|
c = t % 4;
|
|
this._Ke[r][c] = tk[i];
|
|
this._Kd[rounds - r][c] = tk[i++];
|
|
t++;
|
|
}
|
|
}
|
|
|
|
// inverse-cipher-ify the decryption round key (fips-197 section 5.3)
|
|
for (var r = 1; r < rounds; r++) {
|
|
for (var c = 0; c < 4; c++) {
|
|
tt = this._Kd[r][c];
|
|
this._Kd[r][c] = (U1[(tt >> 24) & 0xFF] ^
|
|
U2[(tt >> 16) & 0xFF] ^
|
|
U3[(tt >> 8) & 0xFF] ^
|
|
U4[ tt & 0xFF]);
|
|
}
|
|
}
|
|
}
|
|
|
|
AES.prototype.encrypt = function(plaintext) {
|
|
if (plaintext.length != 16) {
|
|
throw new Error('invalid plaintext size (must be 16 bytes)');
|
|
}
|
|
|
|
var rounds = this._Ke.length - 1;
|
|
var a = [0, 0, 0, 0];
|
|
|
|
// convert plaintext to (ints ^ key)
|
|
var t = convertToInt32(plaintext);
|
|
for (var i = 0; i < 4; i++) {
|
|
t[i] ^= this._Ke[0][i];
|
|
}
|
|
|
|
// apply round transforms
|
|
for (var r = 1; r < rounds; r++) {
|
|
for (var i = 0; i < 4; i++) {
|
|
a[i] = (T1[(t[ i ] >> 24) & 0xff] ^
|
|
T2[(t[(i + 1) % 4] >> 16) & 0xff] ^
|
|
T3[(t[(i + 2) % 4] >> 8) & 0xff] ^
|
|
T4[ t[(i + 3) % 4] & 0xff] ^
|
|
this._Ke[r][i]);
|
|
}
|
|
t = a.slice();
|
|
}
|
|
|
|
// the last round is special
|
|
var result = createArray(16), tt;
|
|
for (var i = 0; i < 4; i++) {
|
|
tt = this._Ke[rounds][i];
|
|
result[4 * i ] = (S[(t[ i ] >> 24) & 0xff] ^ (tt >> 24)) & 0xff;
|
|
result[4 * i + 1] = (S[(t[(i + 1) % 4] >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
|
|
result[4 * i + 2] = (S[(t[(i + 2) % 4] >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
|
|
result[4 * i + 3] = (S[ t[(i + 3) % 4] & 0xff] ^ tt ) & 0xff;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
AES.prototype.decrypt = function(ciphertext) {
|
|
if (ciphertext.length != 16) {
|
|
throw new Error('invalid ciphertext size (must be 16 bytes)');
|
|
}
|
|
|
|
var rounds = this._Kd.length - 1;
|
|
var a = [0, 0, 0, 0];
|
|
|
|
// convert plaintext to (ints ^ key)
|
|
var t = convertToInt32(ciphertext);
|
|
for (var i = 0; i < 4; i++) {
|
|
t[i] ^= this._Kd[0][i];
|
|
}
|
|
|
|
// apply round transforms
|
|
for (var r = 1; r < rounds; r++) {
|
|
for (var i = 0; i < 4; i++) {
|
|
a[i] = (T5[(t[ i ] >> 24) & 0xff] ^
|
|
T6[(t[(i + 3) % 4] >> 16) & 0xff] ^
|
|
T7[(t[(i + 2) % 4] >> 8) & 0xff] ^
|
|
T8[ t[(i + 1) % 4] & 0xff] ^
|
|
this._Kd[r][i]);
|
|
}
|
|
t = a.slice();
|
|
}
|
|
|
|
// the last round is special
|
|
var result = createArray(16), tt;
|
|
for (var i = 0; i < 4; i++) {
|
|
tt = this._Kd[rounds][i];
|
|
result[4 * i ] = (Si[(t[ i ] >> 24) & 0xff] ^ (tt >> 24)) & 0xff;
|
|
result[4 * i + 1] = (Si[(t[(i + 3) % 4] >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
|
|
result[4 * i + 2] = (Si[(t[(i + 2) % 4] >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
|
|
result[4 * i + 3] = (Si[ t[(i + 1) % 4] & 0xff] ^ tt ) & 0xff;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Mode Of Operation - Electonic Codebook (ECB)
|
|
*/
|
|
var ModeOfOperationECB = function(key) {
|
|
if (!(this instanceof ModeOfOperationECB)) {
|
|
throw Error('AES must be instanitated with `new`');
|
|
}
|
|
|
|
this.description = "Electronic Code Block";
|
|
this.name = "ecb";
|
|
|
|
this._aes = new AES(key);
|
|
}
|
|
|
|
ModeOfOperationECB.prototype.encrypt = function(plaintext) {
|
|
plaintext = coerceArray(plaintext);
|
|
|
|
if ((plaintext.length % 16) !== 0) {
|
|
throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
|
|
}
|
|
|
|
var ciphertext = createArray(plaintext.length);
|
|
var block = createArray(16);
|
|
|
|
for (var i = 0; i < plaintext.length; i += 16) {
|
|
copyArray(plaintext, block, 0, i, i + 16);
|
|
block = this._aes.encrypt(block);
|
|
copyArray(block, ciphertext, i);
|
|
}
|
|
|
|
return ciphertext;
|
|
}
|
|
|
|
ModeOfOperationECB.prototype.decrypt = function(ciphertext) {
|
|
ciphertext = coerceArray(ciphertext);
|
|
|
|
if ((ciphertext.length % 16) !== 0) {
|
|
throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
|
|
}
|
|
|
|
var plaintext = createArray(ciphertext.length);
|
|
var block = createArray(16);
|
|
|
|
for (var i = 0; i < ciphertext.length; i += 16) {
|
|
copyArray(ciphertext, block, 0, i, i + 16);
|
|
block = this._aes.decrypt(block);
|
|
copyArray(block, plaintext, i);
|
|
}
|
|
|
|
return plaintext;
|
|
}
|
|
|
|
|
|
/**
|
|
* Mode Of Operation - Cipher Block Chaining (CBC)
|
|
*/
|
|
var ModeOfOperationCBC = function(key, iv) {
|
|
if (!(this instanceof ModeOfOperationCBC)) {
|
|
throw Error('AES must be instanitated with `new`');
|
|
}
|
|
|
|
this.description = "Cipher Block Chaining";
|
|
this.name = "cbc";
|
|
|
|
if (!iv) {
|
|
iv = createArray(16);
|
|
|
|
} else if (iv.length != 16) {
|
|
throw new Error('invalid initialation vector size (must be 16 bytes)');
|
|
}
|
|
|
|
this._lastCipherblock = coerceArray(iv, true);
|
|
|
|
this._aes = new AES(key);
|
|
}
|
|
|
|
ModeOfOperationCBC.prototype.encrypt = function(plaintext) {
|
|
plaintext = coerceArray(plaintext);
|
|
|
|
if ((plaintext.length % 16) !== 0) {
|
|
throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
|
|
}
|
|
|
|
var ciphertext = createArray(plaintext.length);
|
|
var block = createArray(16);
|
|
|
|
for (var i = 0; i < plaintext.length; i += 16) {
|
|
copyArray(plaintext, block, 0, i, i + 16);
|
|
|
|
for (var j = 0; j < 16; j++) {
|
|
block[j] ^= this._lastCipherblock[j];
|
|
}
|
|
|
|
this._lastCipherblock = this._aes.encrypt(block);
|
|
copyArray(this._lastCipherblock, ciphertext, i);
|
|
}
|
|
|
|
return ciphertext;
|
|
}
|
|
|
|
ModeOfOperationCBC.prototype.decrypt = function(ciphertext) {
|
|
ciphertext = coerceArray(ciphertext);
|
|
|
|
if ((ciphertext.length % 16) !== 0) {
|
|
throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
|
|
}
|
|
|
|
var plaintext = createArray(ciphertext.length);
|
|
var block = createArray(16);
|
|
|
|
for (var i = 0; i < ciphertext.length; i += 16) {
|
|
copyArray(ciphertext, block, 0, i, i + 16);
|
|
block = this._aes.decrypt(block);
|
|
|
|
for (var j = 0; j < 16; j++) {
|
|
plaintext[i + j] = block[j] ^ this._lastCipherblock[j];
|
|
}
|
|
|
|
copyArray(ciphertext, this._lastCipherblock, 0, i, i + 16);
|
|
}
|
|
|
|
return plaintext;
|
|
}
|
|
|
|
|
|
/**
|
|
* Mode Of Operation - Cipher Feedback (CFB)
|
|
*/
|
|
var ModeOfOperationCFB = function(key, iv, segmentSize) {
|
|
if (!(this instanceof ModeOfOperationCFB)) {
|
|
throw Error('AES must be instanitated with `new`');
|
|
}
|
|
|
|
this.description = "Cipher Feedback";
|
|
this.name = "cfb";
|
|
|
|
if (!iv) {
|
|
iv = createArray(16);
|
|
|
|
} else if (iv.length != 16) {
|
|
throw new Error('invalid initialation vector size (must be 16 size)');
|
|
}
|
|
|
|
if (!segmentSize) { segmentSize = 1; }
|
|
|
|
this.segmentSize = segmentSize;
|
|
|
|
this._shiftRegister = coerceArray(iv, true);
|
|
|
|
this._aes = new AES(key);
|
|
}
|
|
|
|
ModeOfOperationCFB.prototype.encrypt = function(plaintext) {
|
|
if ((plaintext.length % this.segmentSize) != 0) {
|
|
throw new Error('invalid plaintext size (must be segmentSize bytes)');
|
|
}
|
|
|
|
var encrypted = coerceArray(plaintext, true);
|
|
|
|
var xorSegment;
|
|
for (var i = 0; i < encrypted.length; i += this.segmentSize) {
|
|
xorSegment = this._aes.encrypt(this._shiftRegister);
|
|
for (var j = 0; j < this.segmentSize; j++) {
|
|
encrypted[i + j] ^= xorSegment[j];
|
|
}
|
|
|
|
// Shift the register
|
|
copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
|
|
copyArray(encrypted, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
|
|
}
|
|
|
|
return encrypted;
|
|
}
|
|
|
|
ModeOfOperationCFB.prototype.decrypt = function(ciphertext) {
|
|
if ((ciphertext.length % this.segmentSize) != 0) {
|
|
throw new Error('invalid ciphertext size (must be segmentSize bytes)');
|
|
}
|
|
|
|
var plaintext = coerceArray(ciphertext, true);
|
|
|
|
var xorSegment;
|
|
for (var i = 0; i < plaintext.length; i += this.segmentSize) {
|
|
xorSegment = this._aes.encrypt(this._shiftRegister);
|
|
|
|
for (var j = 0; j < this.segmentSize; j++) {
|
|
plaintext[i + j] ^= xorSegment[j];
|
|
}
|
|
|
|
// Shift the register
|
|
copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
|
|
copyArray(ciphertext, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
|
|
}
|
|
|
|
return plaintext;
|
|
}
|
|
|
|
/**
|
|
* Mode Of Operation - Output Feedback (OFB)
|
|
*/
|
|
var ModeOfOperationOFB = function(key, iv) {
|
|
if (!(this instanceof ModeOfOperationOFB)) {
|
|
throw Error('AES must be instanitated with `new`');
|
|
}
|
|
|
|
this.description = "Output Feedback";
|
|
this.name = "ofb";
|
|
|
|
if (!iv) {
|
|
iv = createArray(16);
|
|
|
|
} else if (iv.length != 16) {
|
|
throw new Error('invalid initialation vector size (must be 16 bytes)');
|
|
}
|
|
|
|
this._lastPrecipher = coerceArray(iv, true);
|
|
this._lastPrecipherIndex = 16;
|
|
|
|
this._aes = new AES(key);
|
|
}
|
|
|
|
ModeOfOperationOFB.prototype.encrypt = function(plaintext) {
|
|
var encrypted = coerceArray(plaintext, true);
|
|
|
|
for (var i = 0; i < encrypted.length; i++) {
|
|
if (this._lastPrecipherIndex === 16) {
|
|
this._lastPrecipher = this._aes.encrypt(this._lastPrecipher);
|
|
this._lastPrecipherIndex = 0;
|
|
}
|
|
encrypted[i] ^= this._lastPrecipher[this._lastPrecipherIndex++];
|
|
}
|
|
|
|
return encrypted;
|
|
}
|
|
|
|
// Decryption is symetric
|
|
ModeOfOperationOFB.prototype.decrypt = ModeOfOperationOFB.prototype.encrypt;
|
|
|
|
|
|
/**
|
|
* Counter object for CTR common mode of operation
|
|
*/
|
|
var Counter = function(initialValue) {
|
|
if (!(this instanceof Counter)) {
|
|
throw Error('Counter must be instanitated with `new`');
|
|
}
|
|
|
|
// We allow 0, but anything false-ish uses the default 1
|
|
if (initialValue !== 0 && !initialValue) { initialValue = 1; }
|
|
|
|
if (typeof(initialValue) === 'number') {
|
|
this._counter = createArray(16);
|
|
this.setValue(initialValue);
|
|
|
|
} else {
|
|
this.setBytes(initialValue);
|
|
}
|
|
}
|
|
|
|
Counter.prototype.setValue = function(value) {
|
|
if (typeof(value) !== 'number' || parseInt(value) != value) {
|
|
throw new Error('invalid counter value (must be an integer)');
|
|
}
|
|
|
|
// We cannot safely handle numbers beyond the safe range for integers
|
|
if (value > Number.MAX_SAFE_INTEGER) {
|
|
throw new Error('integer value out of safe range');
|
|
}
|
|
|
|
for (var index = 15; index >= 0; --index) {
|
|
this._counter[index] = value % 256;
|
|
value = parseInt(value / 256);
|
|
}
|
|
}
|
|
|
|
Counter.prototype.setBytes = function(bytes) {
|
|
bytes = coerceArray(bytes, true);
|
|
|
|
if (bytes.length != 16) {
|
|
throw new Error('invalid counter bytes size (must be 16 bytes)');
|
|
}
|
|
|
|
this._counter = bytes;
|
|
};
|
|
|
|
Counter.prototype.increment = function() {
|
|
for (var i = 15; i >= 0; i--) {
|
|
if (this._counter[i] === 255) {
|
|
this._counter[i] = 0;
|
|
} else {
|
|
this._counter[i]++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Mode Of Operation - Counter (CTR)
|
|
*/
|
|
var ModeOfOperationCTR = function(key, counter) {
|
|
if (!(this instanceof ModeOfOperationCTR)) {
|
|
throw Error('AES must be instanitated with `new`');
|
|
}
|
|
|
|
this.description = "Counter";
|
|
this.name = "ctr";
|
|
|
|
if (!(counter instanceof Counter)) {
|
|
counter = new Counter(counter)
|
|
}
|
|
|
|
this._counter = counter;
|
|
|
|
this._remainingCounter = null;
|
|
this._remainingCounterIndex = 16;
|
|
|
|
this._aes = new AES(key);
|
|
}
|
|
|
|
ModeOfOperationCTR.prototype.encrypt = function(plaintext) {
|
|
var encrypted = coerceArray(plaintext, true);
|
|
|
|
for (var i = 0; i < encrypted.length; i++) {
|
|
if (this._remainingCounterIndex === 16) {
|
|
this._remainingCounter = this._aes.encrypt(this._counter._counter);
|
|
this._remainingCounterIndex = 0;
|
|
this._counter.increment();
|
|
}
|
|
encrypted[i] ^= this._remainingCounter[this._remainingCounterIndex++];
|
|
}
|
|
|
|
return encrypted;
|
|
}
|
|
|
|
// Decryption is symetric
|
|
ModeOfOperationCTR.prototype.decrypt = ModeOfOperationCTR.prototype.encrypt;
|
|
|
|
|
|
///////////////////////
|
|
// Padding
|
|
|
|
// See:https://tools.ietf.org/html/rfc2315
|
|
function pkcs7pad(data) {
|
|
data = coerceArray(data, true);
|
|
var padder = 16 - (data.length % 16);
|
|
var result = createArray(data.length + padder);
|
|
copyArray(data, result);
|
|
for (var i = data.length; i < result.length; i++) {
|
|
result[i] = padder;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function pkcs7strip(data) {
|
|
data = coerceArray(data, true);
|
|
if (data.length < 16) { throw new Error('PKCS#7 invalid length'); }
|
|
|
|
var padder = data[data.length - 1];
|
|
if (padder > 16) { throw new Error('PKCS#7 padding byte out of range'); }
|
|
|
|
var length = data.length - padder;
|
|
for (var i = 0; i < padder; i++) {
|
|
if (data[length + i] !== padder) {
|
|
throw new Error('PKCS#7 invalid padding byte');
|
|
}
|
|
}
|
|
|
|
var result = createArray(length);
|
|
copyArray(data, result, 0, 0, length);
|
|
return result;
|
|
}
|
|
|
|
///////////////////////
|
|
// Exporting
|
|
|
|
|
|
// The block cipher
|
|
var aesjs = {
|
|
AES: AES,
|
|
Counter: Counter,
|
|
|
|
ModeOfOperation: {
|
|
ecb: ModeOfOperationECB,
|
|
cbc: ModeOfOperationCBC,
|
|
cfb: ModeOfOperationCFB,
|
|
ofb: ModeOfOperationOFB,
|
|
ctr: ModeOfOperationCTR
|
|
},
|
|
|
|
utils: {
|
|
hex: convertHex,
|
|
utf8: convertUtf8
|
|
},
|
|
|
|
padding: {
|
|
pkcs7: {
|
|
pad: pkcs7pad,
|
|
strip: pkcs7strip
|
|
}
|
|
},
|
|
|
|
_arrayTest: {
|
|
coerceArray: coerceArray,
|
|
createArray: createArray,
|
|
copyArray: copyArray,
|
|
}
|
|
};
|
|
|
|
|
|
// node.js
|
|
if (typeof exports !== 'undefined') {
|
|
module.exports = aesjs
|
|
|
|
// RequireJS/AMD
|
|
// http://www.requirejs.org/docs/api.html
|
|
// https://github.com/amdjs/amdjs-api/wiki/AMD
|
|
} else if (typeof(define) === 'function' && define.amd) {
|
|
define([], function() { return aesjs; });
|
|
|
|
// Web Browsers
|
|
} else {
|
|
|
|
// If there was an existing library at "aesjs" make sure it's still available
|
|
if (root.aesjs) {
|
|
aesjs._aesjs = root.aesjs;
|
|
}
|
|
|
|
root.aesjs = aesjs;
|
|
}
|
|
|
|
|
|
})(this);
|
|
|
|
"use strict";
|
|
|
|
var RangeCoder //no dependencies
|
|
,Stream //no dependencies
|
|
,BitStream //depands on [Stream]
|
|
,Util //depands on [Stream]
|
|
,BWT //depands on [Util(Stream)]
|
|
,LogDistanceModel //depands on [Util(Stream)]
|
|
,NoModel //depands on [Util(Stream),BitStream(Stream)]
|
|
,DefSumModel //depands on [RangeCoder, Stream, Util(Stream)]
|
|
,FenwickModel //depands on [RangeCoder, Stream, Util(Stream)]
|
|
,BWTC //depands on [RangeCoder, Stream, BitStream(Stream), Util(Stream), BWT(Util(Stream)), LogDistanceModel(Util(Stream)), NoModel(Util(Stream),BitStream(Stream)), DefSumModel(RangeCoder, Stream, Util(Stream)), FenwickModel(RangeCoder, Stream, Util(Stream))]
|
|
;
|
|
|
|
|
|
RangeCoder = (function(){
|
|
/* Range Coder. Inspired by rangecod.c from rngcod13.zip from
|
|
* http://www.compressconsult.com/rangecoder/
|
|
* This JavaScript version is:
|
|
* Copyright (c) 2013 C. Scott Ananian.
|
|
*/
|
|
// Uses 32-bit integer math. Hopefully the JavaScript runtime figures
|
|
// that out. ;)
|
|
// see https://github.com/kripken/emscripten/wiki/LLVM-Types-in-JavaScript
|
|
// for some hints on doing 32-bit unsigned match in JavaScript.
|
|
// One key is the use of ">>>0" to change a signed result to unsigned.
|
|
var CODE_BITS = 32;
|
|
var Top_value = Math.pow(2, CODE_BITS - 1);
|
|
var SHIFT_BITS = (CODE_BITS - 9);
|
|
var EXTRA_BITS = ((CODE_BITS - 2) % 8 + 1);
|
|
var Bottom_value = (Top_value >>> 8);
|
|
|
|
var MAX_INT = Math.pow(2, CODE_BITS) - 1;
|
|
|
|
/* it is highly recommended that the total frequency count is less */
|
|
/* than 1 << 19 to minimize rounding effects. */
|
|
/* the total frequency count MUST be less than 1<<23 */
|
|
|
|
|
|
var RangeCoder = function(stream) {
|
|
this.low = 0; /* low end of interval */
|
|
this.range = Top_value; /* length of interval */
|
|
this.buffer = 0; /* buffer for input/output */
|
|
this.help = 0; /* bytes_to_follow / intermediate value */
|
|
this.bytecount = 0; /* counter for output bytes */
|
|
this.stream = stream;
|
|
};
|
|
|
|
/* Do the normalization before we need a defined state, instead of
|
|
* after messing it up. This simplifies starting and ending. */
|
|
var enc_normalize = function(rc, outputStream) {
|
|
while (rc.range <= Bottom_value) { /* do we need renormalization? */
|
|
if (rc.low < (0xFF << SHIFT_BITS)) { //no carry possible, so output
|
|
outputStream.writeByte(rc.buffer);
|
|
for (; rc.help; rc.help--)
|
|
outputStream.writeByte(0xFF);
|
|
rc.buffer = (rc.low >>> SHIFT_BITS) & 0xFF;
|
|
} else if (rc.low & Top_value) { /* carry now, no future carry */
|
|
outputStream.writeByte(rc.buffer + 1);
|
|
for (; rc.help; rc.help--)
|
|
outputStream.writeByte(0x00);
|
|
rc.buffer = (rc.low >>> SHIFT_BITS) & 0xFF;
|
|
} else {
|
|
rc.help++;
|
|
if (rc.help > MAX_INT)
|
|
throw new Error("Too many bytes outstanding, " +
|
|
"file too large!");
|
|
}
|
|
rc.range = (rc.range << 8) >>> 0; /*ensure result remains positive*/
|
|
rc.low = ((rc.low << 8) & (Top_value - 1)) >>> 0; /* unsigned */
|
|
rc.bytecount++;
|
|
}
|
|
};
|
|
|
|
/* Start the encoder */
|
|
/* c is written as the first byte in the datastream.
|
|
* one could do w/o, but then you have an additional if per output byte */
|
|
RangeCoder.prototype.encodeStart = function(c, initlength) {
|
|
this.low = 0;
|
|
this.range = Top_value;
|
|
this.buffer = c;
|
|
this.help = 0;
|
|
this.bytecount = initlength;
|
|
};
|
|
|
|
/* Encode a symbol using frequencies */
|
|
/* rc is the range coder to be used */
|
|
/* sy_f is the interval length (frequency of the symbol) */
|
|
/* lt_f is the lower end (frequency sum of < symbols) */
|
|
/* tot_f is the total interval length (total frequency sum) */
|
|
/* or (faster): tot_f = (code_value)1<<shift */
|
|
RangeCoder.prototype.encodeFreq = function(sy_f, lt_f, tot_f) {
|
|
enc_normalize(this, this.stream);
|
|
var r = (this.range / tot_f) >>> 0; // note coercion to integer
|
|
var tmp = r * lt_f;
|
|
this.low += tmp;
|
|
if ((lt_f + sy_f) < tot_f) {
|
|
this.range = r * sy_f;
|
|
} else {
|
|
this.range -= tmp;
|
|
}
|
|
};
|
|
RangeCoder.prototype.encodeShift = function(sy_f, lt_f, shift) {
|
|
enc_normalize(this, this.stream);
|
|
var r = this.range >>> shift;
|
|
var tmp = r * lt_f;
|
|
this.low += tmp;
|
|
if ((lt_f + sy_f) >>> shift) {
|
|
this.range -= tmp;
|
|
} else {
|
|
this.range = r * sy_f;
|
|
}
|
|
};
|
|
/* Encode a bit w/o modelling. */
|
|
RangeCoder.prototype.encodeBit = function(b) {
|
|
this.encodeShift(1, b ? 1 : 0, 1);
|
|
};
|
|
/* Encode a byte w/o modelling. */
|
|
RangeCoder.prototype.encodeByte = function(b) {
|
|
this.encodeShift(1, b, 8);
|
|
};
|
|
/* Encode a short w/o modelling. */
|
|
RangeCoder.prototype.encodeShort = function(s) {
|
|
this.encodeShift(1, s, 16);
|
|
};
|
|
|
|
/* Finish encoding */
|
|
/* returns number of bytes written */
|
|
RangeCoder.prototype.encodeFinish = function() {
|
|
var outputStream = this.stream;
|
|
enc_normalize(this, outputStream);
|
|
this.bytecount += 5;
|
|
var tmp = this.low >>> SHIFT_BITS;
|
|
if ((this.low & (Bottom_value - 1)) >= ((this.bytecount & 0xFFFFFF) >>> 1)) {
|
|
tmp++;
|
|
}
|
|
if (tmp > 0xFF) { /* we have a carry */
|
|
outputStream.writeByte(this.buffer + 1);
|
|
for (; this.help; this.help--)
|
|
outputStream.writeByte(0x00);
|
|
} else { /* no carry */
|
|
outputStream.writeByte(this.buffer);
|
|
for (; this.help; this.help--)
|
|
outputStream.writeByte(0xFF);
|
|
}
|
|
outputStream.writeByte(tmp & 0xFF);
|
|
// XXX: i'm pretty sure these could be three arbitrary bytes
|
|
// they are consumed by the decoder at the end
|
|
outputStream.writeByte((this.bytecount >>> 16) & 0xFF);
|
|
outputStream.writeByte((this.bytecount >>> 8) & 0xFF);
|
|
outputStream.writeByte((this.bytecount) & 0xFF);
|
|
return this.bytecount;
|
|
};
|
|
|
|
/* Start the decoder; you need to provide the *second* byte from the
|
|
* datastream. (The first byte was provided to startEncoding and is
|
|
* ignored by the decoder.)
|
|
*/
|
|
RangeCoder.prototype.decodeStart = function(skipInitialRead) {
|
|
var c = skipInitialRead ? 0 : this.stream.readByte();
|
|
if (typeof(c) !== 'number' || c < 0) {
|
|
return c; // EOF
|
|
}
|
|
this.buffer = this.stream.readByte();
|
|
this.low = this.buffer >>> (8 - EXTRA_BITS);
|
|
this.range = 1 << EXTRA_BITS;
|
|
return c;
|
|
};
|
|
|
|
var dec_normalize = function(rc, inputStream) {
|
|
while (rc.range <= Bottom_value) {
|
|
rc.low = (rc.low << 8) | ((rc.buffer << EXTRA_BITS) & 0xFF);
|
|
/* rc.low could be negative here; don't fix it quite yet */
|
|
rc.buffer = inputStream.readByte();
|
|
rc.low |= rc.buffer >>> (8 - EXTRA_BITS);
|
|
rc.low = rc.low >>> 0; /* fix it now */
|
|
rc.range = (rc.range << 8) >>> 0; /* ensure stays positive */
|
|
}
|
|
};
|
|
|
|
/* Calculate cumulative frequency for next symbol. Does NO update!*/
|
|
/* rc is the range coder to be used */
|
|
/* tot_f is the total frequency */
|
|
/* or: totf is (code_value)1<<shift */
|
|
/* returns the <= cumulative frequency */
|
|
RangeCoder.prototype.decodeCulFreq = function(tot_f) {
|
|
dec_normalize(this, this.stream);
|
|
this.help = (this.range / tot_f) >>> 0; // note coercion to integer
|
|
var tmp = (this.low / this.help) >>> 0; // again
|
|
return (tmp >= tot_f ? tot_f - 1 : tmp);
|
|
};
|
|
RangeCoder.prototype.decodeCulShift = function(shift) {
|
|
dec_normalize(this, this.stream);
|
|
this.help = this.range >>> shift;
|
|
var tmp = (this.low / this.help) >>> 0; // coercion to unsigned
|
|
// shift is less than 31, so shift below will remain positive
|
|
return ((tmp >>> shift) ? (1 << shift) - 1 : tmp);
|
|
};
|
|
|
|
/* Update decoding state */
|
|
/* rc is the range coder to be used */
|
|
/* sy_f is the interval length (frequency of the symbol) */
|
|
/* lt_f is the lower end (frequency sum of < symbols) */
|
|
/* tot_f is the total interval length (total frequency sum) */
|
|
RangeCoder.prototype.decodeUpdate = function(sy_f, lt_f, tot_f) {
|
|
var tmp = this.help * lt_f; // should not overflow!
|
|
this.low -= tmp;
|
|
if (lt_f + sy_f < tot_f) {
|
|
this.range = (this.help * sy_f);
|
|
} else {
|
|
this.range -= tmp;
|
|
}
|
|
};
|
|
|
|
/* Decode a bit w/o modelling. */
|
|
RangeCoder.prototype.decodeBit = function() {
|
|
var tmp = this.decodeCulShift(1);
|
|
this.decodeUpdate(1, tmp, 1 << 1);
|
|
return tmp;
|
|
};
|
|
/* decode a byte w/o modelling */
|
|
RangeCoder.prototype.decodeByte = function() {
|
|
var tmp = this.decodeCulShift(8);
|
|
this.decodeUpdate(1, tmp, 1 << 8);
|
|
return tmp;
|
|
};
|
|
/* decode a short w/o modelling */
|
|
RangeCoder.prototype.decodeShort = function() {
|
|
var tmp = this.decodeCulShift(16);
|
|
this.decodeUpdate(1, tmp, 1 << 16);
|
|
return tmp;
|
|
};
|
|
|
|
/* Finish decoding */
|
|
RangeCoder.prototype.decodeFinish = function() {
|
|
/* normalize to use up all bytes */
|
|
dec_normalize(this, this.stream);
|
|
};
|
|
|
|
/** Utility functions */
|
|
|
|
// bitstream interface
|
|
RangeCoder.prototype.writeBit = RangeCoder.prototype.encodeBit;
|
|
RangeCoder.prototype.readBit = RangeCoder.prototype.decodeBit;
|
|
|
|
// stream interface
|
|
RangeCoder.prototype.writeByte = RangeCoder.prototype.encodeByte;
|
|
RangeCoder.prototype.readByte = RangeCoder.prototype.decodeByte;
|
|
|
|
return RangeCoder;
|
|
|
|
}());
|
|
|
|
|
|
Stream = (function(){
|
|
/** Abstract Stream interface, for byte-oriented i/o. */
|
|
var EOF = -1;
|
|
|
|
var Stream = function() {
|
|
/* ABSTRACT */
|
|
};
|
|
// you must define one of read / readByte for a readable stream
|
|
Stream.prototype.readByte = function() {
|
|
var buf = [0];
|
|
var len = this.read(buf, 0, 1);
|
|
if (len === 0) { this._eof = true; return EOF; }
|
|
return buf[0];
|
|
};
|
|
Stream.prototype.read = function(buf, bufOffset, length) {
|
|
var ch, bytesRead = 0;
|
|
while (bytesRead < length) {
|
|
ch = this.readByte();
|
|
if (ch === EOF) { this._eof = true; break; }
|
|
buf[bufOffset + (bytesRead++)] = ch;
|
|
}
|
|
return bytesRead;
|
|
};
|
|
Stream.prototype.eof = function() { return !!this._eof; }; // reasonable default implementation of 'eof'
|
|
Stream.prototype.seek = function(pos) { // not all readable streams are seekable
|
|
throw new Error('Stream is not seekable.');
|
|
};
|
|
Stream.prototype.tell = function() {
|
|
throw new Error('Stream is not seekable.');
|
|
};
|
|
Stream.prototype.writeByte = function(_byte) { // you must define one of write / writeByte for a writable stream
|
|
var buf = [_byte];
|
|
this.write(buf, 0, 1);
|
|
};
|
|
Stream.prototype.write = function(buf, bufOffset, length) {
|
|
var i;
|
|
for (i = 0; i < length; i++) {
|
|
this.writeByte(buf[bufOffset + i]);
|
|
}
|
|
return length;
|
|
};
|
|
Stream.prototype.flush = function(){}; //flush will happily do nothing if you don't override it.
|
|
Stream.EOF = EOF; //export EOF as a constant.
|
|
|
|
return Stream;
|
|
}());
|
|
|
|
|
|
BitStream = (function(){
|
|
/** Big-Endian Bit Stream, implemented on top of a (normal byte) stream. */
|
|
var BitStream = function(stream) {
|
|
(function() {
|
|
var bufferByte = 0x100; // private var for readers
|
|
this.readBit = function() {
|
|
if ((bufferByte & 0xFF) === 0) {
|
|
var ch = stream.readByte();
|
|
if (ch === Stream.EOF) {
|
|
this._eof = true;
|
|
return ch; /* !!! */
|
|
}
|
|
bufferByte = (ch << 1) | 1;
|
|
}
|
|
var bit = (bufferByte & 0x100) ? 1 : 0;
|
|
bufferByte <<= 1;
|
|
return bit;
|
|
};
|
|
// seekable iff the provided stream is
|
|
this.seekBit = function(pos) {
|
|
var n_byte = pos >>> 3;
|
|
var n_bit = pos - (n_byte * 8);
|
|
this.seek(n_byte);
|
|
this._eof = false;
|
|
this.readBits(n_bit);
|
|
};
|
|
this.tellBit = function() {
|
|
var pos = stream.tell() * 8;
|
|
var b = bufferByte;
|
|
while ((b & 0xFF) !== 0) {
|
|
pos--;
|
|
b <<= 1;
|
|
}
|
|
return pos;
|
|
};
|
|
// implement byte stream interface as well.
|
|
this.readByte = function() {
|
|
if ((bufferByte & 0xFF) === 0) {
|
|
return stream.readByte();
|
|
}
|
|
return this.readBits(8);
|
|
};
|
|
this.seek = function(pos) {
|
|
stream.seek(pos);
|
|
bufferByte = 0x100;
|
|
};
|
|
})
|
|
.call(this);
|
|
(function() {
|
|
var bufferByte = 1; // private var for writers
|
|
this.writeBit = function(b) {
|
|
bufferByte <<= 1;
|
|
if (b) { bufferByte |= 1; }
|
|
if (bufferByte & 0x100) {
|
|
stream.writeByte(bufferByte & 0xFF);
|
|
bufferByte = 1;
|
|
}
|
|
};
|
|
// implement byte stream interface as well
|
|
this.writeByte = function(_byte) {
|
|
if (bufferByte === 1) {
|
|
stream.writeByte(_byte);
|
|
} else {
|
|
stream.writeBits(8, _byte);
|
|
}
|
|
};
|
|
this.flush = function() {
|
|
while (bufferByte !== 1) {
|
|
this.writeBit(0);
|
|
}
|
|
if (stream.flush) { stream.flush(); }
|
|
};
|
|
})
|
|
.call(this);
|
|
};
|
|
// inherit read/write methods from Stream.
|
|
BitStream.EOF = Stream.EOF;
|
|
BitStream.prototype = Object.create(Stream.prototype);
|
|
// bit chunk read/write
|
|
BitStream.prototype.readBits = function(n) {
|
|
var i, r = 0
|
|
, b;
|
|
if (n > 31) {
|
|
r = this.readBits(n - 16) * 0x10000; // fp multiply, not shift
|
|
return r + this.readBits(16);
|
|
}
|
|
for (i = 0; i < n; i++) {
|
|
r <<= 1; // this could make a negative value if n>31
|
|
// bits read past EOF are all zeros!
|
|
if (this.readBit() > 0) { r++; }
|
|
}
|
|
return r;
|
|
};
|
|
BitStream.prototype.writeBits = function(n, value) {
|
|
if (n > 32) {
|
|
var low = (value & 0xFFFF);
|
|
var high = (value - low) / (0x10000); // fp division, not shift
|
|
this.writeBits(n - 16, high);
|
|
this.writeBits(16, low);
|
|
return;
|
|
}
|
|
var i;
|
|
for (i = n - 1; i >= 0; i--) {
|
|
this.writeBit((value >>> i) & 1);
|
|
}
|
|
};
|
|
|
|
return BitStream;
|
|
}());
|
|
|
|
|
|
Util = (function(){
|
|
var Util = Object.create(null);
|
|
|
|
var EOF = Stream.EOF;
|
|
|
|
/* Take a buffer, array, or stream, and return an input stream. */
|
|
Util.coerceInputStream = function(input, forceRead) {
|
|
if (!('readByte' in input)) {
|
|
var buffer = input;
|
|
input = new Stream();
|
|
input.size = buffer.length;
|
|
input.pos = 0;
|
|
input.readByte = function() {
|
|
if (this.pos >= this.size) { return EOF; }
|
|
return buffer[this.pos++];
|
|
};
|
|
input.read = function(buf, bufOffset, length) {
|
|
var bytesRead = 0;
|
|
while (bytesRead < length && this.pos < buffer.length) {
|
|
buf[bufOffset++] = buffer[this.pos++];
|
|
bytesRead++;
|
|
}
|
|
return bytesRead;
|
|
};
|
|
input.seek = function(pos) { this.pos = pos; };
|
|
input.tell = function() { return this.pos; };
|
|
input.eof = function() { return this.pos >= buffer.length; };
|
|
} else if (forceRead && !('read' in input)) {
|
|
// wrap input if it doesn't implement read
|
|
var s = input;
|
|
input = new Stream();
|
|
input.readByte = function() {
|
|
var ch = s.readByte();
|
|
if (ch === EOF) { this._eof = true; }
|
|
return ch;
|
|
};
|
|
if ('size' in s) { input.size = s.size; }
|
|
if ('seek' in s) {
|
|
input.seek = function(pos) {
|
|
s.seek(pos); // may throw if s doesn't implement seek
|
|
this._eof = false;
|
|
};
|
|
}
|
|
if ('tell' in s) {
|
|
input.tell = s.tell.bind(s);
|
|
}
|
|
}
|
|
return input;
|
|
};
|
|
|
|
var BufferStream = function(buffer, resizeOk) {
|
|
this.buffer = buffer;
|
|
this.resizeOk = resizeOk;
|
|
this.pos = 0;
|
|
};
|
|
BufferStream.prototype = Object.create(Stream.prototype);
|
|
BufferStream.prototype.writeByte = function(_byte) {
|
|
if (this.resizeOk && this.pos >= this.buffer.length) {
|
|
var newBuffer = Util.makeU8Buffer(this.buffer.length * 2);
|
|
newBuffer.set(this.buffer);
|
|
this.buffer = newBuffer;
|
|
}
|
|
this.buffer[this.pos++] = _byte;
|
|
};
|
|
BufferStream.prototype.getBuffer = function() {
|
|
// trim buffer if needed
|
|
if (this.pos !== this.buffer.length) {
|
|
if (!this.resizeOk)
|
|
throw new TypeError('outputsize does not match decoded input');
|
|
var newBuffer = Util.makeU8Buffer(this.pos);
|
|
newBuffer.set(this.buffer.subarray(0, this.pos));
|
|
this.buffer = newBuffer;
|
|
}
|
|
return this.buffer;
|
|
};
|
|
|
|
/* Take a stream (or not) and an (optional) size, and return an
|
|
* output stream. Return an object with a 'retval' field equal to
|
|
* the output stream (if that was given) or else a pointer at the
|
|
* internal Uint8Array/buffer/array; and a 'stream' field equal to
|
|
* an output stream to use.
|
|
*/
|
|
Util.coerceOutputStream = function(output, size) {
|
|
var r = { stream: output, retval: output };
|
|
if (output) {
|
|
if (typeof(output) === 'object' && 'writeByte' in output) {
|
|
return r; /* leave output alone */
|
|
} else if (typeof(size) === 'number') {
|
|
console.assert(size >= 0);
|
|
r.stream = new BufferStream(Util.makeU8Buffer(size), false);
|
|
} else { // output is a buffer
|
|
r.stream = new BufferStream(output, false);
|
|
}
|
|
} else {
|
|
r.stream = new BufferStream(Util.makeU8Buffer(16384), true);
|
|
}
|
|
Object.defineProperty(r, 'retval', {
|
|
get: r.stream.getBuffer.bind(r.stream)
|
|
});
|
|
return r;
|
|
};
|
|
|
|
Util.compressFileHelper = function(magic, guts, suppressFinalByte) {
|
|
return function(inStream, outStream, props) {
|
|
inStream = Util.coerceInputStream(inStream);
|
|
var o = Util.coerceOutputStream(outStream, outStream);
|
|
outStream = o.stream;
|
|
|
|
// write the magic number to identify this file type
|
|
// (it better be ASCII, we're not doing utf-8 conversion)
|
|
var i;
|
|
for (i = 0; i < magic.length; i++) {
|
|
outStream.writeByte(magic.charCodeAt(i));
|
|
}
|
|
|
|
// if we know the size, write it
|
|
var fileSize;
|
|
if ('size' in inStream && inStream.size >= 0) {
|
|
fileSize = inStream.size;
|
|
} else {
|
|
fileSize = -1; // size unknown
|
|
}
|
|
if (suppressFinalByte) {
|
|
var tmpOutput = Util.coerceOutputStream([]);
|
|
Util.writeUnsignedNumber(tmpOutput.stream, fileSize + 1);
|
|
tmpOutput = tmpOutput.retval;
|
|
for (i = 0; i < tmpOutput.length - 1; i++) {
|
|
outStream.writeByte(tmpOutput[i]);
|
|
}
|
|
suppressFinalByte = tmpOutput[tmpOutput.length - 1];
|
|
} else {
|
|
Util.writeUnsignedNumber(outStream, fileSize + 1);
|
|
}
|
|
|
|
// call the guts to do the real compression
|
|
guts(inStream, outStream, fileSize, props, suppressFinalByte);
|
|
|
|
return o.retval;
|
|
};
|
|
};
|
|
Util.decompressFileHelper = function(magic, guts) {
|
|
return function(inStream, outStream) {
|
|
inStream = Util.coerceInputStream(inStream);
|
|
|
|
// read the magic number to confirm this file type
|
|
// (it better be ASCII, we're not doing utf-8 conversion)
|
|
var i;
|
|
for (i = 0; i < magic.length; i++) {
|
|
if (magic.charCodeAt(i) !== inStream.readByte()) {
|
|
throw new Error("Bad magic");
|
|
}
|
|
}
|
|
|
|
// read the file size & create an appropriate output stream/buffer
|
|
var fileSize = Util.readUnsignedNumber(inStream) - 1;
|
|
var o = Util.coerceOutputStream(outStream, fileSize);
|
|
outStream = o.stream;
|
|
|
|
// call the guts to do the real decompression
|
|
guts(inStream, outStream, fileSize);
|
|
|
|
return o.retval;
|
|
};
|
|
};
|
|
// a helper for simple self-test of model encode
|
|
Util.compressWithModel = function(inStream, fileSize, model) {
|
|
var inSize = 0;
|
|
while (inSize !== fileSize) {
|
|
var ch = inStream.readByte();
|
|
if (ch === EOF) {
|
|
model.encode(256); // end of stream;
|
|
break;
|
|
}
|
|
model.encode(ch);
|
|
inSize++;
|
|
}
|
|
};
|
|
// a helper for simple self-test of model decode
|
|
Util.decompressWithModel = function(outStream, fileSize, model) {
|
|
var outSize = 0;
|
|
while (outSize !== fileSize) {
|
|
var ch = model.decode();
|
|
if (ch === 256) {
|
|
break; // end of stream;
|
|
}
|
|
outStream.writeByte(ch);
|
|
outSize++;
|
|
}
|
|
};
|
|
|
|
/** Write a number using a self-delimiting big-endian encoding. */
|
|
Util.writeUnsignedNumber = function(output, n) {
|
|
console.assert(n >= 0);
|
|
var bytes = []
|
|
, i;
|
|
do {
|
|
bytes.push(n & 0x7F);
|
|
// use division instead of shift to allow encoding numbers up to
|
|
// 2^53
|
|
n = Math.floor(n / 128);
|
|
} while (n !== 0);
|
|
bytes[0] |= 0x80; // mark end of encoding.
|
|
for (i = bytes.length - 1; i >= 0; i--) {
|
|
output.writeByte(bytes[i]); // write in big-endian order
|
|
}
|
|
return output;
|
|
};
|
|
|
|
/** Read a number using a self-delimiting big-endian encoding. */
|
|
Util.readUnsignedNumber = function(input) {
|
|
var n = 0
|
|
, c;
|
|
while (true) {
|
|
c = input.readByte();
|
|
if (c & 0x80) { n += (c & 0x7F); break; }
|
|
// using + and * instead of << allows decoding numbers up to 2^53
|
|
n = (n + c) * 128;
|
|
}
|
|
return n;
|
|
};
|
|
|
|
// Compatibility thunks for Buffer/TypedArray constructors.
|
|
|
|
var zerofill = function(a) {
|
|
for (var i = 0, len = a.length; i < len; i++) {
|
|
a[i] = 0;
|
|
}
|
|
return a;
|
|
};
|
|
|
|
var fallbackarray = function(size) {
|
|
return zerofill(new Array(size));
|
|
};
|
|
|
|
// Node 0.11.6 - 0.11.10ish don't properly zero fill typed arrays.
|
|
// See https://github.com/joyent/node/issues/6664
|
|
// Try to detect and workaround the bug.
|
|
var ensureZeroed = function id(a) { return a; };
|
|
if ((typeof(process) !== 'undefined') &&
|
|
Array.prototype.some.call(new Uint32Array(128), function(x) {
|
|
return x !== 0;
|
|
})) {
|
|
//console.warn('Working around broken TypedArray');
|
|
ensureZeroed = zerofill;
|
|
}
|
|
|
|
/** Portable 8-bit unsigned buffer. */
|
|
Util.makeU8Buffer = (typeof(Uint8Array) !== 'undefined') ? function(size) {
|
|
// Uint8Array ought to be automatically zero-filled
|
|
return ensureZeroed(new Uint8Array(size));
|
|
} : (typeof(Buffer) !== 'undefined') ? function(size) {
|
|
var b = new Buffer(size);
|
|
b.fill(0);
|
|
return b;
|
|
} : fallbackarray;
|
|
|
|
/** Portable 16-bit unsigned buffer. */
|
|
Util.makeU16Buffer = (typeof(Uint16Array) !== 'undefined') ? function(size) {
|
|
// Uint16Array ought to be automatically zero-filled
|
|
return ensureZeroed(new Uint16Array(size));
|
|
} : fallbackarray;
|
|
|
|
/** Portable 32-bit unsigned buffer. */
|
|
Util.makeU32Buffer = (typeof(Uint32Array) !== 'undefined') ? function(size) {
|
|
// Uint32Array ought to be automatically zero-filled
|
|
return ensureZeroed(new Uint32Array(size));
|
|
} : fallbackarray;
|
|
|
|
/** Portable 32-bit signed buffer. */
|
|
Util.makeS32Buffer = (typeof(Int32Array) !== 'undefined') ? function(size) {
|
|
// Int32Array ought to be automatically zero-filled
|
|
return ensureZeroed(new Int32Array(size));
|
|
} : fallbackarray;
|
|
|
|
Util.arraycopy = function(dst, src) {
|
|
console.assert(dst.length >= src.length);
|
|
for (var i = 0, len = src.length; i < len; i++) {
|
|
dst[i] = src[i];
|
|
}
|
|
return dst;
|
|
};
|
|
|
|
/** Highest bit set in a byte. */
|
|
var bytemsb = [
|
|
0
|
|
,1
|
|
,2, 2
|
|
,3, 3, 3, 3
|
|
,4, 4, 4, 4, 4, 4, 4, 4
|
|
,5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
|
|
,6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
|
|
,7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
|
|
,8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 /* 256 */
|
|
];
|
|
console.assert(bytemsb.length === 0x100);
|
|
/** Find last set (most significant bit).
|
|
* @return the last bit set in the argument.
|
|
* <code>fls(0)==0</code> and <code>fls(1)==1</code>. */
|
|
var fls = Util.fls = function(v) {
|
|
console.assert(v >= 0);
|
|
if (v > 0xFFFFFFFF) { // use floating-point mojo
|
|
return 32 + fls(Math.floor(v / 0x100000000));
|
|
}
|
|
if ((v & 0xFFFF0000) !== 0) {
|
|
if ((v & 0xFF000000) !== 0) {
|
|
return 24 + bytemsb[(v >>> 24) & 0xFF];
|
|
} else {
|
|
return 16 + bytemsb[v >>> 16];
|
|
}
|
|
} else if ((v & 0x0000FF00) !== 0) {
|
|
return 8 + bytemsb[v >>> 8];
|
|
} else {
|
|
return bytemsb[v];
|
|
}
|
|
};
|
|
/** Returns ceil(log2(n)) */
|
|
Util.log2c = function(v) {
|
|
return (v === 0) ? -1 : fls(v - 1);
|
|
};
|
|
|
|
return Util; // ensure constants are recognized as such.
|
|
}());
|
|
|
|
|
|
BWT = (function(){
|
|
/** Burrows-Wheeler transform, computed with the Induced Sorting Suffix Array
|
|
* construction mechanism (sais). Code is a port of:
|
|
* https://sites.google.com/site/yuta256/sais
|
|
* which is:
|
|
* Copyright (c) 2008-2010 Yuta Mori All Rights Reserved.
|
|
* and licensed under an MIT/X11 license. I generally looked at both
|
|
* the C and the Java implementations to guide my work.
|
|
*
|
|
* This JavaScript port is:
|
|
* Copyright (c) 2013 C. Scott Ananian
|
|
* and licensed under GPLv2; see the README at the top level of this package.
|
|
*/
|
|
|
|
var ASSERT = console.assert.bind(console);
|
|
|
|
// we're dispensing with the "arbitrary alphabet" stuff of the source
|
|
// and just using Uint8Arrays.
|
|
|
|
/** Find the start or end of each bucket. */
|
|
var getCounts = function(T, C, n, k) {
|
|
var i;
|
|
for (i = 0; i < k; i++) { C[i] = 0; }
|
|
for (i = 0; i < n; i++) { C[T[i]]++; }
|
|
};
|
|
var getBuckets = function(C, B, k, end) {
|
|
var i, sum = 0;
|
|
if (end) {
|
|
for (i = 0; i < k; i++) {
|
|
sum += C[i];
|
|
B[i] = sum;
|
|
}
|
|
} else {
|
|
for (i = 0; i < k; i++) {
|
|
sum += C[i];
|
|
B[i] = sum - C[i];
|
|
}
|
|
}
|
|
};
|
|
|
|
/** Sort all type LMS suffixes */
|
|
var LMSsort = function(T, SA, C, B, n, k) {
|
|
var b, i, j;
|
|
var c0, c1;
|
|
/* compute SAl */
|
|
if (C === B) { getCounts(T, C, n, k); }
|
|
getBuckets(C, B, k, false); /* find starts of buckets */
|
|
j = n - 1;
|
|
b = B[c1 = T[j]];
|
|
j--;
|
|
SA[b++] = (T[j] < c1) ? ~j : j;
|
|
for (i = 0; i < n; i++) {
|
|
if ((j = SA[i]) > 0) {
|
|
ASSERT(T[j] >= T[j + 1]);
|
|
if ((c0 = T[j]) !== c1) {
|
|
B[c1] = b;
|
|
b = B[c1 = c0];
|
|
}
|
|
ASSERT(i < b);
|
|
j--;
|
|
SA[b++] = (T[j] < c1) ? ~j : j;
|
|
SA[i] = 0;
|
|
} else if (j < 0) {
|
|
SA[i] = ~j;
|
|
}
|
|
}
|
|
/* compute SAs */
|
|
if (C === B) { getCounts(T, C, n, k); }
|
|
getBuckets(C, B, k, 1); /* find ends of buckets */
|
|
for (i = n - 1, b = B[c1 = 0]; i >= 0; i--) {
|
|
if ((j = SA[i]) > 0) {
|
|
ASSERT(T[j] <= T[j + 1]);
|
|
if ((c0 = T[j]) !== c1) {
|
|
B[c1] = b;
|
|
b = B[c1 = c0];
|
|
}
|
|
ASSERT(b <= i);
|
|
j--;
|
|
SA[--b] = (T[j] > c1) ? ~(j + 1) : j;
|
|
SA[i] = 0;
|
|
}
|
|
}
|
|
};
|
|
|
|
var LMSpostproc = function(T, SA, n, m) {
|
|
var i, j, p, q, plen, qlen, name;
|
|
var c0, c1;
|
|
var diff;
|
|
|
|
/* compact all the sorted substrings into the first m items of SA
|
|
* 2*m must not be larger than n (provable) */
|
|
ASSERT(n > 0);
|
|
for (i = 0;
|
|
(p = SA[i]) < 0; i++) {
|
|
SA[i] = ~p;
|
|
ASSERT((i + 1) < n);
|
|
}
|
|
if (i < m) {
|
|
for (j = i, i++;; i++) {
|
|
ASSERT(i < n);
|
|
if ((p = SA[i]) < 0) {
|
|
SA[j++] = ~p;
|
|
SA[i] = 0;
|
|
if (j === m) { break; }
|
|
}
|
|
}
|
|
}
|
|
|
|
/* store the length of all substrings */
|
|
c0 = T[i = j = n - 1];
|
|
do { c1 = c0; } while (((--i) >= 0) && ((c0 = T[i]) >= c1));
|
|
for (; i >= 0;) {
|
|
do { c1 = c0; } while (((--i) >= 0) && ((c0 = T[i]) <= c1));
|
|
if (i >= 0) {
|
|
SA[m + ((i + 1) >>> 1)] = j - i;
|
|
j = i + 1;
|
|
do { c1 = c0; } while (((--i) >= 0) && ((c0 = T[i]) >= c1));
|
|
}
|
|
}
|
|
|
|
/* find the lexicographic names of all substrings */
|
|
for (i = 0, name = 0, q = n, qlen = 0; i < m; i++) {
|
|
p = SA[i];
|
|
plen = SA[m + (p >>> 1)];
|
|
diff = true;
|
|
if ((plen === qlen) && ((q + plen) < n)) {
|
|
for (j = 0;
|
|
(j < plen) && (T[p + j] === T[q + j]);) { j++; }
|
|
if (j === plen) { diff = false; }
|
|
}
|
|
if (diff) {
|
|
name++;
|
|
q = p;
|
|
qlen = plen;
|
|
}
|
|
SA[m + (p >>> 1)] = name;
|
|
}
|
|
|
|
return name;
|
|
};
|
|
|
|
/* compute SA and BWT */
|
|
var induceSA = function(T, SA, C, B, n, k) {
|
|
var b, i, j;
|
|
var c0, c1;
|
|
/* compute SAl */
|
|
if (C === B) { getCounts(T, C, n, k); }
|
|
getBuckets(C, B, k, false); /* find starts of buckets */
|
|
j = n - 1;
|
|
b = B[c1 = T[j]];
|
|
SA[b++] = ((j > 0) && (T[j - 1] < c1)) ? ~j : j;
|
|
for (i = 0; i < n; i++) {
|
|
j = SA[i];
|
|
SA[i] = ~j;
|
|
if (j > 0) {
|
|
j--;
|
|
ASSERT(T[j] >= T[j + 1]);
|
|
if ((c0 = T[j]) !== c1) {
|
|
B[c1] = b;
|
|
b = B[c1 = c0];
|
|
}
|
|
ASSERT(i < b);
|
|
SA[b++] = ((j > 0) && (T[j - 1] < c1)) ? ~j : j;
|
|
}
|
|
}
|
|
/* compute SAs */
|
|
if (C === B) { getCounts(T, C, n, k); }
|
|
getBuckets(C, B, k, true); /* find ends of buckets */
|
|
for (i = n - 1, b = B[c1 = 0]; i >= 0; i--) {
|
|
if ((j = SA[i]) > 0) {
|
|
j--;
|
|
ASSERT(T[j] <= T[j + 1]);
|
|
if ((c0 = T[j]) !== c1) {
|
|
B[c1] = b;
|
|
b = B[c1 = c0];
|
|
}
|
|
ASSERT(b <= i);
|
|
SA[--b] = ((j === 0) || (T[j - 1] > c1)) ? ~j : j;
|
|
} else {
|
|
SA[i] = ~j;
|
|
}
|
|
}
|
|
};
|
|
|
|
var computeBWT = function(T, SA, C, B, n, k) {
|
|
var b, i, j, pidx = -1;
|
|
var c0, c1;
|
|
/* compute SAl */
|
|
if (C === B) { getCounts(T, C, n, k); }
|
|
getBuckets(C, B, k, false); /* find starts of buckets */
|
|
j = n - 1;
|
|
b = B[c1 = T[j]];
|
|
SA[b++] = ((j > 0) && (T[j - 1] < c1)) ? ~j : j;
|
|
for (i = 0; i < n; i++) {
|
|
if ((j = SA[i]) > 0) {
|
|
j--;
|
|
ASSERT(T[j] >= T[j + 1]);
|
|
SA[i] = ~(c0 = T[j]);
|
|
if (c0 !== c1) {
|
|
B[c1] = b;
|
|
b = B[c1 = c0];
|
|
}
|
|
ASSERT(i < b);
|
|
SA[b++] = ((j > 0) && (T[j - 1] < c1)) ? ~j : j;
|
|
} else if (j !== 0) {
|
|
SA[i] = ~j;
|
|
}
|
|
}
|
|
/* compute SAs */
|
|
if (C === B) { getCounts(T, C, n, k); }
|
|
getBuckets(C, B, k, true); /* find ends of buckets */
|
|
for (i = n - 1, b = B[c1 = 0]; i >= 0; i--) {
|
|
if ((j = SA[i]) > 0) {
|
|
j--;
|
|
ASSERT(T[j] <= T[j + 1]);
|
|
SA[i] = c0 = T[j];
|
|
if (c0 !== c1) {
|
|
B[c1] = b;
|
|
b = B[c1 = c0];
|
|
}
|
|
ASSERT(b <= i);
|
|
SA[--b] = ((j > 0) && (T[j - 1] > c1)) ? (~T[j - 1]) : j;
|
|
} else if (j !== 0) {
|
|
SA[i] = ~j;
|
|
} else {
|
|
pidx = i;
|
|
}
|
|
}
|
|
return pidx;
|
|
};
|
|
|
|
/* find the suffix array SA of T[0..n-1] in {0..k-1}^n
|
|
use a working space (excluding T and SA) of at most 2n+O(1) for a
|
|
constant alphabet */
|
|
var SA_IS = function(T, SA, fs, n, k, isbwt) {
|
|
var C, B, RA;
|
|
var i, j, b, c, m, p, q, name, pidx = 0
|
|
, newfs;
|
|
var c0, c1;
|
|
var flags = 0;
|
|
|
|
// allocate temporary storage [CSA]
|
|
if (k <= 256) {
|
|
C = Util.makeS32Buffer(k);
|
|
if (k <= fs) {
|
|
B = SA.subarray(n + fs - k);
|
|
flags = 1;
|
|
} else {
|
|
B = Util.makeS32Buffer(k);
|
|
flags = 3;
|
|
}
|
|
} else if (k <= fs) {
|
|
C = SA.subarray(n + fs - k);
|
|
if (k <= (fs - k)) {
|
|
B = SA.subarray(n + fs - k * 2);
|
|
flags = 0;
|
|
} else if (k <= 1024) {
|
|
B = Util.makeS32Buffer(k);
|
|
flags = 2;
|
|
} else {
|
|
B = C;
|
|
flags = 8;
|
|
}
|
|
} else {
|
|
C = B = Util.makeS32Buffer(k);
|
|
flags = 4 | 8;
|
|
}
|
|
|
|
/* stage 1: reduce the problem by at least 1/2
|
|
sort all the LMS-substrings */
|
|
getCounts(T, C, n, k);
|
|
getBuckets(C, B, k, true); /* find ends of buckets */
|
|
for (i = 0; i < n; i++) { SA[i] = 0; }
|
|
b = -1;
|
|
i = n - 1;
|
|
j = n;
|
|
m = 0;
|
|
c0 = T[n - 1];
|
|
do { c1 = c0; } while ((--i >= 0) && ((c0 = T[i]) >= c1));
|
|
for (; i >= 0;) {
|
|
do { c1 = c0; } while ((--i >= 0) && ((c0 = T[i]) <= c1));
|
|
if (i >= 0) {
|
|
if (b >= 0) { SA[b] = j; }
|
|
b = --B[c1];
|
|
j = i;
|
|
++m;
|
|
do { c1 = c0; } while ((--i >= 0) && ((c0 = T[i]) >= c1));
|
|
}
|
|
}
|
|
|
|
if (m > 1) {
|
|
LMSsort(T, SA, C, B, n, k);
|
|
name = LMSpostproc(T, SA, n, m);
|
|
} else if (m === 1) {
|
|
SA[b] = j + 1;
|
|
name = 1;
|
|
} else {
|
|
name = 0;
|
|
}
|
|
|
|
/* stage 2: solve the reduced problem
|
|
recurse if names are not yet unique */
|
|
if (name < m) {
|
|
if ((flags & 4) !== 0) {
|
|
C = null;
|
|
B = null;
|
|
}
|
|
if ((flags & 2) !== 0) { B = null; }
|
|
newfs = (n + fs) - (m * 2);
|
|
if ((flags & (1 | 4 | 8)) === 0) {
|
|
if ((k + name) <= newfs) { newfs -= k; } else { flags |= 8; }
|
|
}
|
|
ASSERT((n >>> 1) <= (newfs + m));
|
|
for (i = m + (n >>> 1) - 1, j = m * 2 + newfs - 1; m <= i; i--) {
|
|
if (SA[i] !== 0) { SA[j--] = SA[i] - 1; }
|
|
}
|
|
RA = SA.subarray(m + newfs);
|
|
SA_IS(RA, SA, newfs, m, name, false);
|
|
RA = null;
|
|
|
|
i = n - 1;
|
|
j = m * 2 - 1;
|
|
c0 = T[n - 1];
|
|
do { c1 = c0; } while ((--i >= 0) && ((c0 = T[i]) >= c1));
|
|
for (; i >= 0;) {
|
|
do { c1 = c0; } while ((--i >= 0) && ((c0 = T[i]) <= c1));
|
|
if (i >= 0) {
|
|
SA[j--] = i + 1;
|
|
do { c1 = c0; } while ((--i >= 0) && ((c0 = T[i]) >= c1));
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < m; i++) { SA[i] = SA[m + SA[i]]; }
|
|
if ((flags & 4) !== 0) { C = B = Util.makeS32Buffer(k); }
|
|
if ((flags & 2) !== 0) { B = Util.makeS32Buffer(k); }
|
|
}
|
|
|
|
/* stage 3: induce the result for the original problem */
|
|
if ((flags & 8) !== 0) { getCounts(T, C, n, k); }
|
|
/* put all left-most S characters into their buckets */
|
|
if (m > 1) {
|
|
getBuckets(C, B, k, true); /* find ends of buckets */
|
|
i = m - 1;
|
|
j = n;
|
|
p = SA[m - 1];
|
|
c1 = T[p];
|
|
do {
|
|
q = B[c0 = c1];
|
|
while (q < j) { SA[--j] = 0; }
|
|
do {
|
|
SA[--j] = p;
|
|
if (--i < 0) { break; }
|
|
p = SA[i];
|
|
} while ((c1 = T[p]) === c0);
|
|
} while (i >= 0);
|
|
while (j > 0) { SA[--j] = 0; }
|
|
}
|
|
if (!isbwt) { induceSA(T, SA, C, B, n, k); } else { pidx = computeBWT(T, SA, C, B, n, k); }
|
|
C = null;
|
|
B = null;
|
|
return pidx;
|
|
};
|
|
|
|
var BWT = Object.create(null);
|
|
/** SA should be a Int32Array (signed!); T can be any typed array.
|
|
* alphabetSize is optional if T is an Uint8Array or Uint16Array. */
|
|
BWT.suffixsort = function(T, SA, n, alphabetSize) {
|
|
ASSERT(T && SA && T.length >= n && SA.length >= n);
|
|
if (n <= 1) {
|
|
if (n === 1) { SA[0] = 0; }
|
|
return 0;
|
|
}
|
|
if (!alphabetSize) {
|
|
if (T.BYTES_PER_ELEMENT === 1) { alphabetSize = 256; } else if (T.BYTES_PER_ELEMENT === 2) { alphabetSize = 65536; } else throw new Error('Need to specify alphabetSize');
|
|
}
|
|
ASSERT(alphabetSize > 0);
|
|
if (T.BYTES_PER_ELEMENT) {
|
|
ASSERT(alphabetSize <= (1 << (T.BYTES_PER_ELEMENT * 8)));
|
|
}
|
|
return SA_IS(T, SA, 0, n, alphabetSize, false);
|
|
};
|
|
/** Burrows-Wheeler Transform.
|
|
A should be Int32Array (signed!); T can be any typed array.
|
|
U is the same type as T (it is used for output).
|
|
alphabetSize is optional if T is an Uint8Array or Uint16Array.
|
|
ASSUMES STRING IS TERMINATED WITH AN EOF CHARACTER.
|
|
*/
|
|
BWT.bwtransform = function(T, U, A, n, alphabetSize) {
|
|
var i, pidx;
|
|
ASSERT(T && U && A);
|
|
ASSERT(T.length >= n && U.length >= n && A.length >= n);
|
|
if (n <= 1) {
|
|
if (n === 1) { U[0] = T[0]; }
|
|
return n;
|
|
}
|
|
if (!alphabetSize) {
|
|
if (T.BYTES_PER_ELEMENT === 1) { alphabetSize = 256; } else if (T.BYTES_PER_ELEMENT === 2) { alphabetSize = 65536; } else throw new Error('Need to specify alphabetSize');
|
|
}
|
|
ASSERT(alphabetSize > 0);
|
|
if (T.BYTES_PER_ELEMENT) {
|
|
ASSERT(alphabetSize <= (1 << (T.BYTES_PER_ELEMENT * 8)));
|
|
}
|
|
pidx = SA_IS(T, A, 0, n, alphabetSize, true);
|
|
U[0] = T[n - 1];
|
|
for (i = 0; i < pidx; i++) { U[i + 1] = A[i]; }
|
|
for (i += 1; i < n; i++) { U[i] = A[i]; }
|
|
return pidx + 1;
|
|
};
|
|
/** Reverses transform above. (ASSUMED STRING IS TERMINATED WITH EOF.) */
|
|
BWT.unbwtransform = function(T, U, LF, n, pidx) {
|
|
var C = Util.makeU32Buffer(256);
|
|
var i, t;
|
|
for (i = 0; i < 256; i++) { C[i] = 0; }
|
|
for (i = 0; i < n; i++) { LF[i] = C[T[i]]++; }
|
|
for (i = 0, t = 0; i < 256; i++) {
|
|
t += C[i];
|
|
C[i] = t - C[i];
|
|
}
|
|
for (i = n - 1, t = 0; i >= 0; i--) {
|
|
t = LF[t] + C[U[i] = T[t]];
|
|
t += (t < pidx) ? 1 : 0;
|
|
}
|
|
C = null;
|
|
};
|
|
|
|
/** Burrows-Wheeler Transform.
|
|
A should be Int32Array (signed!); T can be any typed array.
|
|
U is the same type as T (it is used for output).
|
|
alphabetSize is optional if T is an Uint8Array or Uint16Array.
|
|
ASSUMES STRING IS CYCLIC.
|
|
(XXX: this is twice as inefficient as I'd like! [CSA])
|
|
*/
|
|
BWT.bwtransform2 = function(T, U, n, alphabetSize) {
|
|
var i, j, pidx = 0;
|
|
ASSERT(T && U);
|
|
ASSERT(T.length >= n && U.length >= n);
|
|
if (n <= 1) {
|
|
if (n === 1) { U[0] = T[0]; }
|
|
return 0;
|
|
}
|
|
if (!alphabetSize) {
|
|
if (T.BYTES_PER_ELEMENT === 1) { alphabetSize = 256; } else if (T.BYTES_PER_ELEMENT === 2) { alphabetSize = 65536; } else throw new Error('Need to specify alphabetSize');
|
|
}
|
|
ASSERT(alphabetSize > 0);
|
|
if (T.BYTES_PER_ELEMENT) {
|
|
ASSERT(alphabetSize <= (1 << (T.BYTES_PER_ELEMENT * 8)));
|
|
}
|
|
// double length of T
|
|
var TT;
|
|
if (T.length >= n * 2) {
|
|
TT = T; // do it in place if possible
|
|
} else if (alphabetSize <= 256) {
|
|
TT = Util.makeU8Buffer(n * 2);
|
|
} else if (alphabetSize <= 65536) {
|
|
TT = Util.makeU16Buffer(n * 2);
|
|
} else {
|
|
TT = Util.makeU32Buffer(n * 2);
|
|
}
|
|
if (TT !== T) {
|
|
for (i = 0; i < n; i++) { TT[i] = T[i]; }
|
|
}
|
|
for (i = 0; i < n; i++) { TT[n + i] = TT[i]; }
|
|
// sort doubled string
|
|
var A = Util.makeS32Buffer(n * 2);
|
|
SA_IS(TT, A, 0, n * 2, alphabetSize, false);
|
|
for (i = 0, j = 0; i < 2 * n; i++) {
|
|
var s = A[i];
|
|
if (s < n) {
|
|
if (s === 0) { pidx = j; }
|
|
if (--s < 0) { s = n - 1; }
|
|
U[j++] = T[s];
|
|
}
|
|
}
|
|
ASSERT(j === n);
|
|
return pidx;
|
|
};
|
|
|
|
return BWT;
|
|
}());
|
|
|
|
|
|
LogDistanceModel = (function(){
|
|
/** Simple (log n)(n) distance model. */
|
|
|
|
// lengthBitsModelFactory will be called with arguments 2, 4, 8, 16, etc
|
|
// and must return an appropriate model or coder.
|
|
var LogDistanceModel = function(size, extraStates
|
|
, lgDistanceModelFactory
|
|
, lengthBitsModelFactory) {
|
|
var i;
|
|
var bits = Util.fls(size - 1);
|
|
this.extraStates = +extraStates || 0;
|
|
this.lgDistanceModel = lgDistanceModelFactory(1 + bits + extraStates);
|
|
// this.distanceModel[n] used for distances which are n-bits long,
|
|
// but only n-1 bits are encoded: the top bit is known to be one.
|
|
this.distanceModel = [];
|
|
for (i = 2; i <= bits; i++) {
|
|
var numBits = i - 1;
|
|
this.distanceModel[i] = lengthBitsModelFactory(1 << numBits);
|
|
}
|
|
};
|
|
/* you can give this model arguments between 0 and (size-1), or else
|
|
a negative argument which is one of the 'extra states'. */
|
|
LogDistanceModel.prototype.encode = function(distance) {
|
|
if (distance < 2) { // small distance or an 'extra state'
|
|
this.lgDistanceModel.encode(distance + this.extraStates);
|
|
return;
|
|
}
|
|
var lgDistance = Util.fls(distance);
|
|
console.assert(distance & (1 << (lgDistance - 1))); // top bit is set
|
|
console.assert(lgDistance >= 2);
|
|
this.lgDistanceModel.encode(lgDistance + this.extraStates);
|
|
// now encode the rest of the bits.
|
|
var rest = distance & ((1 << (lgDistance - 1)) - 1);
|
|
this.distanceModel[lgDistance].encode(rest);
|
|
};
|
|
LogDistanceModel.prototype.decode = function() {
|
|
var lgDistance = this.lgDistanceModel.decode() - this.extraStates;
|
|
if (lgDistance < 2) {
|
|
return lgDistance; // this is a small distance or an 'extra state'
|
|
}
|
|
var rest = this.distanceModel[lgDistance].decode();
|
|
return (1 << (lgDistance - 1)) + rest;
|
|
};
|
|
|
|
return LogDistanceModel;
|
|
|
|
}());
|
|
|
|
|
|
NoModel = (function(){
|
|
/** Simple "lack of model" -- just encode the bits directly.
|
|
* Useful especially with sparse spaces or Huffman coders where there's
|
|
* no obvious prediction to be made that will pay for itself.
|
|
*/
|
|
|
|
var NoModel = function(bitstream, size) {
|
|
this.bitstream = bitstream;
|
|
this.bits = Util.fls(size - 1);
|
|
};
|
|
NoModel.factory = function(bitstream) {
|
|
return function(size) { return new NoModel(bitstream, size); };
|
|
};
|
|
NoModel.prototype.encode = function(symbol) {
|
|
var i;
|
|
for (i = this.bits - 1; i >= 0; i--) {
|
|
var b = (symbol >>> i) & 1;
|
|
this.bitstream.writeBit(b);
|
|
}
|
|
};
|
|
NoModel.prototype.decode = function() {
|
|
var i, r = 0;
|
|
for (i = this.bits - 1; i >= 0; i--) {
|
|
r <<= 1;
|
|
if (this.bitstream.readBit()) r++;
|
|
}
|
|
return r;
|
|
};
|
|
|
|
/** Brain-dead self-test. */
|
|
NoModel.MAGIC = 'nomo';
|
|
NoModel.compressFile = Util.compressFileHelper(NoModel.MAGIC, function(inStream, outStream, fileSize, props) {
|
|
var bitstream = new BitStream(outStream);
|
|
var model = new NoModel(bitstream, (fileSize < 0) ? 257 : 256);
|
|
Util.compressWithModel(inStream, fileSize, model);
|
|
bitstream.flush();
|
|
});
|
|
NoModel.decompressFile = Util.decompressFileHelper(NoModel.MAGIC, function(inStream, outStream, fileSize) {
|
|
var bitstream = new BitStream(inStream);
|
|
var model = new NoModel(bitstream, (fileSize < 0) ? 257 : 256);
|
|
Util.decompressWithModel(outStream, fileSize, model);
|
|
});
|
|
|
|
return NoModel;
|
|
|
|
|
|
}());
|
|
|
|
|
|
DefSumModel = (function(){
|
|
|
|
/** Deferred-sum model, suitable for small ( ~ 256 ) ranges. */
|
|
// See http://cbloom.com/src/defsum.zip
|
|
// http://cbloom.com/papers/context.pdf
|
|
|
|
var LOG_PROB_TOTAL = 8;
|
|
var PROB_TOTAL = 1 << LOG_PROB_TOTAL;
|
|
var MAX_ESCAPE_COUNT = 40;
|
|
|
|
var DefSumModel = function(coder, size, isDecoder) {
|
|
var i;
|
|
console.assert(size < 300); // not meant for sparse
|
|
var ESCAPE = this.numSyms = size;
|
|
this.coder = coder;
|
|
this.prob = Util.makeU16Buffer(size + 2); /* size + ESC + 1 */
|
|
this.escape = Util.makeU16Buffer(size + 1); /* size + 1*/
|
|
this.update = Util.makeU16Buffer(size + 1); /* size + ESC */
|
|
this.prob[ESCAPE + 1] = PROB_TOTAL;
|
|
for (i = 0; i <= this.numSyms; i++) {
|
|
this.escape[i] = i;
|
|
}
|
|
this.updateCount = 0;
|
|
this.updateThresh = PROB_TOTAL - Math.floor(PROB_TOTAL / 2);
|
|
if (!isDecoder) {
|
|
return;
|
|
}
|
|
// extra tables for fast decoding
|
|
this.probToSym = Util.makeU16Buffer(PROB_TOTAL);
|
|
this.escProbToSym = Util.makeU16Buffer(this.numSyms);
|
|
for (i = 0; i < PROB_TOTAL; i++) {
|
|
this.probToSym[i] = ESCAPE;
|
|
}
|
|
for (i = 0; i < this.numSyms; i++) {
|
|
this.escProbToSym[i] = i;
|
|
}
|
|
};
|
|
DefSumModel.factory = function(coder, isDecoder) {
|
|
return function(size) {
|
|
return new DefSumModel(coder, size, isDecoder);
|
|
};
|
|
};
|
|
DefSumModel.prototype._update = function(symbol, isDecoder) {
|
|
if (symbol === this.numSyms) {
|
|
// some special cases for the escape character
|
|
if (this.update[symbol] >= MAX_ESCAPE_COUNT) {
|
|
return;
|
|
} // hard limit
|
|
// don't let an escape character trigger an update, because then the
|
|
// escaped character might find itself unescaped after the tables have
|
|
// been updated!
|
|
if (this.updateCount >= (this.updateThresh - 1)) {
|
|
return;
|
|
}
|
|
}
|
|
this.update[symbol]++;
|
|
this.updateCount++;
|
|
// is it time to transfer the updated probabilities?
|
|
if (this.updateCount < this.updateThresh) {
|
|
return; //defer update
|
|
}
|
|
var cumProb, cumEscProb, odd, i, j, k;
|
|
this.escape[0] = this.prob[0] = cumProb = cumEscProb = odd = 0;
|
|
for (i = 0; i < this.numSyms + 1; i++) {
|
|
var newProb = ((this.prob[i + 1] - this.prob[i]) >>> 1) + this.update[i];
|
|
if (newProb) {
|
|
// live 'un
|
|
this.prob[i] = cumProb;
|
|
cumProb += newProb;
|
|
if (newProb & 1) {
|
|
odd++;
|
|
}
|
|
this.escape[i] = cumEscProb;
|
|
} else {
|
|
// this symbol will escape
|
|
this.prob[i] = cumProb;
|
|
this.escape[i] = cumEscProb;
|
|
cumEscProb++;
|
|
}
|
|
}
|
|
this.prob[i] = cumProb;
|
|
console.assert(cumProb === PROB_TOTAL);
|
|
/* how many updates will be required after current probs are halved? */
|
|
this.updateThresh = PROB_TOTAL - Math.floor((cumProb - odd) / 2);
|
|
/* reset the update table */
|
|
for (i = 0; i < (this.numSyms + 1); i++) {
|
|
this.update[i] = 0;
|
|
}
|
|
this.update[this.numSyms] = 1; // ensure that escape never goes away
|
|
this.updateCount = 1;
|
|
/* compute decode table, if this is a decoder */
|
|
if (!isDecoder) {
|
|
return;
|
|
}
|
|
for (i = 0, j = 0, k = 0; i < (this.numSyms + 1); i++) {
|
|
var probLimit = this.prob[i + 1];
|
|
for (; j < probLimit; j++) {
|
|
this.probToSym[j] = i;
|
|
}
|
|
var escProbLimit = this.escape[i + 1];
|
|
for (; k < escProbLimit; k++) {
|
|
this.escProbToSym[k] = i;
|
|
}
|
|
}
|
|
};
|
|
DefSumModel.prototype.encode = function(symbol) {
|
|
var lt_f = this.prob[symbol];
|
|
var sy_f = this.prob[symbol + 1] - lt_f;
|
|
console.assert(this.prob[this.numSyms + 1] === PROB_TOTAL);
|
|
if (sy_f) {
|
|
this.coder.encodeShift(sy_f, lt_f, LOG_PROB_TOTAL);
|
|
return this._update(symbol);
|
|
}
|
|
// escape!
|
|
console.assert(symbol !== this.numSyms); // catch infinite recursion
|
|
this.encode(this.numSyms); // guaranteed non-zero probability
|
|
// code symbol as literal, taking advantage of reduced escape range.
|
|
lt_f = this.escape[symbol];
|
|
sy_f = this.escape[symbol + 1] - lt_f;
|
|
var tot_f = this.escape[this.numSyms];
|
|
this.coder.encodeFreq(sy_f, lt_f, tot_f);
|
|
return this._update(symbol);
|
|
};
|
|
DefSumModel.prototype.decode = function() {
|
|
var prob = this.coder.decodeCulShift(LOG_PROB_TOTAL);
|
|
var symbol = this.probToSym[prob];
|
|
var lt_f = this.prob[symbol];
|
|
var sy_f = this.prob[symbol + 1] - lt_f;
|
|
this.coder.decodeUpdate(sy_f, lt_f, PROB_TOTAL);
|
|
this._update(symbol, true);
|
|
if (symbol !== this.numSyms) {
|
|
return symbol;
|
|
}
|
|
// escape!
|
|
var tot_f = this.escape[this.numSyms];
|
|
prob = this.coder.decodeCulFreq(tot_f);
|
|
symbol = this.escProbToSym[prob];
|
|
lt_f = this.escape[symbol];
|
|
sy_f = this.escape[symbol + 1] - lt_f;
|
|
this.coder.decodeUpdate(sy_f, lt_f, tot_f);
|
|
this._update(symbol, true);
|
|
return symbol;
|
|
};
|
|
|
|
DefSumModel.MAGIC = 'dfsm';
|
|
/** Simple order-0 compressor, as self-test. */
|
|
DefSumModel.compressFile = Util.compressFileHelper(DefSumModel.MAGIC, function(inStream, outStream, fileSize, props, finalByte) {
|
|
var range = new RangeCoder(outStream);
|
|
range.encodeStart(finalByte, 1);
|
|
var model = new DefSumModel(range, (fileSize < 0) ? 257 : 256);
|
|
Util.compressWithModel(inStream, fileSize, model);
|
|
range.encodeFinish();
|
|
}, true);
|
|
/** Simple order-0 decompresser, as self-test. */
|
|
DefSumModel.decompressFile = Util.decompressFileHelper(DefSumModel.MAGIC, function(inStream, outStream, fileSize) {
|
|
var range = new RangeCoder(inStream);
|
|
range.decodeStart(true /*already read the final byte*/ );
|
|
var model = new DefSumModel(range, (fileSize < 0) ? 257 : 256, true);
|
|
Util.decompressWithModel(outStream, fileSize, model);
|
|
range.decodeFinish();
|
|
});
|
|
|
|
return DefSumModel;
|
|
}());
|
|
|
|
|
|
FenwickModel = (function(){
|
|
/** Range coding model based on Fenwick trees for O(ln N) query/update. */
|
|
|
|
/** We store two probabilities in a U32, so max prob is going to be 0xFFFF */
|
|
var DEFAULT_MAX_PROB = 0xFF00;
|
|
var DEFAULT_INCREMENT = 0x0100;
|
|
|
|
var ESC_MASK = 0x0000FFFF
|
|
, ESC_SHIFT = 0;
|
|
var SYM_MASK = 0xFFFF0000
|
|
, SYM_SHIFT = 16;
|
|
var SCALE_MASK = 0xFFFEFFFE;
|
|
|
|
var FenwickModel = function(coder, size, max_prob, increment) {
|
|
this.coder = coder;
|
|
this.numSyms = size + 1; // save space for an escape symbol
|
|
this.tree = Util.makeU32Buffer(this.numSyms * 2);
|
|
this.increment = (+increment) || DEFAULT_INCREMENT;
|
|
this.max_prob = (+max_prob) || DEFAULT_MAX_PROB;
|
|
// sanity-check to prevent overflow.
|
|
console.assert((this.max_prob + (this.increment - 1)) <= 0xFFFF);
|
|
console.assert(size <= 0xFFFF);
|
|
// record escape probability as 1.
|
|
var i;
|
|
for (i = 0; i < size; i++) {
|
|
this.tree[this.numSyms + i] = // escape prob=1, sym prob = 0
|
|
(1 << ESC_SHIFT) | (0 << SYM_SHIFT);
|
|
}
|
|
this.tree[this.numSyms + i] = // escape prob = 0, sym prob = 1
|
|
(0 << ESC_SHIFT) | (this.increment << SYM_SHIFT);
|
|
this._sumTree();
|
|
// probability sums are in this.tree[1]. this.tree[0] is unused.
|
|
};
|
|
FenwickModel.factory = function(coder, max_prob, increment) {
|
|
return function(size) {
|
|
return new FenwickModel(coder, size, max_prob, increment);
|
|
};
|
|
};
|
|
FenwickModel.prototype.clone = function() {
|
|
var newModel = new FenwickModel(this.coder, this.size
|
|
, this.max_prob, this.increment);
|
|
var i;
|
|
for (i = 1; i < this.tree.length; i++) {
|
|
newModel.tree[i] = this.tree[i];
|
|
}
|
|
return newModel;
|
|
};
|
|
FenwickModel.prototype.encode = function(symbol) {
|
|
var i = this.numSyms + symbol;
|
|
var sy_f = this.tree[i];
|
|
var mask = SYM_MASK
|
|
, shift = SYM_SHIFT;
|
|
var update = (this.increment << SYM_SHIFT);
|
|
|
|
if ((sy_f & SYM_MASK) === 0) { // escape!
|
|
this.encode(this.numSyms - 1);
|
|
mask = ESC_MASK;
|
|
update -= (1 << ESC_SHIFT); // not going to escape no mo'
|
|
shift = ESC_SHIFT;
|
|
} else if (symbol === (this.numSyms - 1) &&
|
|
((this.tree[1] & ESC_MASK) >>> ESC_SHIFT) === 1) {
|
|
// this is the last escape, zero it out
|
|
update = -this.tree[i];
|
|
}
|
|
// sum up the proper lt_f
|
|
var lt_f = 0;
|
|
while (i > 1) {
|
|
var isRight = (i & 1);
|
|
var parent = (i >>> 1);
|
|
// if we're the right child, we need to
|
|
// add the prob from the left child
|
|
if (isRight) {
|
|
lt_f += this.tree[2 * parent];
|
|
}
|
|
// update sums
|
|
this.tree[i] += update; // increase sym / decrease esc
|
|
i = parent;
|
|
}
|
|
var tot_f = this.tree[1];
|
|
this.tree[1] += update; // update prob in root
|
|
sy_f = (sy_f & mask) >>> shift;
|
|
lt_f = (lt_f & mask) >>> shift;
|
|
tot_f = (tot_f & mask) >>> shift;
|
|
this.coder.encodeFreq(sy_f, lt_f, tot_f);
|
|
// rescale?
|
|
if (((this.tree[1] & SYM_MASK) >>> SYM_SHIFT) >= this.max_prob) {
|
|
this._rescale();
|
|
}
|
|
};
|
|
FenwickModel.prototype._decode = function(isEscape) {
|
|
var mask = SYM_MASK
|
|
, shift = SYM_SHIFT;
|
|
var update = (this.increment << SYM_SHIFT);
|
|
if (isEscape) {
|
|
mask = ESC_MASK;
|
|
update -= (1 << ESC_SHIFT);
|
|
shift = ESC_SHIFT;
|
|
}
|
|
var tot_f = (this.tree[1] & mask) >>> shift;
|
|
var prob = this.coder.decodeCulFreq(tot_f);
|
|
// travel down the tree looking for this
|
|
var i = 1
|
|
, lt_f = 0;
|
|
while (i < this.numSyms) {
|
|
this.tree[i] += update;
|
|
// look at probability in left child.
|
|
var leftProb = (this.tree[2 * i] & mask) >>> shift;
|
|
i *= 2;
|
|
if ((prob - lt_f) >= leftProb) {
|
|
lt_f += leftProb;
|
|
i++; // take the right child.
|
|
}
|
|
}
|
|
var symbol = i - this.numSyms;
|
|
var sy_f = (this.tree[i] & mask) >>> shift;
|
|
this.tree[i] += update;
|
|
this.coder.decodeUpdate(sy_f, lt_f, tot_f);
|
|
// was this the last escape?
|
|
if (symbol === (this.numSyms - 1) &&
|
|
((this.tree[1] & ESC_MASK) >>> ESC_SHIFT) === 1) {
|
|
update = -this.tree[i]; // zero it out
|
|
while (i >= 1) {
|
|
this.tree[i] += update;
|
|
i = (i >>> 1); // parent
|
|
}
|
|
}
|
|
// rescale?
|
|
if (((this.tree[1] & SYM_MASK) >>> SYM_SHIFT) >= this.max_prob) {
|
|
this._rescale();
|
|
}
|
|
return symbol;
|
|
};
|
|
FenwickModel.prototype.decode = function() {
|
|
var symbol = this._decode(false); // not escape
|
|
if (symbol === (this.numSyms - 1)) {
|
|
// this was an escape!
|
|
symbol = this._decode(true); // an escape!
|
|
}
|
|
return symbol;
|
|
};
|
|
FenwickModel.prototype._rescale = function() {
|
|
var i, prob, noEscape = true;
|
|
// scale symbols (possible causing them to escape)
|
|
for (i = 0; i < this.numSyms - 1; i++) {
|
|
prob = this.tree[this.numSyms + i];
|
|
if ((prob & ESC_MASK) !== 0) {
|
|
// this symbol escapes
|
|
noEscape = false;
|
|
continue;
|
|
}
|
|
prob = (prob & SCALE_MASK) >>> 1;
|
|
if (prob === 0) {
|
|
// this symbol newly escapes
|
|
prob = (1 << ESC_SHIFT);
|
|
noEscape = false;
|
|
}
|
|
this.tree[this.numSyms + i] = prob;
|
|
}
|
|
// scale the escape symbol
|
|
prob = this.tree[this.numSyms + i];
|
|
prob = (prob & SCALE_MASK) >>> 1;
|
|
// prob should be zero if there are no escaping symbols, otherwise
|
|
// it must be at least 1.
|
|
if (noEscape) {
|
|
prob = 0;
|
|
} else if (prob === 0) {
|
|
prob = (1 << SYM_SHIFT);
|
|
}
|
|
this.tree[this.numSyms + i] = prob;
|
|
// sum it all up afresh
|
|
this._sumTree();
|
|
};
|
|
FenwickModel.prototype._sumTree = function() {
|
|
var i;
|
|
// sum it all. (we know we won't overflow)
|
|
for (i = this.numSyms - 1; i > 0; i--) {
|
|
this.tree[i] = this.tree[2 * i] + this.tree[2 * i + 1];
|
|
}
|
|
};
|
|
|
|
FenwickModel.MAGIC = 'fenw';
|
|
/** Simple order-0 compressor, as self-test. */
|
|
FenwickModel.compressFile = Util.compressFileHelper(FenwickModel.MAGIC, function(inStream, outStream, fileSize, props, finalByte) {
|
|
var range = new RangeCoder(outStream);
|
|
range.encodeStart(finalByte, 1);
|
|
var model = new FenwickModel(range, (fileSize < 0) ? 257 : 256);
|
|
Util.compressWithModel(inStream, fileSize, model);
|
|
range.encodeFinish();
|
|
}, true);
|
|
|
|
/** Simple order-0 decompresser, as self-test. */
|
|
FenwickModel.decompressFile = Util.decompressFileHelper(FenwickModel.MAGIC, function(inStream, outStream, fileSize) {
|
|
var range = new RangeCoder(inStream);
|
|
range.decodeStart(true /*already read the final byte*/ );
|
|
var model = new FenwickModel(range, (fileSize < 0) ? 257 : 256);
|
|
Util.decompressWithModel(outStream, fileSize, model);
|
|
range.decodeFinish();
|
|
});
|
|
|
|
return FenwickModel;
|
|
|
|
}());
|
|
|
|
|
|
BWTC = (function(){
|
|
/* A simple bzip-like BWT compressor with a range encoder; written as a
|
|
* self-test of the BWT package. */
|
|
|
|
var EOF = Stream.EOF;
|
|
|
|
var F_PROB_MAX = 0xFF00;
|
|
var F_PROB_INCR = 0x0100;
|
|
|
|
BWTC = Object.create(null);
|
|
BWTC.MAGIC = "bwtc";
|
|
BWTC.compressFile = Util.compressFileHelper(BWTC.MAGIC, function(input, output, size, props, finalByte) {
|
|
var encoder = new RangeCoder(output);
|
|
encoder.encodeStart(finalByte, 1);
|
|
|
|
var blockSize = document.getElementById("complevel").value;
|
|
if (typeof(props) === 'number' && props >= 1 && props <= 9) {
|
|
blockSize = props;
|
|
}
|
|
encoder.encodeByte(blockSize);
|
|
var fast = (blockSize <= 5);
|
|
blockSize *= 100000;
|
|
|
|
var block = Util.makeU8Buffer(blockSize);
|
|
var readBlock = function() {
|
|
var pos;
|
|
for (pos = 0; pos < blockSize;) {
|
|
var ch = input.readByte();
|
|
if (ch < 0) { break; }
|
|
block[pos++] = ch;
|
|
}
|
|
return pos;
|
|
};
|
|
var U = Util.makeU8Buffer(blockSize);
|
|
var A = Util.makeS32Buffer(blockSize);
|
|
var M = Util.makeU8Buffer(256); // move to front array
|
|
var bitModelFactory = NoModel.factory(encoder);
|
|
var lenModel = new LogDistanceModel(blockSize, 0
|
|
, bitModelFactory
|
|
, bitModelFactory);
|
|
var length, b, c, pidx, i, j;
|
|
do {
|
|
length = readBlock();
|
|
if (length === 0) { break; }
|
|
// indicate that there's another block comin'
|
|
// and encode the length of the block if necessary
|
|
if (length === block.length) {
|
|
encoder.encodeFreq(1, 0, 3); // "full size block"
|
|
b = block;
|
|
} else {
|
|
encoder.encodeFreq(1, 1, 3); // "short block"
|
|
lenModel.encode(length);
|
|
b = block.subarray(0, length);
|
|
}
|
|
pidx = BWT.bwtransform(b, U, A, length, 256);
|
|
lenModel.encode(pidx); // starting index
|
|
// encode the alphabet subset used
|
|
var useTree = Util.makeU16Buffer(512);
|
|
for (i = 0; i < length; i++) {
|
|
c = U[i];
|
|
useTree[256 + c] = 1;
|
|
}
|
|
for (i = 255; i > 0; i--) { // sum all the way up the tree
|
|
useTree[i] = useTree[2 * i] + useTree[2 * i + 1];
|
|
}
|
|
useTree[0] = 1; // sentinel
|
|
for (i = 1; i < 512; i++) {
|
|
var parent = i >>> 1;
|
|
var full = 1 << (9 - Util.fls(i));
|
|
if (useTree[parent] === 0 || useTree[parent] === (full * 2)) {
|
|
/* already known full/empty */
|
|
} else if (i >= 256) {
|
|
encoder.encodeBit(useTree[i]); // leaf node
|
|
} else {
|
|
var v = useTree[i];
|
|
v = (v === 0) ? 0 : (v === full) ? 2 : 1;
|
|
encoder.encodeFreq(1, v, 3);
|
|
}
|
|
}
|
|
// remap symbols to this subset
|
|
var alphabetSize = 0;
|
|
for (i = 0; i < 256; i++) {
|
|
if (useTree[256 + i]) { // symbol in use
|
|
M[alphabetSize++] = i;
|
|
}
|
|
}
|
|
useTree = null;
|
|
// MTF encoding of U
|
|
for (i = 0; i < length; i++) {
|
|
c = U[i];
|
|
for (j = 0; j < alphabetSize; j++) {
|
|
if (M[j] === c) {
|
|
break;
|
|
}
|
|
}
|
|
console.assert(j < alphabetSize);
|
|
U[i] = j;
|
|
// move to front
|
|
for (; j > 0; j--) {
|
|
M[j] = M[j - 1];
|
|
}
|
|
M[0] = c;
|
|
}
|
|
// RLE/range encoding
|
|
var model = new FenwickModel(encoder, alphabetSize + 1
|
|
, F_PROB_MAX, F_PROB_INCR);
|
|
if (fast) { model = new DefSumModel(encoder, alphabetSize + 1); }
|
|
var runLength = 0;
|
|
var emitLastRun = function() {
|
|
// binary encode runs of zeros
|
|
while (runLength !== 0) {
|
|
if (runLength & 1) {
|
|
model.encode(0); // RUNA
|
|
runLength -= 1;
|
|
} else {
|
|
model.encode(1); // RUNB
|
|
runLength -= 2;
|
|
}
|
|
runLength >>>= 1;
|
|
}
|
|
};
|
|
for (i = 0; i < length; i++) {
|
|
c = U[i];
|
|
if (c === 0) {
|
|
runLength++;
|
|
} else {
|
|
emitLastRun();
|
|
model.encode(c + 1);
|
|
// reset for next
|
|
runLength = 0;
|
|
}
|
|
}
|
|
emitLastRun();
|
|
// done with this block!
|
|
} while (length === block.length);
|
|
|
|
encoder.encodeFreq(1, 2, 3); // "no more blocks"
|
|
encoder.encodeFinish();
|
|
}, true);
|
|
|
|
BWTC.decompressFile = Util.decompressFileHelper(BWTC.MAGIC, function(input, output, size) {
|
|
var decoder = new RangeCoder(input);
|
|
decoder.decodeStart(true /* already read the extra byte */ );
|
|
var blockSize = decoder.decodeByte();
|
|
console.assert(blockSize >= 1 && blockSize <= 9);
|
|
var fast = (blockSize <= 5);
|
|
blockSize *= 100000;
|
|
|
|
var block = Util.makeU8Buffer(blockSize);
|
|
var U = Util.makeU8Buffer(blockSize);
|
|
var A = Util.makeS32Buffer(blockSize);
|
|
var M = Util.makeU8Buffer(256); // move to front array
|
|
var bitModelFactory = NoModel.factory(decoder);
|
|
var lenModel = new LogDistanceModel(blockSize, 0
|
|
, bitModelFactory
|
|
, bitModelFactory);
|
|
var b, length, i, j, c;
|
|
while (true) {
|
|
var blockIndicator = decoder.decodeCulFreq(3);
|
|
decoder.decodeUpdate(1, blockIndicator, 3);
|
|
if (blockIndicator === 0) { // full-length block
|
|
length = blockSize;
|
|
b = block;
|
|
} else if (blockIndicator === 1) { // short block
|
|
length = lenModel.decode();
|
|
b = block.subarray(0, length);
|
|
} else if (blockIndicator === 2) { // all done, no more blocks
|
|
break;
|
|
}
|
|
// read starting index for unBWT
|
|
var pidx = lenModel.decode();
|
|
// decode the alphabet subset used
|
|
var useTree = Util.makeU16Buffer(512);
|
|
useTree[0] = 1; // sentinel
|
|
for (i = 1; i < 512; i++) {
|
|
var parent = i >>> 1;
|
|
var full = 1 << (9 - Util.fls(i));
|
|
if (useTree[parent] === 0 || useTree[parent] === (full * 2)) {
|
|
/* already known full/empty */
|
|
useTree[i] = useTree[parent] >>> 1;
|
|
} else if (i >= 256) {
|
|
useTree[i] = decoder.decodeBit(); // leaf node
|
|
} else {
|
|
var v = decoder.decodeCulFreq(3);
|
|
decoder.decodeUpdate(1, v, 3);
|
|
useTree[i] = (v === 2) ? full : v;
|
|
}
|
|
}
|
|
// remap symbols to this subset
|
|
var alphabetSize = 0;
|
|
for (i = 0; i < 256; i++) {
|
|
if (useTree[256 + i]) { // symbol in use
|
|
M[alphabetSize++] = i;
|
|
}
|
|
}
|
|
useTree = null;
|
|
// RLE/range decoding
|
|
var model = new FenwickModel(decoder, alphabetSize + 1
|
|
, F_PROB_MAX, F_PROB_INCR);
|
|
if (fast) { model = new DefSumModel(decoder, alphabetSize + 1, true); }
|
|
var val = 1; // repeat count
|
|
for (i = 0; i < length;) {
|
|
c = model.decode();
|
|
if (c === 0) {
|
|
for (j = 0; j < val; j++) { b[i++] = 0; }
|
|
val *= 2;
|
|
} else if (c === 1) {
|
|
for (j = 0; j < val; j++) {
|
|
b[i++] = 0;
|
|
b[i++] = 0;
|
|
}
|
|
val *= 2;
|
|
} else {
|
|
val = 1;
|
|
b[i++] = c - 1;
|
|
}
|
|
}
|
|
// MTF decode
|
|
for (i = 0; i < length; i++) {
|
|
j = b[i];
|
|
b[i] = c = M[j];
|
|
// move to front
|
|
for (; j > 0; j--) {
|
|
M[j] = M[j - 1];
|
|
}
|
|
M[0] = c;
|
|
}
|
|
// unBWT
|
|
BWT.unbwtransform(block, U, A, length, pidx);
|
|
// emit!
|
|
output.write(U, 0, length);
|
|
}
|
|
decoder.decodeFinish();
|
|
});
|
|
|
|
return BWTC;
|
|
}());
|
|
|
|
|
|
|
|
window.addEvent('load', function() {
|
|
|
|
document.getElementById("fileToRead").addEventListener("change",function(){
|
|
var file = this.files[0];
|
|
|
|
if (file) {
|
|
var reader = new FileReader();
|
|
|
|
reader.onload = function (evt) {
|
|
console.log(evt);
|
|
document.getElementById("textbox").value = endianMark + evt.target.result;
|
|
};
|
|
|
|
reader.onerror = function (evt) {
|
|
console.error("An error ocurred reading the file",evt);
|
|
};
|
|
|
|
reader.readAsText(file, "UTF-16BE");
|
|
}
|
|
},false);
|
|
|
|
document.getElementById('download').addEventListener('click', function(){
|
|
if (document.getElementById("base64textarea").value.startsWith(endianMark)) {
|
|
|
|
downloadUtf16(document.getElementById("base64textarea").value.substring(1), document.getElementById("B3Kfilename").value)
|
|
}
|
|
else {
|
|
downloadUtf16(document.getElementById("base64textarea").value, document.getElementById("B3Kfilename").value)
|
|
}
|
|
|
|
});
|
|
|
|
function downloadUtf16(str, filename) {
|
|
|
|
// ref: https://stackoverflow.com/q/6226189
|
|
var charCode, byteArray = [];
|
|
|
|
// BE BOM
|
|
byteArray.push(254, 255);
|
|
|
|
// LE BOM
|
|
// byteArray.push(255, 254);
|
|
|
|
for (var i = 0; i < str.length; ++i) {
|
|
|
|
charCode = str.charCodeAt(i);
|
|
|
|
// BE Bytes
|
|
byteArray.push((charCode & 0xFF00) >>> 8);
|
|
byteArray.push(charCode & 0xFF);
|
|
|
|
// LE Bytes
|
|
// byteArray.push(charCode & 0xff);
|
|
// byteArray.push(charCode / 256 >>> 0);
|
|
}
|
|
|
|
var blob = new Blob([new Uint8Array(byteArray)], {type:'text/plain;charset=UTF-16BE;'});
|
|
var blobUrl = URL.createObjectURL(blob);
|
|
|
|
// ref: https://stackoverflow.com/a/18197511
|
|
var link = document.createElement('a');
|
|
link.href = blobUrl;
|
|
link.download = filename;
|
|
|
|
if (document.createEvent) {
|
|
var event = document.createEvent('MouseEvents');
|
|
event.initEvent('click', true, true);
|
|
link.dispatchEvent(event);
|
|
} else {
|
|
link.click();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 0x0000 - 0x18ff => 0x3400 - 0x4cff
|
|
// 0x1900 - 0x69ff => 0x4e00 - 0x9eff
|
|
// 0x6a00 - 0x8000 => 0xac00 - 0xc200
|
|
function num2cjk(num) {
|
|
if (num < 0 || 0x8000 < num)
|
|
throw "num2cjk";
|
|
return String.fromCharCode(
|
|
num < 0x1900 ? num + 0x3400 :
|
|
num < 0x6a00 ? num - 0x1900 + 0x4e00 :
|
|
num - 0x6a00 + 0xac00);
|
|
}
|
|
|
|
// 0x3400 - 0x4cff => 0x0000 - 0x18ff
|
|
// 0x4e00 - 0x9eff => 0x1900 - 0x69ff
|
|
// 0xac00 - 0xc200 => 0x6a00 - 0x8000
|
|
function cjk2num(cjk) {
|
|
var code = cjk.charCodeAt(0);
|
|
if (0x3400 <= code && code < 0x4d00) return code - 0x3400;
|
|
if (0x4e00 <= code && code < 0x9f00) return code - 0x4e00 + 0x1900;
|
|
if (0xac00 <= code && code < 0xc201) return code - 0xac00 + 0x6a00;
|
|
throw "cjk2num";
|
|
}
|
|
|
|
// [1,2,3] => "㒁㓀숀"
|
|
function base32768enc(bin) {
|
|
var str = "";
|
|
var t = 0;
|
|
var tl = 0;
|
|
for (var i = 0; i < bin.length; i++) {
|
|
if (tl <= 7) {
|
|
t |= bin[i] << (7 - tl);
|
|
tl += 8;
|
|
} else {
|
|
t |= bin[i] >> (tl - 7);
|
|
str += num2cjk(t);
|
|
t = bin[i] << (22 - tl) & 0x7fff,
|
|
tl -= 7;
|
|
}
|
|
}
|
|
if (tl > 0) {
|
|
str += num2cjk(t);
|
|
if (tl >= 9)
|
|
str += num2cjk(0x8000);
|
|
}
|
|
return str;
|
|
}
|
|
|
|
// "㒁㓀숀" => [1,2,3]
|
|
function base32768dec(str) {
|
|
if (str.length == 0)
|
|
return new Uint8Array(0);
|
|
|
|
var sl = str.length;
|
|
var bl = ((sl - 1) >> 3) * 15 + (sl - 1) % 8 * 2;
|
|
var f = str[sl - 1] == num2cjk(0x8000);
|
|
if ((sl - 1) % 8 == 0 && !f)
|
|
bl++;
|
|
if ((sl - 1) % 8 != 0 && f)
|
|
bl--;
|
|
if (f)
|
|
sl--;
|
|
|
|
var bin = new Uint8Array(bl);
|
|
var t = 0;
|
|
var tl = 0;
|
|
var si = 0;
|
|
for (var bi = 0; bi < bl; bi++) {
|
|
if (tl < 8) {
|
|
t = t << 15 | cjk2num(str[si++]);
|
|
tl += 15;
|
|
}
|
|
bin[bi] = t >> (tl - 8) & 0xff;
|
|
tl -= 8;
|
|
}
|
|
return bin;
|
|
}
|
|
|
|
function uint8equal(a, b) {
|
|
if (a.length != b.length)
|
|
return false;
|
|
for (var i = 0; i < a.length; i++)
|
|
if (a[i] != b[i])
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
// [1,2,3] => "010203"
|
|
function uint82hex(a) {
|
|
var h = "";
|
|
for (var i = 0; i < a.length; i++)
|
|
h += (a[i] < 16 ? "0" : "") + a[i].toString(16)
|
|
return h;
|
|
}
|
|
|
|
// "010203" => [1,2,3]
|
|
function hex2uint8(h) {
|
|
var a = new Uint8Array(h.length / 2);
|
|
for (var i = 0; i < a.length; i++)
|
|
a[i] = parseInt(h.substr(i * 2, 2), 16);
|
|
return a;
|
|
}
|
|
|
|
|
|
var endianMark = "\ufeff";
|
|
var pre = "\u4d00";
|
|
var suf = "\u4d01";
|
|
var re = new RegExp(endianMark+pre+"(.+?)"+suf);
|
|
var re2 = new RegExp(pre+"(.+?)"+suf);
|
|
var compLevel = document.getElementById("complevel").value;
|
|
|
|
var handleFileSelect = function(evt) {
|
|
var files = evt.target.files;
|
|
var file = files[0];
|
|
|
|
if (files && file) {
|
|
var reader = new FileReader();
|
|
|
|
reader.onload = function(readerEvt) {
|
|
|
|
var arrayBuffer = readerEvt.target.result;
|
|
array = new Uint8Array(arrayBuffer),
|
|
//var binaryString = readerEvt.target.result;
|
|
|
|
compLevel = document.getElementById("complevel").value,
|
|
document.getElementById("base64textarea").value = endianMark+pre+base32768enc(new aesjs.ModeOfOperation.ctr(sha256.array(document.getElementById("ascii85password").value), new aesjs.Counter(1)).encrypt(BWTC.compressFile(array, undefined, compLevel)))+suf;
|
|
};
|
|
|
|
reader.readAsArrayBuffer(file);
|
|
}
|
|
};
|
|
|
|
if (window.File && window.FileReader && window.FileList && window.Blob) {
|
|
document.getElementById('filePicker').addEventListener('change', handleFileSelect, false);
|
|
} else {
|
|
alert('The File APIs are not fully supported in this browser.');
|
|
}
|
|
|
|
// 0x0000 - 0x18ff => 0x3400 - 0x4cff
|
|
// 0x1900 - 0x69ff => 0x4e00 - 0x9eff
|
|
// 0x6a00 - 0x8000 => 0xac00 - 0xc200
|
|
function num2cjk(num) {
|
|
if (num < 0 || 0x8000 < num)
|
|
throw "num2cjk";
|
|
return String.fromCharCode(
|
|
num < 0x1900 ? num + 0x3400 :
|
|
num < 0x6a00 ? num - 0x1900 + 0x4e00 :
|
|
num - 0x6a00 + 0xac00);
|
|
}
|
|
|
|
// 0x3400 - 0x4cff => 0x0000 - 0x18ff
|
|
// 0x4e00 - 0x9eff => 0x1900 - 0x69ff
|
|
// 0xac00 - 0xc200 => 0x6a00 - 0x8000
|
|
function cjk2num(cjk) {
|
|
var code = cjk.charCodeAt(0);
|
|
if (0x3400 <= code && code < 0x4d00) return code - 0x3400;
|
|
if (0x4e00 <= code && code < 0x9f00) return code - 0x4e00 + 0x1900;
|
|
if (0xac00 <= code && code < 0xc201) return code - 0xac00 + 0x6a00;
|
|
throw "cjk2num";
|
|
}
|
|
|
|
// [1,2,3] => "㒁㓀숀"
|
|
function base32768enc(bin) {
|
|
var str = "";
|
|
var t = 0;
|
|
var tl = 0;
|
|
for (var i = 0; i < bin.length; i++) {
|
|
if (tl <= 7) {
|
|
t |= bin[i] << (7 - tl);
|
|
tl += 8;
|
|
} else {
|
|
t |= bin[i] >> (tl - 7);
|
|
str += num2cjk(t);
|
|
t = bin[i] << (22 - tl) & 0x7fff,
|
|
tl -= 7;
|
|
}
|
|
}
|
|
if (tl > 0) {
|
|
str += num2cjk(t);
|
|
if (tl >= 9)
|
|
str += num2cjk(0x8000);
|
|
}
|
|
return str;
|
|
}
|
|
|
|
// "㒁㓀숀" => [1,2,3]
|
|
function base32768dec(str) {
|
|
if (str.length == 0)
|
|
return new Uint8Array(0);
|
|
|
|
var sl = str.length;
|
|
var bl = ((sl - 1) >> 3) * 15 + (sl - 1) % 8 * 2;
|
|
var f = str[sl - 1] == num2cjk(0x8000);
|
|
if ((sl - 1) % 8 == 0 && !f)
|
|
bl++;
|
|
if ((sl - 1) % 8 != 0 && f)
|
|
bl--;
|
|
if (f)
|
|
sl--;
|
|
|
|
var bin = new Uint8Array(bl);
|
|
var t = 0;
|
|
var tl = 0;
|
|
var si = 0;
|
|
for (var bi = 0; bi < bl; bi++) {
|
|
if (tl < 8) {
|
|
t = t << 15 | cjk2num(str[si++]);
|
|
tl += 15;
|
|
}
|
|
bin[bi] = t >> (tl - 8) & 0xff;
|
|
tl -= 8;
|
|
}
|
|
return bin;
|
|
}
|
|
|
|
function uint8equal(a, b) {
|
|
if (a.length != b.length)
|
|
return false;
|
|
for (var i = 0; i < a.length; i++)
|
|
if (a[i] != b[i])
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
// [1,2,3] => "010203"
|
|
function uint82hex(a) {
|
|
var h = "";
|
|
for (var i = 0; i < a.length; i++)
|
|
h += (a[i] < 16 ? "0" : "") + a[i].toString(16)
|
|
return h;
|
|
}
|
|
|
|
// "010203" => [1,2,3]
|
|
function hex2uint8(h) {
|
|
var a = new Uint8Array(h.length / 2);
|
|
for (var i = 0; i < a.length; i++)
|
|
a[i] = parseInt(h.substr(i * 2, 2), 16);
|
|
return a;
|
|
}
|
|
|
|
(function() {
|
|
var textFile = null,
|
|
makeTextFile = function(text) {
|
|
var m = text.match(re);
|
|
var m2 = text.match(re2);
|
|
|
|
if (!m && !m2)
|
|
{
|
|
alert('Invalid decoder input!');
|
|
};
|
|
if (m) {
|
|
var data = new Blob([BWTC.decompressFile(new aesjs.ModeOfOperation.ctr(sha256.array(document.getElementById("ascii85password").value), new aesjs.Counter(1)).decrypt(base32768dec(text.substr(1).slice(1, -1))))], {
|
|
type: document.getElementById('filetype').value
|
|
});
|
|
}
|
|
else if (m2) {
|
|
var data = new Blob([BWTC.decompressFile(new aesjs.ModeOfOperation.ctr(sha256.array(document.getElementById("ascii85password").value), new aesjs.Counter(1)).decrypt(base32768dec(text.slice(1, -1))))], {
|
|
type: document.getElementById('filetype').value
|
|
});
|
|
}
|
|
// If we are replacing a previously generated file we need to
|
|
// manually revoke the object URL to avoid memory leaks.
|
|
if (textFile !== null) {
|
|
window.URL.revokeObjectURL(textFile);
|
|
}
|
|
|
|
textFile = window.URL.createObjectURL(data);
|
|
|
|
return textFile;
|
|
};
|
|
|
|
|
|
var create = document.getElementById('create'),
|
|
textbox = document.getElementById('textbox');
|
|
|
|
create.addEventListener('click', function() {
|
|
var link = document.getElementById('downloadlink');
|
|
link.href = makeTextFile(textbox.value);
|
|
link.download = document.getElementById('filename').value;
|
|
link.style.display = 'block';
|
|
}, false);
|
|
})();
|
|
|
|
});
|
|
|
|
//]]>
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
</body></html>
|