Created
June 16, 2016 22:44
-
-
Save tmarshall/fc17d6a638a683d2422fb0c2f423584d to your computer and use it in GitHub Desktop.
Mootools-ish clone I wrote in '09
This file contains hidden or 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
| (function() { | |
| var | |
| window = this, | |
| Denizen = window.Denizen = function(name, options) { | |
| options = options || {}; | |
| var object = options.init || options.extend || new Function; | |
| object.constructor = Denizen; | |
| object.$type = name.toLowerCase(); | |
| object.prototype.constructor = options.extend || object; | |
| //object.prototype.$type = 'denizen'; | |
| object.prototype.implemented = ['implement', 'isImplemented', 'unImplement', '$type', '$key']; | |
| object.prototype.isImplemented = function(key) { | |
| if($chk(Array.indexOf) === true) | |
| return (this.implemented.indexOf(key) === -1) ? false : true; | |
| else { | |
| for(var i = 0; i < this.implemented.length; i++) | |
| if(this.implemented[i] === key) return true; | |
| return false; | |
| } | |
| }; | |
| object.prototype.unImplement = function(key) { | |
| delete this.prototype[key]; | |
| this.implemented.erase(key); | |
| return this; | |
| }; | |
| object.prototype.implement = function(funcs) { | |
| for(var fn in funcs) { | |
| this.prototype[fn] = funcs[fn]; | |
| this.implemented.push(fn); | |
| } | |
| return this; | |
| }; | |
| window[name] = object; | |
| return object; | |
| }, | |
| Deft = window.Deft = { | |
| version: '0.1.5', | |
| author: { | |
| company: 'Duzo Design, LLC', | |
| person: 'Timothy Marshall' | |
| } | |
| }, | |
| _bank = { | |
| $: {}, | |
| events: {} | |
| }, | |
| $_key = 0, | |
| $_genKey = function(el) { | |
| return el.$key || (el.$key = ++$_key); | |
| }; | |
| /* | |
| utility functions | |
| */ | |
| var | |
| $type = window.$type = function(obj) { | |
| return (obj === undefined || obj === null) ? false : | |
| (obj.$type) ? obj.$type : | |
| (obj.nodeName) ? (function() { | |
| switch(obj.nodeType) { | |
| case 1: return 'element'; | |
| case 3: return 'text'; | |
| } | |
| })() : | |
| (typeof obj == 'object') ? (function() { | |
| return (typeof obj.length == 'number') ? 'array' : 'object'; | |
| })() : | |
| typeof obj; | |
| }, | |
| $chk = window.$chk = function(obj) { | |
| return (obj === undefined || obj === null) ? false : true; | |
| }, | |
| $random = window.$random = function(min, max) { | |
| if(!$chk(max)) { | |
| max = min; | |
| min = 0; | |
| } | |
| return Math.floor(Math.random() * (max - min + 1) + min); | |
| }, | |
| $func = window.$func = function() { return this; }, | |
| $args = window.$args = function(args) { | |
| var returns = []; | |
| for (var i = 0, l = args.length; i < l; i++) | |
| returns.push(args[i]); | |
| return returns; | |
| }, | |
| $implement = window.$implement = function(Denizens, funcs) { | |
| Denizens = $args(Denizens); | |
| $H(funcs).each(function(name, func) { | |
| if(Denizens.any(function(den) { | |
| return den.isImplemented(name); | |
| })) | |
| return; | |
| Denizens.each(function(den) { | |
| den.prototype[name] = func; | |
| den.implemented.push(name); | |
| }); | |
| }); | |
| return Denizens; | |
| }, | |
| $fetch = window.$fetch = function(key) { | |
| if(!key) | |
| return _bank.$; | |
| return _bank.$[key]; | |
| }, | |
| $store = window.$store = function() { | |
| if(arguments.length === 2) { | |
| _bank.$[arguments[0]] = arguments[1]; | |
| return true; | |
| } | |
| arguments[0].each(function(k, v) { | |
| _bank.$[k] = v; | |
| }); | |
| }; | |
| /* | |
| implementing denizens | |
| */ | |
| (function() { | |
| var inits = { | |
| 'Array': Array, | |
| 'String': String, | |
| 'Function': Function, | |
| 'Number': Number, | |
| 'Date': Date, | |
| 'Document': window.Document, | |
| 'Window': window.Document.body | |
| }; | |
| for (var i in inits) | |
| new Denizen(i, { | |
| extend: inits[i] | |
| }); | |
| })(); | |
| /* | |
| FUNCTION DENIZEN | |
| */ | |
| Function.implement({ | |
| bind: function() { | |
| var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift(); | |
| return function() { | |
| return fn.apply(object, args.concat(Array.prototype.slice.call(arguments))); | |
| }; | |
| } | |
| }); | |
| /* | |
| Hash DENIZEN | |
| */ | |
| new Denizen('Hash', { | |
| init: function(obj) { | |
| this.prototype.constructor = obj || {}; | |
| } | |
| }); | |
| Hash.implement({ | |
| add: function(obj) { | |
| var returns = this.clone(); | |
| obj.keys().each(function(o) { | |
| if(!$chk(this[o])) | |
| this[o] = obj[o]; | |
| }.bind(returns)); | |
| return returns; | |
| }, | |
| any: function(fn) { | |
| var ya = false; | |
| this.keys().each(function(o, i) { | |
| if(fn.call(this, o, this[o], i) === true) | |
| ya = true; | |
| }.bind(this)); | |
| return ya; | |
| }, | |
| clean: function() { | |
| var returns = {}; | |
| this.each(function(k, v) { | |
| returns[k] = v; | |
| }); | |
| return returns; | |
| }, | |
| clone: function() { | |
| var c = {}; | |
| this.each(function(k, v) { | |
| c[k] = (['object', 'array'].has($type(v))) ? v.clone() : v; | |
| }); | |
| return c; | |
| }, | |
| each: function(fn) { | |
| this.keys().each(function(o, i) { | |
| fn.call(this, o, this[o], i); | |
| }.bind(this)); | |
| return this; | |
| }, | |
| erase: function(keys) { | |
| var returns = this.clone(); | |
| keys.each(function(o) { | |
| delete this[o]; | |
| }.bind(returns)); | |
| return returns; | |
| }, | |
| every: function(fn) { | |
| var ya = true; | |
| this.keys().each(function(o, i) { | |
| if(fn.call(this, o, this[o], i) === false) | |
| ya = false; | |
| }.bind(this)); | |
| return ya; | |
| }, | |
| extend: function(obj) { | |
| obj.keys().each(function(o) { | |
| this[o] = obj[o]; | |
| }.bind(this)); | |
| return this; | |
| }, | |
| filter: function(fn) { | |
| var i = 0, result = {}; | |
| for(var o in this) | |
| if(!Object.isImplemented(o)) { | |
| if(fn.call(this, o, this[o], i) === true) | |
| result[o] = this[o]; | |
| i++; | |
| } | |
| return $H(result); | |
| }, | |
| firstKey: function() { | |
| return this.keys()[0]; | |
| }, | |
| firstValue: function() { | |
| return this[this.firstKey()]; | |
| }, | |
| hasKey: function(key) { | |
| return this.hasOwnProperty(key); | |
| }, | |
| hasValue: function(val) { | |
| return this.values().has(val); | |
| }, | |
| keyOf: function(val) { | |
| var is = false; | |
| this.keys().each(function(o) { | |
| if(this[o] === val) | |
| is = o; | |
| }.bind(this)); | |
| return is; | |
| }, | |
| keys: function() { | |
| var result = []; | |
| for(var o in this) | |
| if(!Object.isImplemented(o)) | |
| result.push(o); | |
| return result; | |
| }, | |
| lastKey: function() { | |
| return this.keys().last(); | |
| }, | |
| lastValue: function() { | |
| return this.values().last(); | |
| }, | |
| none: function(fn) { | |
| return !this.any(fn); | |
| }, | |
| random: function(fn) { | |
| var keys = this.keys(); | |
| var key = keys[$random(keys.length - 1)]; | |
| fn.call(this, key, this[key]); | |
| return this; | |
| }, | |
| values: function() { | |
| var result = []; | |
| for(var o in this) | |
| if(!Object.isImplemented(o)) | |
| result.push(this[o]); | |
| return result; | |
| } | |
| }); | |
| var $H = window.$H = function(obj) { | |
| /* | |
| return ($type(obj) == 'object') ? obj : (function() { | |
| var returns = {}; | |
| for (var i = 0, l = this.length; i < l; i++) | |
| returns[i] = this[i]; | |
| return returns; | |
| }.bind(arguments))(); | |
| */ | |
| return new Hash(obj); | |
| }; | |
| /* | |
| ARRAY DENIZEN | |
| */ | |
| Array.implement({ | |
| add: function(objs) { | |
| objs = $A(objs); | |
| var c = this.copy(); | |
| objs.each(function(o) { | |
| c.push(o); | |
| }); | |
| return c; | |
| }, | |
| any: function(fn) { | |
| var ya = false; | |
| this.each(function(o, i) { | |
| if(fn.call(this, o, i) === true) | |
| ya = true; | |
| }.bind(this)); | |
| return ya; | |
| }, | |
| clone: function() { | |
| var c = []; | |
| this.each(function(o) { | |
| c.push((['object', 'array'].has($type(o))) ? o.clone() : o); | |
| }); | |
| return c; | |
| }, | |
| count: function(obj) { | |
| return this.filter(function(o) { | |
| return o === obj; | |
| }).length; | |
| }, | |
| each: function(fn) { | |
| for (var i = 0, l = this.length; i < l; i++) | |
| fn.call(this, this[i], i); | |
| return this; | |
| }, | |
| erase: function(objs) { | |
| var c = this.clone(), objs = $A(objs); | |
| for (var i = c.length - 1, l = 0; l <= i; i--) | |
| if(objs.has(c[i])) | |
| c.splice(i, 1); | |
| return c; | |
| }, | |
| every: function(fn) { | |
| var ya = true; | |
| this.each(function(o, i) { | |
| if(fn.call(this, o, i) === false) | |
| ya = false; | |
| }.bind(this)); | |
| return ya; | |
| }, | |
| filter: function(fn) { | |
| var c = this.clone(); | |
| for (var i = c.length - 1, l = 0; l <= i; i--) | |
| if(fn.call(c, c[i], i) === false) | |
| c.splice(i, 1); | |
| return c; | |
| }, | |
| has: function() { | |
| var objs = $args(arguments); | |
| if(objs.length === 1) | |
| return ($chk(Array.indexOf) === true) ? | |
| (this.indexOf(objs[0]) === -1) ? false : true : | |
| (function() { | |
| for(var i = 0; i < this.length; i++) | |
| if(this[i] === objs[0]) | |
| return true; | |
| return false; | |
| }.bind(this))(); | |
| var ya = true; | |
| objs.each(function(o) { | |
| if(!this.has(o)) | |
| ya = false; | |
| }.bind(this)); | |
| return ya; | |
| }, | |
| include: function(objs) { | |
| objs = $A(objs); | |
| var c = this.clone(); | |
| objs.each(function(o) { | |
| if(!this.has(o)) | |
| this.push(o); | |
| }.bind(c)); | |
| return c; | |
| }, | |
| indexOf: function(val) { | |
| try { | |
| return this.indexOf(val); | |
| } catch(e) { | |
| for (var i = 0, l = this.length; i < l; i++) | |
| if(this[i] === val) | |
| return i; | |
| return -1; | |
| } | |
| }, | |
| none: function(fn) { | |
| return !this.any(fn); | |
| }, | |
| unique: function() { | |
| var c = this.clone().filter(function(o) { | |
| return this.count(o) === 1; | |
| }.bind(this)); | |
| this.filter(function(o) { | |
| return this.count(o) !== 1; | |
| }.bind(this)).each(function(o) { | |
| c = c.include(o); | |
| }); | |
| return c; | |
| } | |
| }); | |
| var $A = window.$A = function() { | |
| if(arguments.length === 1 && $type(arguments[0]) == 'array') | |
| return arguments[0]; | |
| var returns = []; | |
| for (var i = 0, l = arguments.length; i < l; i++) | |
| returns.push(arguments[i]); | |
| return returns; | |
| }; | |
| /* | |
| NUMBER DENIZEN | |
| */ | |
| Number.implement({ | |
| abs: function() { | |
| return Math.abs(this); | |
| }, | |
| negative: function() { | |
| return this < 0; | |
| }, | |
| positive: function() { | |
| return this > 0; | |
| }, | |
| random: function() { | |
| return $random(this.round()); | |
| }, | |
| round: function() { | |
| return (Math.ceil(this) - this < 0.5) ? | |
| ((this >= 0) ? Math.ceil(this) : Math.floor(this)) : | |
| ((this >= 0) ? Math.floor(this) : Math.ceil(this)); | |
| }, | |
| times: function(fn) { | |
| for (var i = 0, l = parseInt(this); i < l; i++) | |
| fn.call(this, i); | |
| return this; | |
| } | |
| }); | |
| /* | |
| STRING DENIZEN | |
| */ | |
| String.implement({ | |
| blank: function() { | |
| return this.trim().length === 0; | |
| }, | |
| empty: function() { | |
| return this.length === 0; | |
| }, | |
| escapeRegExp: function(){ | |
| return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1'); | |
| }, | |
| stripScripts: function() { | |
| return this.replace(/<script[^>]*>.*<\/script>/gi, ''); | |
| }, | |
| stripTags: function() { | |
| return this.replace(/<\/?[^>]+>/gi, ''); | |
| }, | |
| substitute: function(obj, options) { | |
| options = options || { | |
| left: '{', | |
| right: '}' | |
| }; | |
| var c = this; | |
| obj.keys().each(function(o) { | |
| c = c.replace(new RegExp(options.left.escapeRegExp() + o + options.right.escapeRegExp(), 'g'), obj[o]); | |
| }); | |
| return c; | |
| }, | |
| test: function(reg) { | |
| return reg.test(this); | |
| }, | |
| trim: function() { | |
| return this.replace(/^\s*|\s*$/g, ''); | |
| } | |
| }); | |
| /* | |
| ELEMENT DENIZEN | |
| */ | |
| new Denizen('Element', { | |
| extend: window.Element, | |
| init: function(obj, butes) { | |
| if($type(obj) == 'element' && $chk(obj.$type) !== false) | |
| return obj; | |
| var el = ($type(obj) == 'string') ? document.createElement(obj) : obj; | |
| Element.prototype.keys().each(function(o) { | |
| this[o] = Element.prototype[o]; | |
| }.bind(el)); | |
| return ($type(obj) == 'string') ? el.set(butes || {}) : el; | |
| } | |
| }); | |
| Element.implement({ | |
| $: function(sel) { | |
| return $(sel, this); | |
| }, | |
| $$: function(sel) { | |
| return $$(sel, this); | |
| }, | |
| fetch: function(key) { | |
| if(!key) | |
| return _bank[this.$key] || {}; | |
| return (_bank[this.$key] || {})[key]; | |
| }, | |
| filter: function(sel) { | |
| return $.filter(sel, this); | |
| }, | |
| find: function(sel) { | |
| return $.find(sel, this); | |
| }, | |
| get: function(str) { | |
| return str === 'tag' ? this.tagName.toLowerCase() || null : | |
| $type(this.innerHTML) === false ? null : | |
| str === 'text' ? this.innerHTML.stripScripts().stripTags() : | |
| str === 'html' ? this.innerHTML : | |
| null; | |
| }, | |
| getStyle: function(what) { | |
| return this.style[what] || false; | |
| }, | |
| inject: function(parent) { | |
| parent.appendChild(this); | |
| return this; | |
| }, | |
| match: function(sel) { | |
| return $.match(sel, this); | |
| }, | |
| parse: function(sel) { | |
| return $.parse(sel, this); | |
| }, | |
| purge: function() { | |
| this.innerHTML = ''; | |
| }, | |
| search: function(sel) { | |
| return $.search(sel, this); | |
| }, | |
| set: function(butes) { | |
| butes.each(function(k, v) { | |
| switch(k) { | |
| case 'text': | |
| this.innerHTML = v.stripScripts().stripTags() | |
| break; | |
| case 'html': | |
| this.innerHTML = v; | |
| break; | |
| default: | |
| this.setAttribute(k, v); | |
| } | |
| }.bind(this)); | |
| return this; | |
| }, | |
| store: function() { | |
| if(!$chk(_bank[this.$key])) | |
| _bank[this.$key] = {}; | |
| if(arguments.length === 2) { | |
| _bank[this.$key][arguments[0]] = arguments[1]; | |
| return this; | |
| } | |
| $H(arguments[0]).each(function(k, v) { | |
| _bank[this.$key][k] = v; | |
| }.bind(this)); | |
| } | |
| }); | |
| /* | |
| AJAX DENIZEN | |
| */ | |
| new Denizen('Ajax', { | |
| init: function(settings) { | |
| var onComplete = settings.onComplete || $func; | |
| var onFailure = settings.onFailure || $func; | |
| this.req = (function() { | |
| return (window.XMLHttpRequest) ? new XMLHttpRequest() : | |
| (window.ActiveXObject) ? new ActiveXObject('Microsoft.XMLHTTP') : | |
| $func; | |
| })(); | |
| this.req.onComplete = onComplete; | |
| this.req.onFailure = onFailure; | |
| this.req.method = settings.method; | |
| this.req.url = settings.url; | |
| this.req.data = settings.data || {}; | |
| this.req.onreadystatechange = function() { | |
| if(this.readyState === 4) | |
| this.onComplete(this.responseText); | |
| }.bind(this.req); | |
| return this; | |
| } | |
| }); | |
| Ajax.implement({ | |
| request: function() { | |
| try { | |
| this.req.open(this.req.method, this.req.url, true); | |
| this.req.send(this.req.data); | |
| } | |
| catch(err) { | |
| this.req.onFailure(err); | |
| } | |
| }, | |
| requestJSON: function() { | |
| this.req.onComplete = function(r) { | |
| try { | |
| this.onComplete(r.decodeJSON()); | |
| } | |
| catch(err) { | |
| this.onFailure(err); | |
| } | |
| }.bind(this.req); | |
| this.request(); | |
| } | |
| }); | |
| /* | |
| Event Denizen | |
| */ | |
| new Denizen('Event', { | |
| init: function(ev) { | |
| ev = ev || win.event; | |
| var target = ev.target || ev.srcElement; | |
| while(target && target.nodeType == 3) | |
| target = target.parentNode; | |
| return ev; | |
| } | |
| }); | |
| Event.implement({ | |
| /* stop: function() { | |
| this.returnValue = false; | |
| } */ | |
| stop: function(){ | |
| return this.stopPropagation().preventDefault(); | |
| }, | |
| stopPropagation: function(){ | |
| this.cancelBubble = true; | |
| return this; | |
| }, | |
| preventDefault: function(){ | |
| this.returnValue = false; | |
| return this; | |
| } | |
| }); | |
| /* | |
| a: | |
| onblur | |
| onclick | |
| ondblclick | |
| onfocus | |
| onmousedown | |
| onmousemove | |
| onmouseout | |
| onmouseover | |
| onmouseup | |
| onkeydown | |
| onkeypress | |
| onkeyup | |
| */ | |
| $implement([Element, Document, Window], { | |
| listen: function(ev, func) { | |
| $_genKey(this); | |
| if(!_bank.events[this.$key]) | |
| _bank.events[this.$key] = []; | |
| _bank.events[this.$key].push(func); | |
| this['on' + ev.replace(/^on/, '')] = function(e) { | |
| this.each(function(o) { | |
| o(e); | |
| }); | |
| }.bind($H(_bank.events[this.$key])); | |
| } | |
| }); | |
| /* | |
| JSON support | |
| inspired by JSON.js | |
| http://www.JSON.org/json2.js | |
| */ | |
| (function() { | |
| var | |
| f = function(n) { | |
| return n < 10 ? '0' + n : n; | |
| }, | |
| cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, | |
| escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, | |
| gap, | |
| indent, | |
| meta = { | |
| '\b': '\\b', | |
| '\t': '\\t', | |
| '\n': '\\n', | |
| '\f': '\\f', | |
| '\r': '\\r', | |
| '"' : '\\"', | |
| '\\': '\\\\' | |
| }, | |
| rep, | |
| JSON = {}; | |
| var toJSON = function(obj) { | |
| if($type(obj) === 'date') | |
| return obj.getUTCFullYear() + '-' + | |
| f(obj.getUTCMonth() + 1) + '-' + | |
| f(obj.getUTCDate()) + 'T' + | |
| f(obj.getUTCHours()) + ':' + | |
| f(obj.getUTCMinutes()) + ':' + | |
| f(obj.getUTCSeconds()) + 'Z'; | |
| return obj.valueOf(); | |
| }; | |
| var quote = function(str) { | |
| escapable.lastIndex = 0; | |
| return escapable.test(str) ? | |
| '"' + str.replace(escapable, function (a) { | |
| var c = meta[a]; | |
| return $type(c) === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); | |
| }) + '"' : '"' + string + '"'; | |
| }; | |
| var str = function(key, holder) { | |
| var i, k, v, length, mind = gap, partial, value = holder[key]; | |
| if(value && $type(value) === 'object' && $type(toJSON(value)) === 'function') | |
| value = toJSON(key); | |
| if($type(rep) === 'function') | |
| value = rep.call(holder, key, value); | |
| switch($type(value)) { | |
| case 'string': | |
| return quote(value); | |
| case 'number': | |
| return isFinite(value) ? String(value) : 'null'; | |
| case 'boolean': | |
| case 'null': | |
| return String(value); | |
| case 'object': | |
| if (!value) | |
| return 'null'; | |
| gap += indent; | |
| partial = []; | |
| if(Object.toString.apply(value) === '[object Array]') { | |
| length = value.length; | |
| for(i = 0; i < length; i += 1) | |
| partial[i] = str(i, value) || 'null'; | |
| v = partial.length === 0 ? '[]' : gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : | |
| '[' + partial.join(',') + ']'; | |
| gap = mind; | |
| return v; | |
| } | |
| if(rep && $type(rep) === 'object') { | |
| length = rep.length; | |
| for(i = 0; i < length; i += 1) { | |
| k = rep[i]; | |
| if($type(k) === 'string') { | |
| v = str(k, value); | |
| if(v) | |
| partial.push(quote(k) + (gap ? ': ' : ':') + v); | |
| } | |
| } | |
| } | |
| else { | |
| value.each(function(k, val) { | |
| if(Object.hasKey.call(val, k)) { | |
| v = str(k, val); | |
| if(v) | |
| partial.push(quote(k) + (gap ? ': ' : ':') + v); | |
| } | |
| }); | |
| } | |
| v = partial.length === 0 ? '{}' : | |
| gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : '{' + partial.join(',') + '}'; | |
| gap = mind; | |
| return v; | |
| } | |
| }; | |
| JSON.stringify = function (value, replacer, space) { | |
| var i; | |
| gap = ''; | |
| indent = ''; | |
| if($type(space) === 'number') | |
| for (i = 0; i < space; i += 1) | |
| indent += ' '; | |
| else if($type(space) === 'string') | |
| indent = space; | |
| rep = replacer; | |
| if(replacer && $type(replacer) !== 'function' && ($type(replacer) !== 'object' || $type(replacer.length) !== 'number')) | |
| throw new Error('JSON stringify failed'); | |
| return str('', {'': value}); | |
| }; | |
| JSON.parse = function (text, reviver) { | |
| var walk = function(holder, key) { | |
| var k, v, value = holder[key]; | |
| if(value && $type(value) === 'object') { | |
| value.each(function(k, val) { | |
| if(Object.hasOwnProperty.call(val, k)) | |
| v = walk(value, k); | |
| if(v !== undefined) | |
| value[k] = v; | |
| else | |
| delete value[k]; | |
| }); | |
| } | |
| return reviver.call(holder, key, value); | |
| }; | |
| cx.lastIndex = 0; | |
| if(cx.test(text)) | |
| text = text.replace(cx, function (a) { | |
| return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); | |
| }); | |
| if(/^[\],:{}\s]*$/ | |
| .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') | |
| .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') | |
| .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { | |
| j = eval('(' + text + ')'); | |
| return $type(reviver) === 'function' ? walk({'': j}, '') : j; | |
| } | |
| throw new Error('JSON parsing failed'); | |
| }; | |
| String.implement({ | |
| decodeJSON: function() { | |
| return JSON.parse(this); | |
| } | |
| }); | |
| })(); | |
| /* | |
| Sly v1.0rc0 <http://sly.digitarald.com> | |
| (C) 2009 Harald Kirschner <http://digitarald.de> | |
| Open source under MIT License | |
| ---- | |
| Modified by Timothy Marshall | |
| */ | |
| (function() { | |
| var cache = {}; | |
| /** | |
| * Sly::constructor | |
| */ | |
| var Sly = function(text) { | |
| return cache[text] || (cache[text] = new Sly.initialize(text)); | |
| }; | |
| Sly.initialize = function(text) { | |
| // normalise | |
| this.text = (typeof(text) == 'string') ? text.replace(/^\s+/, '') : ''; | |
| }; | |
| var proto = Sly.initialize.prototype = Sly.prototype; | |
| /** | |
| * Sly.implement | |
| */ | |
| Sly.implement = function(key, properties) { | |
| for (var prop in properties) Sly[key][prop] = properties[prop]; | |
| }; | |
| /** | |
| * Sly.features | |
| * | |
| * @todo Check proper support with more tests | |
| */ | |
| var features = Sly.features = { | |
| querySelector: !!(document.querySelectorAll), | |
| elementsByClass: !!(document.getElementsByClassName) | |
| }; | |
| var locateFast = function() { | |
| return true; | |
| }; | |
| /** | |
| * Sly::search | |
| */ | |
| proto.search = function(context) { | |
| var iterate; | |
| if (!context) context = document; | |
| else iterate = (context instanceof Array); | |
| var results; // overall result | |
| if (features.querySelector && !iterate && context.nodeType == 9) { | |
| try { | |
| results = context.querySelectorAll(this.text); | |
| } catch(e) {} | |
| if (results) return Sly.toArray(results); | |
| } | |
| var parsed = this.parse(); | |
| var current = {}, // unique ids for one iteration process | |
| combined, // found nodes from one iteration process | |
| nodes, // context nodes from one iteration process | |
| all = {}, // unique ids for overall result | |
| state = {}; // matchers temporary state | |
| // unifiers | |
| var getUid = Sly.getUid; | |
| var locateCurrent = function(node) { | |
| var uid = getUid(node); | |
| return (current[uid]) ? null : (current[uid] = true); | |
| }; | |
| for (var i = 0, selector; (selector = parsed[i]); i++) { | |
| var locate = locateCurrent; | |
| if (selector.first) { | |
| if (!results) locate = locateFast; | |
| if (iterate) nodes = context; | |
| else if (selector.combinator) nodes = [context]; // allows combinators before selectors | |
| } | |
| if (selector.last && results) { | |
| current = all; | |
| combined = results; | |
| } else { | |
| // default stack | |
| current = {}; | |
| combined = []; | |
| } | |
| if (!selector.combinator) { | |
| // without prepended combinator | |
| combined = selector.combine(combined, context, selector, state, locate, !(combined.length)); | |
| } else { | |
| // with prepended combinators | |
| for (var k = 0, l = nodes.length; k < l; k++) { | |
| combined = selector.combine(combined, nodes[k], selector, state, locate); | |
| } | |
| } | |
| if (selector.last) { | |
| if (combined.length) results = combined; | |
| } else { | |
| nodes = combined; | |
| } | |
| } | |
| return results || []; | |
| }; | |
| /** | |
| * Sly::find | |
| */ | |
| proto.find = function(context) { | |
| return this.search(context)[0]; | |
| }; | |
| /** | |
| * Sly::match | |
| */ | |
| proto.match = function(node) { | |
| return !!(this.parse()[0].match(node, {})); | |
| }; | |
| /** | |
| * Sly::filter | |
| */ | |
| proto.filter = function(nodes) { | |
| var results = [], match = this.parse()[0].match; | |
| for (var i = 0, node; (node = nodes[i]); i++) { | |
| if (match(node)) results.push(node); | |
| } | |
| return results; | |
| }; | |
| /** | |
| * Sly.recompile() | |
| */ | |
| var pattern; | |
| Sly.recompile = function() { | |
| var key, combList = [','], operList = ['!']; | |
| for (key in combinators) { | |
| if (key != ' ') { | |
| combList[(key.length > 1) ? 'unshift' : 'push'](Sly.escapeRegExp(key)); | |
| } | |
| } | |
| for (key in operators) operList.push(key); | |
| /** | |
| The regexp is a group of every possible selector part including combinators. | |
| "|" separates the possible selectors. | |
| Capturing parentheses: | |
| 1 - Combinator (only requires to allow multiple-character combinators) | |
| 2 - Attribute name | |
| 3 - Attribute operator | |
| 4, 5, 6 - The value | |
| 7 - Pseudo name | |
| 8, 9, 10 - The value | |
| */ | |
| pattern = new RegExp( | |
| // A tagname | |
| '[\\w\\u00c0-\\uFFFF][\\w\\u00c0-\\uFFFF-]*|' + | |
| // An id or the classname | |
| '[#.][\\w\\u00c0-\\uFFFF-]+|' + | |
| // Whitespace (descendant combinator) | |
| '[ \\t\\r\\n\\f](?=[\\w\\u00c0-\\uFFFF*#.[:])|' + | |
| // Other combinators and the comma | |
| '(' + combList.join('|') + ')[ \\t\\r\\n\\f]*|' + | |
| // An attribute, with the various and optional value formats ([name], [name=value], [name="value"], [name='value'] | |
| '\\[([\\w\\u00c0-\\uFFFF-]+)(?:([' + operList.join('') + ']?=)(?:"([^"]*)"|\'([^\']*)\'|([^\\]]*)))?]|' + | |
| // A pseudo-class, with various formats | |
| ':([-\\w\\u00c0-\\uFFFF]+)(?:\\((?:"([^"]*)"|\'([^\']*)\'|([^)]*))\\))?|' + | |
| // The universial selector, not process | |
| '\\*', 'g' | |
| ); | |
| }; | |
| // I prefer it outside, not sure if this is faster | |
| var create = function(combinator) { | |
| return { | |
| ident: [], | |
| classes: [], | |
| attributes: [], | |
| pseudos: [], | |
| combinator: combinator | |
| }; | |
| }; | |
| var blank = function($0) { | |
| return $0; | |
| }; | |
| /** | |
| * Sly::parse | |
| * | |
| * Returns an array with one object for every selector: | |
| * | |
| * { | |
| * tag: (String) Tagname (defaults to null for universal *) | |
| * id: (String) Id | |
| * classes: (Array) Classnames | |
| * attributes: (Array) Attribute objects with "name", "operator" and "value" | |
| * pseudos: (Array) Pseudo objects with "name" and "value" | |
| * operator: (Char) The prepended operator (not comma) | |
| * first: (Boolean) true if it is the first selector or the first after a comma | |
| * last: (Boolean) true if it is the last selector or the last before a comma | |
| * ident: (Array) All parsed matches, can be used as cache identifier. | |
| * } | |
| */ | |
| proto.parse = function(plain) { | |
| var save = (plain) ? 'plain' : 'parsed'; | |
| if (this[save]) return this[save]; | |
| var compute = (plain) ? blank : this.compute; | |
| var parsed = [], current = create(null); | |
| current.first = true; | |
| var refresh = function(combinator) { | |
| parsed.push(compute(current)); | |
| current = create(combinator); | |
| }; | |
| var match, $0; | |
| while ((match = pattern.exec(this.text))) { | |
| $0 = match[0]; | |
| switch ($0.charAt(0)) { | |
| case '.': | |
| current.classes.push($0.slice(1)); | |
| break; | |
| case '#': | |
| current.id = $0.slice(1); | |
| break; | |
| case '[': | |
| current.attributes.push({ | |
| name: match[2], | |
| operator: match[3] || null, | |
| value: match[4] || match[5] || match[6] || null | |
| }); | |
| break; | |
| case ':': | |
| current.pseudos.push({ | |
| name: match[7], | |
| value: match[8] || match[9] || match[10] || null | |
| }); | |
| break; | |
| case ',': | |
| current.last = true; | |
| refresh(null); | |
| current.first = true; | |
| continue; | |
| case ' ': case '\t': case '\r': case '\n': case '\f': | |
| match[1] = ' '; | |
| default: | |
| var combinator = match[1]; | |
| if (combinator) { | |
| if (current.first && !current.ident.length) current.combinator = combinator; | |
| else refresh(combinator); | |
| } else { | |
| if ($0 != '*') current.tag = $0; | |
| } | |
| } | |
| current.ident.push($0); | |
| } | |
| current.last = true; | |
| parsed.push(compute(current)); | |
| return (this[save] = parsed); | |
| }; | |
| // chains two given functions | |
| function chain(prepend, append, aux, unshift) { | |
| var fn = (prepend) ? ((unshift) ? function(node, state) { | |
| return append(node, aux, state) && prepend(node, state); | |
| } : function(node, state) { | |
| return prepend(node, state) && append(node, aux, state); | |
| }) : function(node, state) { | |
| return append(node, aux, state); | |
| }; | |
| fn.$slyIndex = (prepend) ? (prepend.$slyIndex + 1) : 0; | |
| return fn; | |
| }; | |
| // prepared match comperators, probably needs namespacing | |
| var empty = function() { | |
| return true; | |
| }; | |
| var matchId = function(node, id) { | |
| return (node.id == id); | |
| }; | |
| var matchTag = function(node, tag) { | |
| return (node.nodeName == tag); | |
| }; | |
| var prepareClass = function(name) { | |
| return (new RegExp('(?:^|[ \\t\\r\\n\\f])' + name + '(?:$|[ \\t\\r\\n\\f])')); | |
| }; | |
| var matchClass = function(node, expr) { | |
| return node.className && expr.test(node.className); | |
| }; | |
| var prepareAttribute = function(attr) { | |
| if (!attr.operator || !attr.value) return attr; | |
| var parser = operators[attr.operator]; | |
| if (parser) { // @todo: Allow functions, not only regex | |
| attr.escaped = Sly.escapeRegExp(attr.value); | |
| attr.pattern = new RegExp(parser(attr.value, attr.escaped, attr)); | |
| } | |
| return attr; | |
| }; | |
| var matchAttribute = function(node, attr) { | |
| var read = Sly.getAttribute(node, attr.name); | |
| switch (attr.operator) { | |
| case null: return read; | |
| case '=': return (read == attr.value); | |
| case '!=': return (read != attr.value); | |
| } | |
| if (!read && attr.value) return false; | |
| return attr.pattern.test(read); | |
| }; | |
| /** | |
| * Sly::compute | |
| * | |
| * Attaches the following methods to the selector object: | |
| * | |
| * { | |
| * search: Uses the most convinient properties (id, tag and/or class) of the selector as search. | |
| * matchAux: If search does not contain all selector properties, this method matches an element against the rest. | |
| * match: Matches an element against all properties. | |
| * simple: Set when matchAux is not needed. | |
| * combine: The callback for the combinator | |
| * } | |
| */ | |
| proto.compute = function(selector) { | |
| var i, item, match, search, matchSearch, tagged, | |
| tag = selector.tag, | |
| id = selector.id, | |
| classes = selector.classes; | |
| var nodeName = (tag) ? tag.toUpperCase() : null; | |
| if (id) { | |
| tagged = true; | |
| matchSearch = chain(matchSearch, matchId, id); | |
| search = function(context) { | |
| if (context.getElementById) { | |
| var el = context.getElementById(id); | |
| return (el && (!nodeName || el.nodeName == nodeName)) ? [el] : []; | |
| } | |
| var query = context.getElementsByTagName(tag || '*'); | |
| for (var j = 0, node; (node = query[j]); j++) { | |
| if (node.id == id) return [node]; | |
| } | |
| return []; | |
| }; | |
| } | |
| if (classes.length > 0) { | |
| if (!search && Sly.features.elementsByClass) { | |
| for (i = 0; (item = classes[i]); i++) { | |
| matchSearch = chain(matchSearch, matchClass, prepareClass(item)); | |
| } | |
| var joined = classes.join(' '); | |
| search = function(context) { | |
| return context.getElementsByClassName(joined); | |
| }; | |
| } else if (!search && classes.length == 1) { // optimised for typical .one-class-only | |
| tagged = true; | |
| var expr = prepareClass(classes[0]); | |
| matchSearch = chain(matchSearch, matchClass, expr); | |
| search = function(context) { | |
| var query = context.getElementsByTagName(tag || '*'); | |
| var found = []; | |
| for (var i = 0, node; (node = query[i]); i++) { | |
| if (node.className && expr.test(node.className)) found.push(node); | |
| } | |
| return found; | |
| }; | |
| } else { | |
| for (i = 0; (item = classes[i]); i++) { | |
| match = chain(match, matchClass, prepareClass(item)); | |
| } | |
| } | |
| } | |
| if (tag) { | |
| if (!search) { | |
| matchSearch = chain(matchSearch, matchTag, nodeName); | |
| search = function(context) { | |
| return context.getElementsByTagName(tag); | |
| }; | |
| } else if (!tagged) { // search does not filter by tag yet | |
| match = chain(match, matchTag, nodeName); | |
| } | |
| } else if (!search) { // default engine | |
| search = function(context) { | |
| return context.getElementsByTagName('*'); | |
| }; | |
| } | |
| for (i = 0; (item = selector.pseudos[i]); i++) { | |
| if (item.name == 'not') { // optimised :not(), fast as possible | |
| match = chain(match, function(node, not) { | |
| return !not(node); | |
| }, Sly(item.value).parse()[0].match); | |
| } else { | |
| var parser = pseudos[item.name]; | |
| // chain(match, matchAttribute, prepareAttribute(item)) | |
| if (parser) match = chain(match, parser, item.value); | |
| } | |
| } | |
| for (i = 0; (item = selector.attributes[i]); i++) { | |
| match = chain(match, matchAttribute, prepareAttribute(item)); | |
| } | |
| if ((selector.simple = !(match))) { | |
| selector.matchAux = empty; | |
| } else { | |
| selector.matchAux = match; | |
| matchSearch = chain(matchSearch, match); | |
| } | |
| selector.match = matchSearch || empty; | |
| selector.combine = Sly.combinators[selector.combinator || ' ']; | |
| selector.search = search; | |
| return selector; | |
| }; | |
| /** | |
| * Combinators | |
| */ | |
| var combinators = Sly.combinators = { | |
| ' ': function(combined, context, selector, state, locate, fast) { | |
| var nodes = selector.search(context); | |
| if (fast && selector.simple) return Sly.toArray(nodes); | |
| for (var i = 0, node, aux = selector.matchAux; (node = nodes[i]); i++) { | |
| if (locate(node) && aux(node, state)) combined.push(node); | |
| } | |
| return combined; | |
| }, | |
| '>': function(combined, context, selector, state, locate) { | |
| var nodes = selector.search(context); | |
| for (var i = 0, node; (node = nodes[i]); i++) { | |
| if (node.parentNode == context && locate(node) && selector.matchAux(node, state)) combined.push(node); | |
| } | |
| return combined; | |
| }, | |
| '+': function(combined, context, selector, state, locate) { | |
| while ((context = context.nextSibling)) { | |
| if (context.nodeType == 1) { | |
| if (locate(context) && selector.match(context, state)) combined.push(context); | |
| break; | |
| } | |
| } | |
| return combined; | |
| }, | |
| '~': function(combined, context, selector, state, locate) { | |
| while ((context = context.nextSibling)) { | |
| if (context.nodeType == 1) { | |
| if (!locate(context)) break; | |
| if (selector.match(context, state)) combined.push(context); | |
| } | |
| } | |
| return combined; | |
| } | |
| }; | |
| /** | |
| * Pseudo-Classes | |
| */ | |
| var pseudos = Sly.pseudos = { | |
| // w3c pseudo classes | |
| 'first-child': function(node) { | |
| return pseudos.index(node, 0); | |
| }, | |
| '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-child': function(node, value, state) { | |
| var parsed = Sly.parseNth(value || 'n'); | |
| if (parsed.special != 'n') return pseudos[parsed.special](node, parsed.a, state); | |
| state = state || {}; // just to be sure | |
| state.positions = state.positions || {}; | |
| var uid = Sly.getUid(node) ; | |
| if (!state.positions[uid]) { | |
| var count = 0; | |
| while ((node = node.previousSibling)) { | |
| if (node.nodeType != 1) continue; | |
| count++; | |
| var position = state.positions[Sly.getUid(node)]; | |
| if (position != undefined) { | |
| count = position + count; | |
| break; | |
| } | |
| } | |
| state.positions[uid] = count; | |
| } | |
| return (state.positions[uid] % parsed.a == parsed.b); | |
| }, | |
| 'empty': function(node) { | |
| return !(node.innerText || node.textContent || '').length; | |
| }, | |
| 'contains': function(node, text) { | |
| return (node.innerText || node.textContent || '').indexOf(text) != -1; | |
| }, | |
| 'index': function(node, index) { | |
| var count = 0; | |
| while ((node = node.previousSibling)) { | |
| if (node.nodeType == 1 && ++count > index) return false; | |
| } | |
| return (count == index); | |
| }, | |
| 'even': function(node, value, state) { | |
| return pseudos['nth-child'](node, '2n', state); | |
| }, | |
| 'odd': function(node, value, state) { | |
| return pseudos['nth-child'](node, '2n+1', state); | |
| } | |
| }; | |
| /** | |
| * Attribute operators | |
| */ | |
| var operators = Sly.operators = { | |
| '*=': function(value, escaped) { | |
| return escaped; | |
| }, | |
| '^=': function(value, escaped) { | |
| return '^' + escaped; | |
| }, | |
| '$=': function(value, escaped) { | |
| return value + '$'; | |
| }, | |
| '~=': function(value, escaped) { | |
| return '(?:^|[ \\t\\r\\n\\f])' + escaped + '(?:$|[ \\t\\r\\n\\f])'; | |
| }, | |
| '|=': function(value, escaped) { | |
| return '(?:^|\\|)' + escaped + '(?:$|\\|)'; | |
| } | |
| }; | |
| // public, overridable | |
| Sly.getAttribute = function(node, name) { | |
| if (name == 'class') return node.className; | |
| return node.getAttribute(name, 2); // 2 for IE, others ignore it | |
| }; | |
| var toArray = function(nodes) { | |
| return Array.prototype.slice.call(nodes); | |
| }; | |
| try { | |
| toArray(document.documentElement.childNodes); | |
| } catch (e) { | |
| toArray = function(nodes) { | |
| if (nodes instanceof Array) return nodes; | |
| var i = nodes.length, results = new Array(i); | |
| while (i--) results[i] = nodes[i]; | |
| return results; | |
| }; | |
| } | |
| Sly.toArray = toArray; | |
| var nextUid = 1; | |
| Sly.getUid = (window.ActiveXObject) ? function(node) { | |
| return (node.$slyUid || (node.$slyUid = {id: nextUid++})).id; | |
| } : function(node) { | |
| return node.$slyUid || (node.$slyUid = nextUid++); | |
| }; | |
| var nthCache = {}; | |
| Sly.parseNth = function(value) { | |
| if (nthCache[value]) return nthCache[value]; | |
| var parsed = value.match(/^([+-]?\d*)?([a-z]+)?([+-]?\d*)?$/); | |
| if (!parsed) return false; | |
| var a = parseInt(parsed[1], 10), b = (parseInt(parsed[3], 10) || 0) - 1; | |
| if ((a = (isNaN(a)) ? 1 : a)) { | |
| while (b < 1) b += a; | |
| while (b >= a) b -= a; | |
| } | |
| switch (parsed[2]) { | |
| case 'n': parsed = {a: a, b: b, special: 'n'}; break; | |
| case 'odd': parsed = {a: 2, b: 0, special: 'n'}; break; | |
| case 'even': parsed = {a: 2, b: 1, special: 'n'}; break; | |
| case 'first': parsed = {a: 0, special: 'index'}; break; | |
| case 'last': parsed = {special: 'last-child'}; break; | |
| case 'only': parsed = {special: 'only-child'}; break; | |
| default: parsed = {a: (a) ? (a - 1) : b, special: 'index'}; | |
| } | |
| return (nthCache[value] = parsed); | |
| }; | |
| Sly.escapeRegExp = function(text) { | |
| return text.replace(/[-.*+?^${}()|[\]\/\\]/g, '\\$&'); | |
| }; | |
| // generic accessors | |
| Sly.generise = function(name) { | |
| Sly[name] = function(text) { | |
| var cls = Sly(text); | |
| return cls[name].apply(cls, Array.prototype.slice.call(arguments, 1)); | |
| } | |
| }; | |
| var generics = ['parse', 'search', 'find', 'match', 'filter']; | |
| for (var i = 0; generics[i]; i++) Sly.generise(generics[i]); | |
| // compile pattern for the first time | |
| Sly.recompile(); | |
| // FIN | |
| window.$ = document.$ = function(sel, par) { | |
| switch($type(sel)) { | |
| case 'string': | |
| return $(Sly.find('#' + sel.replace(/^#?/, ''), par)); | |
| break; | |
| case 'array': | |
| sel.each(function(o) { | |
| sel = $(sel, par); | |
| }); | |
| return sel; | |
| break; | |
| } | |
| return new Element(sel); | |
| }; | |
| window.$$ = document.$$ = function(sel, par) { | |
| var returns = Sly.search(sel, par); | |
| returns.each(function(o) { | |
| o = new Element(o); | |
| }); | |
| return returns; | |
| }; | |
| ['find', 'search', 'match', 'filter', 'parse'].each(function(o) { | |
| window.$[o] = function(sel, par) { | |
| return $(Sly[o](sel, par)); | |
| }; | |
| }); | |
| })(); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment