Created
March 4, 2015 09:38
-
-
Save DimitarChristoff/c6a88e7ed96af076f7b5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
--- | |
name: Core | |
description: The heart of MooTools. | |
license: MIT-style license. | |
copyright: Copyright (c) 2006-2014 [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] | |
... | |
*/ | |
/*! MooTools: the javascript framework. license: MIT-style license. copyright: Copyright (c) 2006-2014 [Valerio Proietti](http://mad4milk.net/).*/ | |
(function(){ | |
this.MooTools = { | |
version: '1.5.2-dev', | |
build: '%build%' | |
}; | |
// 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 ('callee' in item) 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; | |
}; | |
var hasOwnProperty = Object.prototype.hasOwnProperty; | |
/*<ltIE8>*/ | |
var enumerables = true; | |
for (var i in {toString: 1}) enumerables = null; | |
if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor']; | |
function forEachObjectEnumberableKey(object, fn, bind) { | |
if (enumerables) for (var i = enumerables.length; i--;){ | |
var k = enumerables[i]; | |
// signature has key-value, so overloadSetter can directly pass the | |
// method function, without swapping arguments. | |
if (hasOwnProperty.call(object, k)) fn.call(bind, k, object[k]); | |
} | |
} | |
/*</ltIE8>*/ | |
// Function overloading | |
var Function = this.Function; | |
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]); | |
/*<ltIE8>*/ | |
forEachObjectEnumberableKey(a, self, this); | |
/*</ltIE8>*/ | |
} 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 defineProperty = Object.defineProperty; | |
try { | |
defineProperty({}, '~', {}); | |
} catch (e) { | |
defineProperty = function(){}; | |
} | |
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; | |
defineProperty(this, name, { | |
enumerable: false | |
}); | |
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', 'contains', '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', 'contains' | |
])('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, keys | |
Array.implement({ | |
/*<!ES5>*/ | |
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); | |
} | |
}, | |
/*</!ES5>*/ | |
each: function(fn, bind){ | |
Array.forEach(this, fn, bind); | |
return this; | |
} | |
}); | |
Object.extend({ | |
keys: function(object){ | |
var keys = []; | |
for (var k in object){ | |
if (hasOwnProperty.call(object, k)) keys.push(k); | |
} | |
/*<ltIE8>*/ | |
forEachObjectEnumberableKey(object, function(k){ | |
keys.push(k); | |
}); | |
/*</ltIE8>*/ | |
return keys; | |
}, | |
forEach: function(object, fn, bind){ | |
Object.keys(object).forEach(function(key){ | |
fn.call(bind, object[key], key, object); | |
}); | |
} | |
}); | |
Object.each = Object.forEach; | |
// 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 parseInt(value, 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: 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: 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: String | |
description: Contains String Prototypes like camelCase, capitalize, test, and toInt. | |
license: MIT-style license. | |
requires: [Type, Array] | |
provides: String | |
... | |
*/ | |
String.implement({ | |
//<!ES6> | |
contains: function(string, index){ | |
return (index ? String(this).slice(index) : String(this)).indexOf(string) > -1; | |
}, | |
//</!ES6> | |
test: function(regex, params){ | |
return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this); | |
}, | |
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: 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 parse = function(ua, platform){ | |
ua = ua.toLowerCase(); | |
platform = (platform ? platform.toLowerCase() : ''); | |
var UA = ua.match(/(opera|ie|firefox|chrome|trident|crios|version)[\s\/:]([\w\d\.]+)?.*?(safari|(?:rv[\s\/:]|version[\s\/:])([\w\d\.]+)|$)/) || [null, 'unknown', 0]; | |
if (UA[1] == 'trident'){ | |
UA[1] = 'ie'; | |
if (UA[4]) UA[2] = UA[4]; | |
} else if (UA[1] == 'crios'){ | |
UA[1] = 'chrome'; | |
} | |
platform = ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || platform.match(/mac|win|linux/) || ['other'])[0]; | |
if (platform == 'win') platform = 'windows'; | |
return { | |
extend: Function.prototype.extend, | |
name: (UA[1] == 'version') ? UA[3] : UA[1], | |
version: parseFloat((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]), | |
platform: platform | |
}; | |
}; | |
var Browser = this.Browser = parse(navigator.userAgent, navigator.platform); | |
if (Browser.name == 'ie' && document.documentMode){ | |
Browser.version = document.documentMode; | |
} | |
Browser.extend({ | |
Features: { | |
xpath: !!(document.evaluate), | |
air: !!(window.runtime), | |
query: !!(document.querySelector), | |
json: !!(window.JSON) | |
}, | |
parseUA: parse | |
}); | |
// 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); | |
// 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; | |
window = this.Window = document = 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: 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; | |
this.$family = 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: 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 = {}; | |
var keys = Object.keys(object); | |
for (var i = 0; i < keys.length; i++){ | |
var key = keys[i]; | |
results[key] = fn.call(bind, object[key], key, object); | |
} | |
return results; | |
}, | |
filter: function(object, fn, bind){ | |
var results = {}; | |
var keys = Object.keys(object); | |
for (var i = 0; i < keys.length; i++){ | |
var key = keys[i], value = object[key]; | |
if (fn.call(bind, value, key, object)) results[key] = value; | |
} | |
return results; | |
}, | |
every: function(object, fn, bind){ | |
var keys = Object.keys(object); | |
for (var i = 0; i < keys.length; i++){ | |
var key = keys[i]; | |
if (!fn.call(bind, object[key], key)) return false; | |
} | |
return true; | |
}, | |
some: function(object, fn, bind){ | |
var keys = Object.keys(object); | |
for (var i = 0; i < keys.length; i++){ | |
var key = keys[i]; | |
if (fn.call(bind, object[key], key)) return true; | |
} | |
return false; | |
}, | |
values: function(object){ | |
var values = []; | |
var keys = Object.keys(object); | |
for (var i = 0; i < keys.length; i++){ | |
var k = keys[i]; | |
values.push(object[k]); | |
} | |
return values; | |
}, | |
getLength: function(object){ | |
return Object.keys(object).length; | |
}, | |
keyOf: function(object, value){ | |
var keys = Object.keys(object); | |
for (var i = 0; i < keys.length; i++){ | |
var key = keys[i]; | |
if (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: 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\\u00a1-\\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.matches || /*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, currentExpression; | |
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 = this.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>*/ | |
/*<ltIE9>*/ | |
// #2479 - IE8 Cannot set HTML of style element | |
var canChangeStyleHTML = (function(){ | |
var div = document.createElement('style'), | |
flag = false; | |
try { | |
div.innerHTML = '#justTesing{margin: 0px;}'; | |
flag = !!div.innerHTML; | |
} catch(e){} | |
return flag; | |
})(); | |
/*</ltIE9>*/ | |
Document.implement({ | |
newElement: function(tag, props){ | |
if (props){ | |
if (props.checked != null) props.defaultChecked = props.checked; | |
if ((props.type == 'checkbox' || props.type == 'radio') && props.value == null) props.value = 'on'; | |
/*<ltIE9>*/ // IE needs the type to be set before changing content of style element | |
if (!canChangeStyleHTML && tag == 'style'){ | |
var styleElement = document.createElement('style'); | |
styleElement.setAttribute('type', 'text/css'); | |
if (props.type) delete props.type; | |
return this.id(styleElement).set(props); | |
} | |
/*</ltIE9>*/ | |
/*<ltIE8>*/// Fix for readonly name and type properties in IE < 8 | |
if (createElementAcceptsHTML){ | |
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]; | |
}; | |
}); | |
/*<ltIE9>*/ | |
propertySetters.text = (function(setter){ | |
return function(node, value){ | |
if (node.get('tag') == 'style') node.set('html', value); | |
else node[properties.text] = value; | |
}; | |
})(propertySetters.text); | |
propertyGetters.text = (function(getter){ | |
return function(node){ | |
return (node.get('tag') == 'style') ? node.innerHTML : getter(node); | |
}; | |
})(propertyGetters.text); | |
/*</ltIE9>*/ | |
// 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>*/ | |
/*<ltIE9>*/ | |
// #2479 - IE8 Cannot set HTML of style element | |
var canChangeStyleHTML = (function(){ | |
var div = document.createElement('style'), | |
flag = false; | |
try { | |
div.innerHTML = '#justTesing{margin: 0px;}'; | |
flag = !!div.innerHTML; | |
} catch(e){} | |
return flag; | |
})(); | |
/*</ltIE9>*/ | |
var input = document.createElement('input'), volatileInputValue, html5InputSupport; | |
// #2178 | |
input.value = 't'; | |
input.type = 'submit'; | |
volatileInputValue = input.value != 't'; | |
// #2443 - IE throws "Invalid Argument" when trying to use html5 input types | |
try { | |
input.type = 'email'; | |
html5InputSupport = input.type == 'email'; | |
} catch(e){} | |
input = null; | |
if (volatileInputValue || !html5InputSupport) propertySetters.type = function(node, type){ | |
try { | |
var value = node.value; | |
node.type = type; | |
node.value = value; | |
} catch (e){} | |
}; | |
/*</IE>*/ | |
/* getProperty, setProperty */ | |
/* <ltIE9> */ | |
var pollutesGetAttribute = (function(div){ | |
div.random = 'attribute'; | |
return (div.getAttribute('random') == 'attribute'); | |
})(document.createElement('div')); | |
var hasCloneBug = (function(test){ | |
test.innerHTML = '<object><param name="should_fix" value="the unknown" /></object>'; | |
return test.cloneNode(true).firstChild.childNodes.length != 1; | |
})(document.createElement('div')); | |
/* </ltIE9> */ | |
var hasClassList = !!document.createElement('div').classList; | |
var classes = function(className){ | |
var classNames = (className || '').clean().split(" "), uniques = {}; | |
return classNames.filter(function(className){ | |
if (className !== "" && !uniques[className]) return uniques[className] = className; | |
}); | |
}; | |
var addToClassList = function(name){ | |
this.classList.add(name); | |
}; | |
var removeFromClassList = function(name){ | |
this.classList.remove(name); | |
}; | |
Element.implement({ | |
setProperty: function(name, value){ | |
var setter = propertySetters[name.toLowerCase()]; | |
if (setter){ | |
setter(this, value); | |
} else { | |
/* <ltIE9> */ | |
var attributeWhiteList; | |
if (pollutesGetAttribute) 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: hasClassList ? function(className){ | |
return this.classList.contains(className); | |
} : function(className){ | |
return classes(this.className).contains(className); | |
}, | |
addClass: hasClassList ? function(className){ | |
classes(className).forEach(addToClassList, this); | |
return this; | |
} : function(className){ | |
this.className = classes(className + ' ' + this.className).join(' '); | |
return this; | |
}, | |
removeClass: hasClassList ? function(className){ | |
classes(className).forEach(removeFromClassList, this); | |
return this; | |
} : function(className){ | |
var classNames = classes(this.className); | |
classes(className).forEach(classNames.erase, classNames); | |
this.className = classNames.join(' '); | |
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('&'); | |
} | |
}); | |
// appendHTML | |
var appendInserters = { | |
before: 'beforeBegin', | |
after: 'afterEnd', | |
bottom: 'beforeEnd', | |
top: 'afterBegin', | |
inside: 'beforeEnd' | |
}; | |
Element.implement('appendHTML', ('insertAdjacentHTML' in document.createElement('div')) ? function(html, where){ | |
this.insertAdjacentHTML(appendInserters[where || 'bottom'], html); | |
return this; | |
} : function(html, where){ | |
var temp = new Element('div', {html: html}), | |
children = temp.childNodes, | |
fragment = temp.firstChild; | |
if (!fragment) return this; | |
if (children.length > 1){ | |
fragment = document.createDocumentFragment(); | |
for (var i = 0, l = children.length; i < l; i++){ | |
fragment.appendChild(children[i]); | |
} | |
} | |
inserters[where || 'bottom'](fragment, this); | |
return this; | |
}); | |
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 (hasCloneBug){ | |
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 (window.attachEvent && !window.addEventListener){ | |
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){ | |
var gc = function(){ | |
Object.each(collected, clean); | |
if (window.CollectGarbage) CollectGarbage(); | |
window.removeListener('unload', gc); | |
}; | |
window.addListener('unload', gc); | |
} | |
/*</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(''); | |
/*<ltIE9>*/ | |
if (this.styleSheet && !canChangeStyleHTML) this.styleSheet.cssText = html; | |
else /*</ltIE9>*/this.innerHTML = html; | |
}, | |
erase: function(){ | |
this.set('html', ''); | |
} | |
}; | |
var supportsHTML5Elements = true, supportsTableInnerHTML = true, supportsTRInnerHTML = true; | |
/*<ltIE9>*/ | |
// technique by jdbarlett - http://jdbartlett.com/innershiv/ | |
var div = document.createElement('div'); | |
div.innerHTML = '<nav></nav>'; | |
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>*/ | |
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; | |
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){ | |
/*<ltIE9>*/ | |
if (this.styleSheet) return set.call(this, html); | |
/*</ltIE9>*/ | |
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'); | |
value = String(value); | |
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: 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 normalizeWheelSpeed = function(event){ | |
var normalized; | |
if (event.wheelDelta){ | |
normalized = event.wheelDelta % 120 == 0 ? event.wheelDelta / 120 : event.wheelDelta / 12; | |
} else { | |
var rawAmount = event.deltaY || event.detail || 0; | |
normalized = -(rawAmount % 3 == 0 ? rawAmount / 3 : rawAmount * 10); | |
} | |
return normalized; | |
} | |
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); | |
if (!this.shift || type != 'keypress') this.key = _keys[code]; | |
if (type == 'keydown' || type == 'keyup'){ | |
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 == 'wheel' || 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 == 'wheel' || type == 'mousewheel') this.wheel = normalizeWheelSpeed(event); | |
this.rightClick = (event.which == 3 || event.button == 2); | |
if (type == 'mouseover' || type == 'mouseout' || type == 'mouseenter' || type == 'mouseleave'){ | |
var overTarget = type == 'mouseover' || type == 'mouseenter'; | |
var related = event.relatedTarget || event[(overTarget ? '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: 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 | |
wheel: 2, 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 | |
hashchange: 1, popstate: 2, pageshow: 2, pagehide: 2, // history | |
error: 1, abort: 1, scroll: 1, message: 2 //misc | |
}; | |
Element.Events = { | |
mousewheel: { | |
base: 'onwheel' in document ? 'wheel' : 'onmousewheel' in document ? 'mousewheel' : 'DOMMouseScroll' | |
} | |
}; | |
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)); | |
}; | |
if ('onmouseenter' in document.documentElement){ | |
Element.NativeEvents.mouseenter = Element.NativeEvents.mouseleave = 2; | |
Element.MouseenterCheck = check; | |
} else { | |
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 event.type != 'propertychange' || event.event.propertyName == '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', | |
condition: Element.MouseenterCheck | |
}, | |
mouseleave: { | |
base: 'mouseout', | |
condition: Element.MouseenterCheck | |
}, | |
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--;){ | |
// the form may have been destroyed, so it won't have the | |
// removeEvent method anymore. In that case the event was | |
// removed as well. | |
if (list.forms[i].removeEvent) 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 (_map.condition || elementEvent && elementEvent.condition){ | |
var __match = match, condition = _map.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, _map.capture); | |
} | |
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.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, el; | |
//<ltIE9> | |
// Check for oldIE, which does not remove styles when they're set to null | |
el = document.createElement('div'); | |
el.style.color = 'red'; | |
el.style.color = null; | |
var doesNotRemoveStyles = el.style.color == 'red'; | |
// check for oldIE, which returns border* shorthand styles in the wrong order (color-width-style instead of width-style-color) | |
var border = '1px solid #123abc'; | |
el.style.border = border; | |
var returnsBordersInWrongOrder = el.style.border != border; | |
el = null; | |
//</ltIE9> | |
var hasGetComputedStyle = !!window.getComputedStyle, | |
supportBorderRadius = document.createElement('div').style.borderRadius != null; | |
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'; | |
}; | |
//<ltIE9> | |
var setFilter = function(element, regexp, value){ | |
var style = element.style, | |
filter = style.filter || element.getComputedStyle('filter') || ''; | |
style.filter = (regexp.test(filter) ? filter.replace(regexp, value) : filter + ' ' + value).trim(); | |
if (!style.filter) style.removeAttribute('filter'); | |
}; | |
//</ltIE9> | |
var setOpacity = (hasOpacity ? function(element, opacity){ | |
element.style.opacity = opacity; | |
} : (hasFilter ? function(element, opacity){ | |
if (!element.currentStyle || !element.currentStyle.hasLayout) element.style.zoom = 1; | |
if (opacity == null || opacity == 1){ | |
setFilter(element, reAlpha, ''); | |
if (opacity == 1 && getOpacity(element) != 1) setFilter(element, reAlpha, 'alpha(opacity=100)'); | |
} else { | |
setFilter(element, reAlpha, 'alpha(opacity=' + (opacity * 100).limit(0, 100).round() + ')'); | |
} | |
} : 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', | |
namedPositions = {left: '0%', top: '0%', center: '50%', right: '100%', bottom: '100%'}, | |
hasBackgroundPositionXY = (html.style.backgroundPositionX != null), | |
prefixPattern = /^-(ms)-/; | |
var camelCase = function(property){ | |
return property.replace(prefixPattern, '$1-').camelCase(); | |
} | |
//<ltIE9> | |
var removeStyle = function(style, property){ | |
if (property == 'backgroundPosition'){ | |
style.removeAttribute(property + 'X'); | |
property += 'Y'; | |
} | |
style.removeAttribute(property); | |
}; | |
//</ltIE9> | |
Element.implement({ | |
getComputedStyle: function(property){ | |
if (!hasGetComputedStyle && this.currentStyle) return this.currentStyle[camelCase(property)]; | |
var defaultView = Element.getDocument(this).defaultView, | |
computed = defaultView ? defaultView.getComputedStyle(this, null) : null; | |
return (computed) ? computed.getPropertyValue((property == floatName) ? 'float' : property.hyphenate()) : ''; | |
}, | |
setStyle: function(property, value){ | |
if (property == 'opacity'){ | |
if (value != null) value = parseFloat(value); | |
setOpacity(this, value); | |
return this; | |
} | |
property = camelCase(property == 'float' ? floatName : property); | |
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){ | |
removeStyle(this.style, property); | |
} | |
//</ltIE9> | |
return this; | |
}, | |
getStyle: function(property){ | |
if (property == 'opacity') return getOpacity(this); | |
property = camelCase(property == 'float' ? floatName : property); | |
if (supportBorderRadius && property.indexOf('borderRadius') != -1){ | |
return ['borderTopLeftRadius', 'borderTopRightRadius', 'borderBottomRightRadius', 'borderBottomLeftRadius'].map(function(corner){ | |
return this.style[corner] || '0px'; | |
}, this).join(' '); | |
} | |
var result = this.style[property]; | |
if (!result || property == 'zIndex'){ | |
if (Element.ShortStyles.hasOwnProperty(property)){ | |
result = []; | |
for (var s in Element.ShortStyles[property]) result.push(this.getStyle(s)); | |
return result.join(' '); | |
} | |
result = this.getComputedStyle(property); | |
} | |
if (hasBackgroundPositionXY && /^backgroundPosition[XY]?$/.test(property)){ | |
return result.replace(/(top|right|bottom|left)/g, function(position){ | |
return namedPositions[position]; | |
}) || '0px'; | |
} | |
if (!result && property == 'backgroundPosition') return '0px 0px'; | |
if (result){ | |
result = String(result); | |
var color = result.match(/rgba?\([\d\s,]+\)/); | |
if (color) result = result.replace(color[0], color[0].rgbToHex()); | |
} | |
if (!hasGetComputedStyle && !this.style[property]){ | |
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 ((/^border(.+)Width|margin|padding/).test(property) && isNaN(parseFloat(result))){ | |
return '0px'; | |
} | |
} | |
//<ltIE9> | |
if (returnsBordersInWrongOrder && /^border(Top|Right|Bottom|Left)?$/.test(property) && /^#/.test(result)){ | |
return result.replace(/^(.+)\s(.+)\s(.+)$/, '$2 $3 $1'); | |
} | |
//</ltIE9> | |
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(@, @, @)', backgroundSize: '@px', 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: '@', borderRadius: '@px @px @px @px' | |
}; | |
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(@, @, @)'; | |
}); | |
if (hasBackgroundPositionXY) Element.ShortStyles.backgroundPosition = {backgroundPositionX: '@', backgroundPositionY: '@'}; | |
})(); | |
/* | |
--- | |
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 heightComponents = ['height', 'paddingTop', 'paddingBottom', 'borderTopWidth', 'borderBottomWidth'], | |
widthComponents = ['width', 'paddingLeft', 'paddingRight', 'borderLeftWidth', 'borderRightWidth']; | |
var svgCalculateSize = function(el){ | |
var gCS = window.getComputedStyle(el), | |
bounds = {x: 0, y: 0}; | |
heightComponents.each(function(css){ | |
bounds.y += parseFloat(gCS[css]); | |
}); | |
widthComponents.each(function(css){ | |
bounds.x += parseFloat(gCS[css]); | |
}); | |
return bounds; | |
}; | |
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(); | |
//<ltIE9> | |
// This if clause is because IE8- cannot calculate getBoundingClientRect of elements with visibility hidden. | |
if (!window.getComputedStyle) return {x: this.offsetWidth, y: this.offsetHeight}; | |
//</ltIE9> | |
// This svg section under, calling `svgCalculateSize()`, can be removed when FF fixed the svg size bug. | |
// Bug info: https://bugzilla.mozilla.org/show_bug.cgi?id=530985 | |
if (this.get('tag') == 'svg') return svgCalculateSize(this); | |
try { | |
var bounds = this.getBoundingClientRect(); | |
return {x: bounds.width, y: bounds.height}; | |
} catch(e) { | |
return {x: 0, y: 0}; | |
} | |
}, | |
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(){ | |
var hasGetBoundingClientRect = this.getBoundingClientRect; | |
if (hasGetBoundingClientRect){ | |
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; | |
element = element.offsetParent; | |
} | |
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.isPaused()) pushInstance.call(this, this.options.fps); | |
return this; | |
}, | |
isRunning: function(){ | |
var list = instances[this.options.fps]; | |
return list && list.contains(this); | |
}, | |
isPaused: function(){ | |
return (this.frame < this.frames) && !this.isRunning(); | |
} | |
}); | |
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 && typeof from == 'string' && 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() + '$'); | |
var searchStyles = function(rules){ | |
Array.each(rules, function(rule, i){ | |
if (rule.media){ | |
searchStyles(rule.rules || rule.cssRules); | |
return; | |
} | |
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; | |
}); | |
}); | |
}; | |
Array.each(document.styleSheets, function(sheet, j){ | |
var href = sheet.href; | |
if (href && href.indexOf('://') > -1 && href.indexOf(document.domain) == -1) return; | |
var rules = sheet.rules || sheet.cssRules; | |
searchStyles(rules); | |
}); | |
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.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: 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'){ | |
this.setStyle('visibility', to == 0 ? 'hidden' : 'visible'); | |
} else if (to != 0){ | |
if (fade.$chain.length){ | |
fade.chain(function(){ | |
this.element.setStyle('visibility', 'visible'); | |
this.callChain(); | |
}); | |
} else { | |
this.setStyle('visibility', 'visible'); | |
} | |
} else { | |
fade.chain(function(){ | |
if (this.element.getStyle('opacity')) return; | |
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: 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: '', | |
withCredentials: false,*/ | |
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; | |
if (this.timer){ | |
clearTimeout(this.timer); | |
delete 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.indexOf('?') > -1 ? '&' : '?') + String.uniqueID(); | |
if (data && (method == 'get' || method == 'delete')){ | |
url += (url.indexOf('?') > -1 ? '&' : '?') + 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.withCredentials) && '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(); | |
if (this.timer){ | |
clearTimeout(this.timer); | |
delete 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', 'patch', 'head', 'GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD'].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.secure = true; | |
JSON.decode = function(string, secure){ | |
if (!string || typeOf(string) != 'string') return null; | |
if (secure == null) secure = JSON.secure; | |
if (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, | |
httpOnly: false | |
}, | |
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'; | |
if (this.options.httpOnly) value += '; HttpOnly'; | |
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) { | |
Browser.loaded = ready = true; | |
document.removeListener('DOMContentLoaded', domready).removeListener('readystatechange', check); | |
document.fireEvent('domready'); | |
window.fireEvent('domready'); | |
} | |
// cleanup scope vars | |
document = window = testElement = null; | |
}; | |
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); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment