Browse Source

Add es5-shim v2.0.2 in order to fix IE8 / IE7 compatibility.

See:
- discussion on mailing-list: https://groups.google.com/d/topic/amber-lang/EOlsvFd5ynk/discussion
- es5-shim: https://github.com/kriskowal/es5-shim/
Laurent Laffont 11 years ago
parent
commit
09fc6dab27

+ 2 - 1
js/amber.js

@@ -56,7 +56,8 @@ amber = (function() {
 		}
 
 		loadDependencies();
-		addJSToLoad('compat.js');
+		addJSToLoad('lib/es5-shim-2.0.2/es5-shim.min.js');
+		addJSToLoad('lib/es5-shim-2.0.2/es5-sham.min.js');
 		addJSToLoad('boot.js');
 
 		if (deploy) {

+ 0 - 22
js/compat.js

@@ -1,22 +0,0 @@
-/* IE<9 does not have Array.indexOf , needed for amber */
-if (!Array.prototype.indexOf) {
-  Array.prototype.indexOf = function(elt /*, from*/)
-  {
-    var len = this.length >>> 0;
-
-    var from = Number(arguments[1]) || 0;
-    from = (from < 0)
-         ? Math.ceil(from)
-         : Math.floor(from);
-    if (from < 0)
-      from += len;
-
-    for (; from < len; from++)
-    {
-      if (from in this &&
-          this[from] === elt)
-        return from;
-    }
-    return -1;
-  };
-}

+ 2 - 0
js/lib/es5-shim-2.0.2/.gitignore

@@ -0,0 +1,2 @@
+node_modules
+.DS_Store

+ 93 - 0
js/lib/es5-shim-2.0.2/CHANGES

@@ -0,0 +1,93 @@
+
+2.0.0
+ - Separate reliable shims from dubious shims (shams).
+
+1.2.10
+ - Group-effort Style Cleanup
+ - Took a stab at fixing Object.defineProperty on IE8 without
+   bad side-effects. (@hax)
+ - Object.isExtensible no longer fakes it. (@xavierm)
+ - Date.prototype.toISOString no longer deals with partial
+   ISO dates, per spec (@kitcambridge)
+ - More (mostly from @bryanforbes)
+
+1.2.9
+ - Corrections to toISOString by @kitcambridge
+ - Fixed three bugs in array methods revealed by Jasmine tests.
+ - Cleaned up Function.prototype.bind with more fixes and tests from
+   @bryanforbes.
+
+1.2.8
+ - Actually fixed problems with Function.prototype.bind, and regressions
+   from 1.2.7 (@bryanforbes, @jdalton #36)
+
+1.2.7 - REGRESSED
+ - Fixed problems with Function.prototype.bind when called as a constructor.
+   (@jdalton #36)
+
+1.2.6
+ - Revised Date.parse to match ES 5.1 (kitcambridge)
+
+1.2.5
+ - Fixed a bug for padding it Date..toISOString (tadfisher issue #33)
+
+1.2.4
+ - Fixed a descriptor bug in Object.defineProperty (raynos)
+
+1.2.3
+ - Cleaned up RequireJS and <script> boilerplate
+
+1.2.2
+ - Changed reduce to follow the letter of the spec with regard to having and
+   owning properties.
+ - Fixed a bug where RegExps pass as Functions in some engines in reduce.
+
+1.2.1
+ - Adding few fixes to make jshint happy.
+ - Fix for issue #12, function expressions can cause scoping issues in IE.
+ - NPM will minify on install or when `npm run-script install` is executed.
+ - Adding .gitignore to avoid publishing dev dependencies.
+
+1.2.0
+ - Making script loadable as AMD module.
+ - Adding `indexOf` to the list of safe shims.
+
+1.1.0
+ - Added support for accessor properties where possible (which is all browsers
+   except IE).
+ - Stop exposing bound function's (that are returned by
+   `Function.prototype.bind`) internal properties (`bound, boundTo, boundArgs`)
+   as in some cases (when using facade objects for example) capabilities of the
+   enclosed functions will be leaked.
+ - `Object.create` now explicitly sets `__proto__` property to guarantee
+   correct behavior of `Object.getPrototypeOf`'s on all objects created using
+   `Object.create`.
+ - Switched to `===` from `==` where possible as it's slightly faster on older
+   browsers that are target of this lib.
+ - Added names to all anonymous functions to have a better stack traces.
+
+1.0.0
+ - fixed Date.toISODate, using UTC accessors, as in
+   http://code.google.com/p/v8/source/browse/trunk/src/date.js?r=6120#986
+   (arian)
+
+0.0.4
+ - Revised Object.getPrototypeOf to work in more cases
+   in response to http://ejohn.org/blog/objectgetprototypeof/
+   [issue #2] (fschaefer)
+
+0.0.3
+ - Fixed typos in Object.keys (samsonjs)
+
+0.0.2
+   Per kangax's recommendations:
+ - faster Object.create(null)
+ - fixed a function-scope function declaration statement in Object.create
+
+0.0.1
+ - fixed Object.create(null), in so far as that's possible
+ - reworked Rhino Object.freeze(Function) bug detector and patcher
+
+0.0.0
+ - forked from narwhal-lib
+

+ 25 - 0
js/lib/es5-shim-2.0.2/CONTRIBUTORS.md

@@ -0,0 +1,25 @@
+
+-   kriskowal Kris Kowal Copyright (C) 2009-2011 MIT License
+-   tlrobinson Tom Robinson Copyright (C) 2009-2010 MIT License (Narwhal
+    Project)
+-   dantman Daniel Friesen Copyright (C) 2010 XXX TODO License or CLA
+-   fschaefer Florian Schäfer Copyright (C) 2010 MIT License
+-   Gozala Irakli Gozalishvili Copyright (C) 2010 MIT License
+-   kitcambridge Kit Cambridge Copyright (C) 2011 MIT License
+-   kossnocorp Sasha Koss XXX TODO License or CLA
+-   bryanforbes Bryan Forbes XXX TODO License or CLA
+-   killdream Quildreen Motta Copyright (C) 2011 MIT Licence
+-   michaelficarra Michael Ficarra Copyright (C) 2011 3-clause BSD
+    License
+-   sharkbrainguy Gerard Paapu Copyright (C) 2011 MIT License
+-   bbqsrc Brendan Molloy (C) 2011 Creative Commons Zero (public domain)
+-   iwyg XXX TODO License or CLA
+-   DomenicDenicola Domenic Denicola Copyright (C) 2011 MIT License
+-   xavierm02 Montillet Xavier Copyright (C) 2011 MIT License
+-   Raynos Jake Verbaten Copyright (C) 2011 MIT Licence
+-   samsonjs Sami Samhuri Copyright (C) 2010 MIT License
+-   rwldrn Rick Waldron Copyright (C) 2011 MIT License
+-   lexer Alexey Zakharov XXX TODO License or CLA
+-   280 North Inc. (Now Motorola LLC, a subsidiary of Google Inc.)
+    Copyright (C) 2009 MIT License
+

+ 19 - 0
js/lib/es5-shim-2.0.2/LICENSE

@@ -0,0 +1,19 @@
+
+Copyright 2009, 2010 Kristopher Michael Kowal. All rights reserved.
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.

+ 161 - 0
js/lib/es5-shim-2.0.2/README.md

@@ -0,0 +1,161 @@
+
+`es5-shim.js` and `es5-shim.min.js` monkey-patch a JavaScript context to
+contain all EcmaScript 5 methods that can be faithfully emulated with a
+legacy JavaScript engine.
+
+`es5-sham.js` and `es5-sham.min.js` monkey-patch other ES5 methods as
+closely as possible.  For these methods, as closely as possible to ES5
+is not very close.  Many of these shams are intended only to allow code
+to be written to ES5 without causing run-time errors in older engines.
+In many cases, this means that these shams cause many ES5 methods to
+silently fail.  Decide carefully whether this is what you want.
+
+
+## Tests
+
+The tests are written with the Jasmine BDD test framework.
+To run the tests, navigate to <root-folder>/tests/. 
+
+In order to run against the shim-code, the tests attempt to kill the current 
+implementation of the missing methods. This happens in <root-folder>/tests/helpers/h-kill.js.
+So in order to run the tests against the build-in methods, invalidate that file somehow
+(comment-out, delete the file, delete the script-tag, etc.).
+
+## Shims
+
+### Complete tests ###
+
+* Array.prototype.every
+* Array.prototype.filter
+* Array.prototype.forEach
+* Array.prototype.indexOf
+* Array.prototype.lastIndexOf
+* Array.prototype.map
+* Array.prototype.some
+* Array.prototype.reduce
+* Array.prototype.reduceRight
+* Array.isArray
+* Date.now
+* Date.prototype.toJSON
+* Function.prototype.bind
+    * /!\ Caveat: the bound function's length is always 0.
+    * /!\ Caveat: the bound function has a prototype property.
+    * /!\ Caveat: bound functions do not try too hard to keep you
+      from manipulating their ``arguments`` and ``caller`` properties.
+    * /!\ Caveat: bound functions don't have checks in ``call`` and
+      ``apply`` to avoid executing as a constructor.
+* Object.keys
+* String.prototype.trim
+
+### Untested ###
+
+* Date.parse (for ISO parsing)
+* Date.prototype.toISOString
+
+## Shams
+
+* /?\ Object.create
+
+    For the case of simply "begetting" an object that
+    inherits prototypically from another, this should work
+    fine across legacy engines.
+
+    /!\ Object.create(null) will work only in browsers that
+    support prototype assignment.  This creates an object
+    that does not have any properties inherited from
+    Object.prototype.  It will silently fail otherwise.
+
+    /!\ The second argument is passed to
+    Object.defineProperties which will probably fail
+    silently.
+
+* /?\ Object.getPrototypeOf
+
+    This will return "undefined" in some cases.  It uses
+    __proto__ if it's available.  Failing that, it uses
+    constructor.prototype, which depends on the constructor
+    property of the object's prototype having not been
+    replaced.  If your object was created like this, it
+    won't work:
+
+        function Foo() {
+        }
+        Foo.prototype = {};
+
+    Because the prototype reassignment destroys the
+    constructor property.
+
+    This will work for all objects that were created using
+    `Object.create` implemented with this library.
+
+* /!\ Object.getOwnPropertyNames
+
+    This method uses Object.keys, so it will not be accurate
+    on legacy engines.
+
+* Object.isSealed
+
+    Returns "false" in all legacy engines for all objects,
+    which is conveniently guaranteed to be accurate.
+
+* Object.isFrozen
+
+    Returns "false" in all legacy engines for all objects,
+    which is conveniently guaranteed to be accurate.
+
+* Object.isExtensible
+
+    Works like a charm, by trying very hard to extend the
+    object then redacting the extension.
+
+### Fail silently
+
+* /!\ Object.getOwnPropertyDescriptor
+    
+    The behavior of this shim does not conform to ES5.  It
+    should probably not be used at this time, until its
+    behavior has been reviewed and been confirmed to be
+    useful in legacy engines.
+
+* /!\ Object.defineProperty
+
+    This method will silently fail to set "writable",
+    "enumerable", and "configurable" properties.
+    
+    Providing a getter or setter with "get" or "set" on a
+    descriptor will silently fail on engines that lack
+    "__defineGetter__" and "__defineSetter__", which include
+    all versions of IE up to version 8 so far.
+
+    IE 8 provides a version of this method but it only works
+    on DOM objects.  Thus, the shim will not get installed
+    and attempts to set "value" properties will fail
+    silently on non-DOM objects.
+
+    https://github.com/kriskowal/es5-shim/issues#issue/5
+
+* /!\ Object.defineProperties
+
+    This uses the Object.defineProperty shim
+
+* Object.seal
+
+    Silently fails on all legacy engines.  This should be
+    fine unless you are depending on the safety and security
+    provisions of this method, which you cannot possibly
+    obtain in legacy engines.
+
+* Object.freeze
+
+    Silently fails on all legacy engines.  This should be
+    fine unless you are depending on the safety and security
+    provisions of this method, which you cannot possibly
+    obtain in legacy engines.
+
+* Object.preventExtensions
+
+    Silently fails on all legacy engines.  This should be
+    fine unless you are depending on the safety and security
+    provisions of this method, which you cannot possibly
+    obtain in legacy engines.
+

+ 335 - 0
js/lib/es5-shim-2.0.2/es5-sham.js

@@ -0,0 +1,335 @@
+// Copyright 2009-2012 by contributors, MIT License
+// vim: ts=4 sts=4 sw=4 expandtab
+
+// Module systems magic dance
+(function (definition) {
+    // RequireJS
+    if (typeof define == "function") {
+        define(definition);
+    // YUI3
+    } else if (typeof YUI == "function") {
+        YUI.add("es5-sham", definition);
+    // CommonJS and <script>
+    } else {
+        definition();
+    }
+})(function () {
+
+// ES5 15.2.3.2
+// http://es5.github.com/#x15.2.3.2
+if (!Object.getPrototypeOf) {
+    // https://github.com/kriskowal/es5-shim/issues#issue/2
+    // http://ejohn.org/blog/objectgetprototypeof/
+    // recommended by fschaefer on github
+    Object.getPrototypeOf = function getPrototypeOf(object) {
+        return object.__proto__ || (
+            object.constructor
+                ? object.constructor.prototype
+                : prototypeOfObject
+        );
+    };
+}
+
+// ES5 15.2.3.3
+// http://es5.github.com/#x15.2.3.3
+if (!Object.getOwnPropertyDescriptor) {
+    var ERR_NON_OBJECT = "Object.getOwnPropertyDescriptor called on a non-object: ";
+
+    Object.getOwnPropertyDescriptor = function getOwnPropertyDescriptor(object, property) {
+        if ((typeof object != "object" && typeof object != "function") || object === null) {
+            throw new TypeError(ERR_NON_OBJECT + object);
+        }
+        // If object does not owns property return undefined immediately.
+        if (!owns(object, property)) {
+            return;
+        }
+
+        // If object has a property then it's for sure both `enumerable` and
+        // `configurable`.
+        var descriptor =  { enumerable: true, configurable: true };
+
+        // If JS engine supports accessor properties then property may be a
+        // getter or setter.
+        if (supportsAccessors) {
+            // Unfortunately `__lookupGetter__` will return a getter even
+            // if object has own non getter property along with a same named
+            // inherited getter. To avoid misbehavior we temporary remove
+            // `__proto__` so that `__lookupGetter__` will return getter only
+            // if it's owned by an object.
+            var prototype = object.__proto__;
+            object.__proto__ = prototypeOfObject;
+
+            var getter = lookupGetter(object, property);
+            var setter = lookupSetter(object, property);
+
+            // Once we have getter and setter we can put values back.
+            object.__proto__ = prototype;
+
+            if (getter || setter) {
+                if (getter) {
+                    descriptor.get = getter;
+                }
+                if (setter) {
+                    descriptor.set = setter;
+                }
+                // If it was accessor property we're done and return here
+                // in order to avoid adding `value` to the descriptor.
+                return descriptor;
+            }
+        }
+
+        // If we got this far we know that object has an own property that is
+        // not an accessor so we set it as a value and return descriptor.
+        descriptor.value = object[property];
+        return descriptor;
+    };
+}
+
+// ES5 15.2.3.4
+// http://es5.github.com/#x15.2.3.4
+if (!Object.getOwnPropertyNames) {
+    Object.getOwnPropertyNames = function getOwnPropertyNames(object) {
+        return Object.keys(object);
+    };
+}
+
+// ES5 15.2.3.5
+// http://es5.github.com/#x15.2.3.5
+if (!Object.create) {
+    Object.create = function create(prototype, properties) {
+
+        var object;
+        function Type() {}  // An empty constructor.
+
+        if (prototype === null) {
+            object = { "__proto__": null };
+        } else {
+            if (typeof prototype !== "object" && typeof prototype !== "function") {
+                // In the native implementation `parent` can be `null`
+                // OR *any* `instanceof Object`  (Object|Function|Array|RegExp|etc)
+                // Use `typeof` tho, b/c in old IE, DOM elements are not `instanceof Object`
+                // like they are in modern browsers. Using `Object.create` on DOM elements
+                // is...err...probably inappropriate, but the native version allows for it.
+                throw new TypeError("Object prototype may only be an Object or null"); // same msg as Chrome
+            }
+            Type.prototype = prototype;
+            object = new Type();
+            // IE has no built-in implementation of `Object.getPrototypeOf`
+            // neither `__proto__`, but this manually setting `__proto__` will
+            // guarantee that `Object.getPrototypeOf` will work as expected with
+            // objects created using `Object.create`
+            object.__proto__ = prototype;
+        }
+
+        if (properties !== void 0) {
+            Object.defineProperties(object, properties);
+        }
+
+        return object;
+    };
+}
+
+// ES5 15.2.3.6
+// http://es5.github.com/#x15.2.3.6
+
+// Patch for WebKit and IE8 standard mode
+// Designed by hax <hax.github.com>
+// related issue: https://github.com/kriskowal/es5-shim/issues#issue/5
+// IE8 Reference:
+//     http://msdn.microsoft.com/en-us/library/dd282900.aspx
+//     http://msdn.microsoft.com/en-us/library/dd229916.aspx
+// WebKit Bugs:
+//     https://bugs.webkit.org/show_bug.cgi?id=36423
+
+function doesDefinePropertyWork(object) {
+    try {
+        Object.defineProperty(object, "sentinel", {});
+        return "sentinel" in object;
+    } catch (exception) {
+        // returns falsy
+    }
+}
+
+// check whether defineProperty works if it's given. Otherwise,
+// shim partially.
+if (Object.defineProperty) {
+    var definePropertyWorksOnObject = doesDefinePropertyWork({});
+    var definePropertyWorksOnDom = typeof document == "undefined" ||
+        doesDefinePropertyWork(document.createElement("div"));
+    if (!definePropertyWorksOnObject || !definePropertyWorksOnDom) {
+        var definePropertyFallback = Object.defineProperty;
+    }
+}
+
+if (!Object.defineProperty || definePropertyFallback) {
+    var ERR_NON_OBJECT_DESCRIPTOR = "Property description must be an object: ";
+    var ERR_NON_OBJECT_TARGET = "Object.defineProperty called on non-object: "
+    var ERR_ACCESSORS_NOT_SUPPORTED = "getters & setters can not be defined " +
+                                      "on this javascript engine";
+
+    Object.defineProperty = function defineProperty(object, property, descriptor) {
+        if ((typeof object != "object" && typeof object != "function") || object === null) {
+            throw new TypeError(ERR_NON_OBJECT_TARGET + object);
+        }
+        if ((typeof descriptor != "object" && typeof descriptor != "function") || descriptor === null) {
+            throw new TypeError(ERR_NON_OBJECT_DESCRIPTOR + descriptor);
+        }
+        // make a valiant attempt to use the real defineProperty
+        // for I8's DOM elements.
+        if (definePropertyFallback) {
+            try {
+                return definePropertyFallback.call(Object, object, property, descriptor);
+            } catch (exception) {
+                // try the shim if the real one doesn't work
+            }
+        }
+
+        // If it's a data property.
+        if (owns(descriptor, "value")) {
+            // fail silently if "writable", "enumerable", or "configurable"
+            // are requested but not supported
+            /*
+            // alternate approach:
+            if ( // can't implement these features; allow false but not true
+                !(owns(descriptor, "writable") ? descriptor.writable : true) ||
+                !(owns(descriptor, "enumerable") ? descriptor.enumerable : true) ||
+                !(owns(descriptor, "configurable") ? descriptor.configurable : true)
+            )
+                throw new RangeError(
+                    "This implementation of Object.defineProperty does not " +
+                    "support configurable, enumerable, or writable."
+                );
+            */
+
+            if (supportsAccessors && (lookupGetter(object, property) ||
+                                      lookupSetter(object, property)))
+            {
+                // As accessors are supported only on engines implementing
+                // `__proto__` we can safely override `__proto__` while defining
+                // a property to make sure that we don't hit an inherited
+                // accessor.
+                var prototype = object.__proto__;
+                object.__proto__ = prototypeOfObject;
+                // Deleting a property anyway since getter / setter may be
+                // defined on object itself.
+                delete object[property];
+                object[property] = descriptor.value;
+                // Setting original `__proto__` back now.
+                object.__proto__ = prototype;
+            } else {
+                object[property] = descriptor.value;
+            }
+        } else {
+            if (!supportsAccessors) {
+                throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);
+            }
+            // If we got that far then getters and setters can be defined !!
+            if (owns(descriptor, "get")) {
+                defineGetter(object, property, descriptor.get);
+            }
+            if (owns(descriptor, "set")) {
+                defineSetter(object, property, descriptor.set);
+            }
+        }
+        return object;
+    };
+}
+
+// ES5 15.2.3.7
+// http://es5.github.com/#x15.2.3.7
+if (!Object.defineProperties) {
+    Object.defineProperties = function defineProperties(object, properties) {
+        for (var property in properties) {
+            if (owns(properties, property) && property != "__proto__") {
+                Object.defineProperty(object, property, properties[property]);
+            }
+        }
+        return object;
+    };
+}
+
+// ES5 15.2.3.8
+// http://es5.github.com/#x15.2.3.8
+if (!Object.seal) {
+    Object.seal = function seal(object) {
+        // this is misleading and breaks feature-detection, but
+        // allows "securable" code to "gracefully" degrade to working
+        // but insecure code.
+        return object;
+    };
+}
+
+// ES5 15.2.3.9
+// http://es5.github.com/#x15.2.3.9
+if (!Object.freeze) {
+    Object.freeze = function freeze(object) {
+        // this is misleading and breaks feature-detection, but
+        // allows "securable" code to "gracefully" degrade to working
+        // but insecure code.
+        return object;
+    };
+}
+
+// detect a Rhino bug and patch it
+try {
+    Object.freeze(function () {});
+} catch (exception) {
+    Object.freeze = (function freeze(freezeObject) {
+        return function freeze(object) {
+            if (typeof object == "function") {
+                return object;
+            } else {
+                return freezeObject(object);
+            }
+        };
+    })(Object.freeze);
+}
+
+// ES5 15.2.3.10
+// http://es5.github.com/#x15.2.3.10
+if (!Object.preventExtensions) {
+    Object.preventExtensions = function preventExtensions(object) {
+        // this is misleading and breaks feature-detection, but
+        // allows "securable" code to "gracefully" degrade to working
+        // but insecure code.
+        return object;
+    };
+}
+
+// ES5 15.2.3.11
+// http://es5.github.com/#x15.2.3.11
+if (!Object.isSealed) {
+    Object.isSealed = function isSealed(object) {
+        return false;
+    };
+}
+
+// ES5 15.2.3.12
+// http://es5.github.com/#x15.2.3.12
+if (!Object.isFrozen) {
+    Object.isFrozen = function isFrozen(object) {
+        return false;
+    };
+}
+
+// ES5 15.2.3.13
+// http://es5.github.com/#x15.2.3.13
+if (!Object.isExtensible) {
+    Object.isExtensible = function isExtensible(object) {
+        // 1. If Type(O) is not Object throw a TypeError exception.
+        if (Object(object) !== object) {
+            throw new TypeError(); // TODO message
+        }
+        // 2. Return the Boolean value of the [[Extensible]] internal property of O.
+        var name = '';
+        while (owns(object, name)) {
+            name += '?';
+        }
+        object[name] = true;
+        var returnValue = owns(object, name);
+        delete object[name];
+        return returnValue;
+    };
+}
+
+});

+ 6 - 0
js/lib/es5-shim-2.0.2/es5-sham.min.js

@@ -0,0 +1,6 @@
+(function(d){"function"==typeof define?define(d):"function"==typeof YUI?YUI.add("es5-sham",d):d()})(function(){function d(a){try{return Object.defineProperty(a,"sentinel",{}),"sentinel"in a}catch(b){}}Object.getPrototypeOf||(Object.getPrototypeOf=function(a){return a.__proto__||(a.constructor?a.constructor.prototype:prototypeOfObject)});Object.getOwnPropertyDescriptor||(Object.getOwnPropertyDescriptor=function(a,b){if(typeof a!="object"&&typeof a!="function"||a===null)throw new TypeError("Object.getOwnPropertyDescriptor called on a non-object: "+
+a);if(owns(a,b)){var c={enumerable:true,configurable:true};if(supportsAccessors){var g=a.__proto__;a.__proto__=prototypeOfObject;var d=lookupGetter(a,b),e=lookupSetter(a,b);a.__proto__=g;if(d||e){if(d)c.get=d;if(e)c.set=e;return c}}c.value=a[b];return c}});Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(a){return Object.keys(a)});Object.create||(Object.create=function(a,b){function c(){}var d;if(a===null)d={__proto__:null};else{if(typeof a!=="object"&&typeof a!=="function")throw new TypeError("Object prototype may only be an Object or null");
+c.prototype=a;d=new c;d.__proto__=a}b!==void 0&&Object.defineProperties(d,b);return d});if(Object.defineProperty){var f=d({}),h="undefined"==typeof document||d(document.createElement("div"));if(!f||!h)var e=Object.defineProperty}if(!Object.defineProperty||e)Object.defineProperty=function(a,b,c){if(typeof a!="object"&&typeof a!="function"||a===null)throw new TypeError("Object.defineProperty called on non-object: "+a);if(typeof c!="object"&&typeof c!="function"||c===null)throw new TypeError("Property description must be an object: "+
+c);if(e)try{return e.call(Object,a,b,c)}catch(d){}if(owns(c,"value"))if(supportsAccessors&&(lookupGetter(a,b)||lookupSetter(a,b))){var f=a.__proto__;a.__proto__=prototypeOfObject;delete a[b];a[b]=c.value;a.__proto__=f}else a[b]=c.value;else{if(!supportsAccessors)throw new TypeError("getters & setters can not be defined on this javascript engine");owns(c,"get")&&defineGetter(a,b,c.get);owns(c,"set")&&defineSetter(a,b,c.set)}return a};Object.defineProperties||(Object.defineProperties=function(a,b){for(var c in b)owns(b,
+c)&&c!="__proto__"&&Object.defineProperty(a,c,b[c]);return a});Object.seal||(Object.seal=function(a){return a});Object.freeze||(Object.freeze=function(a){return a});try{Object.freeze(function(){})}catch(j){var i=Object.freeze;Object.freeze=function(a){return typeof a=="function"?a:i(a)}}Object.preventExtensions||(Object.preventExtensions=function(a){return a});Object.isSealed||(Object.isSealed=function(){return false});Object.isFrozen||(Object.isFrozen=function(){return false});Object.isExtensible||
+(Object.isExtensible=function(a){if(Object(a)!==a)throw new TypeError;for(var b="";owns(a,b);)b=b+"?";a[b]=true;var c=owns(a,b);delete a[b];return c})});

+ 963 - 0
js/lib/es5-shim-2.0.2/es5-shim.js

@@ -0,0 +1,963 @@
+// Copyright 2009-2012 by contributors, MIT License
+// vim: ts=4 sts=4 sw=4 expandtab
+
+// Module systems magic dance
+(function (definition) {
+    // RequireJS
+    if (typeof define == "function") {
+        define(definition);
+    // YUI3
+    } else if (typeof YUI == "function") {
+        YUI.add("es5", definition);
+    // CommonJS and <script>
+    } else {
+        definition();
+    }
+})(function () {
+
+/**
+ * Brings an environment as close to ECMAScript 5 compliance
+ * as is possible with the facilities of erstwhile engines.
+ *
+ * Annotated ES5: http://es5.github.com/ (specific links below)
+ * ES5 Spec: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
+ * Required reading: http://javascriptweblog.wordpress.com/2011/12/05/extending-javascript-natives/
+ */
+
+//
+// Function
+// ========
+//
+
+// ES-5 15.3.4.5
+// http://es5.github.com/#x15.3.4.5
+
+if (!Function.prototype.bind) {
+    Function.prototype.bind = function bind(that) { // .length is 1
+        // 1. Let Target be the this value.
+        var target = this;
+        // 2. If IsCallable(Target) is false, throw a TypeError exception.
+        if (typeof target != "function") {
+            throw new TypeError("Function.prototype.bind called on incompatible " + target);
+        }
+        // 3. Let A be a new (possibly empty) internal list of all of the
+        //   argument values provided after thisArg (arg1, arg2 etc), in order.
+        // XXX slicedArgs will stand in for "A" if used
+        var args = slice.call(arguments, 1); // for normal call
+        // 4. Let F be a new native ECMAScript object.
+        // 11. Set the [[Prototype]] internal property of F to the standard
+        //   built-in Function prototype object as specified in 15.3.3.1.
+        // 12. Set the [[Call]] internal property of F as described in
+        //   15.3.4.5.1.
+        // 13. Set the [[Construct]] internal property of F as described in
+        //   15.3.4.5.2.
+        // 14. Set the [[HasInstance]] internal property of F as described in
+        //   15.3.4.5.3.
+        var bound = function () {
+
+            if (this instanceof bound) {
+                // 15.3.4.5.2 [[Construct]]
+                // When the [[Construct]] internal method of a function object,
+                // F that was created using the bind function is called with a
+                // list of arguments ExtraArgs, the following steps are taken:
+                // 1. Let target be the value of F's [[TargetFunction]]
+                //   internal property.
+                // 2. If target has no [[Construct]] internal method, a
+                //   TypeError exception is thrown.
+                // 3. Let boundArgs be the value of F's [[BoundArgs]] internal
+                //   property.
+                // 4. Let args be a new list containing the same values as the
+                //   list boundArgs in the same order followed by the same
+                //   values as the list ExtraArgs in the same order.
+                // 5. Return the result of calling the [[Construct]] internal
+                //   method of target providing args as the arguments.
+
+                var result = target.apply(
+                    this,
+                    args.concat(slice.call(arguments))
+                );
+                if (Object(result) === result) {
+                    return result;
+                }
+                return this;
+
+            } else {
+                // 15.3.4.5.1 [[Call]]
+                // When the [[Call]] internal method of a function object, F,
+                // which was created using the bind function is called with a
+                // this value and a list of arguments ExtraArgs, the following
+                // steps are taken:
+                // 1. Let boundArgs be the value of F's [[BoundArgs]] internal
+                //   property.
+                // 2. Let boundThis be the value of F's [[BoundThis]] internal
+                //   property.
+                // 3. Let target be the value of F's [[TargetFunction]] internal
+                //   property.
+                // 4. Let args be a new list containing the same values as the
+                //   list boundArgs in the same order followed by the same
+                //   values as the list ExtraArgs in the same order.
+                // 5. Return the result of calling the [[Call]] internal method
+                //   of target providing boundThis as the this value and
+                //   providing args as the arguments.
+
+                // equiv: target.call(this, ...boundArgs, ...args)
+                return target.apply(
+                    that,
+                    args.concat(slice.call(arguments))
+                );
+
+            }
+
+        };
+        if(target.prototype) {
+            bound.prototype = Object.create(target.prototype);
+        }
+        // XXX bound.length is never writable, so don't even try
+        //
+        // 15. If the [[Class]] internal property of Target is "Function", then
+        //     a. Let L be the length property of Target minus the length of A.
+        //     b. Set the length own property of F to either 0 or L, whichever is
+        //       larger.
+        // 16. Else set the length own property of F to 0.
+        // 17. Set the attributes of the length own property of F to the values
+        //   specified in 15.3.5.1.
+
+        // TODO
+        // 18. Set the [[Extensible]] internal property of F to true.
+
+        // TODO
+        // 19. Let thrower be the [[ThrowTypeError]] function Object (13.2.3).
+        // 20. Call the [[DefineOwnProperty]] internal method of F with
+        //   arguments "caller", PropertyDescriptor {[[Get]]: thrower, [[Set]]:
+        //   thrower, [[Enumerable]]: false, [[Configurable]]: false}, and
+        //   false.
+        // 21. Call the [[DefineOwnProperty]] internal method of F with
+        //   arguments "arguments", PropertyDescriptor {[[Get]]: thrower,
+        //   [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false},
+        //   and false.
+
+        // TODO
+        // NOTE Function objects created using Function.prototype.bind do not
+        // have a prototype property or the [[Code]], [[FormalParameters]], and
+        // [[Scope]] internal properties.
+        // XXX can't delete prototype in pure-js.
+
+        // 22. Return F.
+        return bound;
+    };
+}
+
+// Shortcut to an often accessed properties, in order to avoid multiple
+// dereference that costs universally.
+// _Please note: Shortcuts are defined after `Function.prototype.bind` as we
+// us it in defining shortcuts.
+var call = Function.prototype.call;
+var prototypeOfArray = Array.prototype;
+var prototypeOfObject = Object.prototype;
+var slice = prototypeOfArray.slice;
+// Having a toString local variable name breaks in Opera so use _toString.
+var _toString = call.bind(prototypeOfObject.toString);
+var owns = call.bind(prototypeOfObject.hasOwnProperty);
+
+// If JS engine supports accessors creating shortcuts.
+var defineGetter;
+var defineSetter;
+var lookupGetter;
+var lookupSetter;
+var supportsAccessors;
+if ((supportsAccessors = owns(prototypeOfObject, "__defineGetter__"))) {
+    defineGetter = call.bind(prototypeOfObject.__defineGetter__);
+    defineSetter = call.bind(prototypeOfObject.__defineSetter__);
+    lookupGetter = call.bind(prototypeOfObject.__lookupGetter__);
+    lookupSetter = call.bind(prototypeOfObject.__lookupSetter__);
+}
+
+//
+// Array
+// =====
+//
+
+// ES5 15.4.4.12
+// http://es5.github.com/#x15.4.4.12
+// Default value for second param
+// [bugfix, ielt9, old browsers]
+// IE < 9 bug: [1,2].splice(0).join("") == "" but should be "12"
+if ([1,2].splice(0).length != 2) {
+    var array_splice = Array.prototype.splice;
+    Array.prototype.splice = function(start, deleteCount) {
+        if (!arguments.length) {
+            return [];
+        } else {
+            return array_splice.apply(this, [
+                start === void 0 ? 0 : start,
+                deleteCount === void 0 ? (this.length - start) : deleteCount
+            ].concat(slice.call(arguments, 2)))
+        }
+    };
+}
+
+// ES5 15.4.3.2
+// http://es5.github.com/#x15.4.3.2
+// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/isArray
+if (!Array.isArray) {
+    Array.isArray = function isArray(obj) {
+        return _toString(obj) == "[object Array]";
+    };
+}
+
+// The IsCallable() check in the Array functions
+// has been replaced with a strict check on the
+// internal class of the object to trap cases where
+// the provided function was actually a regular
+// expression literal, which in V8 and
+// JavaScriptCore is a typeof "function".  Only in
+// V8 are regular expression literals permitted as
+// reduce parameters, so it is desirable in the
+// general case for the shim to match the more
+// strict and common behavior of rejecting regular
+// expressions.
+
+// ES5 15.4.4.18
+// http://es5.github.com/#x15.4.4.18
+// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/forEach
+
+// Check failure of by-index access of string characters (IE < 9)
+// and failure of `0 in boxedString` (Rhino)
+var boxedString = Object("a"),
+    splitString = boxedString[0] != "a" || !(0 in boxedString);
+
+if (!Array.prototype.forEach) {
+    Array.prototype.forEach = function forEach(fun /*, thisp*/) {
+        var object = toObject(this),
+            self = splitString && _toString(this) == "[object String]" ?
+                this.split("") :
+                object,
+            thisp = arguments[1],
+            i = -1,
+            length = self.length >>> 0;
+
+        // If no callback function or if callback is not a callable function
+        if (_toString(fun) != "[object Function]") {
+            throw new TypeError(); // TODO message
+        }
+
+        while (++i < length) {
+            if (i in self) {
+                // Invoke the callback function with call, passing arguments:
+                // context, property value, property key, thisArg object
+                // context
+                fun.call(thisp, self[i], i, object);
+            }
+        }
+    };
+}
+
+// ES5 15.4.4.19
+// http://es5.github.com/#x15.4.4.19
+// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map
+if (!Array.prototype.map) {
+    Array.prototype.map = function map(fun /*, thisp*/) {
+        var object = toObject(this),
+            self = splitString && _toString(this) == "[object String]" ?
+                this.split("") :
+                object,
+            length = self.length >>> 0,
+            result = Array(length),
+            thisp = arguments[1];
+
+        // If no callback function or if callback is not a callable function
+        if (_toString(fun) != "[object Function]") {
+            throw new TypeError(fun + " is not a function");
+        }
+
+        for (var i = 0; i < length; i++) {
+            if (i in self)
+                result[i] = fun.call(thisp, self[i], i, object);
+        }
+        return result;
+    };
+}
+
+// ES5 15.4.4.20
+// http://es5.github.com/#x15.4.4.20
+// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter
+if (!Array.prototype.filter) {
+    Array.prototype.filter = function filter(fun /*, thisp */) {
+        var object = toObject(this),
+            self = splitString && _toString(this) == "[object String]" ?
+                this.split("") :
+                    object,
+            length = self.length >>> 0,
+            result = [],
+            value,
+            thisp = arguments[1];
+
+        // If no callback function or if callback is not a callable function
+        if (_toString(fun) != "[object Function]") {
+            throw new TypeError(fun + " is not a function");
+        }
+
+        for (var i = 0; i < length; i++) {
+            if (i in self) {
+                value = self[i];
+                if (fun.call(thisp, value, i, object)) {
+                    result.push(value);
+                }
+            }
+        }
+        return result;
+    };
+}
+
+// ES5 15.4.4.16
+// http://es5.github.com/#x15.4.4.16
+// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/every
+if (!Array.prototype.every) {
+    Array.prototype.every = function every(fun /*, thisp */) {
+        var object = toObject(this),
+            self = splitString && _toString(this) == "[object String]" ?
+                this.split("") :
+                object,
+            length = self.length >>> 0,
+            thisp = arguments[1];
+
+        // If no callback function or if callback is not a callable function
+        if (_toString(fun) != "[object Function]") {
+            throw new TypeError(fun + " is not a function");
+        }
+
+        for (var i = 0; i < length; i++) {
+            if (i in self && !fun.call(thisp, self[i], i, object)) {
+                return false;
+            }
+        }
+        return true;
+    };
+}
+
+// ES5 15.4.4.17
+// http://es5.github.com/#x15.4.4.17
+// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some
+if (!Array.prototype.some) {
+    Array.prototype.some = function some(fun /*, thisp */) {
+        var object = toObject(this),
+            self = splitString && _toString(this) == "[object String]" ?
+                this.split("") :
+                object,
+            length = self.length >>> 0,
+            thisp = arguments[1];
+
+        // If no callback function or if callback is not a callable function
+        if (_toString(fun) != "[object Function]") {
+            throw new TypeError(fun + " is not a function");
+        }
+
+        for (var i = 0; i < length; i++) {
+            if (i in self && fun.call(thisp, self[i], i, object)) {
+                return true;
+            }
+        }
+        return false;
+    };
+}
+
+// ES5 15.4.4.21
+// http://es5.github.com/#x15.4.4.21
+// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce
+if (!Array.prototype.reduce) {
+    Array.prototype.reduce = function reduce(fun /*, initial*/) {
+        var object = toObject(this),
+            self = splitString && _toString(this) == "[object String]" ?
+                this.split("") :
+                object,
+            length = self.length >>> 0;
+
+        // If no callback function or if callback is not a callable function
+        if (_toString(fun) != "[object Function]") {
+            throw new TypeError(fun + " is not a function");
+        }
+
+        // no value to return if no initial value and an empty array
+        if (!length && arguments.length == 1) {
+            throw new TypeError("reduce of empty array with no initial value");
+        }
+
+        var i = 0;
+        var result;
+        if (arguments.length >= 2) {
+            result = arguments[1];
+        } else {
+            do {
+                if (i in self) {
+                    result = self[i++];
+                    break;
+                }
+
+                // if array contains no values, no initial value to return
+                if (++i >= length) {
+                    throw new TypeError("reduce of empty array with no initial value");
+                }
+            } while (true);
+        }
+
+        for (; i < length; i++) {
+            if (i in self) {
+                result = fun.call(void 0, result, self[i], i, object);
+            }
+        }
+
+        return result;
+    };
+}
+
+// ES5 15.4.4.22
+// http://es5.github.com/#x15.4.4.22
+// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduceRight
+if (!Array.prototype.reduceRight) {
+    Array.prototype.reduceRight = function reduceRight(fun /*, initial*/) {
+        var object = toObject(this),
+            self = splitString && _toString(this) == "[object String]" ?
+                this.split("") :
+                object,
+            length = self.length >>> 0;
+
+        // If no callback function or if callback is not a callable function
+        if (_toString(fun) != "[object Function]") {
+            throw new TypeError(fun + " is not a function");
+        }
+
+        // no value to return if no initial value, empty array
+        if (!length && arguments.length == 1) {
+            throw new TypeError("reduceRight of empty array with no initial value");
+        }
+
+        var result, i = length - 1;
+        if (arguments.length >= 2) {
+            result = arguments[1];
+        } else {
+            do {
+                if (i in self) {
+                    result = self[i--];
+                    break;
+                }
+
+                // if array contains no values, no initial value to return
+                if (--i < 0) {
+                    throw new TypeError("reduceRight of empty array with no initial value");
+                }
+            } while (true);
+        }
+
+        do {
+            if (i in this) {
+                result = fun.call(void 0, result, self[i], i, object);
+            }
+        } while (i--);
+
+        return result;
+    };
+}
+
+// ES5 15.4.4.14
+// http://es5.github.com/#x15.4.4.14
+// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf
+if (!Array.prototype.indexOf || ([0, 1].indexOf(1, 2) != -1)) {
+    Array.prototype.indexOf = function indexOf(sought /*, fromIndex */ ) {
+        var self = splitString && _toString(this) == "[object String]" ?
+                this.split("") :
+                toObject(this),
+            length = self.length >>> 0;
+
+        if (!length) {
+            return -1;
+        }
+
+        var i = 0;
+        if (arguments.length > 1) {
+            i = toInteger(arguments[1]);
+        }
+
+        // handle negative indices
+        i = i >= 0 ? i : Math.max(0, length + i);
+        for (; i < length; i++) {
+            if (i in self && self[i] === sought) {
+                return i;
+            }
+        }
+        return -1;
+    };
+}
+
+// ES5 15.4.4.15
+// http://es5.github.com/#x15.4.4.15
+// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/lastIndexOf
+if (!Array.prototype.lastIndexOf || ([0, 1].lastIndexOf(0, -3) != -1)) {
+    Array.prototype.lastIndexOf = function lastIndexOf(sought /*, fromIndex */) {
+        var self = splitString && _toString(this) == "[object String]" ?
+                this.split("") :
+                toObject(this),
+            length = self.length >>> 0;
+
+        if (!length) {
+            return -1;
+        }
+        var i = length - 1;
+        if (arguments.length > 1) {
+            i = Math.min(i, toInteger(arguments[1]));
+        }
+        // handle negative indices
+        i = i >= 0 ? i : length - Math.abs(i);
+        for (; i >= 0; i--) {
+            if (i in self && sought === self[i]) {
+                return i;
+            }
+        }
+        return -1;
+    };
+}
+
+//
+// Object
+// ======
+//
+
+// ES5 15.2.3.14
+// http://es5.github.com/#x15.2.3.14
+if (!Object.keys) {
+    // http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation
+    var hasDontEnumBug = true,
+        dontEnums = [
+            "toString",
+            "toLocaleString",
+            "valueOf",
+            "hasOwnProperty",
+            "isPrototypeOf",
+            "propertyIsEnumerable",
+            "constructor"
+        ],
+        dontEnumsLength = dontEnums.length;
+
+    for (var key in {"toString": null}) {
+        hasDontEnumBug = false;
+    }
+
+    Object.keys = function keys(object) {
+
+        if (
+            (typeof object != "object" && typeof object != "function") ||
+            object === null
+        ) {
+            throw new TypeError("Object.keys called on a non-object");
+        }
+
+        var keys = [];
+        for (var name in object) {
+            if (owns(object, name)) {
+                keys.push(name);
+            }
+        }
+
+        if (hasDontEnumBug) {
+            for (var i = 0, ii = dontEnumsLength; i < ii; i++) {
+                var dontEnum = dontEnums[i];
+                if (owns(object, dontEnum)) {
+                    keys.push(dontEnum);
+                }
+            }
+        }
+        return keys;
+    };
+
+}
+
+//
+// Date
+// ====
+//
+
+// ES5 15.9.5.43
+// http://es5.github.com/#x15.9.5.43
+// This function returns a String value represent the instance in time
+// represented by this Date object. The format of the String is the Date Time
+// string format defined in 15.9.1.15. All fields are present in the String.
+// The time zone is always UTC, denoted by the suffix Z. If the time value of
+// this object is not a finite Number a RangeError exception is thrown.
+var negativeDate = -62198755200000,
+    negativeYearString = "-000001";
+if (
+    !Date.prototype.toISOString ||
+    (new Date(negativeDate).toISOString().indexOf(negativeYearString) === -1)
+) {
+    Date.prototype.toISOString = function toISOString() {
+        var result, length, value, year, month;
+        if (!isFinite(this)) {
+            throw new RangeError("Date.prototype.toISOString called on non-finite value.");
+        }
+
+        year = this.getUTCFullYear();
+
+        month = this.getUTCMonth();
+        // see https://github.com/kriskowal/es5-shim/issues/111
+        year += Math.floor(month / 12);
+        month = (month % 12 + 12) % 12;
+
+        // the date time string format is specified in 15.9.1.15.
+        result = [month + 1, this.getUTCDate(),
+            this.getUTCHours(), this.getUTCMinutes(), this.getUTCSeconds()];
+        year = (
+            (year < 0 ? "-" : (year > 9999 ? "+" : "")) +
+            ("00000" + Math.abs(year))
+            .slice(0 <= year && year <= 9999 ? -4 : -6)
+        );
+
+        length = result.length;
+        while (length--) {
+            value = result[length];
+            // pad months, days, hours, minutes, and seconds to have two
+            // digits.
+            if (value < 10) {
+                result[length] = "0" + value;
+            }
+        }
+        // pad milliseconds to have three digits.
+        return (
+            year + "-" + result.slice(0, 2).join("-") +
+            "T" + result.slice(2).join(":") + "." +
+            ("000" + this.getUTCMilliseconds()).slice(-3) + "Z"
+        );
+    };
+}
+
+
+// ES5 15.9.5.44
+// http://es5.github.com/#x15.9.5.44
+// This function provides a String representation of a Date object for use by
+// JSON.stringify (15.12.3).
+var dateToJSONIsSupported = false;
+try {
+    dateToJSONIsSupported = (
+        Date.prototype.toJSON &&
+        new Date(NaN).toJSON() === null &&
+        new Date(negativeDate).toJSON().indexOf(negativeYearString) !== -1 &&
+        Date.prototype.toJSON.call({ // generic
+            toISOString: function () {
+                return true;
+            }
+        })
+    );
+} catch (e) {
+}
+if (!dateToJSONIsSupported) {
+    Date.prototype.toJSON = function toJSON(key) {
+        // When the toJSON method is called with argument key, the following
+        // steps are taken:
+
+        // 1.  Let O be the result of calling ToObject, giving it the this
+        // value as its argument.
+        // 2. Let tv be toPrimitive(O, hint Number).
+        var o = Object(this),
+            tv = toPrimitive(o),
+            toISO;
+        // 3. If tv is a Number and is not finite, return null.
+        if (typeof tv === "number" && !isFinite(tv)) {
+            return null;
+        }
+        // 4. Let toISO be the result of calling the [[Get]] internal method of
+        // O with argument "toISOString".
+        toISO = o.toISOString;
+        // 5. If IsCallable(toISO) is false, throw a TypeError exception.
+        if (typeof toISO != "function") {
+            throw new TypeError("toISOString property is not callable");
+        }
+        // 6. Return the result of calling the [[Call]] internal method of
+        //  toISO with O as the this value and an empty argument list.
+        return toISO.call(o);
+
+        // NOTE 1 The argument is ignored.
+
+        // NOTE 2 The toJSON function is intentionally generic; it does not
+        // require that its this value be a Date object. Therefore, it can be
+        // transferred to other kinds of objects for use as a method. However,
+        // it does require that any such object have a toISOString method. An
+        // object is free to use the argument key to filter its
+        // stringification.
+    };
+}
+
+// ES5 15.9.4.2
+// http://es5.github.com/#x15.9.4.2
+// based on work shared by Daniel Friesen (dantman)
+// http://gist.github.com/303249
+if (!Date.parse || "Date.parse is buggy") {
+    // XXX global assignment won't work in embeddings that use
+    // an alternate object for the context.
+    Date = (function(NativeDate) {
+
+        // Date.length === 7
+        var newDate = function Date(Y, M, D, h, m, s, ms) {
+            var length = arguments.length;
+            if (this instanceof NativeDate) {
+                var date = length == 1 && String(Y) === Y ? // isString(Y)
+                    // We explicitly pass it through parse:
+                    new NativeDate(newDate.parse(Y)) :
+                    // We have to manually make calls depending on argument
+                    // length here
+                    length >= 7 ? new NativeDate(Y, M, D, h, m, s, ms) :
+                    length >= 6 ? new NativeDate(Y, M, D, h, m, s) :
+                    length >= 5 ? new NativeDate(Y, M, D, h, m) :
+                    length >= 4 ? new NativeDate(Y, M, D, h) :
+                    length >= 3 ? new NativeDate(Y, M, D) :
+                    length >= 2 ? new NativeDate(Y, M) :
+                    length >= 1 ? new NativeDate(Y) :
+                                  new NativeDate();
+                // Prevent mixups with unfixed Date object
+                date.constructor = newDate;
+                return date;
+            }
+            return NativeDate.apply(this, arguments);
+        };
+
+        // 15.9.1.15 Date Time String Format.
+        var isoDateExpression = new RegExp("^" +
+            "(\\d{4}|[\+\-]\\d{6})" + // four-digit year capture or sign +
+                                      // 6-digit extended year
+            "(?:-(\\d{2})" + // optional month capture
+            "(?:-(\\d{2})" + // optional day capture
+            "(?:" + // capture hours:minutes:seconds.milliseconds
+                "T(\\d{2})" + // hours capture
+                ":(\\d{2})" + // minutes capture
+                "(?:" + // optional :seconds.milliseconds
+                    ":(\\d{2})" + // seconds capture
+                    "(?:\\.(\\d{3}))?" + // milliseconds capture
+                ")?" +
+            "(" + // capture UTC offset component
+                "Z|" + // UTC capture
+                "(?:" + // offset specifier +/-hours:minutes
+                    "([-+])" + // sign capture
+                    "(\\d{2})" + // hours offset capture
+                    ":(\\d{2})" + // minutes offset capture
+                ")" +
+            ")?)?)?)?" +
+        "$");
+
+        var months = [
+            0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
+        ];
+
+        function dayFromMonth(year, month) {
+            var t = month > 1 ? 1 : 0;
+            return (
+                months[month] +
+                Math.floor((year - 1969 + t) / 4) -
+                Math.floor((year - 1901 + t) / 100) +
+                Math.floor((year - 1601 + t) / 400) +
+                365 * (year - 1970)
+            );
+        }
+
+        // Copy any custom methods a 3rd party library may have added
+        for (var key in NativeDate) {
+            newDate[key] = NativeDate[key];
+        }
+
+        // Copy "native" methods explicitly; they may be non-enumerable
+        newDate.now = NativeDate.now;
+        newDate.UTC = NativeDate.UTC;
+        newDate.prototype = NativeDate.prototype;
+        newDate.prototype.constructor = Date;
+
+        // Upgrade Date.parse to handle simplified ISO 8601 strings
+        newDate.parse = function parse(string) {
+            var match = isoDateExpression.exec(string);
+            if (match) {
+                // parse months, days, hours, minutes, seconds, and milliseconds
+                // provide default values if necessary
+                // parse the UTC offset component
+                var year = Number(match[1]),
+                    month = Number(match[2] || 1) - 1,
+                    day = Number(match[3] || 1) - 1,
+                    hour = Number(match[4] || 0),
+                    minute = Number(match[5] || 0),
+                    second = Number(match[6] || 0),
+                    millisecond = Number(match[7] || 0),
+                    // When time zone is missed, local offset should be used
+                    // (ES 5.1 bug)
+                    // see https://bugs.ecmascript.org/show_bug.cgi?id=112
+                    offset = !match[4] || match[8] ?
+                        0 : Number(new Date(1970, 0)),
+                    signOffset = match[9] === "-" ? 1 : -1,
+                    hourOffset = Number(match[10] || 0),
+                    minuteOffset = Number(match[11] || 0),
+                    result;
+                if (
+                    hour < (
+                        minute > 0 || second > 0 || millisecond > 0 ?
+                        24 : 25
+                    ) &&
+                    minute < 60 && second < 60 && millisecond < 1000 &&
+                    month > -1 && month < 12 && hourOffset < 24 &&
+                    minuteOffset < 60 && // detect invalid offsets
+                    day > -1 &&
+                    day < (
+                        dayFromMonth(year, month + 1) -
+                        dayFromMonth(year, month)
+                    )
+                ) {
+                    result = (
+                        (dayFromMonth(year, month) + day) * 24 +
+                        hour +
+                        hourOffset * signOffset
+                    ) * 60;
+                    result = (
+                        (result + minute + minuteOffset * signOffset) * 60 +
+                        second
+                    ) * 1000 + millisecond + offset;
+                    if (-8.64e15 <= result && result <= 8.64e15) {
+                        return result;
+                    }
+                }
+                return NaN;
+            }
+            return NativeDate.parse.apply(this, arguments);
+        };
+
+        return newDate;
+    })(Date);
+}
+
+// ES5 15.9.4.4
+// http://es5.github.com/#x15.9.4.4
+if (!Date.now) {
+    Date.now = function now() {
+        return new Date().getTime();
+    };
+}
+
+
+//
+// String
+// ======
+//
+
+
+// ES5 15.5.4.14
+// http://es5.github.com/#x15.5.4.14
+// [bugfix, chrome]
+// If separator is undefined, then the result array contains just one String,
+// which is the this value (converted to a String). If limit is not undefined,
+// then the output array is truncated so that it contains no more than limit
+// elements.
+// "0".split(undefined, 0) -> []
+if("0".split(void 0, 0).length) {
+    var string_split = String.prototype.split;
+    String.prototype.split = function(separator, limit) {
+        if(separator === void 0 && limit === 0)return [];
+        return string_split.apply(this, arguments);
+    }
+}
+
+// ECMA-262, 3rd B.2.3
+// Note an ECMAScript standart, although ECMAScript 3rd Edition has a
+// non-normative section suggesting uniform semantics and it should be
+// normalized across all browsers
+// [bugfix, IE lt 9] IE < 9 substr() with negative value not working in IE
+if("".substr && "0b".substr(-1) !== "b") {
+    var string_substr = String.prototype.substr;
+    /**
+     *  Get the substring of a string
+     *  @param  {integer}  start   where to start the substring
+     *  @param  {integer}  length  how many characters to return
+     *  @return {string}
+     */
+    String.prototype.substr = function(start, length) {
+        return string_substr.call(
+            this,
+            start < 0 ? (start = this.length + start) < 0 ? 0 : start : start,
+            length
+        );
+    }
+}
+
+// ES5 15.5.4.20
+// http://es5.github.com/#x15.5.4.20
+var ws = "\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003" +
+    "\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028" +
+    "\u2029\uFEFF";
+if (!String.prototype.trim || ws.trim()) {
+    // http://blog.stevenlevithan.com/archives/faster-trim-javascript
+    // http://perfectionkills.com/whitespace-deviations/
+    ws = "[" + ws + "]";
+    var trimBeginRegexp = new RegExp("^" + ws + ws + "*"),
+        trimEndRegexp = new RegExp(ws + ws + "*$");
+    String.prototype.trim = function trim() {
+        if (this === undefined || this === null) {
+            throw new TypeError("can't convert "+this+" to object");
+        }
+        return String(this)
+            .replace(trimBeginRegexp, "")
+            .replace(trimEndRegexp, "");
+    };
+}
+
+//
+// Util
+// ======
+//
+
+// ES5 9.4
+// http://es5.github.com/#x9.4
+// http://jsperf.com/to-integer
+
+function toInteger(n) {
+    n = +n;
+    if (n !== n) { // isNaN
+        n = 0;
+    } else if (n !== 0 && n !== (1/0) && n !== -(1/0)) {
+        n = (n > 0 || -1) * Math.floor(Math.abs(n));
+    }
+    return n;
+}
+
+function isPrimitive(input) {
+    var type = typeof input;
+    return (
+        input === null ||
+        type === "undefined" ||
+        type === "boolean" ||
+        type === "number" ||
+        type === "string"
+    );
+}
+
+function toPrimitive(input) {
+    var val, valueOf, toString;
+    if (isPrimitive(input)) {
+        return input;
+    }
+    valueOf = input.valueOf;
+    if (typeof valueOf === "function") {
+        val = valueOf.call(input);
+        if (isPrimitive(val)) {
+            return val;
+        }
+    }
+    toString = input.toString;
+    if (typeof toString === "function") {
+        val = toString.call(input);
+        if (isPrimitive(val)) {
+            return val;
+        }
+    }
+    throw new TypeError();
+}
+
+// ES5 9.9
+// http://es5.github.com/#x9.9
+var toObject = function (o) {
+    if (o == null) { // this matches both null and undefined
+        throw new TypeError("can't convert "+o+" to object");
+    }
+    return Object(o);
+};
+
+});

+ 16 - 0
js/lib/es5-shim-2.0.2/es5-shim.min.js

@@ -0,0 +1,16 @@
+(function(p){"function"==typeof define?define(p):"function"==typeof YUI?YUI.add("es5",p):p()})(function(){function p(a){a=+a;a!==a?a=0:0!==a&&(a!==1/0&&a!==-(1/0))&&(a=(0<a||-1)*Math.floor(Math.abs(a)));return a}function s(a){var b=typeof a;return null===a||"undefined"===b||"boolean"===b||"number"===b||"string"===b}Function.prototype.bind||(Function.prototype.bind=function(a){var b=this;if("function"!=typeof b)throw new TypeError("Function.prototype.bind called on incompatible "+b);var d=q.call(arguments,
+1),c=function(){if(this instanceof c){var e=b.apply(this,d.concat(q.call(arguments)));return Object(e)===e?e:this}return b.apply(a,d.concat(q.call(arguments)))};b.prototype&&(c.prototype=Object.create(b.prototype));return c});var m=Function.prototype.call,o=Object.prototype,q=Array.prototype.slice,h=m.bind(o.toString),t=m.bind(o.hasOwnProperty);t(o,"__defineGetter__")&&(m.bind(o.__defineGetter__),m.bind(o.__defineSetter__),m.bind(o.__lookupGetter__),m.bind(o.__lookupSetter__));if(2!=[1,2].splice(0).length){var x=
+Array.prototype.splice;Array.prototype.splice=function(a,b){return arguments.length?x.apply(this,[a===void 0?0:a,b===void 0?this.length-a:b].concat(q.call(arguments,2))):[]}}Array.isArray||(Array.isArray=function(a){return h(a)=="[object Array]"});var m=Object("a"),k="a"!=m[0]||!(0 in m);Array.prototype.forEach||(Array.prototype.forEach=function(a,b){var d=l(this),c=k&&h(this)=="[object String]"?this.split(""):d,e=-1,f=c.length>>>0;if(h(a)!="[object Function]")throw new TypeError;for(;++e<f;)e in
+c&&a.call(b,c[e],e,d)});Array.prototype.map||(Array.prototype.map=function(a,b){var d=l(this),c=k&&h(this)=="[object String]"?this.split(""):d,e=c.length>>>0,f=Array(e);if(h(a)!="[object Function]")throw new TypeError(a+" is not a function");for(var g=0;g<e;g++)g in c&&(f[g]=a.call(b,c[g],g,d));return f});Array.prototype.filter||(Array.prototype.filter=function(a,b){var d=l(this),c=k&&h(this)=="[object String]"?this.split(""):d,e=c.length>>>0,f=[],g;if(h(a)!="[object Function]")throw new TypeError(a+
+" is not a function");for(var i=0;i<e;i++)if(i in c){g=c[i];a.call(b,g,i,d)&&f.push(g)}return f});Array.prototype.every||(Array.prototype.every=function(a,b){var d=l(this),c=k&&h(this)=="[object String]"?this.split(""):d,e=c.length>>>0;if(h(a)!="[object Function]")throw new TypeError(a+" is not a function");for(var f=0;f<e;f++)if(f in c&&!a.call(b,c[f],f,d))return false;return true});Array.prototype.some||(Array.prototype.some=function(a,b){var d=l(this),c=k&&h(this)=="[object String]"?this.split(""):
+d,e=c.length>>>0;if(h(a)!="[object Function]")throw new TypeError(a+" is not a function");for(var f=0;f<e;f++)if(f in c&&a.call(b,c[f],f,d))return true;return false});Array.prototype.reduce||(Array.prototype.reduce=function(a){var b=l(this),d=k&&h(this)=="[object String]"?this.split(""):b,c=d.length>>>0;if(h(a)!="[object Function]")throw new TypeError(a+" is not a function");if(!c&&arguments.length==1)throw new TypeError("reduce of empty array with no initial value");var e=0,f;if(arguments.length>=
+2)f=arguments[1];else{do{if(e in d){f=d[e++];break}if(++e>=c)throw new TypeError("reduce of empty array with no initial value");}while(1)}for(;e<c;e++)e in d&&(f=a.call(void 0,f,d[e],e,b));return f});Array.prototype.reduceRight||(Array.prototype.reduceRight=function(a){var b=l(this),d=k&&h(this)=="[object String]"?this.split(""):b,c=d.length>>>0;if(h(a)!="[object Function]")throw new TypeError(a+" is not a function");if(!c&&arguments.length==1)throw new TypeError("reduceRight of empty array with no initial value");
+var e,c=c-1;if(arguments.length>=2)e=arguments[1];else{do{if(c in d){e=d[c--];break}if(--c<0)throw new TypeError("reduceRight of empty array with no initial value");}while(1)}do c in this&&(e=a.call(void 0,e,d[c],c,b));while(c--);return e});if(!Array.prototype.indexOf||-1!=[0,1].indexOf(1,2))Array.prototype.indexOf=function(a){var b=k&&h(this)=="[object String]"?this.split(""):l(this),d=b.length>>>0;if(!d)return-1;var c=0;arguments.length>1&&(c=p(arguments[1]));for(c=c>=0?c:Math.max(0,d+c);c<d;c++)if(c in
+b&&b[c]===a)return c;return-1};if(!Array.prototype.lastIndexOf||-1!=[0,1].lastIndexOf(0,-3))Array.prototype.lastIndexOf=function(a){var b=k&&h(this)=="[object String]"?this.split(""):l(this),d=b.length>>>0;if(!d)return-1;var c=d-1;arguments.length>1&&(c=Math.min(c,p(arguments[1])));for(c=c>=0?c:d-Math.abs(c);c>=0;c--)if(c in b&&a===b[c])return c;return-1};if(!Object.keys){var v=!0,w="toString toLocaleString valueOf hasOwnProperty isPrototypeOf propertyIsEnumerable constructor".split(" "),y=w.length,
+r;for(r in{toString:null})v=!1;Object.keys=function(a){if(typeof a!="object"&&typeof a!="function"||a===null)throw new TypeError("Object.keys called on a non-object");var b=[],d;for(d in a)t(a,d)&&b.push(d);if(v)for(d=0;d<y;d++){var c=w[d];t(a,c)&&b.push(c)}return b}}if(!Date.prototype.toISOString||-1===(new Date(-621987552E5)).toISOString().indexOf("-000001"))Date.prototype.toISOString=function(){var a,b,d,c;if(!isFinite(this))throw new RangeError("Date.prototype.toISOString called on non-finite value.");
+c=this.getUTCFullYear();a=this.getUTCMonth();c=c+Math.floor(a/12);a=[(a%12+12)%12+1,this.getUTCDate(),this.getUTCHours(),this.getUTCMinutes(),this.getUTCSeconds()];c=(c<0?"-":c>9999?"+":"")+("00000"+Math.abs(c)).slice(0<=c&&c<=9999?-4:-6);for(b=a.length;b--;){d=a[b];d<10&&(a[b]="0"+d)}return c+"-"+a.slice(0,2).join("-")+"T"+a.slice(2).join(":")+"."+("000"+this.getUTCMilliseconds()).slice(-3)+"Z"};r=!1;try{r=Date.prototype.toJSON&&null===(new Date(NaN)).toJSON()&&-1!==(new Date(-621987552E5)).toJSON().indexOf("-000001")&&
+Date.prototype.toJSON.call({toISOString:function(){return true}})}catch(F){}r||(Date.prototype.toJSON=function(){var a=Object(this),b;a:if(s(a))b=a;else{b=a.valueOf;if(typeof b==="function"){b=b.call(a);if(s(b))break a}b=a.toString;if(typeof b==="function"){b=b.call(a);if(s(b))break a}throw new TypeError;}if(typeof b==="number"&&!isFinite(b))return null;b=a.toISOString;if(typeof b!="function")throw new TypeError("toISOString property is not callable");return b.call(a)});var g=Date,u=function(a,b){var d=
+b>1?1:0;return z[b]+Math.floor((a-1969+d)/4)-Math.floor((a-1901+d)/100)+Math.floor((a-1601+d)/400)+365*(a-1970)},n=function(a,b,d,c,e,f,h){var i=arguments.length;if(this instanceof g){i=i==1&&String(a)===a?new g(n.parse(a)):i>=7?new g(a,b,d,c,e,f,h):i>=6?new g(a,b,d,c,e,f):i>=5?new g(a,b,d,c,e):i>=4?new g(a,b,d,c):i>=3?new g(a,b,d):i>=2?new g(a,b):i>=1?new g(a):new g;i.constructor=n;return i}return g.apply(this,arguments)},A=RegExp("^(\\d{4}|[+-]\\d{6})(?:-(\\d{2})(?:-(\\d{2})(?:T(\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.(\\d{3}))?)?(Z|(?:([-+])(\\d{2}):(\\d{2})))?)?)?)?$"),
+z=[0,31,59,90,120,151,181,212,243,273,304,334,365],j;for(j in g)n[j]=g[j];n.now=g.now;n.UTC=g.UTC;n.prototype=g.prototype;n.prototype.constructor=Date;n.parse=function(a){var b=A.exec(a);if(b){var d=Number(b[1]),c=Number(b[2]||1)-1,e=Number(b[3]||1)-1,f=Number(b[4]||0),h=Number(b[5]||0),i=Number(b[6]||0),j=Number(b[7]||0),m=!b[4]||b[8]?0:Number(new Date(1970,0)),k=b[9]==="-"?1:-1,l=Number(b[10]||0),b=Number(b[11]||0);if(f<(h>0||i>0||j>0?24:25)&&h<60&&i<60&&j<1E3&&c>-1&&c<12&&l<24&&b<60&&e>-1&&e<u(d,
+c+1)-u(d,c)){d=((u(d,c)+e)*24+f+l*k)*60;d=((d+h+b*k)*60+i)*1E3+j+m;if(-864E13<=d&&d<=864E13)return d}return NaN}return g.parse.apply(this,arguments)};Date=n;Date.now||(Date.now=function(){return(new Date).getTime()});if("0".split(void 0,0).length){var B=String.prototype.split;String.prototype.split=function(a,b){return a===void 0&&b===0?[]:B.apply(this,arguments)}}if("".substr&&"b"!=="0b".substr(-1)){var C=String.prototype.substr;String.prototype.substr=function(a,b){return C.call(this,a<0?(a=this.length+
+a)<0?0:a:a,b)}}j="\t\n\x0B\f\r \u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\ufeff";if(!String.prototype.trim||j.trim()){j="["+j+"]";var D=RegExp("^"+j+j+"*"),E=RegExp(j+j+"*$");String.prototype.trim=function(){if(this===void 0||this===null)throw new TypeError("can't convert "+this+" to object");return String(this).replace(D,"").replace(E,"")}}var l=function(a){if(a==null)throw new TypeError("can't convert "+a+" to object");return Object(a)}});

+ 2 - 0
js/lib/es5-shim-2.0.2/minify

@@ -0,0 +1,2 @@
+closure < es5-shim.js > es5-shim.min.js
+closure < es5-sham.js > es5-sham.min.js

+ 31 - 0
js/lib/es5-shim-2.0.2/package.json

@@ -0,0 +1,31 @@
+{
+    "name": "es5-shim",
+    "version": "2.0.2",
+    "description": "ES5 as implementable on previous engines",
+    "homepage": "http://github.com/kriskowal/es5-shim/",
+    "contributors": [
+        "Kris Kowal <kris@cixar.com> (http://github.com/kriskowal/)",
+        "Sami Samhuri <sami.samhuri@gmail.com> (http://samhuri.net/)",
+        "Florian Schäfer <florian.schaefer@gmail.com> (http://github.com/fschaefer)",
+        "Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)",
+        "Kit Cambridge <kitcambridge@gmail.com> (http://kitcambridge.github.com)"
+    ],
+    "bugs": {
+        "mail": "kris@cixar.com",
+        "url": "http://github.com/kriskowal/es5-shim/issues"
+    },
+    "licenses": [
+        {
+            "type": "MIT",
+            "url": "http://github.com/kriskowal/es5-shim/raw/master/LICENSE"
+        }
+    ],
+    "main": "es5-shim.js",
+    "repository": {
+        "type": "git",
+        "url": "http://github.com/kriskowal/es5-shim.git"
+    },
+    "engines": {
+        "node": ">=0.2.0"
+    }
+}

+ 59 - 0
js/lib/es5-shim-2.0.2/tests/helpers/h-kill.js

@@ -0,0 +1,59 @@
+// This methods allows the killing of built-in functions,
+// so the shim can take over with that implementation
+var HLP = (function() {
+    "use strict";
+    var kill;
+    
+    kill = function(_class, methods) {
+        /*if(!Array.isArray(methods))
+            return;*/
+        if(!_class.originals)
+            _class.originals = {};
+
+        for (var i = 0, len = methods.length; i < len; i++) {
+            var obj = methods[i];
+            _class.originals[obj] = _class[obj];
+            delete _class[obj];
+            if (obj in _class) {
+                // try something more aggressive since V8 at least
+                // appears to ignore the delete.
+                _class[obj] = null;
+                if (_class[obj]) {
+                    console.log("Couln't overwrite", obj, "of", _class);
+                }
+            }
+        }
+    };
+    return { kill: kill };
+}());
+
+HLP.kill(Function.prototype, [
+    'bind'
+]);
+
+HLP.kill(Array, [
+    'isArray'
+]);
+
+HLP.kill(String.prototype, [
+    "trim"
+]);
+
+HLP.kill(Object, [
+    'keys'
+]);
+
+HLP.kill(Date, [
+    'now', 'parse'
+]);
+
+HLP.kill(Date.prototype, [
+    "toJSON", "toISOString"
+]);
+
+HLP.kill(Array.prototype, [
+    'forEach', 'some', 'every', 
+    'indexOf', 'lastIndexOf', 
+    'map', 'filter', 
+    'reduce', 'reduceRight'
+]);

+ 34 - 0
js/lib/es5-shim-2.0.2/tests/helpers/h-matchers.js

@@ -0,0 +1,34 @@
+beforeEach(function() {
+    this.addMatchers({
+        toExactlyMatch: function(expected) {
+            var a1, a2,
+                l, i,
+                key,
+                actual = this.actual;
+            
+            var getKeys = function(o) {
+                var a = [];
+                for(key in o) {
+                    if(o.hasOwnProperty(key)) {
+                        a.push(key);
+                    }
+                }
+                return a;
+            }
+            a1 = getKeys(actual);
+            a2 = getKeys(expected);
+            
+            l = a1.length;
+            if(l !== a2.length) {
+                return false;
+            }
+            for(i = 0; i < l; i++) {
+                key = a1[i];
+                expect(key).toEqual(a2[i]);
+                expect(actual[key]).toEqual(expected[key]);
+            }
+            
+            return true;
+        }
+    })
+});

+ 3 - 0
js/lib/es5-shim-2.0.2/tests/helpers/h.js

@@ -0,0 +1,3 @@
+function implement() {
+    throw 'Not implemented';
+}

+ 62 - 0
js/lib/es5-shim-2.0.2/tests/index.html

@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+	<title>Jasmine Spec Runner</title>
+
+	<link rel="shortcut icon" type="image/png" href="lib/jasmine_favicon.png">
+
+	<link rel="stylesheet" type="text/css" href="lib/jasmine.css">
+	<script type="text/javascript" src="lib/jasmine.js"></script>
+	<script type="text/javascript" src="lib/jasmine-html.js"></script>
+	<script type="text/javascript" src="lib/json2.js"></script>
+
+	<!-- include helper files here... -->
+	<script src="helpers/h.js"></script>
+	<script src="helpers/h-kill.js"></script>
+	<script src="helpers/h-matchers.js"></script>
+
+	<!-- include source files here... -->
+	<script src="../es5-shim.js"></script>
+
+	<!-- include spec files here... -->
+	<script src="spec/s-array.js"></script>
+	<script src="spec/s-function.js"></script>
+	<script src="spec/s-string.js"></script>
+	<script src="spec/s-object.js"></script>
+	<script src="spec/s-date.js"></script>
+
+
+	<script type="text/javascript">
+		(function() {
+			var jasmineEnv = jasmine.getEnv();
+			jasmineEnv.updateInterval = 1000;
+
+			var trivialReporter = new jasmine.TrivialReporter();
+
+			jasmineEnv.addReporter(trivialReporter);
+
+			jasmineEnv.specFilter = function(spec) {
+				return trivialReporter.specFilter(spec);
+			};
+
+			var currentWindowOnload = window.onload;
+
+			window.onload = function() {
+				if (currentWindowOnload) {
+					currentWindowOnload();
+				}
+				execJasmine();
+			};
+
+			function execJasmine() {
+				jasmineEnv.execute();
+			}
+
+		})();
+	</script>
+
+</head>
+
+<body>
+</body>
+</html>

+ 190 - 0
js/lib/es5-shim-2.0.2/tests/lib/jasmine-html.js

@@ -0,0 +1,190 @@
+jasmine.TrivialReporter = function(doc) {
+  this.document = doc || document;
+  this.suiteDivs = {};
+  this.logRunningSpecs = false;
+};
+
+jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
+  var el = document.createElement(type);
+
+  for (var i = 2; i < arguments.length; i++) {
+    var child = arguments[i];
+
+    if (typeof child === 'string') {
+      el.appendChild(document.createTextNode(child));
+    } else {
+      if (child) { el.appendChild(child); }
+    }
+  }
+
+  for (var attr in attrs) {
+    if (attr == "className") {
+      el[attr] = attrs[attr];
+    } else {
+      el.setAttribute(attr, attrs[attr]);
+    }
+  }
+
+  return el;
+};
+
+jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
+  var showPassed, showSkipped;
+
+  this.outerDiv = this.createDom('div', { className: 'jasmine_reporter' },
+      this.createDom('div', { className: 'banner' },
+        this.createDom('div', { className: 'logo' },
+            this.createDom('span', { className: 'title' }, "Jasmine"),
+            this.createDom('span', { className: 'version' }, runner.env.versionString())),
+        this.createDom('div', { className: 'options' },
+            "Show ",
+            showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
+            this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
+            showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
+            this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
+            )
+          ),
+
+      this.runnerDiv = this.createDom('div', { className: 'runner running' },
+          this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
+          this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
+          this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
+      );
+
+  this.document.body.appendChild(this.outerDiv);
+
+  var suites = runner.suites();
+  for (var i = 0; i < suites.length; i++) {
+    var suite = suites[i];
+    var suiteDiv = this.createDom('div', { className: 'suite' },
+        this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
+        this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
+    this.suiteDivs[suite.id] = suiteDiv;
+    var parentDiv = this.outerDiv;
+    if (suite.parentSuite) {
+      parentDiv = this.suiteDivs[suite.parentSuite.id];
+    }
+    parentDiv.appendChild(suiteDiv);
+  }
+
+  this.startedAt = new Date();
+
+  var self = this;
+  showPassed.onclick = function(evt) {
+    if (showPassed.checked) {
+      self.outerDiv.className += ' show-passed';
+    } else {
+      self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
+    }
+  };
+
+  showSkipped.onclick = function(evt) {
+    if (showSkipped.checked) {
+      self.outerDiv.className += ' show-skipped';
+    } else {
+      self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
+    }
+  };
+};
+
+jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
+  var results = runner.results();
+  var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
+  this.runnerDiv.setAttribute("class", className);
+  //do it twice for IE
+  this.runnerDiv.setAttribute("className", className);
+  var specs = runner.specs();
+  var specCount = 0;
+  for (var i = 0; i < specs.length; i++) {
+    if (this.specFilter(specs[i])) {
+      specCount++;
+    }
+  }
+  var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
+  message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
+  this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
+
+  this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
+};
+
+jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
+  var results = suite.results();
+  var status = results.passed() ? 'passed' : 'failed';
+  if (results.totalCount === 0) { // todo: change this to check results.skipped
+    status = 'skipped';
+  }
+  this.suiteDivs[suite.id].className += " " + status;
+};
+
+jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
+  if (this.logRunningSpecs) {
+    this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
+  }
+};
+
+jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
+  var results = spec.results();
+  var status = results.passed() ? 'passed' : 'failed';
+  if (results.skipped) {
+    status = 'skipped';
+  }
+  var specDiv = this.createDom('div', { className: 'spec '  + status },
+      this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
+      this.createDom('a', {
+        className: 'description',
+        href: '?spec=' + encodeURIComponent(spec.getFullName()),
+        title: spec.getFullName()
+      }, spec.description));
+
+
+  var resultItems = results.getItems();
+  var messagesDiv = this.createDom('div', { className: 'messages' });
+  for (var i = 0; i < resultItems.length; i++) {
+    var result = resultItems[i];
+
+    if (result.type == 'log') {
+      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
+    } else if (result.type == 'expect' && result.passed && !result.passed()) {
+      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
+
+      if (result.trace.stack) {
+        messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
+      }
+    }
+  }
+
+  if (messagesDiv.childNodes.length > 0) {
+    specDiv.appendChild(messagesDiv);
+  }
+
+  this.suiteDivs[spec.suite.id].appendChild(specDiv);
+};
+
+jasmine.TrivialReporter.prototype.log = function() {
+  var console = jasmine.getGlobal().console;
+  if (console && console.log) {
+    if (console.log.apply) {
+      console.log.apply(console, arguments);
+    } else {
+      console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
+    }
+  }
+};
+
+jasmine.TrivialReporter.prototype.getLocation = function() {
+  return this.document.location;
+};
+
+jasmine.TrivialReporter.prototype.specFilter = function(spec) {
+  var paramMap = {};
+  var params = this.getLocation().search.substring(1).split('&');
+  for (var i = 0; i < params.length; i++) {
+    var p = params[i].split('=');
+    paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
+  }
+
+  if (!paramMap.spec) {
+    return true;
+  }
+  return spec.getFullName().indexOf(paramMap.spec) === 0;
+};

+ 166 - 0
js/lib/es5-shim-2.0.2/tests/lib/jasmine.css

@@ -0,0 +1,166 @@
+body {
+  font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif;
+}
+
+
+.jasmine_reporter a:visited, .jasmine_reporter a {
+  color: #303; 
+}
+
+.jasmine_reporter a:hover, .jasmine_reporter a:active {
+  color: blue; 
+}
+
+.run_spec {
+  float:right;
+  padding-right: 5px;
+  font-size: .8em;
+  text-decoration: none;
+}
+
+.jasmine_reporter {
+  margin: 0 5px;
+}
+
+.banner {
+  color: #303;
+  background-color: #fef;
+  padding: 5px;
+}
+
+.logo {
+  float: left;
+  font-size: 1.1em;
+  padding-left: 5px;
+}
+
+.logo .version {
+  font-size: .6em;
+  padding-left: 1em;
+}
+
+.runner.running {
+  background-color: yellow;
+}
+
+
+.options {
+  text-align: right;
+  font-size: .8em;
+}
+
+
+
+
+.suite {
+  border: 1px outset gray;
+  margin: 5px 0;
+  padding-left: 1em;
+}
+
+.suite .suite {
+  margin: 5px; 
+}
+
+.suite.passed {
+  background-color: #dfd;
+}
+
+.suite.failed {
+  background-color: #fdd;
+}
+
+.spec {
+  margin: 5px;
+  padding-left: 1em;
+  clear: both;
+}
+
+.spec.failed, .spec.passed, .spec.skipped {
+  padding-bottom: 5px;
+  border: 1px solid gray;
+}
+
+.spec.failed {
+  background-color: #fbb;
+  border-color: red;
+}
+
+.spec.passed {
+  background-color: #bfb;
+  border-color: green;
+}
+
+.spec.skipped {
+  background-color: #bbb;
+}
+
+.messages {
+  border-left: 1px dashed gray;
+  padding-left: 1em;
+  padding-right: 1em;
+}
+
+.passed {
+  background-color: #cfc;
+  display: none;
+}
+
+.failed {
+  background-color: #fbb;
+}
+
+.skipped {
+  color: #777;
+  background-color: #eee;
+  display: none;
+}
+
+
+/*.resultMessage {*/
+  /*white-space: pre;*/
+/*}*/
+
+.resultMessage span.result {
+  display: block;
+  line-height: 2em;
+  color: black;
+}
+
+.resultMessage .mismatch {
+  color: black;
+}
+
+.stackTrace {
+  white-space: pre;
+  font-size: .8em;
+  margin-left: 10px;
+  max-height: 5em;
+  overflow: auto;
+  border: 1px inset red;
+  padding: 1em;
+  background: #eef;
+}
+
+.finished-at {
+  padding-left: 1em;
+  font-size: .6em;
+}
+
+.show-passed .passed,
+.show-skipped .skipped {
+  display: block;
+}
+
+
+#jasmine_content {
+  position:fixed;
+  right: 100%;
+}
+
+.runner {
+  border: 1px solid gray;
+  display: block;
+  margin: 5px 0;
+  padding: 2px 0 2px 10px;
+}

+ 2477 - 0
js/lib/es5-shim-2.0.2/tests/lib/jasmine.js

@@ -0,0 +1,2477 @@
+var isCommonJS = typeof window == "undefined";
+
+/**
+ * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.
+ *
+ * @namespace
+ */
+var jasmine = {};
+if (isCommonJS) exports.jasmine = jasmine;
+/**
+ * @private
+ */
+jasmine.unimplementedMethod_ = function() {
+  throw new Error("unimplemented method");
+};
+
+/**
+ * Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code> is just
+ * a plain old variable and may be redefined by somebody else.
+ *
+ * @private
+ */
+jasmine.undefined = jasmine.___undefined___;
+
+/**
+ * Show diagnostic messages in the console if set to true
+ *
+ */
+jasmine.VERBOSE = false;
+
+/**
+ * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed.
+ *
+ */
+jasmine.DEFAULT_UPDATE_INTERVAL = 250;
+
+/**
+ * Default timeout interval in milliseconds for waitsFor() blocks.
+ */
+jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
+
+jasmine.getGlobal = function() {
+  function getGlobal() {
+    return this;
+  }
+
+  return getGlobal();
+};
+
+/**
+ * Allows for bound functions to be compared.  Internal use only.
+ *
+ * @ignore
+ * @private
+ * @param base {Object} bound 'this' for the function
+ * @param name {Function} function to find
+ */
+jasmine.bindOriginal_ = function(base, name) {
+  var original = base[name];
+  if (original.apply) {
+    return function() {
+      return original.apply(base, arguments);
+    };
+  } else {
+    // IE support
+    return jasmine.getGlobal()[name];
+  }
+};
+
+jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');
+jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');
+jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');
+jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');
+
+jasmine.MessageResult = function(values) {
+  this.type = 'log';
+  this.values = values;
+  this.trace = new Error(); // todo: test better
+};
+
+jasmine.MessageResult.prototype.toString = function() {
+  var text = "";
+  for (var i = 0; i < this.values.length; i++) {
+    if (i > 0) text += " ";
+    if (jasmine.isString_(this.values[i])) {
+      text += this.values[i];
+    } else {
+      text += jasmine.pp(this.values[i]);
+    }
+  }
+  return text;
+};
+
+jasmine.ExpectationResult = function(params) {
+  this.type = 'expect';
+  this.matcherName = params.matcherName;
+  this.passed_ = params.passed;
+  this.expected = params.expected;
+  this.actual = params.actual;
+  this.message = this.passed_ ? 'Passed.' : params.message;
+
+  var trace = (params.trace || new Error(this.message));
+  this.trace = this.passed_ ? '' : trace;
+};
+
+jasmine.ExpectationResult.prototype.toString = function () {
+  return this.message;
+};
+
+jasmine.ExpectationResult.prototype.passed = function () {
+  return this.passed_;
+};
+
+/**
+ * Getter for the Jasmine environment. Ensures one gets created
+ */
+jasmine.getEnv = function() {
+  var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();
+  return env;
+};
+
+/**
+ * @ignore
+ * @private
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isArray_ = function(value) {
+  return jasmine.isA_("Array", value);
+};
+
+/**
+ * @ignore
+ * @private
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isString_ = function(value) {
+  return jasmine.isA_("String", value);
+};
+
+/**
+ * @ignore
+ * @private
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isNumber_ = function(value) {
+  return jasmine.isA_("Number", value);
+};
+
+/**
+ * @ignore
+ * @private
+ * @param {String} typeName
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isA_ = function(typeName, value) {
+  return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
+};
+
+/**
+ * Pretty printer for expecations.  Takes any object and turns it into a human-readable string.
+ *
+ * @param value {Object} an object to be outputted
+ * @returns {String}
+ */
+jasmine.pp = function(value) {
+  var stringPrettyPrinter = new jasmine.StringPrettyPrinter();
+  stringPrettyPrinter.format(value);
+  return stringPrettyPrinter.string;
+};
+
+/**
+ * Returns true if the object is a DOM Node.
+ *
+ * @param {Object} obj object to check
+ * @returns {Boolean}
+ */
+jasmine.isDomNode = function(obj) {
+  return obj.nodeType > 0;
+};
+
+/**
+ * Returns a matchable 'generic' object of the class type.  For use in expecations of type when values don't matter.
+ *
+ * @example
+ * // don't care about which function is passed in, as long as it's a function
+ * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));
+ *
+ * @param {Class} clazz
+ * @returns matchable object of the type clazz
+ */
+jasmine.any = function(clazz) {
+  return new jasmine.Matchers.Any(clazz);
+};
+
+/**
+ * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
+ *
+ * Spies should be created in test setup, before expectations.  They can then be checked, using the standard Jasmine
+ * expectation syntax. Spies can be checked if they were called or not and what the calling params were.
+ *
+ * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).
+ *
+ * Spies are torn down at the end of every spec.
+ *
+ * Note: Do <b>not</b> call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
+ *
+ * @example
+ * // a stub
+ * var myStub = jasmine.createSpy('myStub');  // can be used anywhere
+ *
+ * // spy example
+ * var foo = {
+ *   not: function(bool) { return !bool; }
+ * }
+ *
+ * // actual foo.not will not be called, execution stops
+ * spyOn(foo, 'not');
+
+ // foo.not spied upon, execution will continue to implementation
+ * spyOn(foo, 'not').andCallThrough();
+ *
+ * // fake example
+ * var foo = {
+ *   not: function(bool) { return !bool; }
+ * }
+ *
+ * // foo.not(val) will return val
+ * spyOn(foo, 'not').andCallFake(function(value) {return value;});
+ *
+ * // mock example
+ * foo.not(7 == 7);
+ * expect(foo.not).toHaveBeenCalled();
+ * expect(foo.not).toHaveBeenCalledWith(true);
+ *
+ * @constructor
+ * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
+ * @param {String} name
+ */
+jasmine.Spy = function(name) {
+  /**
+   * The name of the spy, if provided.
+   */
+  this.identity = name || 'unknown';
+  /**
+   *  Is this Object a spy?
+   */
+  this.isSpy = true;
+  /**
+   * The actual function this spy stubs.
+   */
+  this.plan = function() {
+  };
+  /**
+   * Tracking of the most recent call to the spy.
+   * @example
+   * var mySpy = jasmine.createSpy('foo');
+   * mySpy(1, 2);
+   * mySpy.mostRecentCall.args = [1, 2];
+   */
+  this.mostRecentCall = {};
+
+  /**
+   * Holds arguments for each call to the spy, indexed by call count
+   * @example
+   * var mySpy = jasmine.createSpy('foo');
+   * mySpy(1, 2);
+   * mySpy(7, 8);
+   * mySpy.mostRecentCall.args = [7, 8];
+   * mySpy.argsForCall[0] = [1, 2];
+   * mySpy.argsForCall[1] = [7, 8];
+   */
+  this.argsForCall = [];
+  this.calls = [];
+};
+
+/**
+ * Tells a spy to call through to the actual implemenatation.
+ *
+ * @example
+ * var foo = {
+ *   bar: function() { // do some stuff }
+ * }
+ *
+ * // defining a spy on an existing property: foo.bar
+ * spyOn(foo, 'bar').andCallThrough();
+ */
+jasmine.Spy.prototype.andCallThrough = function() {
+  this.plan = this.originalValue;
+  return this;
+};
+
+/**
+ * For setting the return value of a spy.
+ *
+ * @example
+ * // defining a spy from scratch: foo() returns 'baz'
+ * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
+ *
+ * // defining a spy on an existing property: foo.bar() returns 'baz'
+ * spyOn(foo, 'bar').andReturn('baz');
+ *
+ * @param {Object} value
+ */
+jasmine.Spy.prototype.andReturn = function(value) {
+  this.plan = function() {
+    return value;
+  };
+  return this;
+};
+
+/**
+ * For throwing an exception when a spy is called.
+ *
+ * @example
+ * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
+ * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
+ *
+ * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
+ * spyOn(foo, 'bar').andThrow('baz');
+ *
+ * @param {String} exceptionMsg
+ */
+jasmine.Spy.prototype.andThrow = function(exceptionMsg) {
+  this.plan = function() {
+    throw exceptionMsg;
+  };
+  return this;
+};
+
+/**
+ * Calls an alternate implementation when a spy is called.
+ *
+ * @example
+ * var baz = function() {
+ *   // do some stuff, return something
+ * }
+ * // defining a spy from scratch: foo() calls the function baz
+ * var foo = jasmine.createSpy('spy on foo').andCall(baz);
+ *
+ * // defining a spy on an existing property: foo.bar() calls an anonymnous function
+ * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
+ *
+ * @param {Function} fakeFunc
+ */
+jasmine.Spy.prototype.andCallFake = function(fakeFunc) {
+  this.plan = fakeFunc;
+  return this;
+};
+
+/**
+ * Resets all of a spy's the tracking variables so that it can be used again.
+ *
+ * @example
+ * spyOn(foo, 'bar');
+ *
+ * foo.bar();
+ *
+ * expect(foo.bar.callCount).toEqual(1);
+ *
+ * foo.bar.reset();
+ *
+ * expect(foo.bar.callCount).toEqual(0);
+ */
+jasmine.Spy.prototype.reset = function() {
+  this.wasCalled = false;
+  this.callCount = 0;
+  this.argsForCall = [];
+  this.calls = [];
+  this.mostRecentCall = {};
+};
+
+jasmine.createSpy = function(name) {
+
+  var spyObj = function() {
+    spyObj.wasCalled = true;
+    spyObj.callCount++;
+    var args = jasmine.util.argsToArray(arguments);
+    spyObj.mostRecentCall.object = this;
+    spyObj.mostRecentCall.args = args;
+    spyObj.argsForCall.push(args);
+    spyObj.calls.push({object: this, args: args});
+    return spyObj.plan.apply(this, arguments);
+  };
+
+  var spy = new jasmine.Spy(name);
+
+  for (var prop in spy) {
+    spyObj[prop] = spy[prop];
+  }
+
+  spyObj.reset();
+
+  return spyObj;
+};
+
+/**
+ * Determines whether an object is a spy.
+ *
+ * @param {jasmine.Spy|Object} putativeSpy
+ * @returns {Boolean}
+ */
+jasmine.isSpy = function(putativeSpy) {
+  return putativeSpy && putativeSpy.isSpy;
+};
+
+/**
+ * Creates a more complicated spy: an Object that has every property a function that is a spy.  Used for stubbing something
+ * large in one call.
+ *
+ * @param {String} baseName name of spy class
+ * @param {Array} methodNames array of names of methods to make spies
+ */
+jasmine.createSpyObj = function(baseName, methodNames) {
+  if (!jasmine.isArray_(methodNames) || methodNames.length === 0) {
+    throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
+  }
+  var obj = {};
+  for (var i = 0; i < methodNames.length; i++) {
+    obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);
+  }
+  return obj;
+};
+
+/**
+ * All parameters are pretty-printed and concatenated together, then written to the current spec's output.
+ *
+ * Be careful not to leave calls to <code>jasmine.log</code> in production code.
+ */
+jasmine.log = function() {
+  var spec = jasmine.getEnv().currentSpec;
+  spec.log.apply(spec, arguments);
+};
+
+/**
+ * Function that installs a spy on an existing object's method name.  Used within a Spec to create a spy.
+ *
+ * @example
+ * // spy example
+ * var foo = {
+ *   not: function(bool) { return !bool; }
+ * }
+ * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
+ *
+ * @see jasmine.createSpy
+ * @param obj
+ * @param methodName
+ * @returns a Jasmine spy that can be chained with all spy methods
+ */
+var spyOn = function(obj, methodName) {
+  return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
+};
+if (isCommonJS) exports.spyOn = spyOn;
+
+/**
+ * Creates a Jasmine spec that will be added to the current suite.
+ *
+ * // TODO: pending tests
+ *
+ * @example
+ * it('should be true', function() {
+ *   expect(true).toEqual(true);
+ * });
+ *
+ * @param {String} desc description of this specification
+ * @param {Function} func defines the preconditions and expectations of the spec
+ */
+var it = function(desc, func) {
+  return jasmine.getEnv().it(desc, func);
+};
+if (isCommonJS) exports.it = it;
+
+/**
+ * Creates a <em>disabled</em> Jasmine spec.
+ *
+ * A convenience method that allows existing specs to be disabled temporarily during development.
+ *
+ * @param {String} desc description of this specification
+ * @param {Function} func defines the preconditions and expectations of the spec
+ */
+var xit = function(desc, func) {
+  return jasmine.getEnv().xit(desc, func);
+};
+if (isCommonJS) exports.xit = xit;
+
+/**
+ * Starts a chain for a Jasmine expectation.
+ *
+ * It is passed an Object that is the actual value and should chain to one of the many
+ * jasmine.Matchers functions.
+ *
+ * @param {Object} actual Actual value to test against and expected value
+ */
+var expect = function(actual) {
+  return jasmine.getEnv().currentSpec.expect(actual);
+};
+if (isCommonJS) exports.expect = expect;
+
+/**
+ * Defines part of a jasmine spec.  Used in cominbination with waits or waitsFor in asynchrnous specs.
+ *
+ * @param {Function} func Function that defines part of a jasmine spec.
+ */
+var runs = function(func) {
+  jasmine.getEnv().currentSpec.runs(func);
+};
+if (isCommonJS) exports.runs = runs;
+
+/**
+ * Waits a fixed time period before moving to the next block.
+ *
+ * @deprecated Use waitsFor() instead
+ * @param {Number} timeout milliseconds to wait
+ */
+var waits = function(timeout) {
+  jasmine.getEnv().currentSpec.waits(timeout);
+};
+if (isCommonJS) exports.waits = waits;
+
+/**
+ * Waits for the latchFunction to return true before proceeding to the next block.
+ *
+ * @param {Function} latchFunction
+ * @param {String} optional_timeoutMessage
+ * @param {Number} optional_timeout
+ */
+var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
+  jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);
+};
+if (isCommonJS) exports.waitsFor = waitsFor;
+
+/**
+ * A function that is called before each spec in a suite.
+ *
+ * Used for spec setup, including validating assumptions.
+ *
+ * @param {Function} beforeEachFunction
+ */
+var beforeEach = function(beforeEachFunction) {
+  jasmine.getEnv().beforeEach(beforeEachFunction);
+};
+if (isCommonJS) exports.beforeEach = beforeEach;
+
+/**
+ * A function that is called after each spec in a suite.
+ *
+ * Used for restoring any state that is hijacked during spec execution.
+ *
+ * @param {Function} afterEachFunction
+ */
+var afterEach = function(afterEachFunction) {
+  jasmine.getEnv().afterEach(afterEachFunction);
+};
+if (isCommonJS) exports.afterEach = afterEach;
+
+/**
+ * Defines a suite of specifications.
+ *
+ * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
+ * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
+ * of setup in some tests.
+ *
+ * @example
+ * // TODO: a simple suite
+ *
+ * // TODO: a simple suite with a nested describe block
+ *
+ * @param {String} description A string, usually the class under test.
+ * @param {Function} specDefinitions function that defines several specs.
+ */
+var describe = function(description, specDefinitions) {
+  return jasmine.getEnv().describe(description, specDefinitions);
+};
+if (isCommonJS) exports.describe = describe;
+
+/**
+ * Disables a suite of specifications.  Used to disable some suites in a file, or files, temporarily during development.
+ *
+ * @param {String} description A string, usually the class under test.
+ * @param {Function} specDefinitions function that defines several specs.
+ */
+var xdescribe = function(description, specDefinitions) {
+  return jasmine.getEnv().xdescribe(description, specDefinitions);
+};
+if (isCommonJS) exports.xdescribe = xdescribe;
+
+
+// Provide the XMLHttpRequest class for IE 5.x-6.x:
+jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {
+  function tryIt(f) {
+    try {
+      return f();
+    } catch(e) {
+    }
+    return null;
+  }
+
+  var xhr = tryIt(function() {
+    return new ActiveXObject("Msxml2.XMLHTTP.6.0");
+  }) ||
+    tryIt(function() {
+      return new ActiveXObject("Msxml2.XMLHTTP.3.0");
+    }) ||
+    tryIt(function() {
+      return new ActiveXObject("Msxml2.XMLHTTP");
+    }) ||
+    tryIt(function() {
+      return new ActiveXObject("Microsoft.XMLHTTP");
+    });
+
+  if (!xhr) throw new Error("This browser does not support XMLHttpRequest.");
+
+  return xhr;
+} : XMLHttpRequest;
+/**
+ * @namespace
+ */
+jasmine.util = {};
+
+/**
+ * Declare that a child class inherit it's prototype from the parent class.
+ *
+ * @private
+ * @param {Function} childClass
+ * @param {Function} parentClass
+ */
+jasmine.util.inherit = function(childClass, parentClass) {
+  /**
+   * @private
+   */
+  var subclass = function() {
+  };
+  subclass.prototype = parentClass.prototype;
+  childClass.prototype = new subclass();
+};
+
+jasmine.util.formatException = function(e) {
+  var lineNumber;
+  if (e.line) {
+    lineNumber = e.line;
+  }
+  else if (e.lineNumber) {
+    lineNumber = e.lineNumber;
+  }
+
+  var file;
+
+  if (e.sourceURL) {
+    file = e.sourceURL;
+  }
+  else if (e.fileName) {
+    file = e.fileName;
+  }
+
+  var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();
+
+  if (file && lineNumber) {
+    message += ' in ' + file + ' (line ' + lineNumber + ')';
+  }
+
+  return message;
+};
+
+jasmine.util.htmlEscape = function(str) {
+  if (!str) return str;
+  return str.replace(/&/g, '&amp;')
+    .replace(/</g, '&lt;')
+    .replace(/>/g, '&gt;');
+};
+
+jasmine.util.argsToArray = function(args) {
+  var arrayOfArgs = [];
+  for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);
+  return arrayOfArgs;
+};
+
+jasmine.util.extend = function(destination, source) {
+  for (var property in source) destination[property] = source[property];
+  return destination;
+};
+
+/**
+ * Environment for Jasmine
+ *
+ * @constructor
+ */
+jasmine.Env = function() {
+  this.currentSpec = null;
+  this.currentSuite = null;
+  this.currentRunner_ = new jasmine.Runner(this);
+
+  this.reporter = new jasmine.MultiReporter();
+
+  this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;
+  this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL;
+  this.lastUpdate = 0;
+  this.specFilter = function() {
+    return true;
+  };
+
+  this.nextSpecId_ = 0;
+  this.nextSuiteId_ = 0;
+  this.equalityTesters_ = [];
+
+  // wrap matchers
+  this.matchersClass = function() {
+    jasmine.Matchers.apply(this, arguments);
+  };
+  jasmine.util.inherit(this.matchersClass, jasmine.Matchers);
+
+  jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);
+};
+
+
+jasmine.Env.prototype.setTimeout = jasmine.setTimeout;
+jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;
+jasmine.Env.prototype.setInterval = jasmine.setInterval;
+jasmine.Env.prototype.clearInterval = jasmine.clearInterval;
+
+/**
+ * @returns an object containing jasmine version build info, if set.
+ */
+jasmine.Env.prototype.version = function () {
+  if (jasmine.version_) {
+    return jasmine.version_;
+  } else {
+    throw new Error('Version not set');
+  }
+};
+
+/**
+ * @returns string containing jasmine version build info, if set.
+ */
+jasmine.Env.prototype.versionString = function() {
+  if (!jasmine.version_) {
+    return "version unknown";
+  }
+
+  var version = this.version();
+  var versionString = version.major + "." + version.minor + "." + version.build;
+  if (version.release_candidate) {
+    versionString += ".rc" + version.release_candidate
+  }
+  versionString += " revision " + version.revision;
+  return versionString;
+};
+
+/**
+ * @returns a sequential integer starting at 0
+ */
+jasmine.Env.prototype.nextSpecId = function () {
+  return this.nextSpecId_++;
+};
+
+/**
+ * @returns a sequential integer starting at 0
+ */
+jasmine.Env.prototype.nextSuiteId = function () {
+  return this.nextSuiteId_++;
+};
+
+/**
+ * Register a reporter to receive status updates from Jasmine.
+ * @param {jasmine.Reporter} reporter An object which will receive status updates.
+ */
+jasmine.Env.prototype.addReporter = function(reporter) {
+  this.reporter.addReporter(reporter);
+};
+
+jasmine.Env.prototype.execute = function() {
+  this.currentRunner_.execute();
+};
+
+jasmine.Env.prototype.describe = function(description, specDefinitions) {
+  var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);
+
+  var parentSuite = this.currentSuite;
+  if (parentSuite) {
+    parentSuite.add(suite);
+  } else {
+    this.currentRunner_.add(suite);
+  }
+
+  this.currentSuite = suite;
+
+  var declarationError = null;
+  try {
+    specDefinitions.call(suite);
+  } catch(e) {
+    declarationError = e;
+  }
+
+  if (declarationError) {
+    this.it("encountered a declaration exception", function() {
+      throw declarationError;
+    });
+  }
+
+  this.currentSuite = parentSuite;
+
+  return suite;
+};
+
+jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {
+  if (this.currentSuite) {
+    this.currentSuite.beforeEach(beforeEachFunction);
+  } else {
+    this.currentRunner_.beforeEach(beforeEachFunction);
+  }
+};
+
+jasmine.Env.prototype.currentRunner = function () {
+  return this.currentRunner_;
+};
+
+jasmine.Env.prototype.afterEach = function(afterEachFunction) {
+  if (this.currentSuite) {
+    this.currentSuite.afterEach(afterEachFunction);
+  } else {
+    this.currentRunner_.afterEach(afterEachFunction);
+  }
+
+};
+
+jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {
+  return {
+    execute: function() {
+    }
+  };
+};
+
+jasmine.Env.prototype.it = function(description, func) {
+  var spec = new jasmine.Spec(this, this.currentSuite, description);
+  this.currentSuite.add(spec);
+  this.currentSpec = spec;
+
+  if (func) {
+    spec.runs(func);
+  }
+
+  return spec;
+};
+
+jasmine.Env.prototype.xit = function(desc, func) {
+  return {
+    id: this.nextSpecId(),
+    runs: function() {
+    }
+  };
+};
+
+jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {
+  if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {
+    return true;
+  }
+
+  a.__Jasmine_been_here_before__ = b;
+  b.__Jasmine_been_here_before__ = a;
+
+  var hasKey = function(obj, keyName) {
+    return obj !== null && obj[keyName] !== jasmine.undefined;
+  };
+
+  for (var property in b) {
+    if (!hasKey(a, property) && hasKey(b, property)) {
+      mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
+    }
+  }
+  for (property in a) {
+    if (!hasKey(b, property) && hasKey(a, property)) {
+      mismatchKeys.push("expected missing key '" + property + "', but present in actual.");
+    }
+  }
+  for (property in b) {
+    if (property == '__Jasmine_been_here_before__') continue;
+    if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {
+      mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual.");
+    }
+  }
+
+  if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {
+    mismatchValues.push("arrays were not the same length");
+  }
+
+  delete a.__Jasmine_been_here_before__;
+  delete b.__Jasmine_been_here_before__;
+  return (mismatchKeys.length === 0 && mismatchValues.length === 0);
+};
+
+jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {
+  mismatchKeys = mismatchKeys || [];
+  mismatchValues = mismatchValues || [];
+
+  for (var i = 0; i < this.equalityTesters_.length; i++) {
+    var equalityTester = this.equalityTesters_[i];
+    var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);
+    if (result !== jasmine.undefined) return result;
+  }
+
+  if (a === b) return true;
+
+  if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {
+    return (a == jasmine.undefined && b == jasmine.undefined);
+  }
+
+  if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {
+    return a === b;
+  }
+
+  if (a instanceof Date && b instanceof Date) {
+    return a.getTime() == b.getTime();
+  }
+
+  if (a instanceof jasmine.Matchers.Any) {
+    return a.matches(b);
+  }
+
+  if (b instanceof jasmine.Matchers.Any) {
+    return b.matches(a);
+  }
+
+  if (jasmine.isString_(a) && jasmine.isString_(b)) {
+    return (a == b);
+  }
+
+  if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) {
+    return (a == b);
+  }
+
+  if (typeof a === "object" && typeof b === "object") {
+    return this.compareObjects_(a, b, mismatchKeys, mismatchValues);
+  }
+
+  //Straight check
+  return (a === b);
+};
+
+jasmine.Env.prototype.contains_ = function(haystack, needle) {
+  if (jasmine.isArray_(haystack)) {
+    for (var i = 0; i < haystack.length; i++) {
+      if (this.equals_(haystack[i], needle)) return true;
+    }
+    return false;
+  }
+  return haystack.indexOf(needle) >= 0;
+};
+
+jasmine.Env.prototype.addEqualityTester = function(equalityTester) {
+  this.equalityTesters_.push(equalityTester);
+};
+/** No-op base class for Jasmine reporters.
+ *
+ * @constructor
+ */
+jasmine.Reporter = function() {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportRunnerStarting = function(runner) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportRunnerResults = function(runner) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportSuiteResults = function(suite) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportSpecStarting = function(spec) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportSpecResults = function(spec) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.log = function(str) {
+};
+
+/**
+ * Blocks are functions with executable code that make up a spec.
+ *
+ * @constructor
+ * @param {jasmine.Env} env
+ * @param {Function} func
+ * @param {jasmine.Spec} spec
+ */
+jasmine.Block = function(env, func, spec) {
+  this.env = env;
+  this.func = func;
+  this.spec = spec;
+};
+
+jasmine.Block.prototype.execute = function(onComplete) {  
+  try {
+    this.func.apply(this.spec);
+  } catch (e) {
+    this.spec.fail(e);
+  }
+  onComplete();
+};
+/** JavaScript API reporter.
+ *
+ * @constructor
+ */
+jasmine.JsApiReporter = function() {
+  this.started = false;
+  this.finished = false;
+  this.suites_ = [];
+  this.results_ = {};
+};
+
+jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) {
+  this.started = true;
+  var suites = runner.topLevelSuites();
+  for (var i = 0; i < suites.length; i++) {
+    var suite = suites[i];
+    this.suites_.push(this.summarize_(suite));
+  }
+};
+
+jasmine.JsApiReporter.prototype.suites = function() {
+  return this.suites_;
+};
+
+jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {
+  var isSuite = suiteOrSpec instanceof jasmine.Suite;
+  var summary = {
+    id: suiteOrSpec.id,
+    name: suiteOrSpec.description,
+    type: isSuite ? 'suite' : 'spec',
+    children: []
+  };
+  
+  if (isSuite) {
+    var children = suiteOrSpec.children();
+    for (var i = 0; i < children.length; i++) {
+      summary.children.push(this.summarize_(children[i]));
+    }
+  }
+  return summary;
+};
+
+jasmine.JsApiReporter.prototype.results = function() {
+  return this.results_;
+};
+
+jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) {
+  return this.results_[specId];
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {
+  this.finished = true;
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {
+  this.results_[spec.id] = {
+    messages: spec.results().getItems(),
+    result: spec.results().failedCount > 0 ? "failed" : "passed"
+  };
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.log = function(str) {
+};
+
+jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){
+  var results = {};
+  for (var i = 0; i < specIds.length; i++) {
+    var specId = specIds[i];
+    results[specId] = this.summarizeResult_(this.results_[specId]);
+  }
+  return results;
+};
+
+jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){
+  var summaryMessages = [];
+  var messagesLength = result.messages.length;
+  for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {
+    var resultMessage = result.messages[messageIndex];
+    summaryMessages.push({
+      text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined,
+      passed: resultMessage.passed ? resultMessage.passed() : true,
+      type: resultMessage.type,
+      message: resultMessage.message,
+      trace: {
+        stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined
+      }
+    });
+  }
+
+  return {
+    result : result.result,
+    messages : summaryMessages
+  };
+};
+
+/**
+ * @constructor
+ * @param {jasmine.Env} env
+ * @param actual
+ * @param {jasmine.Spec} spec
+ */
+jasmine.Matchers = function(env, actual, spec, opt_isNot) {
+  this.env = env;
+  this.actual = actual;
+  this.spec = spec;
+  this.isNot = opt_isNot || false;
+  this.reportWasCalled_ = false;
+};
+
+// todo: @deprecated as of Jasmine 0.11, remove soon [xw]
+jasmine.Matchers.pp = function(str) {
+  throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!");
+};
+
+// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw]
+jasmine.Matchers.prototype.report = function(result, failing_message, details) {
+  throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs");
+};
+
+jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) {
+  for (var methodName in prototype) {
+    if (methodName == 'report') continue;
+    var orig = prototype[methodName];
+    matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig);
+  }
+};
+
+jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {
+  return function() {
+    var matcherArgs = jasmine.util.argsToArray(arguments);
+    var result = matcherFunction.apply(this, arguments);
+
+    if (this.isNot) {
+      result = !result;
+    }
+
+    if (this.reportWasCalled_) return result;
+
+    var message;
+    if (!result) {
+      if (this.message) {
+        message = this.message.apply(this, arguments);
+        if (jasmine.isArray_(message)) {
+          message = message[this.isNot ? 1 : 0];
+        }
+      } else {
+        var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
+        message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate;
+        if (matcherArgs.length > 0) {
+          for (var i = 0; i < matcherArgs.length; i++) {
+            if (i > 0) message += ",";
+            message += " " + jasmine.pp(matcherArgs[i]);
+          }
+        }
+        message += ".";
+      }
+    }
+    var expectationResult = new jasmine.ExpectationResult({
+      matcherName: matcherName,
+      passed: result,
+      expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0],
+      actual: this.actual,
+      message: message
+    });
+    this.spec.addMatcherResult(expectationResult);
+    return jasmine.undefined;
+  };
+};
+
+
+
+
+/**
+ * toBe: compares the actual to the expected using ===
+ * @param expected
+ */
+jasmine.Matchers.prototype.toBe = function(expected) {
+  return this.actual === expected;
+};
+
+/**
+ * toNotBe: compares the actual to the expected using !==
+ * @param expected
+ * @deprecated as of 1.0. Use not.toBe() instead.
+ */
+jasmine.Matchers.prototype.toNotBe = function(expected) {
+  return this.actual !== expected;
+};
+
+/**
+ * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.
+ *
+ * @param expected
+ */
+jasmine.Matchers.prototype.toEqual = function(expected) {
+  return this.env.equals_(this.actual, expected);
+};
+
+/**
+ * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual
+ * @param expected
+ * @deprecated as of 1.0. Use not.toNotEqual() instead.
+ */
+jasmine.Matchers.prototype.toNotEqual = function(expected) {
+  return !this.env.equals_(this.actual, expected);
+};
+
+/**
+ * Matcher that compares the actual to the expected using a regular expression.  Constructs a RegExp, so takes
+ * a pattern or a String.
+ *
+ * @param expected
+ */
+jasmine.Matchers.prototype.toMatch = function(expected) {
+  return new RegExp(expected).test(this.actual);
+};
+
+/**
+ * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch
+ * @param expected
+ * @deprecated as of 1.0. Use not.toMatch() instead.
+ */
+jasmine.Matchers.prototype.toNotMatch = function(expected) {
+  return !(new RegExp(expected).test(this.actual));
+};
+
+/**
+ * Matcher that compares the actual to jasmine.undefined.
+ */
+jasmine.Matchers.prototype.toBeDefined = function() {
+  return (this.actual !== jasmine.undefined);
+};
+
+/**
+ * Matcher that compares the actual to jasmine.undefined.
+ */
+jasmine.Matchers.prototype.toBeUndefined = function() {
+  return (this.actual === jasmine.undefined);
+};
+
+/**
+ * Matcher that compares the actual to null.
+ */
+jasmine.Matchers.prototype.toBeNull = function() {
+  return (this.actual === null);
+};
+
+/**
+ * Matcher that boolean not-nots the actual.
+ */
+jasmine.Matchers.prototype.toBeTruthy = function() {
+  return !!this.actual;
+};
+
+
+/**
+ * Matcher that boolean nots the actual.
+ */
+jasmine.Matchers.prototype.toBeFalsy = function() {
+  return !this.actual;
+};
+
+
+/**
+ * Matcher that checks to see if the actual, a Jasmine spy, was called.
+ */
+jasmine.Matchers.prototype.toHaveBeenCalled = function() {
+  if (arguments.length > 0) {
+    throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
+  }
+
+  if (!jasmine.isSpy(this.actual)) {
+    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
+  }
+
+  this.message = function() {
+    return [
+      "Expected spy " + this.actual.identity + " to have been called.",
+      "Expected spy " + this.actual.identity + " not to have been called."
+    ];
+  };
+
+  return this.actual.wasCalled;
+};
+
+/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */
+jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled;
+
+/**
+ * Matcher that checks to see if the actual, a Jasmine spy, was not called.
+ *
+ * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead
+ */
+jasmine.Matchers.prototype.wasNotCalled = function() {
+  if (arguments.length > 0) {
+    throw new Error('wasNotCalled does not take arguments');
+  }
+
+  if (!jasmine.isSpy(this.actual)) {
+    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
+  }
+
+  this.message = function() {
+    return [
+      "Expected spy " + this.actual.identity + " to not have been called.",
+      "Expected spy " + this.actual.identity + " to have been called."
+    ];
+  };
+
+  return !this.actual.wasCalled;
+};
+
+/**
+ * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters.
+ *
+ * @example
+ *
+ */
+jasmine.Matchers.prototype.toHaveBeenCalledWith = function() {
+  var expectedArgs = jasmine.util.argsToArray(arguments);
+  if (!jasmine.isSpy(this.actual)) {
+    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
+  }
+  this.message = function() {
+    if (this.actual.callCount === 0) {
+      // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw]
+      return [
+        "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.",
+        "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was."
+      ];
+    } else {
+      return [
+        "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall),
+        "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall)
+      ];
+    }
+  };
+
+  return this.env.contains_(this.actual.argsForCall, expectedArgs);
+};
+
+/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */
+jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith;
+
+/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */
+jasmine.Matchers.prototype.wasNotCalledWith = function() {
+  var expectedArgs = jasmine.util.argsToArray(arguments);
+  if (!jasmine.isSpy(this.actual)) {
+    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
+  }
+
+  this.message = function() {
+    return [
+      "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was",
+      "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was"
+    ];
+  };
+
+  return !this.env.contains_(this.actual.argsForCall, expectedArgs);
+};
+
+/**
+ * Matcher that checks that the expected item is an element in the actual Array.
+ *
+ * @param {Object} expected
+ */
+jasmine.Matchers.prototype.toContain = function(expected) {
+  return this.env.contains_(this.actual, expected);
+};
+
+/**
+ * Matcher that checks that the expected item is NOT an element in the actual Array.
+ *
+ * @param {Object} expected
+ * @deprecated as of 1.0. Use not.toNotContain() instead.
+ */
+jasmine.Matchers.prototype.toNotContain = function(expected) {
+  return !this.env.contains_(this.actual, expected);
+};
+
+jasmine.Matchers.prototype.toBeLessThan = function(expected) {
+  return this.actual < expected;
+};
+
+jasmine.Matchers.prototype.toBeGreaterThan = function(expected) {
+  return this.actual > expected;
+};
+
+/**
+ * Matcher that checks that the expected item is equal to the actual item
+ * up to a given level of decimal precision (default 2).
+ *
+ * @param {Number} expected
+ * @param {Number} precision
+ */
+jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) {
+  if (!(precision === 0)) {
+    precision = precision || 2;
+  }
+  var multiplier = Math.pow(10, precision);
+  var actual = Math.round(this.actual * multiplier);
+  expected = Math.round(expected * multiplier);
+  return expected == actual;
+};
+
+/**
+ * Matcher that checks that the expected exception was thrown by the actual.
+ *
+ * @param {String} expected
+ */
+jasmine.Matchers.prototype.toThrow = function(expected) {
+  var result = false;
+  var exception;
+  if (typeof this.actual != 'function') {
+    throw new Error('Actual is not a function');
+  }
+  try {
+    this.actual();
+  } catch (e) {
+    exception = e;
+  }
+  if (exception) {
+    result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));
+  }
+
+  var not = this.isNot ? "not " : "";
+
+  this.message = function() {
+    if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {
+      return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' ');
+    } else {
+      return "Expected function to throw an exception.";
+    }
+  };
+
+  return result;
+};
+
+jasmine.Matchers.Any = function(expectedClass) {
+  this.expectedClass = expectedClass;
+};
+
+jasmine.Matchers.Any.prototype.matches = function(other) {
+  if (this.expectedClass == String) {
+    return typeof other == 'string' || other instanceof String;
+  }
+
+  if (this.expectedClass == Number) {
+    return typeof other == 'number' || other instanceof Number;
+  }
+
+  if (this.expectedClass == Function) {
+    return typeof other == 'function' || other instanceof Function;
+  }
+
+  if (this.expectedClass == Object) {
+    return typeof other == 'object';
+  }
+
+  return other instanceof this.expectedClass;
+};
+
+jasmine.Matchers.Any.prototype.toString = function() {
+  return '<jasmine.any(' + this.expectedClass + ')>';
+};
+
+/**
+ * @constructor
+ */
+jasmine.MultiReporter = function() {
+  this.subReporters_ = [];
+};
+jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter);
+
+jasmine.MultiReporter.prototype.addReporter = function(reporter) {
+  this.subReporters_.push(reporter);
+};
+
+(function() {
+  var functionNames = [
+    "reportRunnerStarting",
+    "reportRunnerResults",
+    "reportSuiteResults",
+    "reportSpecStarting",
+    "reportSpecResults",
+    "log"
+  ];
+  for (var i = 0; i < functionNames.length; i++) {
+    var functionName = functionNames[i];
+    jasmine.MultiReporter.prototype[functionName] = (function(functionName) {
+      return function() {
+        for (var j = 0; j < this.subReporters_.length; j++) {
+          var subReporter = this.subReporters_[j];
+          if (subReporter[functionName]) {
+            subReporter[functionName].apply(subReporter, arguments);
+          }
+        }
+      };
+    })(functionName);
+  }
+})();
+/**
+ * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults
+ *
+ * @constructor
+ */
+jasmine.NestedResults = function() {
+  /**
+   * The total count of results
+   */
+  this.totalCount = 0;
+  /**
+   * Number of passed results
+   */
+  this.passedCount = 0;
+  /**
+   * Number of failed results
+   */
+  this.failedCount = 0;
+  /**
+   * Was this suite/spec skipped?
+   */
+  this.skipped = false;
+  /**
+   * @ignore
+   */
+  this.items_ = [];
+};
+
+/**
+ * Roll up the result counts.
+ *
+ * @param result
+ */
+jasmine.NestedResults.prototype.rollupCounts = function(result) {
+  this.totalCount += result.totalCount;
+  this.passedCount += result.passedCount;
+  this.failedCount += result.failedCount;
+};
+
+/**
+ * Adds a log message.
+ * @param values Array of message parts which will be concatenated later.
+ */
+jasmine.NestedResults.prototype.log = function(values) {
+  this.items_.push(new jasmine.MessageResult(values));
+};
+
+/**
+ * Getter for the results: message & results.
+ */
+jasmine.NestedResults.prototype.getItems = function() {
+  return this.items_;
+};
+
+/**
+ * Adds a result, tracking counts (total, passed, & failed)
+ * @param {jasmine.ExpectationResult|jasmine.NestedResults} result
+ */
+jasmine.NestedResults.prototype.addResult = function(result) {
+  if (result.type != 'log') {
+    if (result.items_) {
+      this.rollupCounts(result);
+    } else {
+      this.totalCount++;
+      if (result.passed()) {
+        this.passedCount++;
+      } else {
+        this.failedCount++;
+      }
+    }
+  }
+  this.items_.push(result);
+};
+
+/**
+ * @returns {Boolean} True if <b>everything</b> below passed
+ */
+jasmine.NestedResults.prototype.passed = function() {
+  return this.passedCount === this.totalCount;
+};
+/**
+ * Base class for pretty printing for expectation results.
+ */
+jasmine.PrettyPrinter = function() {
+  this.ppNestLevel_ = 0;
+};
+
+/**
+ * Formats a value in a nice, human-readable string.
+ *
+ * @param value
+ */
+jasmine.PrettyPrinter.prototype.format = function(value) {
+  if (this.ppNestLevel_ > 40) {
+    throw new Error('jasmine.PrettyPrinter: format() nested too deeply!');
+  }
+
+  this.ppNestLevel_++;
+  try {
+    if (value === jasmine.undefined) {
+      this.emitScalar('undefined');
+    } else if (value === null) {
+      this.emitScalar('null');
+    } else if (value === jasmine.getGlobal()) {
+      this.emitScalar('<global>');
+    } else if (value instanceof jasmine.Matchers.Any) {
+      this.emitScalar(value.toString());
+    } else if (typeof value === 'string') {
+      this.emitString(value);
+    } else if (jasmine.isSpy(value)) {
+      this.emitScalar("spy on " + value.identity);
+    } else if (value instanceof RegExp) {
+      this.emitScalar(value.toString());
+    } else if (typeof value === 'function') {
+      this.emitScalar('Function');
+    } else if (typeof value.nodeType === 'number') {
+      this.emitScalar('HTMLNode');
+    } else if (value instanceof Date) {
+      this.emitScalar('Date(' + value + ')');
+    } else if (value.__Jasmine_been_here_before__) {
+      this.emitScalar('<circular reference: ' + (jasmine.isArray_(value) ? 'Array' : 'Object') + '>');
+    } else if (jasmine.isArray_(value) || typeof value == 'object') {
+      value.__Jasmine_been_here_before__ = true;
+      if (jasmine.isArray_(value)) {
+        this.emitArray(value);
+      } else {
+        this.emitObject(value);
+      }
+      delete value.__Jasmine_been_here_before__;
+    } else {
+      this.emitScalar(value.toString());
+    }
+  } finally {
+    this.ppNestLevel_--;
+  }
+};
+
+jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) {
+  for (var property in obj) {
+    if (property == '__Jasmine_been_here_before__') continue;
+    fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && 
+                                         obj.__lookupGetter__(property) !== null) : false);
+  }
+};
+
+jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_;
+jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_;
+jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_;
+jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_;
+
+jasmine.StringPrettyPrinter = function() {
+  jasmine.PrettyPrinter.call(this);
+
+  this.string = '';
+};
+jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter);
+
+jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) {
+  this.append(value);
+};
+
+jasmine.StringPrettyPrinter.prototype.emitString = function(value) {
+  this.append("'" + value + "'");
+};
+
+jasmine.StringPrettyPrinter.prototype.emitArray = function(array) {
+  this.append('[ ');
+  for (var i = 0; i < array.length; i++) {
+    if (i > 0) {
+      this.append(', ');
+    }
+    this.format(array[i]);
+  }
+  this.append(' ]');
+};
+
+jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) {
+  var self = this;
+  this.append('{ ');
+  var first = true;
+
+  this.iterateObject(obj, function(property, isGetter) {
+    if (first) {
+      first = false;
+    } else {
+      self.append(', ');
+    }
+
+    self.append(property);
+    self.append(' : ');
+    if (isGetter) {
+      self.append('<getter>');
+    } else {
+      self.format(obj[property]);
+    }
+  });
+
+  this.append(' }');
+};
+
+jasmine.StringPrettyPrinter.prototype.append = function(value) {
+  this.string += value;
+};
+jasmine.Queue = function(env) {
+  this.env = env;
+  this.blocks = [];
+  this.running = false;
+  this.index = 0;
+  this.offset = 0;
+  this.abort = false;
+};
+
+jasmine.Queue.prototype.addBefore = function(block) {
+  this.blocks.unshift(block);
+};
+
+jasmine.Queue.prototype.add = function(block) {
+  this.blocks.push(block);
+};
+
+jasmine.Queue.prototype.insertNext = function(block) {
+  this.blocks.splice((this.index + this.offset + 1), 0, block);
+  this.offset++;
+};
+
+jasmine.Queue.prototype.start = function(onComplete) {
+  this.running = true;
+  this.onComplete = onComplete;
+  this.next_();
+};
+
+jasmine.Queue.prototype.isRunning = function() {
+  return this.running;
+};
+
+jasmine.Queue.LOOP_DONT_RECURSE = true;
+
+jasmine.Queue.prototype.next_ = function() {
+  var self = this;
+  var goAgain = true;
+
+  while (goAgain) {
+    goAgain = false;
+    
+    if (self.index < self.blocks.length && !this.abort) {
+      var calledSynchronously = true;
+      var completedSynchronously = false;
+
+      var onComplete = function () {
+        if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) {
+          completedSynchronously = true;
+          return;
+        }
+
+        if (self.blocks[self.index].abort) {
+          self.abort = true;
+        }
+
+        self.offset = 0;
+        self.index++;
+
+        var now = new Date().getTime();
+        if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) {
+          self.env.lastUpdate = now;
+          self.env.setTimeout(function() {
+            self.next_();
+          }, 0);
+        } else {
+          if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) {
+            goAgain = true;
+          } else {
+            self.next_();
+          }
+        }
+      };
+      self.blocks[self.index].execute(onComplete);
+
+      calledSynchronously = false;
+      if (completedSynchronously) {
+        onComplete();
+      }
+      
+    } else {
+      self.running = false;
+      if (self.onComplete) {
+        self.onComplete();
+      }
+    }
+  }
+};
+
+jasmine.Queue.prototype.results = function() {
+  var results = new jasmine.NestedResults();
+  for (var i = 0; i < this.blocks.length; i++) {
+    if (this.blocks[i].results) {
+      results.addResult(this.blocks[i].results());
+    }
+  }
+  return results;
+};
+
+
+/**
+ * Runner
+ *
+ * @constructor
+ * @param {jasmine.Env} env
+ */
+jasmine.Runner = function(env) {
+  var self = this;
+  self.env = env;
+  self.queue = new jasmine.Queue(env);
+  self.before_ = [];
+  self.after_ = [];
+  self.suites_ = [];
+};
+
+jasmine.Runner.prototype.execute = function() {
+  var self = this;
+  if (self.env.reporter.reportRunnerStarting) {
+    self.env.reporter.reportRunnerStarting(this);
+  }
+  self.queue.start(function () {
+    self.finishCallback();
+  });
+};
+
+jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) {
+  beforeEachFunction.typeName = 'beforeEach';
+  this.before_.splice(0,0,beforeEachFunction);
+};
+
+jasmine.Runner.prototype.afterEach = function(afterEachFunction) {
+  afterEachFunction.typeName = 'afterEach';
+  this.after_.splice(0,0,afterEachFunction);
+};
+
+
+jasmine.Runner.prototype.finishCallback = function() {
+  this.env.reporter.reportRunnerResults(this);
+};
+
+jasmine.Runner.prototype.addSuite = function(suite) {
+  this.suites_.push(suite);
+};
+
+jasmine.Runner.prototype.add = function(block) {
+  if (block instanceof jasmine.Suite) {
+    this.addSuite(block);
+  }
+  this.queue.add(block);
+};
+
+jasmine.Runner.prototype.specs = function () {
+  var suites = this.suites();
+  var specs = [];
+  for (var i = 0; i < suites.length; i++) {
+    specs = specs.concat(suites[i].specs());
+  }
+  return specs;
+};
+
+jasmine.Runner.prototype.suites = function() {
+  return this.suites_;
+};
+
+jasmine.Runner.prototype.topLevelSuites = function() {
+  var topLevelSuites = [];
+  for (var i = 0; i < this.suites_.length; i++) {
+    if (!this.suites_[i].parentSuite) {
+      topLevelSuites.push(this.suites_[i]);
+    }
+  }
+  return topLevelSuites;
+};
+
+jasmine.Runner.prototype.results = function() {
+  return this.queue.results();
+};
+/**
+ * Internal representation of a Jasmine specification, or test.
+ *
+ * @constructor
+ * @param {jasmine.Env} env
+ * @param {jasmine.Suite} suite
+ * @param {String} description
+ */
+jasmine.Spec = function(env, suite, description) {
+  if (!env) {
+    throw new Error('jasmine.Env() required');
+  }
+  if (!suite) {
+    throw new Error('jasmine.Suite() required');
+  }
+  var spec = this;
+  spec.id = env.nextSpecId ? env.nextSpecId() : null;
+  spec.env = env;
+  spec.suite = suite;
+  spec.description = description;
+  spec.queue = new jasmine.Queue(env);
+
+  spec.afterCallbacks = [];
+  spec.spies_ = [];
+
+  spec.results_ = new jasmine.NestedResults();
+  spec.results_.description = description;
+  spec.matchersClass = null;
+};
+
+jasmine.Spec.prototype.getFullName = function() {
+  return this.suite.getFullName() + ' ' + this.description + '.';
+};
+
+
+jasmine.Spec.prototype.results = function() {
+  return this.results_;
+};
+
+/**
+ * All parameters are pretty-printed and concatenated together, then written to the spec's output.
+ *
+ * Be careful not to leave calls to <code>jasmine.log</code> in production code.
+ */
+jasmine.Spec.prototype.log = function() {
+  return this.results_.log(arguments);
+};
+
+jasmine.Spec.prototype.runs = function (func) {
+  var block = new jasmine.Block(this.env, func, this);
+  this.addToQueue(block);
+  return this;
+};
+
+jasmine.Spec.prototype.addToQueue = function (block) {
+  if (this.queue.isRunning()) {
+    this.queue.insertNext(block);
+  } else {
+    this.queue.add(block);
+  }
+};
+
+/**
+ * @param {jasmine.ExpectationResult} result
+ */
+jasmine.Spec.prototype.addMatcherResult = function(result) {
+  this.results_.addResult(result);
+};
+
+jasmine.Spec.prototype.expect = function(actual) {
+  var positive = new (this.getMatchersClass_())(this.env, actual, this);
+  positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);
+  return positive;
+};
+
+/**
+ * Waits a fixed time period before moving to the next block.
+ *
+ * @deprecated Use waitsFor() instead
+ * @param {Number} timeout milliseconds to wait
+ */
+jasmine.Spec.prototype.waits = function(timeout) {
+  var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);
+  this.addToQueue(waitsFunc);
+  return this;
+};
+
+/**
+ * Waits for the latchFunction to return true before proceeding to the next block.
+ *
+ * @param {Function} latchFunction
+ * @param {String} optional_timeoutMessage
+ * @param {Number} optional_timeout
+ */
+jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
+  var latchFunction_ = null;
+  var optional_timeoutMessage_ = null;
+  var optional_timeout_ = null;
+
+  for (var i = 0; i < arguments.length; i++) {
+    var arg = arguments[i];
+    switch (typeof arg) {
+      case 'function':
+        latchFunction_ = arg;
+        break;
+      case 'string':
+        optional_timeoutMessage_ = arg;
+        break;
+      case 'number':
+        optional_timeout_ = arg;
+        break;
+    }
+  }
+
+  var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this);
+  this.addToQueue(waitsForFunc);
+  return this;
+};
+
+jasmine.Spec.prototype.fail = function (e) {
+  var expectationResult = new jasmine.ExpectationResult({
+    passed: false,
+    message: e ? jasmine.util.formatException(e) : 'Exception',
+    trace: { stack: e.stack }
+  });
+  this.results_.addResult(expectationResult);
+};
+
+jasmine.Spec.prototype.getMatchersClass_ = function() {
+  return this.matchersClass || this.env.matchersClass;
+};
+
+jasmine.Spec.prototype.addMatchers = function(matchersPrototype) {
+  var parent = this.getMatchersClass_();
+  var newMatchersClass = function() {
+    parent.apply(this, arguments);
+  };
+  jasmine.util.inherit(newMatchersClass, parent);
+  jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);
+  this.matchersClass = newMatchersClass;
+};
+
+jasmine.Spec.prototype.finishCallback = function() {
+  this.env.reporter.reportSpecResults(this);
+};
+
+jasmine.Spec.prototype.finish = function(onComplete) {
+  this.removeAllSpies();
+  this.finishCallback();
+  if (onComplete) {
+    onComplete();
+  }
+};
+
+jasmine.Spec.prototype.after = function(doAfter) {
+  if (this.queue.isRunning()) {
+    this.queue.add(new jasmine.Block(this.env, doAfter, this));
+  } else {
+    this.afterCallbacks.unshift(doAfter);
+  }
+};
+
+jasmine.Spec.prototype.execute = function(onComplete) {
+  var spec = this;
+  if (!spec.env.specFilter(spec)) {
+    spec.results_.skipped = true;
+    spec.finish(onComplete);
+    return;
+  }
+
+  this.env.reporter.reportSpecStarting(this);
+
+  spec.env.currentSpec = spec;
+
+  spec.addBeforesAndAftersToQueue();
+
+  spec.queue.start(function () {
+    spec.finish(onComplete);
+  });
+};
+
+jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() {
+  var runner = this.env.currentRunner();
+  var i;
+
+  for (var suite = this.suite; suite; suite = suite.parentSuite) {
+    for (i = 0; i < suite.before_.length; i++) {
+      this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this));
+    }
+  }
+  for (i = 0; i < runner.before_.length; i++) {
+    this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this));
+  }
+  for (i = 0; i < this.afterCallbacks.length; i++) {
+    this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this));
+  }
+  for (suite = this.suite; suite; suite = suite.parentSuite) {
+    for (i = 0; i < suite.after_.length; i++) {
+      this.queue.add(new jasmine.Block(this.env, suite.after_[i], this));
+    }
+  }
+  for (i = 0; i < runner.after_.length; i++) {
+    this.queue.add(new jasmine.Block(this.env, runner.after_[i], this));
+  }
+};
+
+jasmine.Spec.prototype.explodes = function() {
+  throw 'explodes function should not have been called';
+};
+
+jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) {
+  if (obj == jasmine.undefined) {
+    throw "spyOn could not find an object to spy upon for " + methodName + "()";
+  }
+
+  if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) {
+    throw methodName + '() method does not exist';
+  }
+
+  if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {
+    throw new Error(methodName + ' has already been spied upon');
+  }
+
+  var spyObj = jasmine.createSpy(methodName);
+
+  this.spies_.push(spyObj);
+  spyObj.baseObj = obj;
+  spyObj.methodName = methodName;
+  spyObj.originalValue = obj[methodName];
+
+  obj[methodName] = spyObj;
+
+  return spyObj;
+};
+
+jasmine.Spec.prototype.removeAllSpies = function() {
+  for (var i = 0; i < this.spies_.length; i++) {
+    var spy = this.spies_[i];
+    spy.baseObj[spy.methodName] = spy.originalValue;
+  }
+  this.spies_ = [];
+};
+
+/**
+ * Internal representation of a Jasmine suite.
+ *
+ * @constructor
+ * @param {jasmine.Env} env
+ * @param {String} description
+ * @param {Function} specDefinitions
+ * @param {jasmine.Suite} parentSuite
+ */
+jasmine.Suite = function(env, description, specDefinitions, parentSuite) {
+  var self = this;
+  self.id = env.nextSuiteId ? env.nextSuiteId() : null;
+  self.description = description;
+  self.queue = new jasmine.Queue(env);
+  self.parentSuite = parentSuite;
+  self.env = env;
+  self.before_ = [];
+  self.after_ = [];
+  self.children_ = [];
+  self.suites_ = [];
+  self.specs_ = [];
+};
+
+jasmine.Suite.prototype.getFullName = function() {
+  var fullName = this.description;
+  for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
+    fullName = parentSuite.description + ' ' + fullName;
+  }
+  return fullName;
+};
+
+jasmine.Suite.prototype.finish = function(onComplete) {
+  this.env.reporter.reportSuiteResults(this);
+  this.finished = true;
+  if (typeof(onComplete) == 'function') {
+    onComplete();
+  }
+};
+
+jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) {
+  beforeEachFunction.typeName = 'beforeEach';
+  this.before_.unshift(beforeEachFunction);
+};
+
+jasmine.Suite.prototype.afterEach = function(afterEachFunction) {
+  afterEachFunction.typeName = 'afterEach';
+  this.after_.unshift(afterEachFunction);
+};
+
+jasmine.Suite.prototype.results = function() {
+  return this.queue.results();
+};
+
+jasmine.Suite.prototype.add = function(suiteOrSpec) {
+  this.children_.push(suiteOrSpec);
+  if (suiteOrSpec instanceof jasmine.Suite) {
+    this.suites_.push(suiteOrSpec);
+    this.env.currentRunner().addSuite(suiteOrSpec);
+  } else {
+    this.specs_.push(suiteOrSpec);
+  }
+  this.queue.add(suiteOrSpec);
+};
+
+jasmine.Suite.prototype.specs = function() {
+  return this.specs_;
+};
+
+jasmine.Suite.prototype.suites = function() {
+  return this.suites_;
+};
+
+jasmine.Suite.prototype.children = function() {
+  return this.children_;
+};
+
+jasmine.Suite.prototype.execute = function(onComplete) {
+  var self = this;
+  this.queue.start(function () {
+    self.finish(onComplete);
+  });
+};
+jasmine.WaitsBlock = function(env, timeout, spec) {
+  this.timeout = timeout;
+  jasmine.Block.call(this, env, null, spec);
+};
+
+jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);
+
+jasmine.WaitsBlock.prototype.execute = function (onComplete) {
+  if (jasmine.VERBOSE) {
+    this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');
+  }
+  this.env.setTimeout(function () {
+    onComplete();
+  }, this.timeout);
+};
+/**
+ * A block which waits for some condition to become true, with timeout.
+ *
+ * @constructor
+ * @extends jasmine.Block
+ * @param {jasmine.Env} env The Jasmine environment.
+ * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.
+ * @param {Function} latchFunction A function which returns true when the desired condition has been met.
+ * @param {String} message The message to display if the desired condition hasn't been met within the given time period.
+ * @param {jasmine.Spec} spec The Jasmine spec.
+ */
+jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {
+  this.timeout = timeout || env.defaultTimeoutInterval;
+  this.latchFunction = latchFunction;
+  this.message = message;
+  this.totalTimeSpentWaitingForLatch = 0;
+  jasmine.Block.call(this, env, null, spec);
+};
+jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
+
+jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;
+
+jasmine.WaitsForBlock.prototype.execute = function(onComplete) {
+  if (jasmine.VERBOSE) {
+    this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
+  }
+  var latchFunctionResult;
+  try {
+    latchFunctionResult = this.latchFunction.apply(this.spec);
+  } catch (e) {
+    this.spec.fail(e);
+    onComplete();
+    return;
+  }
+
+  if (latchFunctionResult) {
+    onComplete();
+  } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) {
+    var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen');
+    this.spec.fail({
+      name: 'timeout',
+      message: message
+    });
+
+    this.abort = true;
+    onComplete();
+  } else {
+    this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;
+    var self = this;
+    this.env.setTimeout(function() {
+      self.execute(onComplete);
+    }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);
+  }
+};
+// Mock setTimeout, clearTimeout
+// Contributed by Pivotal Computer Systems, www.pivotalsf.com
+
+jasmine.FakeTimer = function() {
+  this.reset();
+
+  var self = this;
+  self.setTimeout = function(funcToCall, millis) {
+    self.timeoutsMade++;
+    self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false);
+    return self.timeoutsMade;
+  };
+
+  self.setInterval = function(funcToCall, millis) {
+    self.timeoutsMade++;
+    self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true);
+    return self.timeoutsMade;
+  };
+
+  self.clearTimeout = function(timeoutKey) {
+    self.scheduledFunctions[timeoutKey] = jasmine.undefined;
+  };
+
+  self.clearInterval = function(timeoutKey) {
+    self.scheduledFunctions[timeoutKey] = jasmine.undefined;
+  };
+
+};
+
+jasmine.FakeTimer.prototype.reset = function() {
+  this.timeoutsMade = 0;
+  this.scheduledFunctions = {};
+  this.nowMillis = 0;
+};
+
+jasmine.FakeTimer.prototype.tick = function(millis) {
+  var oldMillis = this.nowMillis;
+  var newMillis = oldMillis + millis;
+  this.runFunctionsWithinRange(oldMillis, newMillis);
+  this.nowMillis = newMillis;
+};
+
+jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) {
+  var scheduledFunc;
+  var funcsToRun = [];
+  for (var timeoutKey in this.scheduledFunctions) {
+    scheduledFunc = this.scheduledFunctions[timeoutKey];
+    if (scheduledFunc != jasmine.undefined &&
+        scheduledFunc.runAtMillis >= oldMillis &&
+        scheduledFunc.runAtMillis <= nowMillis) {
+      funcsToRun.push(scheduledFunc);
+      this.scheduledFunctions[timeoutKey] = jasmine.undefined;
+    }
+  }
+
+  if (funcsToRun.length > 0) {
+    funcsToRun.sort(function(a, b) {
+      return a.runAtMillis - b.runAtMillis;
+    });
+    for (var i = 0; i < funcsToRun.length; ++i) {
+      try {
+        var funcToRun = funcsToRun[i];
+        this.nowMillis = funcToRun.runAtMillis;
+        funcToRun.funcToCall();
+        if (funcToRun.recurring) {
+          this.scheduleFunction(funcToRun.timeoutKey,
+              funcToRun.funcToCall,
+              funcToRun.millis,
+              true);
+        }
+      } catch(e) {
+      }
+    }
+    this.runFunctionsWithinRange(oldMillis, nowMillis);
+  }
+};
+
+jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) {
+  this.scheduledFunctions[timeoutKey] = {
+    runAtMillis: this.nowMillis + millis,
+    funcToCall: funcToCall,
+    recurring: recurring,
+    timeoutKey: timeoutKey,
+    millis: millis
+  };
+};
+
+/**
+ * @namespace
+ */
+jasmine.Clock = {
+  defaultFakeTimer: new jasmine.FakeTimer(),
+
+  reset: function() {
+    jasmine.Clock.assertInstalled();
+    jasmine.Clock.defaultFakeTimer.reset();
+  },
+
+  tick: function(millis) {
+    jasmine.Clock.assertInstalled();
+    jasmine.Clock.defaultFakeTimer.tick(millis);
+  },
+
+  runFunctionsWithinRange: function(oldMillis, nowMillis) {
+    jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis);
+  },
+
+  scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {
+    jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring);
+  },
+
+  useMock: function() {
+    if (!jasmine.Clock.isInstalled()) {
+      var spec = jasmine.getEnv().currentSpec;
+      spec.after(jasmine.Clock.uninstallMock);
+
+      jasmine.Clock.installMock();
+    }
+  },
+
+  installMock: function() {
+    jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer;
+  },
+
+  uninstallMock: function() {
+    jasmine.Clock.assertInstalled();
+    jasmine.Clock.installed = jasmine.Clock.real;
+  },
+
+  real: {
+    setTimeout: jasmine.getGlobal().setTimeout,
+    clearTimeout: jasmine.getGlobal().clearTimeout,
+    setInterval: jasmine.getGlobal().setInterval,
+    clearInterval: jasmine.getGlobal().clearInterval
+  },
+
+  assertInstalled: function() {
+    if (!jasmine.Clock.isInstalled()) {
+      throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()");
+    }
+  },
+
+  isInstalled: function() {
+    return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer;
+  },
+
+  installed: null
+};
+jasmine.Clock.installed = jasmine.Clock.real;
+
+//else for IE support
+jasmine.getGlobal().setTimeout = function(funcToCall, millis) {
+  if (jasmine.Clock.installed.setTimeout.apply) {
+    return jasmine.Clock.installed.setTimeout.apply(this, arguments);
+  } else {
+    return jasmine.Clock.installed.setTimeout(funcToCall, millis);
+  }
+};
+
+jasmine.getGlobal().setInterval = function(funcToCall, millis) {
+  if (jasmine.Clock.installed.setInterval.apply) {
+    return jasmine.Clock.installed.setInterval.apply(this, arguments);
+  } else {
+    return jasmine.Clock.installed.setInterval(funcToCall, millis);
+  }
+};
+
+jasmine.getGlobal().clearTimeout = function(timeoutKey) {
+  if (jasmine.Clock.installed.clearTimeout.apply) {
+    return jasmine.Clock.installed.clearTimeout.apply(this, arguments);
+  } else {
+    return jasmine.Clock.installed.clearTimeout(timeoutKey);
+  }
+};
+
+jasmine.getGlobal().clearInterval = function(timeoutKey) {
+  if (jasmine.Clock.installed.clearTimeout.apply) {
+    return jasmine.Clock.installed.clearInterval.apply(this, arguments);
+  } else {
+    return jasmine.Clock.installed.clearInterval(timeoutKey);
+  }
+};
+
+jasmine.version_= {
+  "major": 1,
+  "minor": 1,
+  "build": 0,
+  "revision": 1308618948,
+  "release_candidate": 1
+};

BIN
js/lib/es5-shim-2.0.2/tests/lib/jasmine_favicon.png


+ 478 - 0
js/lib/es5-shim-2.0.2/tests/lib/json2.js

@@ -0,0 +1,478 @@
+/*
+    http://www.JSON.org/json2.js
+    2009-08-17
+
+    Public Domain.
+
+    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+
+    See http://www.JSON.org/js.html
+
+    This file creates a global JSON object containing two methods: stringify
+    and parse.
+
+        JSON.stringify(value, replacer, space)
+            value       any JavaScript value, usually an object or array.
+
+            replacer    an optional parameter that determines how object
+                        values are stringified for objects. It can be a
+                        function or an array of strings.
+
+            space       an optional parameter that specifies the indentation
+                        of nested structures. If it is omitted, the text will
+                        be packed without extra whitespace. If it is a number,
+                        it will specify the number of spaces to indent at each
+                        level. If it is a string (such as '\t' or '&nbsp;'),
+                        it contains the characters used to indent at each level.
+
+            This method produces a JSON text from a JavaScript value.
+
+            When an object value is found, if the object contains a toJSON
+            method, its toJSON method will be called and the result will be
+            stringified. A toJSON method does not serialize: it returns the
+            value represented by the name/value pair that should be serialized,
+            or undefined if nothing should be serialized. The toJSON method
+            will be passed the key associated with the value, and this will be
+            bound to the value
+
+            For example, this would serialize Dates as ISO strings.
+
+                Date.prototype.toJSON = function (key) {
+                    function f(n) {
+                        // Format integers to have at least two digits.
+                        return n < 10 ? '0' + n : n;
+                    }
+
+                    return this.getUTCFullYear()   + '-' +
+                         f(this.getUTCMonth() + 1) + '-' +
+                         f(this.getUTCDate())      + 'T' +
+                         f(this.getUTCHours())     + ':' +
+                         f(this.getUTCMinutes())   + ':' +
+                         f(this.getUTCSeconds())   + 'Z';
+                };
+
+            You can provide an optional replacer method. It will be passed the
+            key and value of each member, with this bound to the containing
+            object. The value that is returned from your method will be
+            serialized. If your method returns undefined, then the member will
+            be excluded from the serialization.
+
+            If the replacer parameter is an array of strings, then it will be
+            used to select the members to be serialized. It filters the results
+            such that only members with keys listed in the replacer array are
+            stringified.
+
+            Values that do not have JSON representations, such as undefined or
+            functions, will not be serialized. Such values in objects will be
+            dropped; in arrays they will be replaced with null. You can use
+            a replacer function to replace those with JSON values.
+            JSON.stringify(undefined) returns undefined.
+
+            The optional space parameter produces a stringification of the
+            value that is filled with line breaks and indentation to make it
+            easier to read.
+
+            If the space parameter is a non-empty string, then that string will
+            be used for indentation. If the space parameter is a number, then
+            the indentation will be that many spaces.
+
+            Example:
+
+            text = JSON.stringify(['e', {pluribus: 'unum'}]);
+            // text is '["e",{"pluribus":"unum"}]'
+
+
+            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
+            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
+
+            text = JSON.stringify([new Date()], function (key, value) {
+                return this[key] instanceof Date ?
+                    'Date(' + this[key] + ')' : value;
+            });
+            // text is '["Date(---current time---)"]'
+
+
+        JSON.parse(text, reviver)
+            This method parses a JSON text to produce an object or array.
+            It can throw a SyntaxError exception.
+
+            The optional reviver parameter is a function that can filter and
+            transform the results. It receives each of the keys and values,
+            and its return value is used instead of the original value.
+            If it returns what it received, then the structure is not modified.
+            If it returns undefined then the member is deleted.
+
+            Example:
+
+            // Parse the text. Values that look like ISO date strings will
+            // be converted to Date objects.
+
+            myData = JSON.parse(text, function (key, value) {
+                var a;
+                if (typeof value === 'string') {
+                    a =
+/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
+                    if (a) {
+                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+                            +a[5], +a[6]));
+                    }
+                }
+                return value;
+            });
+
+            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+                var d;
+                if (typeof value === 'string' &&
+                        value.slice(0, 5) === 'Date(' &&
+                        value.slice(-1) === ')') {
+                    d = new Date(value.slice(5, -1));
+                    if (d) {
+                        return d;
+                    }
+                }
+                return value;
+            });
+
+
+    This is a reference implementation. You are free to copy, modify, or
+    redistribute.
+
+    This code should be minified before deployment.
+    See http://javascript.crockford.com/jsmin.html
+
+    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+    NOT CONTROL.
+*/
+
+/*jslint evil: true */
+
+/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
+    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
+    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
+    lastIndex, length, parse, prototype, push, replace, slice, stringify,
+    test, toJSON, toString, valueOf
+*/
+
+"use strict";
+
+// Create a JSON object only if one does not already exist. We create the
+// methods in a closure to avoid creating global variables.
+
+if (!this.JSON) {
+    this.JSON = {};
+}
+
+(function () {
+
+    function f(n) {
+        // Format integers to have at least two digits.
+        return n < 10 ? '0' + n : n;
+    }
+
+    if (typeof Date.prototype.toJSON !== 'function') {
+
+        Date.prototype.toJSON = function (key) {
+
+            return isFinite(this.valueOf()) ?
+                   this.getUTCFullYear()   + '-' +
+                 f(this.getUTCMonth() + 1) + '-' +
+                 f(this.getUTCDate())      + 'T' +
+                 f(this.getUTCHours())     + ':' +
+                 f(this.getUTCMinutes())   + ':' +
+                 f(this.getUTCSeconds())   + 'Z' : null;
+        };
+
+        String.prototype.toJSON =
+        Number.prototype.toJSON =
+        Boolean.prototype.toJSON = function (key) {
+            return this.valueOf();
+        };
+    }
+
+    var 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 = {    // table of character substitutions
+            '\b': '\\b',
+            '\t': '\\t',
+            '\n': '\\n',
+            '\f': '\\f',
+            '\r': '\\r',
+            '"' : '\\"',
+            '\\': '\\\\'
+        },
+        rep;
+
+
+    function quote(string) {
+
+// If the string contains no control characters, no quote characters, and no
+// backslash characters, then we can safely slap some quotes around it.
+// Otherwise we must also replace the offending characters with safe escape
+// sequences.
+
+        escapable.lastIndex = 0;
+        return escapable.test(string) ?
+            '"' + string.replace(escapable, function (a) {
+                var c = meta[a];
+                return typeof c === 'string' ? c :
+                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+            }) + '"' :
+            '"' + string + '"';
+    }
+
+
+    function str(key, holder) {
+// Produce a string from holder[key].
+
+        var i,          // The loop counter.
+            k,          // The member key.
+            v,          // The member value.
+            length,
+            mind = gap,
+            partial,
+            value = holder[key];
+
+// If the value has a toJSON method, call it to obtain a replacement value.
+
+        if (value && typeof value === 'object' &&
+                typeof value.toJSON === 'function') {
+            value = value.toJSON(key);
+        }
+
+// If we were called with a replacer function, then call the replacer to
+// obtain a replacement value.
+
+        if (typeof rep === 'function') {
+            value = rep.call(holder, key, value);
+        }
+
+// What happens next depends on the value's type.
+
+        switch (typeof value) {
+        case 'string':
+            return quote(value);
+
+        case 'number':
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+            return isFinite(value) ? String(value) : 'null';
+
+        case 'boolean':
+        case 'null':
+
+// If the value is a boolean or null, convert it to a string. Note:
+// typeof null does not produce 'null'. The case is included here in
+// the remote chance that this gets fixed someday.
+
+            return String(value);
+
+// If the type is 'object', we might be dealing with an object or an array or
+// null.
+
+        case 'object':
+
+// Due to a specification blunder in ECMAScript, typeof null is 'object',
+// so watch out for that case.
+
+            if (!value) {
+                return 'null';
+            }
+
+// Make an array to hold the partial results of stringifying this object value.
+
+            gap += indent;
+            partial = [];
+
+// Is the value an array?
+
+            if (Object.prototype.toString.apply(value) === '[object Array]') {
+
+// The value is an array. Stringify every element. Use null as a placeholder
+// for non-JSON values.
+
+                length = value.length;
+                for (i = 0; i < length; i += 1) {
+                    partial[i] = str(i, value) || 'null';
+                }
+
+// Join all of the elements together, separated with commas, and wrap them in
+// brackets.
+
+                v = partial.length === 0 ? '[]' :
+                    gap ? '[\n' + gap +
+                            partial.join(',\n' + gap) + '\n' +
+                                mind + ']' :
+                          '[' + partial.join(',') + ']';
+                gap = mind;
+                return v;
+            }
+
+// If the replacer is an array, use it to select the members to be stringified.
+
+            if (rep && typeof rep === 'object') {
+                length = rep.length;
+                for (i = 0; i < length; i += 1) {
+                    k = rep[i];
+                    if (typeof k === 'string') {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                        }
+                    }
+                }
+            } else {
+
+// Otherwise, iterate through all of the keys in the object.
+
+                for (k in value) {
+                    if (Object.hasOwnProperty.call(value, k)) {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                        }
+                    }
+                }
+            }
+
+// Join all of the member texts together, separated with commas,
+// and wrap them in braces.
+
+            v = partial.length === 0 ? '{}' :
+                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
+                        mind + '}' : '{' + partial.join(',') + '}';
+            gap = mind;
+            return v;
+        }
+    }
+
+// If the JSON object does not yet have a stringify method, give it one.
+
+    if (typeof JSON.stringify !== 'function') {
+        JSON.stringify = function (value, replacer, space) {
+// The stringify method takes a value and an optional replacer, and an optional
+// space parameter, and returns a JSON text. The replacer can be a function
+// that can replace values, or an array of strings that will select the keys.
+// A default replacer method can be provided. Use of the space parameter can
+// produce text that is more easily readable.
+
+            var i;
+            gap = '';
+            indent = '';
+
+// If the space parameter is a number, make an indent string containing that
+// many spaces.
+
+            if (typeof space === 'number') {
+                for (i = 0; i < space; i += 1) {
+                    indent += ' ';
+                }
+
+// If the space parameter is a string, it will be used as the indent string.
+
+            } else if (typeof space === 'string') {
+                indent = space;
+            }
+
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
+
+            rep = replacer;
+            if (replacer && typeof replacer !== 'function' &&
+                    (typeof replacer !== 'object' ||
+                     typeof replacer.length !== 'number')) {
+                throw new Error('JSON.stringify');
+            }
+
+// Make a fake root object containing our value under the key of ''.
+// Return the result of stringifying the value.
+
+            return str('', {'': value});
+        };
+    }
+
+
+// If the JSON object does not yet have a parse method, give it one.
+
+    if (typeof JSON.parse !== 'function') {
+        JSON.parse = function (text, reviver) {
+
+// The parse method takes a text and an optional reviver function, and returns
+// a JavaScript value if the text is a valid JSON text.
+
+            var j;
+
+            function walk(holder, key) {
+
+// The walk method is used to recursively walk the resulting structure so
+// that modifications can be made.
+
+                var k, v, value = holder[key];
+                if (value && typeof value === 'object') {
+                    for (k in value) {
+                        if (Object.hasOwnProperty.call(value, k)) {
+                            v = walk(value, k);
+                            if (v !== undefined) {
+                                value[k] = v;
+                            } else {
+                                delete value[k];
+                            }
+                        }
+                    }
+                }
+                return reviver.call(holder, key, value);
+            }
+
+
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
+
+            cx.lastIndex = 0;
+            if (cx.test(text)) {
+                text = text.replace(cx, function (a) {
+                    return '\\u' +
+                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+                });
+            }
+
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
+// because they can cause invocation, and '=' because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
+
+// We split the second stage into 4 regexp operations in order to work around
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
+// replace all simple value tokens with ']' characters. Third, we delete all
+// open brackets that follow a colon or comma or that begin the text. Finally,
+// we look to see that the remaining characters are only whitespace or ']' or
+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+            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, ''))) {
+
+// In the third stage we use the eval function to compile the text into a
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+// in JavaScript: it can begin a block or an object literal. We wrap the text
+// in parens to eliminate the ambiguity.
+
+                j = eval('(' + text + ')');
+
+// In the optional fourth stage, we recursively walk the new structure, passing
+// each name/value pair to a reviver function for possible transformation.
+
+                return typeof reviver === 'function' ?
+                    walk({'': j}, '') : j;
+            }
+
+// If the text is not JSON parseable, then a SyntaxError is thrown.
+
+            throw new SyntaxError('JSON.parse');
+        };
+    }
+}());

+ 1138 - 0
js/lib/es5-shim-2.0.2/tests/spec/s-array.js

@@ -0,0 +1,1138 @@
+describe('Array', function() {
+    var testSubject;
+    beforeEach(function() {
+        testSubject = [2, 3, undefined, true, 'hej', null, false, 0];
+        delete testSubject[1];
+    });
+    function createArrayLikeFromArray(arr) {
+        var o = {};
+        Array.prototype.forEach.call(arr, function(e, i) {
+            o[i]=e;
+        });
+        o.length = arr.length;
+        return o;
+    };
+    
+    describe('forEach', function() {
+        "use strict";
+        var expected, actual;
+        
+        beforeEach(function() {
+            expected = {0:2, 2: undefined, 3:true, 4: 'hej', 5:null, 6:false, 7:0 };
+            actual = {};
+        });
+        it('should pass the right parameters', function() {
+            var callback = jasmine.createSpy('callback'),
+                array = ['1'];
+            array.forEach(callback);
+            expect(callback).toHaveBeenCalledWith('1', 0, array);
+        });
+        it('should not affect elements added to the array after it has begun', function() {
+            var arr = [1,2,3],
+                i = 0;
+            arr.forEach(function(a) {
+                i++;
+                arr.push(a+3);
+            });
+            expect(arr).toEqual([1,2,3,4,5,6]);
+            expect(i).toBe(3);
+        });
+        
+        it('should set the right context when given none', function() {
+            var context;
+            [1].forEach(function() {context = this;});
+            expect(context).toBe(function() {return this}.call());
+        });
+        it('should iterate all', function() {
+            testSubject.forEach(function(obj, index) {
+                actual[index] = obj;
+            });
+            expect(actual).toExactlyMatch(expected);
+        });
+        it('should iterate all using a context', function() {
+            var o = { a: actual };
+    
+            testSubject.forEach(function(obj, index) {
+                this.a[index] = obj;
+            }, o);
+            expect(actual).toExactlyMatch(expected);
+        });
+        
+        it('should iterate all in an array-like object', function() {
+            var ts = createArrayLikeFromArray(testSubject);
+            Array.prototype.forEach.call(ts, function(obj, index) {
+                actual[index] = obj;
+            });
+            expect(actual).toExactlyMatch(expected);
+        });
+        it('should iterate all in an array-like object using a context', function() {
+            var ts = createArrayLikeFromArray(testSubject),
+                o = { a: actual };
+            
+            Array.prototype.forEach.call(ts, function(obj, index) {
+                this.a[index] = obj;
+            }, o);
+            expect(actual).toExactlyMatch(expected);
+        });
+
+        describe('strings', function() {
+            var str = 'Hello, World!',
+                toString = Object.prototype.toString;
+            it('should iterate all in a string', function() {
+                actual = [];
+                Array.prototype.forEach.call(str, function(item, index) {
+                    actual[index] = item;
+                });
+                expect(actual).toExactlyMatch(str.split(''));
+            });
+            it('should iterate all in a string using a context', function() {
+                actual = [];
+                var o = { a: actual };
+                Array.prototype.forEach.call(str, function(item, index) {
+                    this.a[index] = item;
+                }, o);
+                expect(actual).toExactlyMatch(str.split(''));
+            });
+            it('should have String object for third argument of callback', function() {
+                Array.prototype.forEach.call(str, function(item, index, obj) {
+                    actual = obj;
+                });
+                expect(typeof actual).toBe("object");
+                expect(toString.call(actual)).toBe("[object String]");
+            });
+        });
+    });
+    describe('some', function() {
+        var actual, expected, numberOfRuns;
+        
+        beforeEach(function() {
+            expected = {0:2, 2: undefined, 3:true };
+            actual = {};
+            numberOfRuns = 0;
+        });
+        
+        it('should pass the correct values along to the callback', function() {
+            var callback = jasmine.createSpy('callback');
+            var array = ['1'];
+            array.some(callback);
+            expect(callback).toHaveBeenCalledWith('1', 0, array);
+        });
+        it('should not affect elements added to the array after it has begun', function() {
+            var arr = [1,2,3],
+                i = 0;
+            arr.some(function(a) {
+                i++;
+                arr.push(a+3);
+                return i > 3;
+            });
+            expect(arr).toEqual([1,2,3,4,5,6]);
+            expect(i).toBe(3);
+        });
+        it('should set the right context when given none', function() {
+            var context;
+            [1].some(function() {context = this;});
+            expect(context).toBe(function() {return this}.call());
+        });
+        
+        it('should return false if it runs to the end', function() {
+            actual = testSubject.some(function() {});
+            expect(actual).toBeFalsy();
+        });
+        it('should return true if it is stopped somewhere', function() {
+            actual = testSubject.some(function() { return true; });
+            expect(actual).toBeTruthy();
+        });
+        it('should return false if there are no elements', function() {
+            actual = [].some(function() { return true; });
+            expect(actual).toBeFalsy();
+        });
+        
+        it('should stop after 3 elements', function() {
+            testSubject.some(function(obj, index) {
+                actual[index] = obj;
+                numberOfRuns += 1;
+                if(numberOfRuns == 3) {
+                    return true;
+                }
+                return false;
+            });
+            expect(actual).toExactlyMatch(expected);
+        });
+        it('should stop after 3 elements using a context', function() {
+            var o = { a: actual };
+            testSubject.some(function(obj, index) {
+                this.a[index] = obj;
+                numberOfRuns += 1;
+                if(numberOfRuns == 3) {
+                    return true;
+                }
+                return false;
+            }, o);
+            expect(actual).toExactlyMatch(expected);
+        });
+    
+        it('should stop after 3 elements in an array-like object', function() {
+            var ts = createArrayLikeFromArray(testSubject);
+            Array.prototype.some.call(ts, function(obj, index) {
+                actual[index] = obj;
+                numberOfRuns += 1;
+                if(numberOfRuns == 3) {
+                    return true;
+                }
+                return false;
+            });
+            expect(actual).toExactlyMatch(expected);
+        });
+        it('should stop after 3 elements in an array-like object using a context', function() {
+            var ts = createArrayLikeFromArray(testSubject);
+            var o = { a: actual };
+            Array.prototype.some.call(ts, function(obj, index) {
+                this.a[index] = obj;
+                numberOfRuns += 1;
+                if(numberOfRuns == 3) {
+                    return true;
+                }
+                return false;
+            }, o);
+            expect(actual).toExactlyMatch(expected);
+        });
+    });
+    describe('every', function() {
+        var actual, expected, numberOfRuns;
+        
+        beforeEach(function() {
+            expected = {0:2, 2: undefined, 3:true };
+            actual = {};
+            numberOfRuns = 0;
+        });
+        
+        it('should pass the correct values along to the callback', function() {
+            var callback = jasmine.createSpy('callback');
+            var array = ['1'];
+            array.every(callback);
+            expect(callback).toHaveBeenCalledWith('1', 0, array);
+        });
+        it('should not affect elements added to the array after it has begun', function() {
+            var arr = [1,2,3],
+                i = 0;
+            arr.every(function(a) {
+                i++;
+                arr.push(a+3);
+                return i <= 3;
+            });
+            expect(arr).toEqual([1,2,3,4,5,6]);
+            expect(i).toBe(3);
+        });
+        it('should set the right context when given none', function() {
+            var context;
+            [1].every(function() {context = this;});
+            expect(context).toBe(function() {return this}.call());
+        });
+        
+        it('should return true if the array is empty', function() {
+            actual = [].every(function() { return true; });
+            expect(actual).toBeTruthy();
+            
+            actual = [].every(function() { return false; });
+            expect(actual).toBeTruthy();
+        });
+        it('should return true if it runs to the end', function() {
+            actual = [1,2,3].every(function() { return true; });
+            expect(actual).toBeTruthy();
+        });
+        it('should return false if it is stopped before the end', function() {
+            actual = [1,2,3].every(function() { return false; });
+            expect(actual).toBeFalsy();
+        });
+        
+        it('should return after 3 elements', function() {
+            testSubject.every(function(obj, index) {
+                actual[index] = obj;
+                numberOfRuns += 1;
+                if(numberOfRuns == 3) {
+                    return false;
+                }
+                return true;
+            });
+            expect(actual).toExactlyMatch(expected);
+        });
+        it('should stop after 3 elements using a context', function() {
+            var o = { a: actual };
+            testSubject.every(function(obj, index) {
+                this.a[index] = obj;
+                numberOfRuns += 1;
+                if(numberOfRuns == 3) {
+                    return false;
+                }
+                return true;
+            }, o);
+            expect(actual).toExactlyMatch(expected);
+        });
+    
+        it('should stop after 3 elements in an array-like object', function() {
+            var ts = createArrayLikeFromArray(testSubject);
+            Array.prototype.every.call(ts, function(obj, index) {
+                actual[index] = obj;
+                numberOfRuns += 1;
+                if(numberOfRuns == 3) {
+                    return false;
+                }
+                return true;
+            });
+            expect(actual).toExactlyMatch(expected);
+        });
+        it('should stop after 3 elements in an array-like object using a context', function() {
+            var ts = createArrayLikeFromArray(testSubject);
+            var o = { a: actual };
+            Array.prototype.every.call(ts, function(obj, index) {
+                this.a[index] = obj;
+                numberOfRuns += 1;
+                if(numberOfRuns == 3) {
+                    return false;
+                }
+                return true;
+            }, o);
+            expect(actual).toExactlyMatch(expected);
+        });
+    });
+    
+    describe('indexOf', function() {
+        "use strict";
+        var actual, expected, testSubject;
+        
+        beforeEach(function() {
+            testSubject = [2, 3, undefined, true, 'hej', null, 2, false, 0];
+            delete testSubject[1];
+    
+        });
+    
+        it('should find the element', function() {
+            expected = 4;
+            actual = testSubject.indexOf('hej');
+            expect(actual).toEqual(expected);
+        });
+        it('should not find the element', function() {
+            expected = -1;
+            actual = testSubject.indexOf('mus');
+            expect(actual).toEqual(expected);
+        });
+        it('should find undefined as well', function() {
+            expected = -1;
+            actual = testSubject.indexOf(undefined);
+            expect(actual).not.toEqual(expected);
+        });
+        it('should skip unset indexes', function() {
+            expected = 2;
+            actual = testSubject.indexOf(undefined);
+            expect(actual).toEqual(expected);
+        });
+        it('should use a strict test', function() {
+            actual = testSubject.indexOf(null);
+            expect(actual).toEqual(5);
+            
+            actual = testSubject.indexOf('2');
+            expect(actual).toEqual(-1);
+        });
+        it('should skip the first if fromIndex is set', function() {
+            expect(testSubject.indexOf(2, 2)).toEqual(6);
+            expect(testSubject.indexOf(2, 0)).toEqual(0);
+            expect(testSubject.indexOf(2, 6)).toEqual(6);
+        });
+        it('should work with negative fromIndex', function() {
+            expect(testSubject.indexOf(2, -3)).toEqual(6);
+            expect(testSubject.indexOf(2, -9)).toEqual(0);
+        });
+        it('should work with fromIndex being greater than the length', function() {
+            expect(testSubject.indexOf(0, 20)).toEqual(-1);
+        });
+        it('should work with fromIndex being negative and greater than the length', function() {
+            expect(testSubject.indexOf('hej', -20)).toEqual(4);
+        });
+        
+        describe('Array-like', function ArrayLike() {
+            var indexOf = Array.prototype.indexOf,
+                testAL;
+            beforeEach(function beforeEach() {
+                testAL = {};
+                testSubject = [2, 3, undefined, true, 'hej', null, 2, false, 0];
+                testSubject.forEach(function (o,i) {
+                    testAL[i] = o;
+                });
+                testAL.length = testSubject.length;
+            });
+            it('should find the element (array-like)', function() {
+                expected = 4;
+                actual = indexOf.call(testAL, 'hej');
+                expect(actual).toEqual(expected);
+            });
+            it('should not find the element (array-like)', function() {
+                expected = -1;
+                actual = indexOf.call(testAL, 'mus');
+                expect(actual).toEqual(expected);
+            });
+            it('should find undefined as well (array-like)', function() {
+                expected = -1;
+                actual = indexOf.call(testAL, undefined);
+                expect(actual).not.toEqual(expected);
+            });
+            it('should skip unset indexes (array-like)', function() {
+                expected = 2;
+                actual = indexOf.call(testAL, undefined);
+                expect(actual).toEqual(expected);
+            });
+            it('should use a strict test (array-like)', function() {
+                actual = Array.prototype.indexOf.call(testAL, null);
+                expect(actual).toEqual(5);
+                
+                actual = Array.prototype.indexOf.call(testAL, '2');
+                expect(actual).toEqual(-1);
+            });
+            it('should skip the first if fromIndex is set (array-like)', function() {
+                expect(indexOf.call(testAL, 2, 2)).toEqual(6);
+                expect(indexOf.call(testAL, 2, 0)).toEqual(0);
+                expect(indexOf.call(testAL, 2, 6)).toEqual(6);
+            });
+            it('should work with negative fromIndex (array-like)', function() {
+                expect(indexOf.call(testAL, 2, -3)).toEqual(6);
+                expect(indexOf.call(testAL, 2, -9)).toEqual(0);
+            });
+            it('should work with fromIndex being greater than the length (array-like)', function() {
+                expect(indexOf.call(testAL, 0, 20)).toEqual(-1);
+            });
+            it('should work with fromIndex being negative and greater than the length (array-like)', function() {
+                expect(indexOf.call(testAL, 'hej', -20)).toEqual(4);
+            });
+        });
+    });
+    describe('lastIndexOf', function() {
+        "use strict";
+        var actual, expected, testSubject, testAL;
+        
+        beforeEach(function() {
+            testSubject = [2, 3, undefined, true, 'hej', null, 2, 3, false, 0];
+            delete testSubject[1];
+            delete testSubject[7];
+        });
+        describe('Array', function() {
+            it('should find the element', function() {
+                expected = 4;
+                actual = testSubject.lastIndexOf('hej');
+                expect(actual).toEqual(expected);
+            });
+            it('should not find the element', function() {
+                expected = -1;
+                actual = testSubject.lastIndexOf('mus');
+                expect(actual).toEqual(expected);
+            });
+            it('should find undefined as well', function() {
+                expected = -1;
+                actual = testSubject.lastIndexOf(undefined);
+                expect(actual).not.toEqual(expected);
+            });
+            it('should skip unset indexes', function() {
+                expected = 2;
+                actual = testSubject.lastIndexOf(undefined);
+                expect(actual).toEqual(expected);
+            });
+            it('should use a strict test', function() {
+                actual = testSubject.lastIndexOf(null);
+                expect(actual).toEqual(5);
+                
+                actual = testSubject.lastIndexOf('2');
+                expect(actual).toEqual(-1);
+            });
+            it('should skip the first if fromIndex is set', function() {
+                expect(testSubject.lastIndexOf(2, 2)).toEqual(0);
+                expect(testSubject.lastIndexOf(2, 0)).toEqual(0);
+                expect(testSubject.lastIndexOf(2, 6)).toEqual(6);
+            });
+            it('should work with negative fromIndex', function() {
+                expect(testSubject.lastIndexOf(2, -3)).toEqual(6);
+                expect(testSubject.lastIndexOf(2, -9)).toEqual(0);
+            });
+            it('should work with fromIndex being greater than the length', function() {
+                expect(testSubject.lastIndexOf(2, 20)).toEqual(6);
+            });
+            it('should work with fromIndex being negative and greater than the length', function() {
+                expect(testSubject.lastIndexOf(2, -20)).toEqual(-1);
+            });
+        });
+    
+        describe('Array like', function() {
+            var lastIndexOf = Array.prototype.lastIndexOf,
+                testAL;
+            beforeEach(function() {
+                testAL = {};
+                testSubject.forEach(function (o,i) {
+                    testAL[i] = o;
+                });
+                testAL.length = testSubject.length;
+            });
+            it('should find the element (array-like)', function() {
+                expected = 4;
+                actual = lastIndexOf.call(testAL, 'hej');
+                expect(actual).toEqual(expected);
+            });
+            it('should not find the element (array-like)', function() {
+                expected = -1;
+                actual = lastIndexOf.call(testAL, 'mus');
+                expect(actual).toEqual(expected);
+            });
+            it('should find undefined as well (array-like)', function() {
+                expected = -1;
+                actual = lastIndexOf.call(testAL, undefined);
+                expect(actual).not.toEqual(expected);
+            });
+            it('should skip unset indexes (array-like)', function() {
+                expected = 2;
+                actual = lastIndexOf.call(testAL, undefined);
+                expect(actual).toEqual(expected);
+            });
+            it('should use a strict test (array-like)', function() {
+                actual = lastIndexOf.call(testAL, null);
+                expect(actual).toEqual(5);
+                
+                actual = lastIndexOf.call(testAL, '2');
+                expect(actual).toEqual(-1);
+            });
+            it('should skip the first if fromIndex is set', function() {
+                expect(lastIndexOf.call(testAL, 2, 2)).toEqual(0);
+                expect(lastIndexOf.call(testAL, 2, 0)).toEqual(0);
+                expect(lastIndexOf.call(testAL, 2, 6)).toEqual(6);
+            });
+            it('should work with negative fromIndex', function() {
+                expect(lastIndexOf.call(testAL, 2, -3)).toEqual(6);
+                expect(lastIndexOf.call(testAL, 2, -9)).toEqual(0);
+            });
+            it('should work with fromIndex being greater than the length', function() {
+                expect(lastIndexOf.call(testAL, 2, 20)).toEqual(6);
+            });
+            it('should work with fromIndex being negative and greater than the length', function() {
+                expect(lastIndexOf.call(testAL, 2, -20)).toEqual(-1);
+            });
+        });
+    });
+    
+    describe('filter', function() {
+        var filteredArray,
+            callback = function callback(o, i, arr) {
+                return (
+                    i != 3 && i != 5
+                );
+            };
+        
+        beforeEach(function() {
+            testSubject = [2, 3, undefined, true, 'hej', 3, null, false, 0];
+            delete testSubject[1];
+            filteredArray = [2, undefined, 'hej', null, false, 0];
+        });
+        describe('Array object', function() {
+
+            it('should call the callback with the proper arguments', function() {
+                var callback = jasmine.createSpy('callback'),
+                    arr = ['1'];
+                arr.filter(callback);
+                expect(callback).toHaveBeenCalledWith('1', 0, arr);
+            });
+            it('should not affect elements added to the array after it has begun', function() {
+                var arr = [1,2,3],
+                    i = 0;
+                arr.filter(function(a) {
+                    i++;
+                    if(i <= 4) {
+                        arr.push(a+3);
+                    }
+                    return true;
+                });
+                expect(arr).toEqual([1,2,3,4,5,6]);
+                expect(i).toBe(3);
+            });
+            it('should skip non-set values', function() {
+                var passedValues = {};
+                testSubject = [1,2,3,4];
+                delete testSubject[1];
+                testSubject.filter(function(o, i) {
+                    passedValues[i] = o;
+                    return true;
+                });
+                expect(passedValues).toExactlyMatch(testSubject);
+            });
+            it('should pass the right context to the filter', function() {
+                var passedValues = {};
+                testSubject = [1,2,3,4];
+                delete testSubject[1];
+                testSubject.filter(function(o, i) {
+                    this[i] = o;
+                    return true;
+                }, passedValues);
+                expect(passedValues).toExactlyMatch(testSubject);
+            });
+            it('should set the right context when given none', function() {
+                var context;
+                [1].filter(function() {context = this;});
+                expect(context).toBe(function() {return this}.call());
+            });
+            it('should remove only the values for which the callback returns false', function() {
+                var result = testSubject.filter(callback);
+                expect(result).toExactlyMatch(filteredArray);
+            });
+            it('should leave the original array untouched', function() {
+                var copy = testSubject.slice();
+                testSubject.filter(callback);
+                expect(testSubject).toExactlyMatch(copy);
+            });
+            it('should not be affected by same-index mutation', function () {
+                var results = [1, 2, 3]
+                .filter(function (value, index, array) {
+                    array[index] = 'a';
+                    return true;
+                });
+                expect(results).toEqual([1, 2, 3]);
+            });
+        });
+        describe('Array like', function() {
+            beforeEach(function() {
+                testSubject = createArrayLikeFromArray(testSubject);
+            });
+            it('should call the callback with the proper arguments', function() {
+                var callback = jasmine.createSpy('callback'),
+                    arr = createArrayLikeFromArray(['1']);
+                Array.prototype.filter.call(arr, callback);
+                expect(callback).toHaveBeenCalledWith('1', 0, arr);
+            });
+            it('should not affect elements added to the array after it has begun', function() {
+                var arr = createArrayLikeFromArray([1,2,3]),
+                    i = 0;
+                Array.prototype.filter.call(arr, function(a) {
+                    i++;
+                    if(i <= 4) {
+                        arr[i+2] = a+3;
+                    }
+                    return true;
+                });
+                delete arr.length;
+                expect(arr).toExactlyMatch([1,2,3,4,5,6]);
+                expect(i).toBe(3);
+            });
+            it('should skip non-set values', function() {
+                var passedValues = {};
+                testSubject = createArrayLikeFromArray([1,2,3,4]);
+                delete testSubject[1];
+                Array.prototype.filter.call(testSubject, function(o, i) {
+                    passedValues[i] = o;
+                    return true;
+                });
+                delete testSubject.length;
+                expect(passedValues).toExactlyMatch(testSubject);
+            });
+            it('should set the right context when given none', function() {
+                var context;
+                Array.prototype.filter.call(createArrayLikeFromArray([1]), function() {context = this;}, undefined);
+                expect(context).toBe(function() {return this}.call());
+            });
+            it('should pass the right context to the filter', function() {
+                var passedValues = {};
+                testSubject = createArrayLikeFromArray([1,2,3,4]);
+                delete testSubject[1];
+                Array.prototype.filter.call(testSubject, function(o, i) {
+                    this[i] = o;
+                    return true;
+                }, passedValues);
+                delete testSubject.length;
+                expect(passedValues).toExactlyMatch(testSubject);
+            });
+            it('should remove only the values for which the callback returns false', function() {
+                var result = Array.prototype.filter.call(testSubject, callback);
+                expect(result).toExactlyMatch(filteredArray);
+            });
+            it('should leave the original array untouched', function() {
+                var copy = createArrayLikeFromArray(testSubject);
+                Array.prototype.filter.call(testSubject, callback);
+                expect(testSubject).toExactlyMatch(copy);
+            });
+        });
+    });
+    describe('map', function() {
+        var callback;
+        beforeEach(function() {
+            var i = 0;
+            callback = function() {
+                return i++;
+            };
+        });
+        describe('Array object', function() {
+            it('should call callback with the right parameters', function() {
+                var callback = jasmine.createSpy('callback'),
+                    array = [1];
+                array.map(callback);
+                expect(callback).toHaveBeenCalledWith(1, 0, array);
+            });
+            it('should set the context correctly', function() {
+                var context = {};
+                testSubject.map(function(o,i) {
+                    this[i] = o;
+                }, context);
+                expect(context).toExactlyMatch(testSubject);
+            });
+            it('should set the right context when given none', function() {
+                var context;
+                [1].map(function() {context = this;});
+                expect(context).toBe(function() {return this}.call());
+            });
+            it('should not change the array it is called on', function() {
+                var copy = testSubject.slice();
+                testSubject.map(callback);
+                expect(testSubject).toExactlyMatch(copy);
+            });
+            it('should only run for the number of objects in the array when it started', function() {
+                var arr = [1,2,3],
+                    i = 0;
+                arr.map(function(o) {
+                    arr.push(o+3);
+                    i++;
+                    return o;
+                });
+                expect(arr).toExactlyMatch([1,2,3,4,5,6]);
+                expect(i).toBe(3);
+            });
+            it('should properly translate the values as according to the callback', function() {
+                var result = testSubject.map(callback),
+                    expected = [0,0,1,2,3,4,5,6];
+                delete expected[1];
+                expect(result).toExactlyMatch(expected);
+            });
+            it('should skip non-existing values', function() {
+                var array = [1,2,3,4], 
+                    i = 0;
+                delete array[2];
+                array.map(function() {
+                    i++;
+                });
+                expect(i).toBe(3);
+            });
+        });
+        describe('Array-like', function() {
+            beforeEach(function() {
+                testSubject = createArrayLikeFromArray(testSubject);
+            });
+            it('should call callback with the right parameters', function() {
+                var callback = jasmine.createSpy('callback'),
+                    array = createArrayLikeFromArray([1]);
+                Array.prototype.map.call(array, callback);
+                expect(callback).toHaveBeenCalledWith(1, 0, array);
+            });
+            it('should set the context correctly', function() {
+                var context = {};
+                Array.prototype.map.call(testSubject, function(o,i) {
+                    this[i] = o;
+                }, context);
+                delete testSubject.length;
+                expect(context).toExactlyMatch(testSubject);
+            });
+            it('should set the right context when given none', function() {
+                var context;
+                Array.prototype.map.call(createArrayLikeFromArray([1]), function() {context = this;});
+                expect(context).toBe(function() {return this}.call());
+            });
+            it('should not change the array it is called on', function() {
+                var copy = createArrayLikeFromArray(testSubject);
+                Array.prototype.map.call(testSubject, callback);
+                expect(testSubject).toExactlyMatch(copy);
+            });
+            it('should only run for the number of objects in the array when it started', function() {
+                var arr = createArrayLikeFromArray([1,2,3]),
+                    i = 0;
+                Array.prototype.map.call(arr, function(o) {
+                    Array.prototype.push.call(arr, o+3);
+                    i++;
+                    return o;
+                });
+                delete arr.length;
+                expect(arr).toExactlyMatch([1,2,3,4,5,6]);
+                expect(i).toBe(3);
+            });
+            it('should properly translate the values as according to the callback', function() {
+                var result = Array.prototype.map.call(testSubject, callback),
+                    expected = [0,0,1,2,3,4,5,6];
+                delete expected[1];
+                expect(result).toExactlyMatch(expected);
+            });
+            it('should skip non-existing values', function() {
+                var array = createArrayLikeFromArray([1,2,3,4]), 
+                    i = 0;
+                delete array[2];
+                Array.prototype.map.call(array, function() {
+                    i++;
+                });
+                expect(i).toBe(3);
+            });
+        });
+    });
+    
+    describe('reduce', function() {
+        beforeEach(function() {
+            testSubject = [1,2,3];
+        });
+        
+        describe('Array', function() {
+            it('should pass the correct arguments to the callback', function() {
+                var spy = jasmine.createSpy().andReturn(0);
+                testSubject.reduce(spy);
+                expect(spy.calls[0].args).toExactlyMatch([1, 2, 1, testSubject]);
+            });
+            it('should start with the right initialValue', function() {
+                var spy = jasmine.createSpy().andReturn(0);
+                testSubject.reduce(spy, 0);
+                expect(spy.calls[0].args).toExactlyMatch([0, 1, 0, testSubject]);
+            });
+            it('should not affect elements added to the array after it has begun', function() {
+                var arr = [1,2,3],
+                    i = 0;
+                arr.reduce(function(a, b) {
+                    i++;
+                    if(i <= 4) {
+                        arr.push(a+3);
+                    };
+                    return b;
+                });
+                expect(arr).toEqual([1,2,3,4,5]);
+                expect(i).toBe(2);
+            });
+            it('should work as expected for empty arrays', function() {
+                var spy = jasmine.createSpy();
+                expect(function() {
+                    [].reduce(spy);
+                }).toThrow();
+                expect(spy).not.toHaveBeenCalled();
+            });
+            it('should throw correctly if no callback is given', function() {
+                expect(function() {
+                    testSubject.reduce();
+                }).toThrow();
+            });
+            it('should return the expected result', function() {
+                expect(testSubject.reduce(function(a,b) {
+                    return (a||'').toString()+(b||'').toString();
+                })).toEqual(testSubject.join(''));
+            });
+            it('should not directly affect the passed array', function() {
+                var copy = testSubject.slice();
+                testSubject.reduce(function(a,b) {
+                    return a+b;
+                });
+                expect(testSubject).toEqual(copy);
+            });
+            it('should skip non-set values', function() {
+                delete testSubject[1];
+                var visited = {};
+                testSubject.reduce(function(a,b) {
+                    if(a)
+                        visited[a] = true;
+                    if(b)
+                        visited[b] = true;
+                    return 0;
+                });
+                
+                expect(visited).toEqual({ '1': true, '3': true });
+            });
+            it('should have the right length', function() {
+                expect(testSubject.reduce.length).toBe(1);
+            });
+        });
+        describe('Array-like objects', function() {
+            beforeEach(function() {
+                testSubject = createArrayLikeFromArray(testSubject);
+                testSubject.reduce = Array.prototype.reduce;
+            });
+            it('should pass the correct arguments to the callback', function() {
+                var spy = jasmine.createSpy().andReturn(0);
+                testSubject.reduce(spy);
+                expect(spy.calls[0].args).toExactlyMatch([1, 2, 1, testSubject]);
+            });
+            it('should start with the right initialValue', function() {
+                var spy = jasmine.createSpy().andReturn(0);
+                testSubject.reduce(spy, 0);
+                expect(spy.calls[0].args).toExactlyMatch([0, 1, 0, testSubject]);
+            });
+            it('should not affect elements added to the array after it has begun', function() {
+                var arr = createArrayLikeFromArray([1,2,3]),
+                    i = 0;
+                Array.prototype.reduce.call(arr, function(a, b) {
+                    i++;
+                    if(i <= 4) {
+                        arr[i+2] = a+3;
+                    };
+                    return b;
+                });
+                expect(arr).toEqual({
+                    0: 1,
+                    1: 2,
+                    2: 3,
+                    3: 4,
+                    4: 5,
+                    length: 3
+                });
+                expect(i).toBe(2);
+            });
+            it('should work as expected for empty arrays', function() {
+                var spy = jasmine.createSpy();
+                expect(function() {
+                    Array.prototype.reduce.call({length: 0}, spy);
+                }).toThrow();
+                expect(spy).not.toHaveBeenCalled();
+            });
+            it('should throw correctly if no callback is given', function() {
+                expect(function() {
+                    testSubject.reduce();
+                }).toThrow();
+            });
+            it('should return the expected result', function() {
+                expect(testSubject.reduce(function(a,b) {
+                    return (a||'').toString()+(b||'').toString();
+                })).toEqual('123');
+            });
+            it('should not directly affect the passed array', function() {
+                var copy = createArrayLikeFromArray(testSubject);
+                testSubject.reduce(function(a,b) {
+                    return a+b;
+                });
+                delete(testSubject.reduce);
+                expect(testSubject).toEqual(copy);
+            });
+            it('should skip non-set values', function() {
+                delete testSubject[1];
+                var visited = {};
+                testSubject.reduce(function(a,b) {
+                    if(a)
+                        visited[a] = true;
+                    if(b)
+                        visited[b] = true;
+                    return 0;
+                });
+                
+                expect(visited).toEqual({ '1': true, '3': true });
+            });
+            it('should have the right length', function() {
+                expect(testSubject.reduce.length).toBe(1);
+            });
+        });
+    });
+    describe('reduceRight', function() {
+        beforeEach(function() {
+            testSubject = [1,2,3];
+        });
+        
+        describe('Array', function() {
+            it('should pass the correct arguments to the callback', function() {
+                var spy = jasmine.createSpy().andReturn(0);
+                testSubject.reduceRight(spy);
+                expect(spy.calls[0].args).toExactlyMatch([3, 2, 1, testSubject]);
+            });
+            it('should start with the right initialValue', function() {
+                var spy = jasmine.createSpy().andReturn(0);
+                testSubject.reduceRight(spy, 0);
+                expect(spy.calls[0].args).toExactlyMatch([0, 3, 2, testSubject]);
+            });
+            it('should not affect elements added to the array after it has begun', function() {
+                var arr = [1,2,3],
+                    i = 0;
+                arr.reduceRight(function(a, b) {
+                    i++;
+                    if(i <= 4) {
+                        arr.push(a+3);
+                    };
+                    return b;
+                });
+                expect(arr).toEqual([1,2,3,6,5]);
+                expect(i).toBe(2);
+            });
+            it('should work as expected for empty arrays', function() {
+                var spy = jasmine.createSpy();
+                expect(function() {
+                    [].reduceRight(spy);
+                }).toThrow();
+                expect(spy).not.toHaveBeenCalled();
+            });
+            it('should throw correctly if no callback is given', function() {
+                expect(function() {
+                    testSubject.reduceRight();
+                }).toThrow();
+            });
+            it('should return the expected result', function() {
+                expect(testSubject.reduceRight(function(a,b) {
+                    return (a||'').toString()+(b||'').toString();
+                })).toEqual('321');
+            });
+            it('should not directly affect the passed array', function() {
+                var copy = testSubject.slice();
+                testSubject.reduceRight(function(a,b) {
+                    return a+b;
+                });
+                expect(testSubject).toEqual(copy);
+            });
+            it('should skip non-set values', function() {
+                delete testSubject[1];
+                var visited = {};
+                testSubject.reduceRight(function(a,b) {
+                    if(a)
+                        visited[a] = true;
+                    if(b)
+                        visited[b] = true;
+                    return 0;
+                });
+                
+                expect(visited).toEqual({ '1': true, '3': true });
+            });
+            it('should have the right length', function() {
+                expect(testSubject.reduceRight.length).toBe(1);
+            });
+        });
+        describe('Array-like objects', function() {
+            beforeEach(function() {
+                testSubject = createArrayLikeFromArray(testSubject);
+                testSubject.reduceRight = Array.prototype.reduceRight;
+            });
+            it('should pass the correct arguments to the callback', function() {
+                var spy = jasmine.createSpy().andReturn(0);
+                testSubject.reduceRight(spy);
+                expect(spy.calls[0].args).toExactlyMatch([3, 2, 1, testSubject]);
+            });
+            it('should start with the right initialValue', function() {
+                var spy = jasmine.createSpy().andReturn(0);
+                testSubject.reduceRight(spy, 0);
+                expect(spy.calls[0].args).toExactlyMatch([0, 3, 2, testSubject]);
+            });
+            it('should not affect elements added to the array after it has begun', function() {
+                var arr = createArrayLikeFromArray([1,2,3]),
+                    i = 0;
+                Array.prototype.reduceRight.call(arr, function(a, b) {
+                    i++;
+                    if(i <= 4) {
+                        arr[i+2] = a+3;
+                    };
+                    return b;
+                });
+                expect(arr).toEqual({
+                    0: 1,
+                    1: 2,
+                    2: 3,
+                    3: 6,
+                    4: 5,
+                    length: 3 // does not get updated on property assignment
+                });
+                expect(i).toBe(2);
+            });
+            it('should work as expected for empty arrays', function() {
+                var spy = jasmine.createSpy();
+                expect(function() {
+                    Array.prototype.reduceRight.call({length:0}, spy);
+                }).toThrow();
+                expect(spy).not.toHaveBeenCalled();
+            });
+            it('should throw correctly if no callback is given', function() {
+                expect(function() {
+                    testSubject.reduceRight();
+                }).toThrow();
+            });
+            it('should return the expected result', function() {
+                expect(testSubject.reduceRight(function(a,b) {
+                    return (a||'').toString()+(b||'').toString();
+                })).toEqual('321');
+            });
+            it('should not directly affect the passed array', function() {
+                var copy = createArrayLikeFromArray(testSubject);
+                testSubject.reduceRight(function(a,b) {
+                    return a+b;
+                });
+                delete(testSubject.reduceRight);
+                expect(testSubject).toEqual(copy);
+            });
+            it('should skip non-set values', function() {
+                delete testSubject[1];
+                var visited = {};
+                testSubject.reduceRight(function(a,b) {
+                    if(a)
+                        visited[a] = true;
+                    if(b)
+                        visited[b] = true;
+                    return 0;
+                });
+                
+                expect(visited).toEqual({ '1': true, '3': true });
+            });
+            it('should have the right length', function() {
+                expect(testSubject.reduceRight.length).toBe(1);
+            });
+        });
+    });
+
+    describe('isArray', function () {
+        it('should work for Array', function () {
+            var ret = Array.isArray([]);
+
+            expect(ret).toBe(true);
+        });
+
+        it('should fail for other objects', function () {
+            var objects = [
+                "someString",
+                true,
+                false,
+                42,
+                0,
+                {},
+                Object.create(null),
+                /foo/,
+                arguments,
+                document.getElementsByTagName("div")
+            ];
+
+            objects.forEach(function (v) {
+                expect(Array.isArray(v)).toBe(false);
+            });
+        });
+    });
+
+    describe('splice', function () {
+        var b = ["b"],
+            a = [1, "a", b],
+            test;
+        beforeEach(function() {
+            test = a.slice(0);
+        });
+        
+        it('basic implementation test 1', function () {
+            expect(test.splice(0)).toEqual(a);
+        });
+        it('basic implementation test 2', function () {
+            test.splice(0, 2);
+            expect(test).toEqual([b]);
+        });            
+                 
+        
+        it('should do nothing if method called with no arguments', function () {
+            expect(test.splice()).toEqual([]);
+            expect(test).toEqual(a);
+        });
+        //TODO:: Is this realy TRUE behavior?
+        it('should set first argument to 0 if first argument is set but undefined', function () {
+            var test2 = test.slice(0);
+            expect(test.splice(void 0, 2)).toEqual(test2.splice(0, 2));
+            expect(test).toEqual(test2);
+        });
+
+        it('should deleted and return all items after "start" when second argument is undefined', function () {
+            expect(test.splice(0)).toEqual(a);
+            expect(test).toEqual([]);
+        });
+        it('should deleted and return all items after "start" when second argument is undefined', function () {
+            expect(test.splice(2)).toEqual([b]);
+            expect(test).toEqual([1, "a"]);
+        });
+        it('runshould have the right length', function () {
+            expect(test.splice.length).toBe(2);
+        }); 
+    });
+
+    
+});

