diff --git a/index.html b/index.html index d8eb618..c31da0e 100644 --- a/index.html +++ b/index.html @@ -11,7 +11,6 @@ - diff --git a/js/libs/resurrect.js b/js/libs/resurrect.js deleted file mode 100644 index 31fdc42..0000000 --- a/js/libs/resurrect.js +++ /dev/null @@ -1,539 +0,0 @@ -/** - * # ResurrectJS - * @version 1.0.3 - * @license Public Domain - * - * ResurrectJS preserves object behavior (prototypes) and reference - * circularity with a special JSON encoding. Unlike regular JSON, - * Date, RegExp, DOM objects, and `undefined` are also properly - * preserved. - * - * ## Examples - * - * function Foo() {} - * Foo.prototype.greet = function() { return "hello"; }; - * - * // Behavior is preserved: - * var necromancer = new Resurrect(); - * var json = necromancer.stringify(new Foo()); - * var foo = necromancer.resurrect(json); - * foo.greet(); // => "hello" - * - * // References to the same object are preserved: - * json = necromancer.stringify([foo, foo]); - * var array = necromancer.resurrect(json); - * array[0] === array[1]; // => true - * array[1].greet(); // => "hello" - * - * // Dates are restored properly - * json = necromancer.stringify(new Date()); - * var date = necromancer.resurrect(json); - * Object.prototype.toString.call(date); // => "[object Date]" - * - * ## Options - * - * Options are provided to the constructor as an object with these - * properties: - * - * prefix ('#'): A prefix string used for temporary properties added - * to objects during serialization and deserialization. It is - * important that you don't use any properties beginning with this - * string. This option must be consistent between both - * serialization and deserialization. - * - * cleanup (false): Perform full property cleanup after both - * serialization and deserialization using the `delete` - * operator. This may cause performance penalties (breaking hidden - * classes in V8) on objects that ResurrectJS touches, so enable - * with care. - * - * revive (true): Restore behavior (__proto__) to objects that have - * been resurrected. If this is set to false during serialization, - * resurrection information will not be encoded. You still get - * circularity and Date support. - * - * resolver (Resurrect.NamespaceResolver(window)): Converts between - * a name and a prototype. Create a custom resolver if your - * constructors are not stored in global variables. The resolver - * has two methods: getName(object) and getPrototype(string). - * - * For example, - * - * var necromancer = new Resurrect({ - * prefix: '__#', - * cleanup: true - * }); - * - * ## Caveats - * - * * With the default resolver, all constructors must be named and - * stored in the global variable under that name. This is required - * so that the prototypes can be looked up and reconnected at - * resurrection time. - * - * * The wrapper objects Boolean, String, and Number will be - * unwrapped. This means extra properties added to these objects - * will not be preserved. - * - * * Functions cannot ever be serialized. Resurrect will throw an - * error if a function is found when traversing a data structure. - * - * @see http://nullprogram.com/blog/2013/03/28/ - */ - -/** - * @param {Object} [options] See options documentation. - * @namespace - * @constructor - */ -function Resurrect(options) { - this.table = null; - this.prefix = '#'; - this.cleanup = false; - this.revive = true; - for (var option in options) { - if (options.hasOwnProperty(option)) { - this[option] = options[option]; - } - } - this.refcode = this.prefix + 'id'; - this.origcode = this.prefix + 'original'; - this.buildcode = this.prefix + '.'; - this.valuecode = this.prefix + 'v'; -} - -/** - * Portable access to the global object (window, global). - * Uses indirect eval. - * @constant - */ -Resurrect.GLOBAL = (0, eval)('this'); - -/** - * Escape special regular expression characters in a string. - * @param {string} string - * @returns {string} The string escaped for exact matches. - * @see http://stackoverflow.com/a/6969486 - */ -Resurrect.escapeRegExp = function (string) { - return string.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); -}; - -/* Helper Objects */ - -/** - * @param {string} [message] - * @constructor - */ -Resurrect.prototype.Error = function ResurrectError(message) { - this.message = message || ''; - this.stack = new Error().stack; -}; -Resurrect.prototype.Error.prototype = Object.create(Error.prototype); -Resurrect.prototype.Error.prototype.name = 'ResurrectError'; - -/** - * Resolves prototypes through the properties on an object and - * constructor names. - * @param {Object} scope - * @constructor - */ -Resurrect.NamespaceResolver = function(scope) { - this.scope = scope; -}; - -/** - * Gets the prototype of the given property name from an object. If - * not found, it throws an error. - * @param {string} name - * @returns {Object} - * @method - */ -Resurrect.NamespaceResolver.prototype.getPrototype = function(name) { - var constructor = this.scope[name]; - if (constructor) { - return constructor.prototype; - } else { - throw new Resurrect.prototype.Error('Unknown constructor: ' + name); - } -}; - -/** - * Get the prototype name for an object, to be fetched later with getPrototype. - * @param {Object} object - * @returns {?string} Null if the constructor is Object. - * @method - */ -Resurrect.NamespaceResolver.prototype.getName = function(object) { - var constructor = object.constructor.name; - if (constructor == null) { // IE - var funcPattern = /^\s*function\s*([A-Za-z0-9_$]*)/; - constructor = funcPattern.exec(object.constructor)[1]; - } - - if (constructor === '') { - var msg = "Can't serialize objects with anonymous constructors."; - throw new Resurrect.prototype.Error(msg); - } else if (constructor === 'Object' || constructor === 'Array') { - return null; - } else { - return constructor; - } -}; - -/* Set the default resolver searches the global object. */ -Resurrect.prototype.resolver = - new Resurrect.NamespaceResolver(Resurrect.GLOBAL); - -/** - * Create a DOM node from HTML source; behaves like a constructor. - * @param {string} html - * @constructor - */ -Resurrect.Node = function(html) { - var div = document.createElement('div'); - div.innerHTML = html; - return div.firstChild; -}; - -/* Type Tests */ - -/** - * @param {string} type - * @returns {Function} A function that tests for type. - */ -Resurrect.is = function(type) { - var string = '[object ' + type + ']'; - return function(object) { - return Object.prototype.toString.call(object) === string; - }; -}; - -Resurrect.isArray = Resurrect.is('Array'); -Resurrect.isString = Resurrect.is('String'); -Resurrect.isBoolean = Resurrect.is('Boolean'); -Resurrect.isNumber = Resurrect.is('Number'); -Resurrect.isFunction = Resurrect.is('Function'); -Resurrect.isDate = Resurrect.is('Date'); -Resurrect.isRegExp = Resurrect.is('RegExp'); -Resurrect.isObject = Resurrect.is('Object'); - -Resurrect.isAtom = function(object) { - return !Resurrect.isObject(object) && !Resurrect.isArray(object); -}; - -/** - * @param {*} object - * @returns {boolean} True if object is a primitive or a primitive wrapper. - */ -Resurrect.isPrimitive = function(object) { - return object == null || - Resurrect.isNumber(object) || - Resurrect.isString(object) || - Resurrect.isBoolean(object); -}; - -/* Methods */ - -/** - * Create a reference (encoding) to an object. - * @param {(Object|undefined)} object - * @returns {Object} - * @method - */ -Resurrect.prototype.ref = function(object) { - var ref = {}; - if (object === undefined) { - ref[this.prefix] = -1; - } else { - ref[this.prefix] = object[this.refcode]; - } - return ref; -}; - -/** - * Lookup an object in the table by reference object. - * @param {Object} ref - * @returns {(Object|undefined)} - * @method - */ -Resurrect.prototype.deref = function(ref) { - return this.table[ref[this.prefix]]; -}; - -/** - * Put a temporary identifier on an object and store it in the table. - * @param {Object} object - * @returns {number} The unique identifier number. - * @method - */ -Resurrect.prototype.tag = function(object) { - if (this.revive) { - var constructor = this.resolver.getName(object); - if (constructor) { - var proto = Object.getPrototypeOf(object); - if (this.resolver.getPrototype(constructor) !== proto) { - throw new this.Error('Constructor mismatch!'); - } else { - object[this.prefix] = constructor; - } - } - } - object[this.refcode] = this.table.length; - this.table.push(object); - return object[this.refcode]; -}; - -/** - * Create a builder object (encoding) for serialization. - * @param {string} name The name of the constructor. - * @param value The value to pass to the constructor. - * @returns {Object} - * @method - */ -Resurrect.prototype.builder = function(name, value) { - var builder = {}; - builder[this.buildcode] = name; - builder[this.valuecode] = value; - return builder; -}; - -/** - * Build a value from a deserialized builder. - * @param {Object} ref - * @returns {Object} - * @method - * @see http://stackoverflow.com/a/14378462 - * @see http://nullprogram.com/blog/2013/03/24/ - */ -Resurrect.prototype.build = function(ref) { - var type = ref[this.buildcode].split(/\./).reduce(function(object, name) { - return object[name]; - }, Resurrect.GLOBAL); - /* Brilliant hack by kybernetikos: */ - var args = [null].concat(ref[this.valuecode]); - var factory = type.bind.apply(type, args); - var result = new factory(); - if (Resurrect.isPrimitive(result)) { - return result.valueOf(); // unwrap - } else { - return result; - } -}; - -/** - * Dereference or build an object or value from an encoding. - * @param {Object} ref - * @returns {(Object|undefined)} - * @method - */ -Resurrect.prototype.decode = function(ref) { - if (this.prefix in ref) { - return this.deref(ref); - } else if (this.buildcode in ref) { - return this.build(ref); - } else { - throw new this.Error('Unknown encoding.'); - } -}; - -/** - * @param {Object} object - * @returns {boolean} True if the provided object is tagged for serialization. - * @method - */ -Resurrect.prototype.isTagged = function(object) { - return (this.refcode in object) && (object[this.refcode] != null); -}; - -/** - * Visit root and all its ancestors, visiting atoms with f. - * @param {*} root - * @param {Function} f - * @param {Function} replacer - * @returns {*} A fresh copy of root to be serialized. - * @method - */ -Resurrect.prototype.visit = function(root, f, replacer) { - if (Resurrect.isAtom(root)) { - return f(root); - } else if (!this.isTagged(root)) { - var copy = null; - if (Resurrect.isArray(root)) { - copy = []; - root[this.refcode] = this.tag(copy); - for (var i = 0; i < root.length; i++) { - copy.push(this.visit(root[i], f, replacer)); - } - } else { /* Object */ - copy = Object.create(Object.getPrototypeOf(root)); - root[this.refcode] = this.tag(copy); - for (var key in root) { - var value = root[key]; - if (root.hasOwnProperty(key)) { - if (replacer && value !== undefined) { - // Call replacer like JSON.stringify's replacer - value = replacer.call(root, key, root[key]); - if (value === undefined) { - continue; // Omit from result - } - } - copy[key] = this.visit(value, f, replacer); - } - } - } - copy[this.origcode] = root; - return this.ref(copy); - } else { - return this.ref(root); - } -}; - -/** - * Manage special atom values, possibly returning an encoding. - * @param {*} atom - * @returns {*} - * @method - */ -Resurrect.prototype.handleAtom = function(atom) { - var Node = Resurrect.GLOBAL.Node || function() {}; - if (Resurrect.isFunction(atom)) { - throw new this.Error("Can't serialize functions."); - } else if (atom instanceof Node) { - var xmls = new XMLSerializer(); - return this.builder('Resurrect.Node', [xmls.serializeToString(atom)]); - } else if (Resurrect.isDate(atom)) { - return this.builder('Date', [atom.toISOString()]); - } else if (Resurrect.isRegExp(atom)) { - var args = atom.toString().match(/\/(.+)\/([gimy]*)/).slice(1); - return this.builder('RegExp', args); - } else if (atom === undefined) { - return this.ref(undefined); - } else if (Resurrect.isNumber(atom) && (isNaN(atom) || !isFinite(atom))) { - return this.builder('Number', [atom.toString()]); - } else { - return atom; - } -}; - -/** - * Hides intrusive keys from a user-supplied replacer. - * @param {Function} replacer function of two arguments (key, value) - * @returns {Function} A function that skips the replacer for intrusive keys. - * @method - */ -Resurrect.prototype.replacerWrapper = function(replacer) { - var skip = new RegExp('^' + Resurrect.escapeRegExp(this.prefix)); - return function(k, v) { - if (skip.test(k)) { - return v; - } else { - return replacer(k, v); - } - }; -}; - -/** - * Serialize an arbitrary JavaScript object, carefully preserving it. - * @param {*} object - * @param {(Function|Array)} replacer - * @param {string} space - * @method - */ -Resurrect.prototype.stringify = function(object, replacer, space) { - if (Resurrect.isFunction(replacer)) { - replacer = this.replacerWrapper(replacer); - } else if (Resurrect.isArray(replacer)) { - var acceptKeys = replacer; - replacer = function(k, v) { - return acceptKeys.indexOf(k) >= 0 ? v : undefined; - }; - } - if (Resurrect.isAtom(object)) { - return JSON.stringify(this.handleAtom(object), replacer, space); - } else { - this.table = []; - this.visit(object, this.handleAtom.bind(this), replacer); - for (var i = 0; i < this.table.length; i++) { - if (this.cleanup) { - delete this.table[i][this.origcode][this.refcode]; - } else { - this.table[i][this.origcode][this.refcode] = null; - } - delete this.table[i][this.refcode]; - delete this.table[i][this.origcode]; - } - var table = this.table; - this.table = null; - return JSON.stringify(table, null, space); - } -}; - -/** - * Restore the __proto__ of the given object to the proper value. - * @param {Object} object - * @returns {Object} Its argument, or a copy, with the prototype restored. - * @method - */ -Resurrect.prototype.fixPrototype = function(object) { - if (this.prefix in object) { - var name = object[this.prefix]; - var prototype = this.resolver.getPrototype(name); - if ('__proto__' in object) { - object.__proto__ = prototype; - if (this.cleanup) { - delete object[this.prefix]; - } - return object; - } else { // IE - var copy = Object.create(prototype); - for (var key in object) { - if (object.hasOwnProperty(key) && key !== this.prefix) { - copy[key] = object[key]; - } - } - return copy; - } - } else { - return object; - } -}; - -/** - * Deserialize an encoded object, restoring circularity and behavior. - * @param {string} string - * @returns {*} The decoded object or value. - * @method - */ -Resurrect.prototype.resurrect = function(string) { - var result = null; - var data = JSON.parse(string); - if (Resurrect.isArray(data)) { - this.table = data; - /* Restore __proto__. */ - if (this.revive) { - for (var i = 0; i < this.table.length; i++) { - this.table[i] = this.fixPrototype(this.table[i]); - } - } - /* Re-establish object references and construct atoms. */ - for (i = 0; i < this.table.length; i++) { - var object = this.table[i]; - for (var key in object) { - if (object.hasOwnProperty(key)) { - if (!(Resurrect.isAtom(object[key]))) { - object[key] = this.decode(object[key]); - } - } - } - } - result = this.table[0]; - } else if (Resurrect.isObject(data)) { - this.table = []; - result = this.decode(data); - } else { - result = data; - } - this.table = null; - return result; -};