9770 lines
309 KiB
HTML
9770 lines
309 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>
|
|
<form name="BWTC32Key" action="">
|
|
<textarea id="base64textarea" name="base32768textarea" placeholder="Base32768 will appear here" cols="50" rows="15"></textarea><br>
|
|
<button id="copy" type="button" onclick="copyOutputToClipboard();">Copy the above string.</button>
|
|
</form>
|
|
<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>
|
|
<!--<form name="b3k">--><textarea id="textbox" name="decinput" cols="25" >Type BWTC32Key-encoded text here to decode</textarea><button id="create">Click to decode file</button><br />
|
|
<p>Output's Filename & MIMEtype <button id="textextract" onclick="copyOutputToClipboard2();" type="button">Copy above box</button></p><!--</form>-->
|
|
<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;
|
|
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);
|
|
})();
|
|
|
|
});
|
|
|
|
|
|
function copyOutputToClipboard() {
|
|
var targetBox = document.BWTC32Key.base32768textarea;
|
|
// https://stackoverflow.com/questions/51158061/copy-data-to-clipboard-without-selecting-any-text
|
|
// - restoring original selection doesn't seem to work
|
|
var origSelectionStart, origSelectionEnd;
|
|
origSelectionStart = targetBox.selectionStart;
|
|
origSelectionEnd = targetBox.selectionEnd;
|
|
// select the content
|
|
var currentFocus = document.activeElement;
|
|
targetBox.focus();
|
|
targetBox.setSelectionRange(0, targetBox.value.length);
|
|
var succeed;
|
|
try {
|
|
succeed = document.execCommand("copy");
|
|
} catch(e) {
|
|
succeed = false;
|
|
}
|
|
// restore original focus
|
|
if (currentFocus && typeof currentFocus.focus === "function") {
|
|
currentFocus.focus();
|
|
}
|
|
// restore prior selection
|
|
targetBox.setSelectionRange(origSelectionStart, origSelectionEnd);
|
|
};
|
|
|
|
function copyOutputToClipboard2() {
|
|
var targetBox2 = document.getElementById('textbox');
|
|
// https://stackoverflow.com/questions/51158061/copy-data-to-clipboard-without-selecting-any-text
|
|
// - restoring original selection doesn't seem to work
|
|
var origSelectionStart, origSelectionEnd;
|
|
origSelectionStart = targetBox2.selectionStart;
|
|
origSelectionEnd = targetBox2.selectionEnd;
|
|
// select the content
|
|
var currentFocus = document.activeElement;
|
|
targetBox2.focus();
|
|
targetBox2.setSelectionRange(0, targetBox2.value.length);
|
|
var succeed;
|
|
try {
|
|
succeed = document.execCommand("copy");
|
|
} catch(e) {
|
|
succeed = false;
|
|
}
|
|
// restore original focus
|
|
if (currentFocus && typeof currentFocus.focus === "function") {
|
|
currentFocus.focus();
|
|
}
|
|
// restore prior selection
|
|
targetBox2.setSelectionRange(origSelectionStart, origSelectionEnd);
|
|
};
|
|
|
|
|
|
//]]>
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
</body></html>
|