+ 117 - 0
js/lib/es5-shim-2.0.2/tests/spec/s-date.js

@@ -0,0 +1,117 @@
+describe('Date', function () {
+    
+    describe('now', function () {
+        it('should be the current time', function () {
+            expect(Date.now() === new Date().getTime()).toBe(true);
+        });
+    });
+
+    describe("parse", function () {
+        // TODO: Write the rest of the test.
+
+        it('should support extended years', function () {
+
+            expect(Date.parse('0001-01-01T00:00:00Z')).toBe(-62135596800000);
+            expect(Date.parse('+275760-09-13T00:00:00.000Z')).toBe(8.64e15);
+            expect(Date.parse('+033658-09-27T01:46:40.000Z')).toBe(1e15);
+            expect(Date.parse('-000001-01-01T00:00:00Z')).toBe(-62198755200000);
+            expect(Date.parse('+002009-12-15T00:00:00Z')).toBe(1260835200000);
+
+        });
+
+        it('should work', function () {
+                                                                                  //Chrome 19     Opera 12      Firefox 11    IE 9          Safari 5.1.1
+            expect(Date.parse("2012-11-31T23:59:59.000Z")).toBeFalsy();           //1354406399000 NaN           NaN           1354406399000 NaN
+            expect(Date.parse("2012-12-31T23:59:59.000Z")).toBe(1356998399000);   //1356998399000 1356998399000 1356998399000 1356998399000 1356998399000
+            expect(Date.parse("2012-12-31T23:59:60.000Z")).toBeFalsy();           //NaN           NaN           NaN           NaN           1356998400000
+            expect(Date.parse("2012-04-04T05:02:02.170Z")).toBe(1333515722170);   //1333515722170 1333515722170 1333515722170 1333515722170 1333515722170
+            expect(Date.parse("2012-04-04T24:00:00.000Z")).toBe(1333584000000);   //NaN           1333584000000 1333584000000 1333584000000 1333584000000
+            expect(Date.parse("2012-04-04T24:00:00.500Z")).toBeFalsy();           //NaN           NaN           1333584000500 1333584000500 NaN
+            expect(Date.parse("2012-12-31T10:08:60.000Z")).toBeFalsy();           //NaN           NaN           NaN           NaN           1356948540000
+            expect(Date.parse("2012-13-01T12:00:00.000Z")).toBeFalsy();           //NaN           NaN           NaN           NaN           NaN
+            expect(Date.parse("2012-12-32T12:00:00.000Z")).toBeFalsy();           //NaN           NaN           NaN           NaN           NaN
+            expect(Date.parse("2012-12-31T25:00:00.000Z")).toBeFalsy();           //NaN           NaN           NaN           NaN           NaN
+            expect(Date.parse("2012-12-31T24:01:00.000Z")).toBeFalsy();           //NaN           NaN           NaN           1356998460000 NaN
+            expect(Date.parse("2012-12-31T12:60:00.000Z")).toBeFalsy();           //NaN           NaN           NaN           NaN           NaN
+            expect(Date.parse("2012-12-31T12:00:60.000Z")).toBeFalsy();           //NaN           NaN           NaN           NaN           1356955260000
+            expect(Date.parse("2012-00-31T23:59:59.000Z")).toBeFalsy();           //NaN           NaN           NaN           NaN           NaN
+            expect(Date.parse("2012-12-00T23:59:59.000Z")).toBeFalsy();           //NaN           NaN           NaN           NaN           NaN
+            expect(Date.parse("2012-02-29T12:00:00.000Z")).toBe(1330516800000);   //1330516800000 1330516800000 1330516800000 1330516800000 1330516800000
+            expect(Date.parse("2011-02-29T12:00:00.000Z")).toBeFalsy();           //1298980800000 NaN           NaN           1298980800000 NaN
+            expect(Date.parse("2011-03-01T12:00:00.000Z")).toBe(1298980800000);   //1298980800000 1298980800000 1298980800000 1298980800000 1298980800000
+
+            // extended years:
+            expect(Date.parse("0000-01-01T00:00:00.000Z")).toBe(-621672192e5);    //-621672192e5  -621672192e5  -621672192e5  -621672192e5  -621672192e5
+            expect(Date.parse("+275760-09-13T00:00:00.000Z")).toBe(8.64e15);      //8.64e15       NaN           8.64e15       8.64e15       8.64e15
+            expect(Date.parse("-271821-04-20T00:00:00.000Z")).toBe(-8.64e15);     //-8.64e15      NaN           -8.64e15      -8.64e15      -8.6400000864e15
+            expect(Date.parse("+275760-09-13T00:00:00.001Z")).toBeFalsy();        //NaN           NaN           NaN           8.64e15 + 1   8.64e15 + 1
+            expect(Date.parse("-271821-04-19T23:59:59.999Z")).toBeFalsy();        //NaN           NaN           NaN           -8.64e15 - 1  -8.6400000864e15 - 1
+
+            // https://github.com/kriskowal/es5-shim/issues/80 Safari bug with leap day
+            expect(Date.parse("2034-03-01T00:00:00.000Z") -
+                        Date.parse("2034-02-27T23:59:59.999Z")).toBe(86400001);   //86400001      86400001       86400001       86400001      1
+
+            // Time Zone Offset
+            expect(Date.parse("2012-01-29T12:00:00.000+01:00")).toBe(132783480e4);//132783480e4 132783480e4  132783480e4  132783480e4     132783480e4
+            expect(Date.parse("2012-01-29T12:00:00.000-00:00")).toBe(132783840e4);//132783840e4 132783840e4  132783840e4  132783840e4     132783840e4
+            expect(Date.parse("2012-01-29T12:00:00.000+00:00")).toBe(132783840e4);//132783840e4 132783840e4  132783840e4  132783840e4     132783840e4
+            expect(Date.parse("2012-01-29T12:00:00.000+23:59")).toBe(132775206e4);//132775206e4 132775206e4  132775206e4  132775206e4     132775206e4
+            expect(Date.parse("2012-01-29T12:00:00.000-23:59")).toBe(132792474e4);//132792474e4 132792474e4  132792474e4  132792474e4     132792474e4
+            expect(Date.parse("2012-01-29T12:00:00.000+24:00")).toBeFalsy();      //NaN         1327752e6    NaN          1327752000000   1327752000000
+            expect(Date.parse("2012-01-29T12:00:00.000+24:01")).toBeFalsy();      //NaN         NaN          NaN          1327751940000   1327751940000
+            expect(Date.parse("2012-01-29T12:00:00.000+24:59")).toBeFalsy();      //NaN         NaN          NaN          1327748460000   1327748460000
+            expect(Date.parse("2012-01-29T12:00:00.000+25:00")).toBeFalsy();      //NaN         NaN          NaN          NaN             NaN
+            expect(Date.parse("2012-01-29T12:00:00.000+00:60")).toBeFalsy();      //NaN         NaN          NaN          NaN             NaN
+            expect(Date.parse("-271821-04-20T00:00:00.000+00:01")).toBeFalsy();   //NaN         NaN          NaN          -864000000006e4 -864000008646e4
+            expect(Date.parse("-271821-04-20T00:01:00.000+00:01")).toBe(-8.64e15);//-8.64e15    NaN          -8.64e15     -8.64e15        -864000008640e4
+
+            // When time zone is missed, local offset should be used (ES 5.1 bug)
+            // see https://bugs.ecmascript.org/show_bug.cgi?id=112
+            var tzOffset = Number(new Date(1970, 0));
+            // same as (new Date().getTimezoneOffset() * 60000)
+            expect(Date.parse('1970-01-01T00:00:00')).toBe(tzOffset);             //tzOffset    0            0            0               NaN
+        });
+    });
+
+    describe("toISOString", function () {
+        // TODO: write the rest of the test.
+       
+        it('should support extended years', function () {
+            expect(new Date(-62198755200000).toISOString().indexOf('-000001-01-01')).toBe(0);
+            expect(new Date(8.64e15).toISOString().indexOf('+275760-09-13')).toBe(0);
+        });
+
+        it('should return correct dates', function () {
+            expect(new Date(-1).toISOString()).toBe('1969-12-31T23:59:59.999Z');// Safari 5.1.5 "1969-12-31T23:59:59.-01Z"
+            expect(new Date(-3509827334573292).toISOString()).toBe('-109252-01-01T10:37:06.708Z'); // Opera 11.61/Opera 12 bug with Date#getUTCMonth
+        });
+
+    });
+
+    describe("toJSON", function () {
+
+        // Opera 11.6x/12 bug
+        it('should call toISOString', function () {
+          var date = new Date(0);
+          date.toISOString = function () {
+            return 1;
+          };
+          expect(date.toJSON()).toBe(1);
+        });
+
+        it('should return null for not finite dates', function () {
+          var date = new Date(NaN),
+              json;
+          try {
+            json = date.toJSON();
+          } catch (e) {}
+          expect(json).toBe(null);
+        });
+
+        it('should return the isoString when stringified', function () {
+            var date = new Date();
+            expect(JSON.stringify(date.toISOString())).toBe(JSON.stringify(date));
+        }) 
+    });
+
+});

+ 147 - 0
js/lib/es5-shim-2.0.2/tests/spec/s-function.js

@@ -0,0 +1,147 @@
+
+describe('Function', function() {
+    "use strict";
+    describe('bind', function() {
+        var actual, expected,
+            testSubject;
+        
+        testSubject = {
+            push: function(o) {
+                this.a.push(o);
+            }
+        };
+        
+        function func() {
+            Array.prototype.forEach.call(arguments, function(a) {
+                this.push(a);
+            }, this);
+            return this;
+        };
+        
+        beforeEach(function() {
+            actual = [];
+            testSubject.a = [];
+        });
+        
+        it('binds properly without a context', function() {
+            var context;
+            testSubject.func = function() {
+                context = this;
+            }.bind();
+            testSubject.func();
+            expect(context).toBe(function() {return this}.call());
+        });
+        it('binds properly without a context, and still supplies bound arguments', function() {
+            var a, context;
+            testSubject.func = function() {
+                a = Array.prototype.slice.call(arguments);
+                context = this;
+            }.bind(undefined, 1,2,3);
+            testSubject.func(1,2,3);
+            expect(a).toEqual([1,2,3,1,2,3]);
+            expect(context).toBe(function() {return this}.call());
+        });
+        it('binds a context properly', function() {
+            testSubject.func = func.bind(actual);
+            testSubject.func(1,2,3);
+            expect(actual).toEqual([1,2,3]);
+            expect(testSubject.a).toEqual([]);
+        });
+        it('binds a context and supplies bound arguments', function() {
+            testSubject.func = func.bind(actual, 1,2,3);
+            testSubject.func(4,5,6);
+            expect(actual).toEqual([1,2,3,4,5,6]);
+            expect(testSubject.a).toEqual([]);
+        });
+        
+        it('returns properly without binding a context', function() {
+            testSubject.func = function() {
+                return this;
+            }.bind();
+            var context = testSubject.func();
+            expect(context).toBe(function() {return this}.call());
+        });
+        it('returns properly without binding a context, and still supplies bound arguments', function() {
+            var context;
+            testSubject.func = function() {
+                context = this;
+                return Array.prototype.slice.call(arguments);
+            }.bind(undefined, 1,2,3);
+            actual = testSubject.func(1,2,3);
+            expect(context).toBe(function() {return this}.call());
+            expect(actual).toEqual([1,2,3,1,2,3]);
+        });
+        it('returns properly while binding a context properly', function() {
+            var ret;
+            testSubject.func = func.bind(actual);
+            ret = testSubject.func(1,2,3);
+            expect(ret).toBe(actual);
+            expect(ret).not.toBe(testSubject);
+        });
+        it('returns properly while binding a context and supplies bound arguments', function() {
+            var ret;
+            testSubject.func = func.bind(actual, 1,2,3);
+            ret = testSubject.func(4,5,6);
+            expect(ret).toBe(actual);
+            expect(ret).not.toBe(testSubject);
+        });
+        it('passes the correct arguments as a constructor', function() {
+            var ret, expected = { name: "Correct" };
+            testSubject.func = function(arg) {
+                return arg;
+            }.bind({ name: "Incorrect" });
+            ret = new testSubject.func(expected);
+            expect(ret).toBe(expected);
+        });
+        it('returns the return value of the bound function when called as a constructor', function () {
+            var oracle = [1, 2, 3];
+            var subject = function () {
+                return oracle;
+            }.bind(null);
+            var result = new subject;
+            expect(result).toBe(oracle);
+        });
+        it('returns the correct value if constructor returns primitive', function() {
+            var oracle = [1, 2, 3];
+            var subject = function () {
+                return oracle;
+            }.bind(null);
+            var result = new subject;
+            expect(result).toBe(oracle);
+
+            oracle = {};
+            result = new subject;
+            expect(result).toBe(oracle);
+
+            oracle = function(){};
+            result = new subject;
+            expect(result).toBe(oracle);
+
+            oracle = "asdf";
+            result = new subject;
+            expect(result).not.toBe(oracle);
+
+            oracle = null;
+            result = new subject;
+            expect(result).not.toBe(oracle);
+
+            oracle = true;
+            result = new subject;
+            expect(result).not.toBe(oracle);
+
+            oracle = 1;
+            result = new subject;
+            expect(result).not.toBe(oracle);
+        });
+        it('returns the value that instance of original "class" when called as a constructor', function() {
+            var classA = function(x) {
+                this.name = x || "A";
+            }
+            var classB = classA.bind(null, "B");
+            
+            var result = new classB;
+            expect(result instanceof classA).toBe(true);
+            expect(result instanceof classB).toBe(true);
+        });
+    });
+});

+ 84 - 0
js/lib/es5-shim-2.0.2/tests/spec/s-object.js

@@ -0,0 +1,84 @@
+describe('Object', function () {
+    "use strict";
+
+    describe("Object.keys", function () {
+        var obj = {
+            "str": "boz",
+            "obj": { },
+            "arr": [],
+            "bool": true,
+            "num": 42,
+            "null": null,
+            "undefined": undefined
+        };
+
+        var loopedValues = [];
+        for (var k in obj) {
+            loopedValues.push(k);
+        }
+
+        var keys = Object.keys(obj);
+        it('should have correct length', function () {
+            expect(keys.length).toBe(7);    
+        });
+
+        it('should return an Array', function () {
+            expect(Array.isArray(keys)).toBe(true);    
+        });
+        
+        it('should return names which are own properties', function () {
+            keys.forEach(function (name) {
+                expect(obj.hasOwnProperty(name)).toBe(true);
+            });    
+        });
+
+        it('should return names which are enumerable', function () {
+            keys.forEach(function (name) {
+                expect(loopedValues.indexOf(name)).toNotBe(-1);
+            }) 
+        });
+        
+        it('should throw error for non object', function () {
+            var e = {};
+            expect(function () {
+                try {
+                    Object.keys(42)
+                } catch (err) {
+                    throw e;
+                }
+            }).toThrow(e);
+        });
+    });
+ 
+	describe("Object.isExtensible", function () {
+        var obj = { };
+
+        it('should return true if object is extensible', function () {
+            expect(Object.isExtensible(obj)).toBe(true);    
+        });
+		
+        it('should return false if object is not extensible', function () {
+            expect(Object.isExtensible(Object.preventExtensions(obj))).toBe(false);    
+        });
+        
+        it('should return false if object is seal', function () {
+            expect(Object.isExtensible(Object.seal(obj))).toBe(false);    
+        });
+		
+        it('should return false if object is freeze', function () {
+            expect(Object.isExtensible(Object.freeze(obj))).toBe(false);    
+        });
+		
+        it('should throw error for non object', function () {
+            var e1 = {};
+            expect(function () {
+                try {
+                    Object.isExtensible(42)
+                } catch (err) {
+                    throw e1;
+                }
+            }).toThrow(e1);
+        });
+    });
+ 
+});

+ 24 - 0
js/lib/es5-shim-2.0.2/tests/spec/s-string.js

@@ -0,0 +1,24 @@
+describe('String', function() {
+    "use strict";
+    describe("trim", function() {
+        var test = "\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFFHello, World!\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF";
+
+        it('trims all ES5 whitespace', function() {
+            expect(test.trim()).toEqual("Hello, World!");
+            expect(test.trim().length).toEqual(13);
+        });
+    });
+
+    describe("split", function() {
+        var test = "ab";
+
+        it('If "separator" is undefined must return Array with one String - "this" string', function() {
+            expect(test.split()).toEqual([test]);
+            expect(test.split(void 0)).toEqual([test]);
+        });
+
+        it('If "separator" is undefined and "limit" set to 0 must return Array[]', function() {
+            expect(test.split(void 0, 0)).toEqual([]);
+        });
+    });
+});