[Pkg-javascript-commits] [node-es5-shim] 01/05: Imported Upstream version 4.1.14+dfsg
Julien Puydt
julien.puydt at laposte.net
Thu Oct 15 06:18:51 UTC 2015
This is an automated email from the git hooks/post-receive script.
jpuydt-guest pushed a commit to branch master
in repository node-es5-shim.
commit de34d43363d8d130a20b6fcb838c604299313798
Author: Julien Puydt <julien.puydt at laposte.net>
Date: Thu Oct 15 07:50:05 2015 +0200
Imported Upstream version 4.1.14+dfsg
---
.eslintrc | 37 +
.gitignore | 2 +
.jscs.json | 125 ++
.travis.yml | 58 +
CHANGES | 280 +++++
CONTRIBUTORS.md | 27 +
LICENSE | 22 +
Makefile | 63 +
README.md | 167 +++
bower.json | 33 +
component.json | 19 +
es5-sham.js | 556 +++++++++
es5-shim.js | 1590 +++++++++++++++++++++++++
package.json | 72 ++
shims.json | 7 +
tests/helpers/h-matchers.js | 40 +
tests/index.html | 62 +
tests/index.min.html | 61 +
tests/lib/jasmine-html.js | 190 +++
tests/lib/jasmine.css | 166 +++
tests/lib/jasmine.js | 2601 +++++++++++++++++++++++++++++++++++++++++
tests/lib/jasmine_favicon.png | Bin 0 -> 905 bytes
tests/lib/json2.js | 478 ++++++++
tests/native.html | 58 +
tests/spec/helpers-jasmine.js | 2 +
tests/spec/s-array.js | 1320 +++++++++++++++++++++
tests/spec/s-date.js | 247 ++++
tests/spec/s-function.js | 176 +++
tests/spec/s-global.js | 55 +
tests/spec/s-number.js | 37 +
tests/spec/s-object.js | 359 ++++++
tests/spec/s-string.js | 232 ++++
32 files changed, 9142 insertions(+)
diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 0000000..8ba8630
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,37 @@
+{
+ "root": true,
+
+ "extends": "@ljharb",
+
+ "rules": {
+ "camelcase": [0],
+ "complexity": [0, 10],
+ "dot-notation": [2, { "allowKeywords": false }],
+ "eqeqeq": [2, "allow-null"],
+ "id-length": [2, { "min": 1, "max": 40 }],
+ "max-nested-callbacks": [2, 5],
+ "max-params": [2, 7],
+ "max-statements": [1, 30],
+ "new-cap": [2, { "capIsNewExceptions": ["ToInteger", "ToObject", "ToPrimitive", "ToUint32"] }],
+ "no-constant-condition": [1],
+ "no-extend-native": [2, {"exceptions": ["Date"]}],
+ "no-extra-parens": [0],
+ "no-extra-semi": [1],
+ "no-func-assign": [1],
+ "no-implicit-coercion": [2, {
+ "boolean": false,
+ "number": false,
+ "string": true
+ }],
+ "no-invalid-this": [0],
+ "no-native-reassign": [2, {"exceptions": ["Date", "parseInt"]}],
+ "no-new-func": [1],
+ "no-restricted-syntax": [2, "ContinueStatement", "DebuggerStatement", "LabeledStatement", "WithStatement"],
+ "no-shadow": [1],
+ "no-unused-vars": [1, { "vars": "all", "args": "after-used" }],
+ "operator-linebreak": [2, "after"],
+ "quote-props": [1, "as-needed", { "keywords": true }],
+ "spaced-comment": [0],
+ "strict": [0]
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..fd4f2b0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+node_modules
+.DS_Store
diff --git a/.jscs.json b/.jscs.json
new file mode 100644
index 0000000..191a0ed
--- /dev/null
+++ b/.jscs.json
@@ -0,0 +1,125 @@
+{
+ "es3": true,
+
+ "additionalRules": [],
+
+ "requireSemicolons": true,
+
+ "disallowMultipleSpaces": true,
+
+ "disallowIdentifierNames": [],
+
+ "requireCurlyBraces": ["if", "else", "for", "while", "do", "try", "catch"],
+
+ "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch", "function"],
+
+ "disallowSpaceAfterKeywords": [],
+
+ "disallowSpaceBeforeComma": true,
+ "disallowSpaceBeforeSemicolon": true,
+
+ "disallowNodeTypes": [
+ "DebuggerStatement",
+ "LabeledStatement",
+ "SwitchCase",
+ "SwitchStatement",
+ "WithStatement"
+ ],
+
+ "requireObjectKeysOnNewLine": false,
+
+ "requireSpacesInAnonymousFunctionExpression": { "beforeOpeningRoundBrace": true, "beforeOpeningCurlyBrace": true },
+ "requireSpacesInNamedFunctionExpression": { "beforeOpeningCurlyBrace": true },
+ "disallowSpacesInNamedFunctionExpression": { "beforeOpeningRoundBrace": true },
+ "requireSpacesInFunctionDeclaration": { "beforeOpeningCurlyBrace": true },
+ "disallowSpacesInFunctionDeclaration": { "beforeOpeningRoundBrace": true },
+
+ "requireSpaceBetweenArguments": true,
+
+ "disallowSpacesInsideParentheses": true,
+
+ "disallowSpacesInsideArrayBrackets": true,
+
+ "disallowQuotedKeysInObjects": "allButReserved",
+
+ "disallowSpaceAfterObjectKeys": true,
+
+ "requireCommaBeforeLineBreak": true,
+
+ "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"],
+ "requireSpaceAfterPrefixUnaryOperators": [],
+
+ "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"],
+ "requireSpaceBeforePostfixUnaryOperators": [],
+
+ "disallowSpaceBeforeBinaryOperators": [],
+ "requireSpaceBeforeBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="],
+
+ "requireSpaceAfterBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="],
+ "disallowSpaceAfterBinaryOperators": [],
+
+ "disallowImplicitTypeConversion": ["binary", "string"],
+
+ "disallowKeywords": ["with", "eval"],
+
+ "requireKeywordsOnNewLine": [],
+ "disallowKeywordsOnNewLine": ["else"],
+
+ "requireLineFeedAtFileEnd": true,
+
+ "disallowTrailingWhitespace": true,
+
+ "disallowTrailingComma": true,
+
+ "excludeFiles": ["node_modules/**", "vendor/**"],
+
+ "disallowMultipleLineStrings": true,
+
+ "requireDotNotation": true,
+
+ "requireParenthesesAroundIIFE": true,
+
+ "validateLineBreaks": "LF",
+
+ "validateQuoteMarks": {
+ "escape": true,
+ "mark": "'"
+ },
+
+ "disallowOperatorBeforeLineBreak": [],
+
+ "requireSpaceBeforeKeywords": [
+ "do",
+ "for",
+ "if",
+ "else",
+ "switch",
+ "case",
+ "try",
+ "catch",
+ "finally",
+ "while",
+ "with",
+ "return"
+ ],
+
+ "validateAlignedFunctionParameters": {
+ "lineBreakAfterOpeningBraces": true,
+ "lineBreakBeforeClosingBraces": true
+ },
+
+ "requirePaddingNewLinesBeforeExport": true,
+
+ "validateNewlineAfterArrayElements": {
+ "maximum": 200
+ },
+
+ "requirePaddingNewLinesAfterUseStrict": true,
+
+ "disallowArrowFunctions": true,
+
+ "disallowMultiLineTernary": false,
+
+ "validateOrderInObjectKeys": false
+}
+
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..4c29ed5
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,58 @@
+language: node_js
+node_js:
+ - "4.1"
+ - "4.0"
+ - "iojs-v3.3"
+ - "iojs-v3.2"
+ - "iojs-v3.1"
+ - "iojs-v3.0"
+ - "iojs-v2.5"
+ - "iojs-v2.4"
+ - "iojs-v2.3"
+ - "iojs-v2.2"
+ - "iojs-v2.1"
+ - "iojs-v2.0"
+ - "iojs-v1.8"
+ - "iojs-v1.7"
+ - "iojs-v1.6"
+ - "iojs-v1.5"
+ - "iojs-v1.4"
+ - "iojs-v1.3"
+ - "iojs-v1.2"
+ - "iojs-v1.1"
+ - "iojs-v1.0"
+ - "0.12"
+ - "0.11"
+ - "0.10"
+ - "0.9"
+ - "0.8"
+ - "0.6"
+ - "0.4"
+before_install:
+ - '[ "${TRAVIS_NODE_VERSION}" = "0.6" ] || npm install -g npm at 1.4.28 && npm install -g npm'
+sudo: false
+matrix:
+ fast_finish: true
+ allow_failures:
+ - node_js: "4.0"
+ - node_js: "iojs-v3.2"
+ - node_js: "iojs-v3.1"
+ - node_js: "iojs-v3.0"
+ - node_js: "iojs-v2.4"
+ - node_js: "iojs-v2.3"
+ - node_js: "iojs-v2.2"
+ - node_js: "iojs-v2.1"
+ - node_js: "iojs-v2.0"
+ - node_js: "iojs-v1.7"
+ - node_js: "iojs-v1.6"
+ - node_js: "iojs-v1.5"
+ - node_js: "iojs-v1.4"
+ - node_js: "iojs-v1.3"
+ - node_js: "iojs-v1.2"
+ - node_js: "iojs-v1.1"
+ - node_js: "iojs-v1.0"
+ - node_js: "0.11"
+ - node_js: "0.9"
+ - node_js: "0.8"
+ - node_js: "0.6"
+ - node_js: "0.4"
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..a509db2
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,280 @@
+4.1.14
+ - [shim: fix] Wrap more things in a try/catch, because IE sucks and sometimes throws on [[Get]] of window.localStorage (#327)
+ - [Refactor] Use `ES.ToUint32` instead of inline `>>>`
+ - [Tests] up to `node` `v4.1`
+ - [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `semver`, `jscs`
+
+4.1.13
+ - [shim: fix] Fix a bug where `Date(x)` threw instead of equalling `String(Date(x))` (#326)
+
+4.1.12
+ - [fix] Make sure uglify doesn't remove function names
+ - [shim: fix] Use `is-arguments` implementation; don't call down legacy code path in modern engines (#325)
+ - [Tests] up to `io.js` `v3.3`
+ - [Dev Deps] update `eslint`, `@ljharb/eslint-config`
+
+4.1.11
+ - [shim: fix] Object.keys in Safari 9 has some bugs. (Already fixed in Webkit Nightly)
+ - [shim: fix] Omit !Date.parse check in the if statement (#323)
+ - [sham: fix] Fix Object.create sham to not set __proto__ (#301)
+ - [sham: fix] Add a typeof check to Object.getPrototypeOf (#319, #320)
+ - [Tests] up to `io.js` `v3.1`
+ - [Tests] Make sure `Object.getPrototypeOf` tests don't fail when engines implement ES6 semantics
+ - [Docs] Switch from vb.teelaun.ch to versionbadg.es for the npm version badge SVG (#318)
+ - [Dev Deps] Update `eslint`, `uglify-js`, `jscs`; use my personal shared `eslint` config
+
+4.1.10
+ - [Fix] Fix IE 8 issue with `window.frameElement` access in a child iframe (#322)
+ - [Tests] Consolidating `Date.parse` extended year tests
+ - [Tests] Account for a `Date.parse` precision variance in Safari 8
+ - [Tests] DRY up some Date.parse tests
+ - [Tests] Don't create globals in Date tests
+ - [Tests] Better failure output when an invalid date's toJSON throws
+ - [Tests] Also compare lengths of array-likes
+ - [Tests] Extra check on Object.keys(arguments)
+ - [Tests] Skip appropriate tests when objects aren't extensible/freezeable/sealable
+
+4.1.9
+ - [Fix] Remove "extended", add "unicode" in `String#split` shim, to match ES6
+ - [Fix] Object.keys: Preserve the IE 8 dontEnum bugfix, and the automation equality bugfix.
+ - [Fix] Object.keys: Prevent a deprecation message from showing up in Chrome.
+ - [Performance] Speed up blacklisted key check for Object.keys automation equality bug.
+ - [Tests] Test on `io.js` `v2.4`
+ - [Dev Deps] Update `eslint`, `semver`
+
+4.1.8
+ - [Fix] Fix an `Object.keys` IE 8 bug where `localStorage.prototype.constructor === localStorage` would throw (#275)
+ - [Fix] Shimmed `Object.defineProperty` should not throw for an empty descriptor (#315)
+ - [Fix] Fix `Date#toISOString` in Safari 5.1 (#243)
+ - [Fix] Use `Object#propertyIsEnumerable` to default the initial "enumerable" value in `Object.getOwnPropertyDescriptor` sham (#289)
+ - [Fix] Fix `Array#splice` with large sparse arrays in Safari 7/8, and Opera 12.15 (#295)
+ - [Robustness] Safely use and reference many builtins internally (also see #313)
+ - [Tests] Add `Date#{getUTCDate,getUTCMonth}` tests to expose Opera 10.6/11.61/12 `Date` bugs
+ - [Dev Deps] Update `eslint`
+
+4.1.7
+ - Make sure `Date.parse` is not enumerable (#310)
+
+4.1.6
+ - Support IE 8 when `document.domain` is set (#306, #150)
+ - Remove version from `bower.json` (#307)
+
+4.1.5
+ - Add a failing runtime check for Safari 8 `Date.parse`
+ - Update `eslint`, `semver`
+ - Test on `io.js` `v2.2`
+
+4.1.4
+ - Make sure copied `Date` properties remain non-enumerable.
+ - Using a more reliable check for supported property descriptors in non-IE ES3
+ - Fix 'constructor' in Object.defineProperties sham in ES3 (#252, #305)
+ - Use a reference to `Array#concat` rather than relying on the runtime environment's `concat`.
+ - Test on `io.js` `v2.1`
+ - Clean up `Array.prototype` iteration methods
+
+4.1.3
+ - Update `license` in `package.json` per https://docs.npmjs.com/files/package.json#license
+ - Update `uglify-js`, `eslint`
+
+4.1.2
+ - In IE 6-8, `Date` inside the function expression does not reference `DateShim` (#303)
+ - Date: Ensure all code paths have the correct `constructor` property
+ - Date: Don't copy non-own properties from original `Date`
+ - Test up to `io.js` `v2.0.0`
+ - Simplify `isPrimitive` check.
+ - Adding sanity check tests for ES5 `Number` constants.
+ - Update `uglify-js`, `eslint`, `semver`
+
+4.1.1
+ - Fix name of `parseInt` replacement.
+ - Update copyright year
+ - Update `eslint`, `jscs`
+ - Lock `uglify-js` down to v2.4.17, since v2.4.18 and v2.4.19 have a breaking change.
+ - All grade A-supported `node`/`iojs` versions now ship with an `npm` that understands `^`.
+ - Run `travis-ci` tests on latest `node` and `iojs`; speed up builds; allow 0.8 failures.
+ - Ensure some Object tests don't fail in ES6
+ - Make sure `Date` instances don't have an enumerable `constructor` property, when possible.
+
+4.1.0
+ - Update `eslint`
+ - Improve type checks: `Array.isArray`, `isRegex`
+ - Replace `isRegex`/`isString`/`isCallable` checks with inlined versions from npm modules
+ - Note which ES abstract methods are replaceable via `es-abstract`
+ - Run `travis-ci` tests on `iojs`!
+
+4.0.6
+ - Update `jscs`, `uglify-js`, add `eslint`
+ - es5-sham: fix Object.defineProperty to not check for own properties (#211)
+ - Fix Array#splice bug in Safari 5 (#284)
+ - Fix `Object.keys` issue with boxed primitives with extra properties in older browsers. (#242, #285)
+
+4.0.5
+ - Update `jscs` so tests pass
+
+4.0.4
+ - Style/indentation/whitespace cleanups.
+ - README tweaks
+
+4.0.3
+ - Fix keywords (#268)
+ - add some Date tests
+ - Note in README that the es5-sham requires the es5-shim (https://github.com/es-shims/es5-shim/issues/256#issuecomment-52875710)
+
+4.0.2
+ - Start including version numbers in minified files (#267)
+
+4.0.1
+ - Fix legacy arguments object detection in Object.keys (#260)
+
+4.0.0
+ - No longer shim the ES5-spec behavior of splice when `deleteCount` is omitted - since no engines implement it, and ES6 changes it. (#255)
+ - Use Object.defineProperty where available, so that polyfills are non-enumerable when possible (#250)
+ - lots of internal refactoring
+ - Fixed a bug referencing String#indexOf and String#lastIndexOf before polyfilling it (#253, #254)
+
+3.4.0
+ - Removed nonstandard SpiderMonkey extension to Array#splice - when `deleteCount` is omitted, it's now treated as 0. (#192, #239)
+ - Fix Object.keys with Arguments objects in Safari 5.0
+ - Now shimming String#split in Opera 10.6
+ - Avoid using "toString" as a variable name, since that breaks Opera
+ - Internal implementation and test cleanups
+
+3.3.2
+ - Remove an internal "bind" call, which should make the shim a bit faster
+ - Fix a bug with object boxing in Array#reduceRight that was failing a test in IE 6
+
+3.3.1
+ - Fixing an Array#splice bug in IE 6/7
+ - cleaning up Array#splice tests
+
+3.3.0
+ - Fix Array#reduceRight in node 0.6 and older browsers (#238)
+
+3.2.0
+ - Fix es5-sham UMD definition to work properly with AMD (#237)
+ - Ensure that Array methods do not autobox context in strict mode (#233)
+
+3.1.1
+ - Update minified files (#231)
+
+3.1.0
+ - Fix String#replace in Firefox up through 29 (#228)
+
+3.0.2
+ - Fix `Function#bind` in IE 7 and 8 (#224, #225, #226)
+
+3.0.1
+ - Version bump to ensure npm has newest minified assets
+
+3.0.0
+ - es5-sham: fix `Object.getPrototypeOf` and `Object.getOwnPropertyDescriptor` for Opera Mini
+ - Better override noncompliant native ES5 methods: `Array#forEach`, `Array#map`, `Array#filter`, `Array#every`, `Array#some`, `Array#reduce`, `Date.parse`, `String#trim`
+ - Added spec-compliant shim for `parseInt`
+ - Ensure `Object.keys` handles more edge cases with `arguments` objects and boxed primitives
+ - Improve minification of builds
+
+2.3.0
+ - parseInt is now properly shimmed in ES3 browsers to default the radix
+ - update URLs to point to the new organization
+
+2.2.0
+ - Function.prototype.bind shim now reports correct length on a bound function
+ - fix node 0.6.x v8 bug in Array#forEach
+ - test improvements
+
+2.1.0
+ - Object.create fixes
+ - tweaks to the Object.defineProperties shim
+
+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
+
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
new file mode 100644
index 0000000..43ebf77
--- /dev/null
+++ b/CONTRIBUTORS.md
@@ -0,0 +1,27 @@
+
+- 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
+- Steven Levithan Copyright (C) 2012 MIT License
+- Jordan Harband (C) 2013 MIT License
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..3dbd7de
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (C) 2009-2014 Kristopher Michael Kowal and contributors
+
+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.
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..5afc70d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,63 @@
+# Since we rely on paths relative to the makefile location, abort if make isn't being run from there.
+$(if $(findstring /,$(MAKEFILE_LIST)),$(error Please only invoke this makefile from the directory it resides in))
+
+ # The files that need updating when incrementing the version number.
+VERSIONED_FILES := *.js *.json *.map README*
+
+
+# Add the local npm packages' bin folder to the PATH, so that `make` can find them, when invoked directly.
+# Note that rather than using `$(npm bin)` the 'node_modules/.bin' path component is hard-coded, so that invocation works even from an environment
+# where npm is (temporarily) unavailable due to having deactivated an nvm instance loaded into the calling shell in order to avoid interference with tests.
+export PATH := $(shell printf '%s' "$$PWD/node_modules/.bin:$$PATH")
+UTILS := semver
+# Make sure that all required utilities can be located.
+UTIL_CHECK := $(or $(shell PATH="$(PATH)" which $(UTILS) >/dev/null && echo 'ok'),$(error Did you forget to run `npm install` after cloning the repo? At least one of the required supporting utilities not found: $(UTILS)))
+
+# Default target (by virtue of being the first non '.'-prefixed in the file).
+.PHONY: _no-target-specified
+_no-target-specified:
+ $(error Please specify the target to make - `make list` shows targets. Alternatively, use `npm test` to run the default tests; `npm run` shows all tests)
+
+# Lists all targets defined in this makefile.
+.PHONY: list
+list:
+ @$(MAKE) -pRrn : -f $(MAKEFILE_LIST) 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | command grep -v -e '^[^[:alnum:]]' -e '^$@$$command ' | sort
+
+# All-tests target: invokes the specified test suites for ALL shells defined in $(SHELLS).
+.PHONY: test
+test:
+ @npm test
+
+.PHONY: _ensure-tag
+_ensure-tag:
+ifndef TAG
+ $(error Please invoke with `make TAG=<new-version> release`, where <new-version> is either an increment specifier (patch, minor, major, prepatch, preminor, premajor, prerelease), or an explicit major.minor.patch version number)
+endif
+
+CHANGELOG_ERROR = $(error No CHANGES specified)
+.PHONY: _ensure-changelog
+_ensure-changelog:
+ @ (git status -sb --porcelain | command grep -E '^( M|[MA] ) CHANGES' > /dev/null) || (echo no CHANGES specified && exit 2)
+
+# Ensures that the git workspace is clean.
+.PHONY: _ensure-clean
+_ensure-clean:
+ @[ -z "$$((git status --porcelain --untracked-files=no || echo err) | command grep -v 'CHANGES')" ] || { echo "Workspace is not clean; please commit changes first." >&2; exit 2; }
+
+# Makes a release; invoke with `make TAG=<versionOrIncrementSpec> release`.
+.PHONY: release
+release: _ensure-tag _ensure-changelog _ensure-clean
+ @old_ver=`git describe --abbrev=0 --tags --match 'v[0-9]*.[0-9]*.[0-9]*'` || { echo "Failed to determine current version." >&2; exit 1; }; old_ver=$${old_ver#v}; \
+ new_ver=`echo "$(TAG)" | sed 's/^v//'`; new_ver=$${new_ver:-patch}; \
+ if printf "$$new_ver" | command grep -q '^[0-9]'; then \
+ semver "$$new_ver" >/dev/null || { echo 'Invalid version number specified: $(TAG) - must be major.minor.patch' >&2; exit 2; }; \
+ semver -r "> $$old_ver" "$$new_ver" >/dev/null || { echo 'Invalid version number specified: $(TAG) - must be HIGHER than current one.' >&2; exit 2; } \
+ else \
+ new_ver=`semver -i "$$new_ver" "$$old_ver"` || { echo 'Invalid version-increment specifier: $(TAG)' >&2; exit 2; } \
+ fi; \
+ printf "=== Bumping version **$$old_ver** to **$$new_ver** before committing and tagging:\n=== TYPE 'proceed' TO PROCEED, anything else to abort: " && read response && [ "$$response" = 'proceed' ] || { echo 'Aborted.' >&2; exit 2; }; \
+ npm run minify; \
+ replace "$$old_ver" "$$new_ver" -- $(VERSIONED_FILES) && \
+ replace "blob/master" "blob/v$$new_ver" -- *.min.js && \
+ git commit -m "v$$new_ver" $(VERSIONED_FILES) CHANGES && \
+ git tag -a -m "v$$new_ver" "v$$new_ver"
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..81d6a87
--- /dev/null
+++ b/README.md
@@ -0,0 +1,167 @@
+#es5-shim <sup>[![Version Badge][npm-version-svg]][npm-url]</sup>
+
+[![npm badge][npm-badge-png]][npm-url]
+
+[![Build Status][travis-svg]][travis-url]
+[![dependency status][deps-svg]][deps-url]
+[![dev dependency status][dev-deps-svg]][dev-deps-url]
+
+`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.
+**Note:** `es5-sham.js` requires `es5-shim.js` to be able to work properly.
+
+
+## Tests
+
+The tests are written with the Jasmine BDD test framework.
+To run the tests, navigate to <root-folder>/tests/ , or,
+simply `npm install` and `npm test`.
+
+## 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
+ * :warning: Caveat: the bound function has a prototype property.
+ * :warning: Caveat: bound functions do not try too hard to keep you
+ from manipulating their ``arguments`` and ``caller`` properties.
+ * :warning: Caveat: bound functions don't have checks in ``call`` and
+ ``apply`` to avoid executing as a constructor.
+* Number.prototype.toFixed
+* Object.keys
+* String.prototype.split
+* String.prototype.trim
+* String.prototype.replace
+ * Firefox (through v29) natively handles capturing groups incorrectly.
+* Date.parse (for ISO parsing)
+* Date.prototype.toISOString
+* parseInt
+
+## Shams
+
+* :warning: Object.create
+
+ For the case of simply "begetting" an object that inherits
+ prototypically from another, this should work fine across legacy
+ engines.
+
+ :warning: The second argument is passed to Object.defineProperties
+ which will probably fail either silently or with extreme prejudice.
+
+* :warning: 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.
+
+* :warning: 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.
+
+### May fail
+
+* :warning: 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.
+
+* :warning: Object.defineProperty
+
+ In the worst of circumstances, IE 8 provides a version of this
+ method that only works on DOM objects. This sham will not be
+ installed. The given version of `defineProperty` will throw an
+ exception if used on non-DOM objects.
+
+ In slightly better circumstances, 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.
+
+ https://github.com/es-shims/es5-shim/issues#issue/5
+
+* :warning: 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.
+
+[npm-url]: https://npmjs.org/package/es5-shim
+[npm-version-svg]: http://versionbadg.es/es-shims/es5-shim.svg
+[travis-svg]: https://travis-ci.org/es-shims/es5-shim.svg
+[travis-url]: https://travis-ci.org/es-shims/es5-shim
+[deps-svg]: https://david-dm.org/es-shims/es5-shim.svg
+[deps-url]: https://david-dm.org/es-shims/es5-shim
+[dev-deps-svg]: https://david-dm.org/es-shims/es5-shim/dev-status.svg
+[dev-deps-url]: https://david-dm.org/es-shims/es5-shim#info=devDependencies
+[npm-badge-png]: https://nodei.co/npm/es5-shim.png?downloads=true&stars=true
+
diff --git a/bower.json b/bower.json
new file mode 100644
index 0000000..dcc0b29
--- /dev/null
+++ b/bower.json
@@ -0,0 +1,33 @@
+{
+ "name": "es5-shim",
+ "main": "es5-shim.js",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/es-shims/es5-shim"
+ },
+ "homepage": "https://github.com/es-shims/es5-shim",
+ "authors": [
+ "Kris Kowal <kris at cixar.com> (http://github.com/kriskowal/)",
+ "Sami Samhuri <sami.samhuri at gmail.com> (http://samhuri.net/)",
+ "Florian Schäfer <florian.schaefer at gmail.com> (http://github.com/fschaefer)",
+ "Irakli Gozalishvili <rfobic at gmail.com> (http://jeditoolkit.com)",
+ "Kit Cambridge <kitcambridge at gmail.com> (http://kitcambridge.github.com)",
+ "Jordan Harband <ljharb at gmail.com> (https://github.com/ljharb/)"
+ ],
+ "description": "ECMAScript 5 compatibility shims for legacy JavaScript engines",
+ "keywords": [
+ "shim",
+ "es5",
+ "es5 shim",
+ "javascript",
+ "ecmascript",
+ "polyfill"
+ ],
+ "license": "MIT",
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "tests"
+ ]
+}
diff --git a/component.json b/component.json
new file mode 100644
index 0000000..477eeca
--- /dev/null
+++ b/component.json
@@ -0,0 +1,19 @@
+{
+ "name": "es5-shim",
+ "repo": "es-shims/es5-shim",
+ "description": "ECMAScript 5 compatibility shims for legacy JavaScript engines",
+ "version": "v4.1.14",
+ "keywords": [
+ "shim",
+ "es5",
+ "es5 shim",
+ "javascript",
+ "ecmascript",
+ "polyfill"
+ ],
+ "license": "MIT",
+ "main": "es5-shim.js",
+ "scripts": [
+ "es5-shim.js"
+ ]
+}
diff --git a/es5-sham.js b/es5-sham.js
new file mode 100644
index 0000000..42e9c98
--- /dev/null
+++ b/es5-sham.js
@@ -0,0 +1,556 @@
+/*!
+ * https://github.com/es-shims/es5-shim
+ * @license es5-shim Copyright 2009-2015 by contributors, MIT License
+ * see https://github.com/es-shims/es5-shim/blob/master/LICENSE
+ */
+
+// vim: ts=4 sts=4 sw=4 expandtab
+
+// Add semicolon to prevent IIFE from being passed as argument to concatenated code.
+;
+
+// UMD (Universal Module Definition)
+// see https://github.com/umdjs/umd/blob/master/returnExports.js
+(function (root, factory) {
+ 'use strict';
+
+ /* global define, exports, module */
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(factory);
+ } else if (typeof exports === 'object') {
+ // Node. Does not work with strict CommonJS, but
+ // only CommonJS-like enviroments that support module.exports,
+ // like Node.
+ module.exports = factory();
+ } else {
+ // Browser globals (root is window)
+ root.returnExports = factory();
+ }
+}(this, function () {
+
+var call = Function.prototype.call;
+var prototypeOfObject = Object.prototype;
+var owns = call.bind(prototypeOfObject.hasOwnProperty);
+var propertyIsEnumerable = call.bind(prototypeOfObject.propertyIsEnumerable);
+var toStr = call.bind(prototypeOfObject.toString);
+
+// If JS engine supports accessors creating shortcuts.
+var defineGetter;
+var defineSetter;
+var lookupGetter;
+var lookupSetter;
+var supportsAccessors = owns(prototypeOfObject, '__defineGetter__');
+if (supportsAccessors) {
+ /* eslint-disable no-underscore-dangle */
+ defineGetter = call.bind(prototypeOfObject.__defineGetter__);
+ defineSetter = call.bind(prototypeOfObject.__defineSetter__);
+ lookupGetter = call.bind(prototypeOfObject.__lookupGetter__);
+ lookupSetter = call.bind(prototypeOfObject.__lookupSetter__);
+ /* eslint-enable no-underscore-dangle */
+}
+
+// ES5 15.2.3.2
+// http://es5.github.com/#x15.2.3.2
+if (!Object.getPrototypeOf) {
+ // https://github.com/es-shims/es5-shim/issues#issue/2
+ // http://ejohn.org/blog/objectgetprototypeof/
+ // recommended by fschaefer on github
+ //
+ // sure, and webreflection says ^_^
+ // ... this will nerever possibly return null
+ // ... Opera Mini breaks here with infinite loops
+ Object.getPrototypeOf = function getPrototypeOf(object) {
+ /* eslint-disable no-proto */
+ var proto = object.__proto__;
+ /* eslint-enable no-proto */
+ if (proto || proto === null) {
+ return proto;
+ } else if (toStr(object.constructor) === '[object Function]') {
+ return object.constructor.prototype;
+ } else if (!(object instanceof Object)) {
+ // Correctly return null for Objects created with `Object.create(null)`
+ // (shammed or native) or `{ __proto__: null}`. Also returns null for
+ // cross-realm objects on browsers that lack `__proto__` support (like
+ // IE <11), but that's the best we can do.
+ return null;
+ } else {
+ return prototypeOfObject;
+ }
+ };
+}
+
+// ES5 15.2.3.3
+// http://es5.github.com/#x15.2.3.3
+
+var doesGetOwnPropertyDescriptorWork = function doesGetOwnPropertyDescriptorWork(object) {
+ try {
+ object.sentinel = 0;
+ return Object.getOwnPropertyDescriptor(object, 'sentinel').value === 0;
+ } catch (exception) {
+ return false;
+ }
+};
+
+// check whether getOwnPropertyDescriptor works if it's given. Otherwise, shim partially.
+if (Object.defineProperty) {
+ var getOwnPropertyDescriptorWorksOnObject = doesGetOwnPropertyDescriptorWork({});
+ var getOwnPropertyDescriptorWorksOnDom = typeof document === 'undefined' ||
+ doesGetOwnPropertyDescriptorWork(document.createElement('div'));
+ if (!getOwnPropertyDescriptorWorksOnDom || !getOwnPropertyDescriptorWorksOnObject) {
+ var getOwnPropertyDescriptorFallback = Object.getOwnPropertyDescriptor;
+ }
+}
+
+if (!Object.getOwnPropertyDescriptor || getOwnPropertyDescriptorFallback) {
+ var ERR_NON_OBJECT = 'Object.getOwnPropertyDescriptor called on a non-object: ';
+
+ /* eslint-disable no-proto */
+ Object.getOwnPropertyDescriptor = function getOwnPropertyDescriptor(object, property) {
+ if ((typeof object !== 'object' && typeof object !== 'function') || object === null) {
+ throw new TypeError(ERR_NON_OBJECT + object);
+ }
+
+ // make a valiant attempt to use the real getOwnPropertyDescriptor
+ // for I8's DOM elements.
+ if (getOwnPropertyDescriptorFallback) {
+ try {
+ return getOwnPropertyDescriptorFallback.call(Object, object, property);
+ } catch (exception) {
+ // try the shim if the real one doesn't work
+ }
+ }
+
+ var descriptor;
+
+ // If object does not owns property return undefined immediately.
+ if (!owns(object, property)) {
+ return descriptor;
+ }
+
+ // If object has a property then it's for sure `configurable`, and
+ // probably `enumerable`. Detect enumerability though.
+ descriptor = {
+ enumerable: propertyIsEnumerable(object, property),
+ 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__;
+ var notPrototypeOfObject = object !== prototypeOfObject;
+ // avoid recursion problem, breaking in Opera Mini when
+ // Object.getOwnPropertyDescriptor(Object.prototype, 'toString')
+ // or any other Object.prototype accessor
+ if (notPrototypeOfObject) {
+ object.__proto__ = prototypeOfObject;
+ }
+
+ var getter = lookupGetter(object, property);
+ var setter = lookupSetter(object, property);
+
+ if (notPrototypeOfObject) {
+ // 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];
+ descriptor.writable = true;
+ return descriptor;
+ };
+ /* eslint-enable no-proto */
+}
+
+// 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) {
+
+ // Contributed by Brandon Benvie, October, 2012
+ var createEmpty;
+ var supportsProto = !({ __proto__: null } instanceof Object);
+ // the following produces false positives
+ // in Opera Mini => not a reliable check
+ // Object.prototype.__proto__ === null
+
+ // Check for document.domain and active x support
+ // No need to use active x approach when document.domain is not set
+ // see https://github.com/es-shims/es5-shim/issues/150
+ // variation of https://github.com/kitcambridge/es5-shim/commit/4f738ac066346
+ /* global ActiveXObject */
+ var shouldUseActiveX = function shouldUseActiveX() {
+ // return early if document.domain not set
+ if (!document.domain) {
+ return false;
+ }
+
+ try {
+ return !!new ActiveXObject('htmlfile');
+ } catch (exception) {
+ return false;
+ }
+ };
+
+ // This supports IE8 when document.domain is used
+ // see https://github.com/es-shims/es5-shim/issues/150
+ // variation of https://github.com/kitcambridge/es5-shim/commit/4f738ac066346
+ var getEmptyViaActiveX = function getEmptyViaActiveX() {
+ var empty;
+ var xDoc;
+
+ xDoc = new ActiveXObject('htmlfile');
+
+ xDoc.write('<script><\/script>');
+ xDoc.close();
+
+ empty = xDoc.parentWindow.Object.prototype;
+ xDoc = null;
+
+ return empty;
+ };
+
+ // The original implementation using an iframe
+ // before the activex approach was added
+ // see https://github.com/es-shims/es5-shim/issues/150
+ var getEmptyViaIFrame = function getEmptyViaIFrame() {
+ var iframe = document.createElement('iframe');
+ var parent = document.body || document.documentElement;
+ var empty;
+
+ iframe.style.display = 'none';
+ parent.appendChild(iframe);
+ /* eslint-disable no-script-url */
+ iframe.src = 'javascript:';
+ /* eslint-enable no-script-url */
+
+ empty = iframe.contentWindow.Object.prototype;
+ parent.removeChild(iframe);
+ iframe = null;
+
+ return empty;
+ };
+
+ /* global document */
+ if (supportsProto || typeof document === 'undefined') {
+ createEmpty = function () {
+ return { __proto__: null };
+ };
+ } else {
+ // In old IE __proto__ can't be used to manually set `null`, nor does
+ // any other method exist to make an object that inherits from nothing,
+ // aside from Object.prototype itself. Instead, create a new global
+ // object and *steal* its Object.prototype and strip it bare. This is
+ // used as the prototype to create nullary objects.
+ createEmpty = function () {
+ // Determine which approach to use
+ // see https://github.com/es-shims/es5-shim/issues/150
+ var empty = shouldUseActiveX() ? getEmptyViaActiveX() : getEmptyViaIFrame();
+
+ delete empty.constructor;
+ delete empty.hasOwnProperty;
+ delete empty.propertyIsEnumerable;
+ delete empty.isPrototypeOf;
+ delete empty.toLocaleString;
+ delete empty.toString;
+ delete empty.valueOf;
+
+ var Empty = function Empty() {};
+ Empty.prototype = empty;
+ // short-circuit future calls
+ createEmpty = function () {
+ return new Empty();
+ };
+ return new Empty();
+ };
+ }
+
+ Object.create = function create(prototype, properties) {
+
+ var object;
+ var Type = function Type() {}; // An empty constructor.
+
+ if (prototype === null) {
+ object = createEmpty();
+ } 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`
+ /* eslint-disable no-proto */
+ object.__proto__ = prototype;
+ /* eslint-enable no-proto */
+ }
+
+ 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/es-shims/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
+
+var doesDefinePropertyWork = function doesDefinePropertyWork(object) {
+ try {
+ Object.defineProperty(object, 'sentinel', {});
+ return 'sentinel' in object;
+ } catch (exception) {
+ return false;
+ }
+};
+
+// 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,
+ definePropertiesFallback = Object.defineProperties;
+ }
+}
+
+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 ('value' in descriptor) {
+ // 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
+ ('writable' in descriptor && !descriptor.writable) ||
+ ('enumerable' in descriptor && !descriptor.enumerable) ||
+ ('configurable' in descriptor && !descriptor.configurable)
+ ))
+ 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.
+ /* eslint-disable no-proto */
+ 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;
+ /* eslint-enable no-proto */
+ } else {
+ object[property] = descriptor.value;
+ }
+ } else {
+ if (!supportsAccessors && (('get' in descriptor) || ('set' in descriptor))) {
+ throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);
+ }
+ // If we got that far then getters and setters can be defined !!
+ if ('get' in descriptor) {
+ defineGetter(object, property, descriptor.get);
+ }
+ if ('set' in descriptor) {
+ defineSetter(object, property, descriptor.set);
+ }
+ }
+ return object;
+ };
+}
+
+// ES5 15.2.3.7
+// http://es5.github.com/#x15.2.3.7
+if (!Object.defineProperties || definePropertiesFallback) {
+ Object.defineProperties = function defineProperties(object, properties) {
+ // make a valiant attempt to use the real defineProperties
+ if (definePropertiesFallback) {
+ try {
+ return definePropertiesFallback.call(Object, object, properties);
+ } catch (exception) {
+ // try the shim if the real one doesn't work
+ }
+ }
+
+ Object.keys(properties).forEach(function (property) {
+ if (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) {
+ if (Object(object) !== object) {
+ throw new TypeError('Object.seal can only be called on Objects.');
+ }
+ // 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) {
+ if (Object(object) !== object) {
+ throw new TypeError('Object.freeze can only be called on Objects.');
+ }
+ // 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 (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) {
+ if (Object(object) !== object) {
+ throw new TypeError('Object.preventExtensions can only be called on Objects.');
+ }
+ // 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) {
+ if (Object(object) !== object) {
+ throw new TypeError('Object.isSealed can only be called on Objects.');
+ }
+ return false;
+ };
+}
+
+// ES5 15.2.3.12
+// http://es5.github.com/#x15.2.3.12
+if (!Object.isFrozen) {
+ Object.isFrozen = function isFrozen(object) {
+ if (Object(object) !== object) {
+ throw new TypeError('Object.isFrozen can only be called on Objects.');
+ }
+ 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('Object.isExtensible can only be called on Objects.');
+ }
+ // 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;
+ };
+}
+
+}));
diff --git a/es5-shim.js b/es5-shim.js
new file mode 100644
index 0000000..879597e
--- /dev/null
+++ b/es5-shim.js
@@ -0,0 +1,1590 @@
+/*!
+ * https://github.com/es-shims/es5-shim
+ * @license es5-shim Copyright 2009-2015 by contributors, MIT License
+ * see https://github.com/es-shims/es5-shim/blob/master/LICENSE
+ */
+
+// vim: ts=4 sts=4 sw=4 expandtab
+
+// Add semicolon to prevent IIFE from being passed as argument to concatenated code.
+;
+
+// UMD (Universal Module Definition)
+// see https://github.com/umdjs/umd/blob/master/returnExports.js
+(function (root, factory) {
+ 'use strict';
+
+ /* global define, exports, module */
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(factory);
+ } else if (typeof exports === 'object') {
+ // Node. Does not work with strict CommonJS, but
+ // only CommonJS-like enviroments that support module.exports,
+ // like Node.
+ module.exports = factory();
+ } else {
+ // Browser globals (root is window)
+ root.returnExports = factory();
+ }
+}(this, 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/
+ */
+
+// Shortcut to an often accessed properties, in order to avoid multiple
+// dereference that costs universally. This also holds a reference to known-good
+// functions.
+var $Array = Array;
+var ArrayPrototype = $Array.prototype;
+var $Object = Object;
+var ObjectPrototype = $Object.prototype;
+var FunctionPrototype = Function.prototype;
+var $String = String;
+var StringPrototype = $String.prototype;
+var $Number = Number;
+var NumberPrototype = $Number.prototype;
+var array_slice = ArrayPrototype.slice;
+var array_splice = ArrayPrototype.splice;
+var array_push = ArrayPrototype.push;
+var array_unshift = ArrayPrototype.unshift;
+var array_concat = ArrayPrototype.concat;
+var call = FunctionPrototype.call;
+var max = Math.max;
+var min = Math.min;
+
+// Having a toString local variable name breaks in Opera so use to_string.
+var to_string = ObjectPrototype.toString;
+
+var hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
+var isCallable; /* inlined from https://npmjs.com/is-callable */ var fnToStr = Function.prototype.toString, tryFunctionObject = function tryFunctionObject(value) { try { fnToStr.call(value); return true; } catch (e) { return false; } }, fnClass = '[object Function]', genClass = '[object GeneratorFunction]'; isCallable = function isCallable(value) { if (typeof value !== 'function') { return false; } if (hasToStringTag) { return tryFunctionObject(value); } var strClass = to_string.call(val [...]
+var isRegex; /* inlined from https://npmjs.com/is-regex */ var regexExec = RegExp.prototype.exec, tryRegexExec = function tryRegexExec(value) { try { regexExec.call(value); return true; } catch (e) { return false; } }, regexClass = '[object RegExp]'; isRegex = function isRegex(value) { if (typeof value !== 'object') { return false; } return hasToStringTag ? tryRegexExec(value) : to_string.call(value) === regexClass; };
+var isString; /* inlined from https://npmjs.com/is-string */ var strValue = String.prototype.valueOf, tryStringObject = function tryStringObject(value) { try { strValue.call(value); return true; } catch (e) { return false; } }, stringClass = '[object String]'; isString = function isString(value) { if (typeof value === 'string') { return true; } if (typeof value !== 'object') { return false; } return hasToStringTag ? tryStringObject(value) : to_string.call(value) === stringClass; };
+
+/* inlined from http://npmjs.com/define-properties */
+var defineProperties = (function (has) {
+ var supportsDescriptors = $Object.defineProperty && (function () {
+ try {
+ var obj = {};
+ $Object.defineProperty(obj, 'x', { enumerable: false, value: obj });
+ for (var _ in obj) { return false; }
+ return obj.x === obj;
+ } catch (e) { /* this is ES3 */
+ return false;
+ }
+ }());
+
+ // Define configurable, writable and non-enumerable props
+ // if they don't exist.
+ var defineProperty;
+ if (supportsDescriptors) {
+ defineProperty = function (object, name, method, forceAssign) {
+ if (!forceAssign && (name in object)) { return; }
+ $Object.defineProperty(object, name, {
+ configurable: true,
+ enumerable: false,
+ writable: true,
+ value: method
+ });
+ };
+ } else {
+ defineProperty = function (object, name, method, forceAssign) {
+ if (!forceAssign && (name in object)) { return; }
+ object[name] = method;
+ };
+ }
+ return function defineProperties(object, map, forceAssign) {
+ for (var name in map) {
+ if (has.call(map, name)) {
+ defineProperty(object, name, map[name], forceAssign);
+ }
+ }
+ };
+}(ObjectPrototype.hasOwnProperty));
+
+//
+// Util
+// ======
+//
+
+/* replaceable with https://npmjs.com/package/es-abstract /helpers/isPrimitive */
+var isPrimitive = function isPrimitive(input) {
+ var type = typeof input;
+ return input === null || (type !== 'object' && type !== 'function');
+};
+
+var ES = {
+ // ES5 9.4
+ // http://es5.github.com/#x9.4
+ // http://jsperf.com/to-integer
+ /* replaceable with https://npmjs.com/package/es-abstract ES5.ToInteger */
+ ToInteger: function ToInteger(num) {
+ var n = +num;
+ 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;
+ },
+
+ /* replaceable with https://npmjs.com/package/es-abstract ES5.ToPrimitive */
+ ToPrimitive: function ToPrimitive(input) {
+ var val, valueOf, toStr;
+ if (isPrimitive(input)) {
+ return input;
+ }
+ valueOf = input.valueOf;
+ if (isCallable(valueOf)) {
+ val = valueOf.call(input);
+ if (isPrimitive(val)) {
+ return val;
+ }
+ }
+ toStr = input.toString;
+ if (isCallable(toStr)) {
+ val = toStr.call(input);
+ if (isPrimitive(val)) {
+ return val;
+ }
+ }
+ throw new TypeError();
+ },
+
+ // ES5 9.9
+ // http://es5.github.com/#x9.9
+ /* replaceable with https://npmjs.com/package/es-abstract ES5.ToObject */
+ ToObject: function (o) {
+ /* jshint eqnull: true */
+ if (o == null) { // this matches both null and undefined
+ throw new TypeError("can't convert " + o + ' to object');
+ }
+ return $Object(o);
+ },
+
+ /* replaceable with https://npmjs.com/package/es-abstract ES5.ToUint32 */
+ ToUint32: function ToUint32(x) {
+ return x >>> 0;
+ }
+};
+
+//
+// Function
+// ========
+//
+
+// ES-5 15.3.4.5
+// http://es5.github.com/#x15.3.4.5
+
+var Empty = function Empty() {};
+
+defineProperties(FunctionPrototype, {
+ 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 (!isCallable(target)) {
+ 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 = array_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;
+ var binder = 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,
+ array_concat.call(args, array_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,
+ array_concat.call(args, array_slice.call(arguments))
+ );
+
+ }
+
+ };
+
+ // 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.
+
+ var boundLength = max(0, target.length - args.length);
+
+ // 17. Set the attributes of the length own property of F to the values
+ // specified in 15.3.5.1.
+ var boundArgs = [];
+ for (var i = 0; i < boundLength; i++) {
+ array_push.call(boundArgs, '$' + i);
+ }
+
+ // XXX Build a dynamic function with desired amount of arguments is the only
+ // way to set the length property of a function.
+ // In environments where Content Security Policies enabled (Chrome extensions,
+ // for ex.) all use of eval or Function costructor throws an exception.
+ // However in all of these environments Function.prototype.bind exists
+ // and so this code will never be executed.
+ bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this, arguments); }')(binder);
+
+ if (target.prototype) {
+ Empty.prototype = target.prototype;
+ bound.prototype = new Empty();
+ // Clean up dangling references.
+ Empty.prototype = null;
+ }
+
+ // 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;
+ }
+});
+
+// _Please note: Shortcuts are defined after `Function.prototype.bind` as we
+// us it in defining shortcuts.
+var owns = call.bind(ObjectPrototype.hasOwnProperty);
+var toStr = call.bind(ObjectPrototype.toString);
+var strSlice = call.bind(StringPrototype.slice);
+var strSplit = call.bind(StringPrototype.split);
+
+//
+// Array
+// =====
+//
+
+var isArray = $Array.isArray || function isArray(obj) {
+ return toStr(obj) === '[object Array]';
+};
+
+// ES5 15.4.4.12
+// http://es5.github.com/#x15.4.4.13
+// Return len+argCount.
+// [bugfix, ielt8]
+// IE < 8 bug: [].unshift(0) === undefined but should be "1"
+var hasUnshiftReturnValueBug = [].unshift(0) !== 1;
+defineProperties(ArrayPrototype, {
+ unshift: function () {
+ array_unshift.apply(this, arguments);
+ return this.length;
+ }
+}, hasUnshiftReturnValueBug);
+
+// ES5 15.4.3.2
+// http://es5.github.com/#x15.4.3.2
+// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/isArray
+defineProperties($Array, { isArray: isArray });
+
+// 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');
+var splitString = boxedString[0] !== 'a' || !(0 in boxedString);
+
+var properlyBoxesContext = function properlyBoxed(method) {
+ // Check node 0.6.21 bug where third parameter is not boxed
+ var properlyBoxesNonStrict = true;
+ var properlyBoxesStrict = true;
+ if (method) {
+ method.call('foo', function (_, __, context) {
+ if (typeof context !== 'object') { properlyBoxesNonStrict = false; }
+ });
+
+ method.call([1], function () {
+ 'use strict';
+
+ properlyBoxesStrict = typeof this === 'string';
+ }, 'x');
+ }
+ return !!method && properlyBoxesNonStrict && properlyBoxesStrict;
+};
+
+defineProperties(ArrayPrototype, {
+ forEach: function forEach(callbackfn /*, thisArg*/) {
+ var object = ES.ToObject(this);
+ var self = splitString && isString(this) ? strSplit(this, '') : object;
+ var i = -1;
+ var length = ES.ToUint32(self.length);
+ var T;
+ if (arguments.length > 1) {
+ T = arguments[1];
+ }
+
+ // If no callback function or if callback is not a callable function
+ if (!isCallable(callbackfn)) {
+ throw new TypeError('Array.prototype.forEach callback must be a function');
+ }
+
+ while (++i < length) {
+ if (i in self) {
+ // Invoke the callback function with call, passing arguments:
+ // context, property value, property key, thisArg object
+ if (typeof T !== 'undefined') {
+ callbackfn.call(T, self[i], i, object);
+ } else {
+ callbackfn(self[i], i, object);
+ }
+ }
+ }
+ }
+}, !properlyBoxesContext(ArrayPrototype.forEach));
+
+// 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
+defineProperties(ArrayPrototype, {
+ map: function map(callbackfn/*, thisArg*/) {
+ var object = ES.ToObject(this);
+ var self = splitString && isString(this) ? strSplit(this, '') : object;
+ var length = ES.ToUint32(self.length);
+ var result = $Array(length);
+ var T;
+ if (arguments.length > 1) {
+ T = arguments[1];
+ }
+
+ // If no callback function or if callback is not a callable function
+ if (!isCallable(callbackfn)) {
+ throw new TypeError('Array.prototype.map callback must be a function');
+ }
+
+ for (var i = 0; i < length; i++) {
+ if (i in self) {
+ if (typeof T !== 'undefined') {
+ result[i] = callbackfn.call(T, self[i], i, object);
+ } else {
+ result[i] = callbackfn(self[i], i, object);
+ }
+ }
+ }
+ return result;
+ }
+}, !properlyBoxesContext(ArrayPrototype.map));
+
+// 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
+defineProperties(ArrayPrototype, {
+ filter: function filter(callbackfn /*, thisArg*/) {
+ var object = ES.ToObject(this);
+ var self = splitString && isString(this) ? strSplit(this, '') : object;
+ var length = ES.ToUint32(self.length);
+ var result = [];
+ var value;
+ var T;
+ if (arguments.length > 1) {
+ T = arguments[1];
+ }
+
+ // If no callback function or if callback is not a callable function
+ if (!isCallable(callbackfn)) {
+ throw new TypeError('Array.prototype.filter callback must be a function');
+ }
+
+ for (var i = 0; i < length; i++) {
+ if (i in self) {
+ value = self[i];
+ if (typeof T === 'undefined' ? callbackfn(value, i, object) : callbackfn.call(T, value, i, object)) {
+ array_push.call(result, value);
+ }
+ }
+ }
+ return result;
+ }
+}, !properlyBoxesContext(ArrayPrototype.filter));
+
+// ES5 15.4.4.16
+// http://es5.github.com/#x15.4.4.16
+// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/every
+defineProperties(ArrayPrototype, {
+ every: function every(callbackfn /*, thisArg*/) {
+ var object = ES.ToObject(this);
+ var self = splitString && isString(this) ? strSplit(this, '') : object;
+ var length = ES.ToUint32(self.length);
+ var T;
+ if (arguments.length > 1) {
+ T = arguments[1];
+ }
+
+ // If no callback function or if callback is not a callable function
+ if (!isCallable(callbackfn)) {
+ throw new TypeError('Array.prototype.every callback must be a function');
+ }
+
+ for (var i = 0; i < length; i++) {
+ if (i in self && !(typeof T === 'undefined' ? callbackfn(self[i], i, object) : callbackfn.call(T, self[i], i, object))) {
+ return false;
+ }
+ }
+ return true;
+ }
+}, !properlyBoxesContext(ArrayPrototype.every));
+
+// ES5 15.4.4.17
+// http://es5.github.com/#x15.4.4.17
+// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some
+defineProperties(ArrayPrototype, {
+ some: function some(callbackfn/*, thisArg */) {
+ var object = ES.ToObject(this);
+ var self = splitString && isString(this) ? strSplit(this, '') : object;
+ var length = ES.ToUint32(self.length);
+ var T;
+ if (arguments.length > 1) {
+ T = arguments[1];
+ }
+
+ // If no callback function or if callback is not a callable function
+ if (!isCallable(callbackfn)) {
+ throw new TypeError('Array.prototype.some callback must be a function');
+ }
+
+ for (var i = 0; i < length; i++) {
+ if (i in self && (typeof T === 'undefined' ? callbackfn(self[i], i, object) : callbackfn.call(T, self[i], i, object))) {
+ return true;
+ }
+ }
+ return false;
+ }
+}, !properlyBoxesContext(ArrayPrototype.some));
+
+// 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
+var reduceCoercesToObject = false;
+if (ArrayPrototype.reduce) {
+ reduceCoercesToObject = typeof ArrayPrototype.reduce.call('es5', function (_, __, ___, list) { return list; }) === 'object';
+}
+defineProperties(ArrayPrototype, {
+ reduce: function reduce(callbackfn /*, initialValue*/) {
+ var object = ES.ToObject(this);
+ var self = splitString && isString(this) ? strSplit(this, '') : object;
+ var length = ES.ToUint32(self.length);
+
+ // If no callback function or if callback is not a callable function
+ if (!isCallable(callbackfn)) {
+ throw new TypeError('Array.prototype.reduce callback must be a function');
+ }
+
+ // no value to return if no initial value and an empty array
+ if (length === 0 && 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 = callbackfn(result, self[i], i, object);
+ }
+ }
+
+ return result;
+ }
+}, !reduceCoercesToObject);
+
+// 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
+var reduceRightCoercesToObject = false;
+if (ArrayPrototype.reduceRight) {
+ reduceRightCoercesToObject = typeof ArrayPrototype.reduceRight.call('es5', function (_, __, ___, list) { return list; }) === 'object';
+}
+defineProperties(ArrayPrototype, {
+ reduceRight: function reduceRight(callbackfn/*, initial*/) {
+ var object = ES.ToObject(this);
+ var self = splitString && isString(this) ? strSplit(this, '') : object;
+ var length = ES.ToUint32(self.length);
+
+ // If no callback function or if callback is not a callable function
+ if (!isCallable(callbackfn)) {
+ throw new TypeError('Array.prototype.reduceRight callback must be a function');
+ }
+
+ // no value to return if no initial value, empty array
+ if (length === 0 && arguments.length === 1) {
+ throw new TypeError('reduceRight of empty array with no initial value');
+ }
+
+ var result;
+ var 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);
+ }
+
+ if (i < 0) {
+ return result;
+ }
+
+ do {
+ if (i in self) {
+ result = callbackfn(result, self[i], i, object);
+ }
+ } while (i--);
+
+ return result;
+ }
+}, !reduceRightCoercesToObject);
+
+// ES5 15.4.4.14
+// http://es5.github.com/#x15.4.4.14
+// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf
+var hasFirefox2IndexOfBug = ArrayPrototype.indexOf && [0, 1].indexOf(1, 2) !== -1;
+defineProperties(ArrayPrototype, {
+ indexOf: function indexOf(searchElement /*, fromIndex */) {
+ var self = splitString && isString(this) ? strSplit(this, '') : ES.ToObject(this);
+ var length = ES.ToUint32(self.length);
+
+ if (length === 0) {
+ return -1;
+ }
+
+ var i = 0;
+ if (arguments.length > 1) {
+ i = ES.ToInteger(arguments[1]);
+ }
+
+ // handle negative indices
+ i = i >= 0 ? i : max(0, length + i);
+ for (; i < length; i++) {
+ if (i in self && self[i] === searchElement) {
+ return i;
+ }
+ }
+ return -1;
+ }
+}, hasFirefox2IndexOfBug);
+
+// ES5 15.4.4.15
+// http://es5.github.com/#x15.4.4.15
+// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/lastIndexOf
+var hasFirefox2LastIndexOfBug = ArrayPrototype.lastIndexOf && [0, 1].lastIndexOf(0, -3) !== -1;
+defineProperties(ArrayPrototype, {
+ lastIndexOf: function lastIndexOf(searchElement /*, fromIndex */) {
+ var self = splitString && isString(this) ? strSplit(this, '') : ES.ToObject(this);
+ var length = ES.ToUint32(self.length);
+
+ if (length === 0) {
+ return -1;
+ }
+ var i = length - 1;
+ if (arguments.length > 1) {
+ i = min(i, ES.ToInteger(arguments[1]));
+ }
+ // handle negative indices
+ i = i >= 0 ? i : length - Math.abs(i);
+ for (; i >= 0; i--) {
+ if (i in self && searchElement === self[i]) {
+ return i;
+ }
+ }
+ return -1;
+ }
+}, hasFirefox2LastIndexOfBug);
+
+// ES5 15.4.4.12
+// http://es5.github.com/#x15.4.4.12
+var spliceNoopReturnsEmptyArray = (function () {
+ var a = [1, 2];
+ var result = a.splice();
+ return a.length === 2 && isArray(result) && result.length === 0;
+}());
+defineProperties(ArrayPrototype, {
+ // Safari 5.0 bug where .splice() returns undefined
+ splice: function splice(start, deleteCount) {
+ if (arguments.length === 0) {
+ return [];
+ } else {
+ return array_splice.apply(this, arguments);
+ }
+ }
+}, !spliceNoopReturnsEmptyArray);
+
+var spliceWorksWithEmptyObject = (function () {
+ var obj = {};
+ ArrayPrototype.splice.call(obj, 0, 0, 1);
+ return obj.length === 1;
+}());
+defineProperties(ArrayPrototype, {
+ splice: function splice(start, deleteCount) {
+ if (arguments.length === 0) { return []; }
+ var args = arguments;
+ this.length = max(ES.ToInteger(this.length), 0);
+ if (arguments.length > 0 && typeof deleteCount !== 'number') {
+ args = array_slice.call(arguments);
+ if (args.length < 2) {
+ array_push.call(args, this.length - start);
+ } else {
+ args[1] = ES.ToInteger(deleteCount);
+ }
+ }
+ return array_splice.apply(this, args);
+ }
+}, !spliceWorksWithEmptyObject);
+var spliceWorksWithLargeSparseArrays = (function () {
+ // Per https://github.com/es-shims/es5-shim/issues/295
+ // Safari 7/8 breaks with sparse arrays of size 1e5 or greater
+ var arr = new $Array(1e5);
+ // note: the index MUST be 8 or larger or the test will false pass
+ arr[8] = 'x';
+ arr.splice(1, 1);
+ // note: this test must be defined *after* the indexOf shim
+ // per https://github.com/es-shims/es5-shim/issues/313
+ return arr.indexOf('x') === 7;
+}());
+var spliceWorksWithSmallSparseArrays = (function () {
+ // Per https://github.com/es-shims/es5-shim/issues/295
+ // Opera 12.15 breaks on this, no idea why.
+ var n = 256;
+ var arr = [];
+ arr[n] = 'a';
+ arr.splice(n + 1, 0, 'b');
+ return arr[n] === 'a';
+}());
+defineProperties(ArrayPrototype, {
+ splice: function splice(start, deleteCount) {
+ var O = ES.ToObject(this);
+ var A = [];
+ var len = ES.ToUint32(O.length);
+ var relativeStart = ES.ToInteger(start);
+ var actualStart = relativeStart < 0 ? max((len + relativeStart), 0) : min(relativeStart, len);
+ var actualDeleteCount = min(max(ES.ToInteger(deleteCount), 0), len - actualStart);
+
+ var k = 0;
+ var from;
+ while (k < actualDeleteCount) {
+ from = $String(actualStart + k);
+ if (owns(O, from)) {
+ A[k] = O[from];
+ }
+ k += 1;
+ }
+
+ var items = array_slice.call(arguments, 2);
+ var itemCount = items.length;
+ var to;
+ if (itemCount < actualDeleteCount) {
+ k = actualStart;
+ while (k < (len - actualDeleteCount)) {
+ from = $String(k + actualDeleteCount);
+ to = $String(k + itemCount);
+ if (owns(O, from)) {
+ O[to] = O[from];
+ } else {
+ delete O[to];
+ }
+ k += 1;
+ }
+ k = len;
+ while (k > (len - actualDeleteCount + itemCount)) {
+ delete O[k - 1];
+ k -= 1;
+ }
+ } else if (itemCount > actualDeleteCount) {
+ k = len - actualDeleteCount;
+ while (k > actualStart) {
+ from = $String(k + actualDeleteCount - 1);
+ to = $String(k + itemCount - 1);
+ if (owns(O, from)) {
+ O[to] = O[from];
+ } else {
+ delete O[to];
+ }
+ k -= 1;
+ }
+ }
+ k = actualStart;
+ for (var i = 0; i < items.length; ++i) {
+ O[k] = items[i];
+ k += 1;
+ }
+ O.length = len - actualDeleteCount + itemCount;
+
+ return A;
+ }
+}, !spliceWorksWithLargeSparseArrays || !spliceWorksWithSmallSparseArrays);
+
+//
+// Object
+// ======
+//
+
+// ES5 15.2.3.14
+// http://es5.github.com/#x15.2.3.14
+
+// http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation
+var hasDontEnumBug = !({ 'toString': null }).propertyIsEnumerable('toString');
+var hasProtoEnumBug = function () {}.propertyIsEnumerable('prototype');
+var hasStringEnumBug = !owns('x', '0');
+var equalsConstructorPrototype = function (o) {
+ var ctor = o.constructor;
+ return ctor && ctor.prototype === o;
+};
+var blacklistedKeys = {
+ $window: true,
+ $console: true,
+ $parent: true,
+ $self: true,
+ $frames: true,
+ $frameElement: true,
+ $webkitIndexedDB: true,
+ $webkitStorageInfo: true
+};
+var hasAutomationEqualityBug = (function () {
+ /* globals window */
+ if (typeof window === 'undefined') { return false; }
+ for (var k in window) {
+ try {
+ if (!blacklistedKeys['$' + k] && owns(window, k) && window[k] !== null && typeof window[k] === 'object') {
+ equalsConstructorPrototype(window[k]);
+ }
+ } catch (e) {
+ return true;
+ }
+ }
+ return false;
+}());
+var equalsConstructorPrototypeIfNotBuggy = function (object) {
+ if (typeof window === 'undefined' || !hasAutomationEqualityBug) { return equalsConstructorPrototype(object); }
+ try {
+ return equalsConstructorPrototype(object);
+ } catch (e) {
+ return false;
+ }
+};
+var dontEnums = [
+ 'toString',
+ 'toLocaleString',
+ 'valueOf',
+ 'hasOwnProperty',
+ 'isPrototypeOf',
+ 'propertyIsEnumerable',
+ 'constructor'
+];
+var dontEnumsLength = dontEnums.length;
+
+// taken directly from https://github.com/ljharb/is-arguments/blob/master/index.js
+// can be replaced with require('is-arguments') if we ever use a build process instead
+var isStandardArguments = function isArguments(value) {
+ return toStr(value) === '[object Arguments]';
+};
+var isLegacyArguments = function isArguments(value) {
+ return value !== null &&
+ typeof value === 'object' &&
+ typeof value.length === 'number' &&
+ value.length >= 0 &&
+ !isArray(value) &&
+ isCallable(value.callee);
+};
+var isArguments = isStandardArguments(arguments) ? isStandardArguments : isLegacyArguments;
+
+defineProperties($Object, {
+ keys: function keys(object) {
+ var isFn = isCallable(object);
+ var isArgs = isArguments(object);
+ var isObject = object !== null && typeof object === 'object';
+ var isStr = isObject && isString(object);
+
+ if (!isObject && !isFn && !isArgs) {
+ throw new TypeError('Object.keys called on a non-object');
+ }
+
+ var theKeys = [];
+ var skipProto = hasProtoEnumBug && isFn;
+ if ((isStr && hasStringEnumBug) || isArgs) {
+ for (var i = 0; i < object.length; ++i) {
+ array_push.call(theKeys, $String(i));
+ }
+ }
+
+ if (!isArgs) {
+ for (var name in object) {
+ if (!(skipProto && name === 'prototype') && owns(object, name)) {
+ array_push.call(theKeys, $String(name));
+ }
+ }
+ }
+
+ if (hasDontEnumBug) {
+ var skipConstructor = equalsConstructorPrototypeIfNotBuggy(object);
+ for (var j = 0; j < dontEnumsLength; j++) {
+ var dontEnum = dontEnums[j];
+ if (!(skipConstructor && dontEnum === 'constructor') && owns(object, dontEnum)) {
+ array_push.call(theKeys, dontEnum);
+ }
+ }
+ }
+ return theKeys;
+ }
+});
+
+var keysWorksWithArguments = $Object.keys && (function () {
+ // Safari 5.0 bug
+ return $Object.keys(arguments).length === 2;
+}(1, 2));
+var keysHasArgumentsLengthBug = $Object.keys && (function () {
+ var argKeys = $Object.keys(arguments);
+ return arguments.length !== 1 || argKeys.length !== 1 || argKeys[0] !== 1;
+}(1));
+var originalKeys = $Object.keys;
+defineProperties($Object, {
+ keys: function keys(object) {
+ if (isArguments(object)) {
+ return originalKeys(array_slice.call(object));
+ } else {
+ return originalKeys(object);
+ }
+ }
+}, !keysWorksWithArguments || keysHasArgumentsLengthBug);
+
+//
+// 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;
+var negativeYearString = '-000001';
+var hasNegativeDateBug = Date.prototype.toISOString && new Date(negativeDate).toISOString().indexOf(negativeYearString) === -1;
+var hasSafari51DateBug = Date.prototype.toISOString && new Date(-1).toISOString() !== '1969-12-31T23:59:59.999Z';
+
+defineProperties(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/es-shims/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 ? '+' : '')) +
+ strSlice('00000' + Math.abs(year), (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 + '-' + array_slice.call(result, 0, 2).join('-') +
+ 'T' + array_slice.call(result, 2).join(':') + '.' +
+ strSlice('000' + this.getUTCMilliseconds(), -3) + 'Z'
+ );
+ }
+}, hasNegativeDateBug || hasSafari51DateBug);
+
+// 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 = (function () {
+ try {
+ return 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) {
+ return false;
+ }
+}());
+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 ES.ToPrimitive(O, hint Number).
+ var O = $Object(this);
+ var tv = ES.ToPrimitive(O);
+ // 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".
+ var toISO = O.toISOString;
+ // 5. If IsCallable(toISO) is false, throw a TypeError exception.
+ if (!isCallable(toISO)) {
+ 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
+var supportsExtendedYears = Date.parse('+033658-09-27T01:46:40.000Z') === 1e15;
+var acceptsInvalidDates = !isNaN(Date.parse('2012-04-04T24:00:00.500Z')) || !isNaN(Date.parse('2012-11-31T23:59:59.000Z')) || !isNaN(Date.parse('2012-12-31T23:59:60.000Z'));
+var doesNotParseY2KNewYear = isNaN(Date.parse('2000-01-01T00:00:00.000Z'));
+if (doesNotParseY2KNewYear || acceptsInvalidDates || !supportsExtendedYears) {
+ // XXX global assignment won't work in embeddings that use
+ // an alternate object for the context.
+ /* global Date: true */
+ /* eslint-disable no-undef */
+ Date = (function (NativeDate) {
+ /* eslint-enable no-undef */
+ // Date.length === 7
+ var DateShim = function Date(Y, M, D, h, m, s, ms) {
+ var length = arguments.length;
+ var date;
+ if (this instanceof NativeDate) {
+ date = length === 1 && $String(Y) === Y ? // isString(Y)
+ // We explicitly pass it through parse:
+ new NativeDate(DateShim.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();
+ } else {
+ date = NativeDate.apply(this, arguments);
+ }
+ if (!isPrimitive(date)) {
+ // Prevent mixups with unfixed Date object
+ defineProperties(date, { constructor: DateShim }, true);
+ }
+ return date;
+ };
+
+ // 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{1,}))?' + // 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];
+
+ var dayFromMonth = 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)
+ );
+ };
+
+ var toUTC = function toUTC(t) {
+ return $Number(new NativeDate(1970, 0, 1, 0, 0, 0, t));
+ };
+
+ // Copy any custom methods a 3rd party library may have added
+ for (var key in NativeDate) {
+ if (owns(NativeDate, key)) {
+ DateShim[key] = NativeDate[key];
+ }
+ }
+
+ // Copy "native" methods explicitly; they may be non-enumerable
+ defineProperties(DateShim, {
+ now: NativeDate.now,
+ UTC: NativeDate.UTC
+ }, true);
+ DateShim.prototype = NativeDate.prototype;
+ defineProperties(DateShim.prototype, {
+ constructor: DateShim
+ }, true);
+
+ // Upgrade Date.parse to handle simplified ISO 8601 strings
+ var parseShim = 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 = Math.floor($Number(match[7] || 0) * 1000),
+ // When time zone is missed, local offset should be used
+ // (ES 5.1 bug)
+ // see https://bugs.ecmascript.org/show_bug.cgi?id=112
+ isLocalTime = Boolean(match[4] && !match[8]),
+ signOffset = match[9] === '-' ? 1 : -1,
+ hourOffset = $Number(match[10] || 0),
+ minuteOffset = $Number(match[11] || 0),
+ result;
+ var hasMinutesOrSecondsOrMilliseconds = minute > 0 || second > 0 || millisecond > 0;
+ if (
+ hour < (hasMinutesOrSecondsOrMilliseconds ? 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;
+ if (isLocalTime) {
+ result = toUTC(result);
+ }
+ if (-8.64e15 <= result && result <= 8.64e15) {
+ return result;
+ }
+ }
+ return NaN;
+ }
+ return NativeDate.parse.apply(this, arguments);
+ };
+ defineProperties(DateShim, { parse: parseShim });
+
+ return DateShim;
+ }(Date));
+ /* global Date: false */
+}
+
+// ES5 15.9.4.4
+// http://es5.github.com/#x15.9.4.4
+if (!Date.now) {
+ Date.now = function now() {
+ return new Date().getTime();
+ };
+}
+
+//
+// Number
+// ======
+//
+
+// ES5.1 15.7.4.5
+// http://es5.github.com/#x15.7.4.5
+var hasToFixedBugs = NumberPrototype.toFixed && (
+ (0.00008).toFixed(3) !== '0.000' ||
+ (0.9).toFixed(0) !== '1' ||
+ (1.255).toFixed(2) !== '1.25' ||
+ (1000000000000000128).toFixed(0) !== '1000000000000000128'
+);
+
+var toFixedHelpers = {
+ base: 1e7,
+ size: 6,
+ data: [0, 0, 0, 0, 0, 0],
+ multiply: function multiply(n, c) {
+ var i = -1;
+ var c2 = c;
+ while (++i < toFixedHelpers.size) {
+ c2 += n * toFixedHelpers.data[i];
+ toFixedHelpers.data[i] = c2 % toFixedHelpers.base;
+ c2 = Math.floor(c2 / toFixedHelpers.base);
+ }
+ },
+ divide: function divide(n) {
+ var i = toFixedHelpers.size, c = 0;
+ while (--i >= 0) {
+ c += toFixedHelpers.data[i];
+ toFixedHelpers.data[i] = Math.floor(c / n);
+ c = (c % n) * toFixedHelpers.base;
+ }
+ },
+ numToString: function numToString() {
+ var i = toFixedHelpers.size;
+ var s = '';
+ while (--i >= 0) {
+ if (s !== '' || i === 0 || toFixedHelpers.data[i] !== 0) {
+ var t = $String(toFixedHelpers.data[i]);
+ if (s === '') {
+ s = t;
+ } else {
+ s += strSlice('0000000', 0, 7 - t.length) + t;
+ }
+ }
+ }
+ return s;
+ },
+ pow: function pow(x, n, acc) {
+ return (n === 0 ? acc : (n % 2 === 1 ? pow(x, n - 1, acc * x) : pow(x * x, n / 2, acc)));
+ },
+ log: function log(x) {
+ var n = 0;
+ var x2 = x;
+ while (x2 >= 4096) {
+ n += 12;
+ x2 /= 4096;
+ }
+ while (x2 >= 2) {
+ n += 1;
+ x2 /= 2;
+ }
+ return n;
+ }
+};
+
+defineProperties(NumberPrototype, {
+ toFixed: function toFixed(fractionDigits) {
+ var f, x, s, m, e, z, j, k;
+
+ // Test for NaN and round fractionDigits down
+ f = $Number(fractionDigits);
+ f = f !== f ? 0 : Math.floor(f);
+
+ if (f < 0 || f > 20) {
+ throw new RangeError('Number.toFixed called with invalid number of decimals');
+ }
+
+ x = $Number(this);
+
+ // Test for NaN
+ if (x !== x) {
+ return 'NaN';
+ }
+
+ // If it is too big or small, return the string value of the number
+ if (x <= -1e21 || x >= 1e21) {
+ return $String(x);
+ }
+
+ s = '';
+
+ if (x < 0) {
+ s = '-';
+ x = -x;
+ }
+
+ m = '0';
+
+ if (x > 1e-21) {
+ // 1e-21 < x < 1e21
+ // -70 < log2(x) < 70
+ e = toFixedHelpers.log(x * toFixedHelpers.pow(2, 69, 1)) - 69;
+ z = (e < 0 ? x * toFixedHelpers.pow(2, -e, 1) : x / toFixedHelpers.pow(2, e, 1));
+ z *= 0x10000000000000; // Math.pow(2, 52);
+ e = 52 - e;
+
+ // -18 < e < 122
+ // x = z / 2 ^ e
+ if (e > 0) {
+ toFixedHelpers.multiply(0, z);
+ j = f;
+
+ while (j >= 7) {
+ toFixedHelpers.multiply(1e7, 0);
+ j -= 7;
+ }
+
+ toFixedHelpers.multiply(toFixedHelpers.pow(10, j, 1), 0);
+ j = e - 1;
+
+ while (j >= 23) {
+ toFixedHelpers.divide(1 << 23);
+ j -= 23;
+ }
+
+ toFixedHelpers.divide(1 << j);
+ toFixedHelpers.multiply(1, 1);
+ toFixedHelpers.divide(2);
+ m = toFixedHelpers.numToString();
+ } else {
+ toFixedHelpers.multiply(0, z);
+ toFixedHelpers.multiply(1 << (-e), 0);
+ m = toFixedHelpers.numToString() + strSlice('0.00000000000000000000', 2, 2 + f);
+ }
+ }
+
+ if (f > 0) {
+ k = m.length;
+
+ if (k <= f) {
+ m = s + strSlice('0.0000000000000000000', 0, f - k + 2) + m;
+ } else {
+ m = s + strSlice(m, 0, k - f) + '.' + strSlice(m, k - f);
+ }
+ } else {
+ m = s + m;
+ }
+
+ return m;
+ }
+}, hasToFixedBugs);
+
+//
+// String
+// ======
+//
+
+// ES5 15.5.4.14
+// http://es5.github.com/#x15.5.4.14
+
+// [bugfix, IE lt 9, firefox 4, Konqueror, Opera, obscure browsers]
+// Many browsers do not split properly with regular expressions or they
+// do not perform the split correctly under obscure conditions.
+// See http://blog.stevenlevithan.com/archives/cross-browser-split
+// I've tested in many browsers and this seems to cover the deviant ones:
+// 'ab'.split(/(?:ab)*/) should be ["", ""], not [""]
+// '.'.split(/(.?)(.?)/) should be ["", ".", "", ""], not ["", ""]
+// 'tesst'.split(/(s)*/) should be ["t", undefined, "e", "s", "t"], not
+// [undefined, "t", undefined, "e", ...]
+// ''.split(/.?/) should be [], not [""]
+// '.'.split(/()()/) should be ["."], not ["", "", "."]
+
+if (
+ 'ab'.split(/(?:ab)*/).length !== 2 ||
+ '.'.split(/(.?)(.?)/).length !== 4 ||
+ 'tesst'.split(/(s)*/)[1] === 't' ||
+ 'test'.split(/(?:)/, -1).length !== 4 ||
+ ''.split(/.?/).length ||
+ '.'.split(/()()/).length > 1
+) {
+ (function () {
+ var compliantExecNpcg = typeof (/()??/).exec('')[1] === 'undefined'; // NPCG: nonparticipating capturing group
+ var maxSafe32BitInt = Math.pow(2, 32) - 1;
+
+ StringPrototype.split = function (separator, limit) {
+ var string = this;
+ if (typeof separator === 'undefined' && limit === 0) {
+ return [];
+ }
+
+ // If `separator` is not a regex, use native split
+ if (!isRegex(separator)) {
+ return strSplit(this, separator, limit);
+ }
+
+ var output = [];
+ var flags = (separator.ignoreCase ? 'i' : '') +
+ (separator.multiline ? 'm' : '') +
+ (separator.unicode ? 'u' : '') + // in ES6
+ (separator.sticky ? 'y' : ''), // Firefox 3+ and ES6
+ lastLastIndex = 0,
+ // Make `global` and avoid `lastIndex` issues by working with a copy
+ separator2, match, lastIndex, lastLength;
+ var separatorCopy = new RegExp(separator.source, flags + 'g');
+ string += ''; // Type-convert
+ if (!compliantExecNpcg) {
+ // Doesn't need flags gy, but they don't hurt
+ separator2 = new RegExp('^' + separatorCopy.source + '$(?!\\s)', flags);
+ }
+ /* Values for `limit`, per the spec:
+ * If undefined: 4294967295 // maxSafe32BitInt
+ * If 0, Infinity, or NaN: 0
+ * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
+ * If negative number: 4294967296 - Math.floor(Math.abs(limit))
+ * If other: Type-convert, then use the above rules
+ */
+ var splitLimit = typeof limit === 'undefined' ? maxSafe32BitInt : ES.ToUint32(limit);
+ match = separatorCopy.exec(string);
+ while (match) {
+ // `separatorCopy.lastIndex` is not reliable cross-browser
+ lastIndex = match.index + match[0].length;
+ if (lastIndex > lastLastIndex) {
+ array_push.call(output, strSlice(string, lastLastIndex, match.index));
+ // Fix browsers whose `exec` methods don't consistently return `undefined` for
+ // nonparticipating capturing groups
+ if (!compliantExecNpcg && match.length > 1) {
+ /* eslint-disable no-loop-func */
+ match[0].replace(separator2, function () {
+ for (var i = 1; i < arguments.length - 2; i++) {
+ if (typeof arguments[i] === 'undefined') {
+ match[i] = void 0;
+ }
+ }
+ });
+ /* eslint-enable no-loop-func */
+ }
+ if (match.length > 1 && match.index < string.length) {
+ array_push.apply(output, array_slice.call(match, 1));
+ }
+ lastLength = match[0].length;
+ lastLastIndex = lastIndex;
+ if (output.length >= splitLimit) {
+ break;
+ }
+ }
+ if (separatorCopy.lastIndex === match.index) {
+ separatorCopy.lastIndex++; // Avoid an infinite loop
+ }
+ match = separatorCopy.exec(string);
+ }
+ if (lastLastIndex === string.length) {
+ if (lastLength || !separatorCopy.test('')) {
+ array_push.call(output, '');
+ }
+ } else {
+ array_push.call(output, strSlice(string, lastLastIndex));
+ }
+ return output.length > splitLimit ? strSlice(output, 0, splitLimit) : output;
+ };
+ }());
+
+// [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) -> []
+} else if ('0'.split(void 0, 0).length) {
+ StringPrototype.split = function split(separator, limit) {
+ if (typeof separator === 'undefined' && limit === 0) { return []; }
+ return strSplit(this, separator, limit);
+ };
+}
+
+var str_replace = StringPrototype.replace;
+var replaceReportsGroupsCorrectly = (function () {
+ var groups = [];
+ 'x'.replace(/x(.)?/g, function (match, group) {
+ array_push.call(groups, group);
+ });
+ return groups.length === 1 && typeof groups[0] === 'undefined';
+}());
+
+if (!replaceReportsGroupsCorrectly) {
+ StringPrototype.replace = function replace(searchValue, replaceValue) {
+ var isFn = isCallable(replaceValue);
+ var hasCapturingGroups = isRegex(searchValue) && (/\)[*?]/).test(searchValue.source);
+ if (!isFn || !hasCapturingGroups) {
+ return str_replace.call(this, searchValue, replaceValue);
+ } else {
+ var wrappedReplaceValue = function (match) {
+ var length = arguments.length;
+ var originalLastIndex = searchValue.lastIndex;
+ searchValue.lastIndex = 0;
+ var args = searchValue.exec(match) || [];
+ searchValue.lastIndex = originalLastIndex;
+ array_push.call(args, arguments[length - 2], arguments[length - 1]);
+ return replaceValue.apply(this, args);
+ };
+ return str_replace.call(this, searchValue, wrappedReplaceValue);
+ }
+ };
+}
+
+// ECMA-262, 3rd B.2.3
+// Not an ECMAScript standard, 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
+var string_substr = StringPrototype.substr;
+var hasNegativeSubstrBug = ''.substr && '0b'.substr(-1) !== 'b';
+defineProperties(StringPrototype, {
+ substr: function substr(start, length) {
+ var normalizedStart = start;
+ if (start < 0) {
+ normalizedStart = max(this.length + start, 0);
+ }
+ return string_substr.call(this, normalizedStart, length);
+ }
+}, hasNegativeSubstrBug);
+
+// ES5 15.5.4.20
+// whitespace from: http://es5.github.io/#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';
+var zeroWidth = '\u200b';
+var wsRegexChars = '[' + ws + ']';
+var trimBeginRegexp = new RegExp('^' + wsRegexChars + wsRegexChars + '*');
+var trimEndRegexp = new RegExp(wsRegexChars + wsRegexChars + '*$');
+var hasTrimWhitespaceBug = StringPrototype.trim && (ws.trim() || !zeroWidth.trim());
+defineProperties(StringPrototype, {
+ // http://blog.stevenlevithan.com/archives/faster-trim-javascript
+ // http://perfectionkills.com/whitespace-deviations/
+ trim: function trim() {
+ if (typeof this === 'undefined' || this === null) {
+ throw new TypeError("can't convert " + this + ' to object');
+ }
+ return $String(this).replace(trimBeginRegexp, '').replace(trimEndRegexp, '');
+ }
+}, hasTrimWhitespaceBug);
+
+// ES-5 15.1.2.2
+if (parseInt(ws + '08') !== 8 || parseInt(ws + '0x16') !== 22) {
+ /* global parseInt: true */
+ parseInt = (function (origParseInt) {
+ var hexRegex = /^0[xX]/;
+ return function parseInt(str, radix) {
+ var string = $String(str).trim();
+ var defaultedRadix = $Number(radix) || (hexRegex.test(string) ? 16 : 10);
+ return origParseInt(string, defaultedRadix);
+ };
+ }(parseInt));
+}
+
+}));
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..b0775fd
--- /dev/null
+++ b/package.json
@@ -0,0 +1,72 @@
+{
+ "name": "es5-shim",
+ "version": "4.1.14",
+ "description": "ECMAScript 5 compatibility shims for legacy JavaScript engines",
+ "homepage": "http://github.com/es-shims/es5-shim/",
+ "contributors": [
+ "Kris Kowal <kris at cixar.com> (http://github.com/kriskowal/)",
+ "Sami Samhuri <sami.samhuri at gmail.com> (http://samhuri.net/)",
+ "Florian Schäfer <florian.schaefer at gmail.com> (http://github.com/fschaefer)",
+ "Irakli Gozalishvili <rfobic at gmail.com> (http://jeditoolkit.com)",
+ "Kit Cambridge <kitcambridge at gmail.com> (http://kitcambridge.github.com)",
+ "Jordan Harband <ljharb at gmail.com> (https://github.com/ljharb/)"
+ ],
+ "bugs": {
+ "mail": "ljharb at gmail.com",
+ "url": "http://github.com/es-shims/es5-shim/issues"
+ },
+ "license": "MIT",
+ "main": "es5-shim.js",
+ "repository": {
+ "type": "git",
+ "url": "http://github.com/es-shims/es5-shim.git"
+ },
+ "scripts": {
+ "minify": "npm run minify-shim && npm run minify-sham",
+ "minify-shim": "uglifyjs es5-shim.js --keep-fnames --comments --source-map=es5-shim.map -m -b ascii_only=true,beautify=false > es5-shim.min.js",
+ "minify-sham": "uglifyjs es5-sham.js --keep-fnames --comments --source-map=es5-sham.map -m -b ascii_only=true,beautify=false > es5-sham.min.js",
+ "test": "npm run lint && jasmine-node --matchall ./ tests/spec/",
+ "test-native": "jasmine-node --matchall tests/spec/",
+ "lint": "npm run jscs && npm run eslint",
+ "eslint": "eslint tests/helpers/*.js tests/spec/*.js es5-shim.js es5-sham.js",
+ "jscs": "jscs tests/helpers/*.js tests/spec/*.js es5-shim.js es5-sham.js"
+ },
+ "devDependencies": {
+ "eslint": "^1.5.1",
+ "@ljharb/eslint-config": "^1.2.0",
+ "jasmine-node": "^1.14.5",
+ "jscs": "^2.2.1",
+ "uglify-js": "^2.4.24",
+ "replace": "^0.3.0",
+ "semver": "^5.0.3"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ },
+ "testling": {
+ "browsers": [
+ "iexplore/6.0..latest",
+ "firefox/3.0..6.0",
+ "firefox/18.0..latest",
+ "firefox/nightly",
+ "chrome/4.0..10.0",
+ "chrome/25.0..latest",
+ "chrome/canary",
+ "opera/10.0..latest",
+ "opera/next",
+ "safari/4.0..latest",
+ "ipad/6.0..latest",
+ "iphone/6.0..latest",
+ "android-browser/4.2"
+ ]
+ },
+ "keywords": [
+ "shim",
+ "es5",
+ "es5 shim",
+ "javascript",
+ "ecmascript",
+ "polyfill"
+ ]
+}
+
diff --git a/shims.json b/shims.json
new file mode 100644
index 0000000..0c5430a
--- /dev/null
+++ b/shims.json
@@ -0,0 +1,7 @@
+{
+ "Object": {
+ "prototype": {},
+ "keys": "object-keys"
+ }
+}
+
diff --git a/tests/helpers/h-matchers.js b/tests/helpers/h-matchers.js
new file mode 100644
index 0000000..9ce9869
--- /dev/null
+++ b/tests/helpers/h-matchers.js
@@ -0,0 +1,40 @@
+/* global beforeEach, expect */
+
+var has = Object.prototype.hasOwnProperty;
+var getKeys = function (o) {
+ 'use strict';
+
+ var key, a = [];
+ for (key in o) {
+ if (has.call(o, key)) {
+ a.push(key);
+ }
+ }
+ return a;
+};
+
+beforeEach(function () {
+ 'use strict';
+
+ this.addMatchers({
+ toExactlyMatch: function (expected) {
+ var a1, a2, l, i, key;
+ var actual = this.actual;
+
+ 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;
+ }
+ });
+});
diff --git a/tests/index.html b/tests/index.html
new file mode 100644
index 0000000..a797edd
--- /dev/null
+++ b/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-matchers.js"></script>
+
+ <!-- include source files here... -->
+ <script src="../es5-shim.js"></script>
+ <script src="../es5-sham.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-number.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>
diff --git a/tests/index.min.html b/tests/index.min.html
new file mode 100644
index 0000000..b8ac467
--- /dev/null
+++ b/tests/index.min.html
@@ -0,0 +1,61 @@
+<!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-matchers.js"></script>
+
+ <!-- include source files here... -->
+ <script src="../es5-shim.min.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-number.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>
diff --git a/tests/lib/jasmine-html.js b/tests/lib/jasmine-html.js
new file mode 100644
index 0000000..7383401
--- /dev/null
+++ b/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;
+};
diff --git a/tests/lib/jasmine.css b/tests/lib/jasmine.css
new file mode 100644
index 0000000..6583fe7
--- /dev/null
+++ b/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;
+}
diff --git a/tests/lib/jasmine.js b/tests/lib/jasmine.js
new file mode 100644
index 0000000..edc003b
--- /dev/null
+++ b/tests/lib/jasmine.js
@@ -0,0 +1,2601 @@
+var isCommonJS = typeof window == "undefined" && typeof exports == "object";
+
+/**
+ * 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;
+
+/**
+ * Maximum levels of nesting that will be included when an object is pretty-printed
+ */
+jasmine.MAX_PRETTY_PRINT_DEPTH = 40;
+
+/**
+ * Default timeout interval in milliseconds for waitsFor() blocks.
+ */
+jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
+
+/**
+ * By default exceptions thrown in the context of a test are caught by jasmine so that it can run the remaining tests in the suite.
+ * Set to false to let the exception bubble up in the browser.
+ *
+ */
+jasmine.CATCH_EXCEPTIONS = true;
+
+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);
+};
+
+/**
+ * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the
+ * attributes on the object.
+ *
+ * @example
+ * // don't care about any other attributes than foo.
+ * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"});
+ *
+ * @param sample {Object} sample
+ * @returns matchable object for the sample
+ */
+jasmine.objectContaining = function (sample) {
+ return new jasmine.Matchers.ObjectContaining(sample);
+};
+
+/**
+ * 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
+ * @return {jasmine.Spy} 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
+ * @return {jasmine.Matchers}
+ */
+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, '&')
+ .replace(/</g, '<')
+ .replace(/>/g, '>');
+};
+
+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.compareRegExps_ = function(a, b, mismatchKeys, mismatchValues) {
+ if (a.source != b.source)
+ mismatchValues.push("expected pattern /" + b.source + "/ is not equal to the pattern /" + a.source + "/");
+
+ if (a.ignoreCase != b.ignoreCase)
+ mismatchValues.push("expected modifier i was" + (b.ignoreCase ? " " : " not ") + "set and does not equal the origin modifier");
+
+ if (a.global != b.global)
+ mismatchValues.push("expected modifier g was" + (b.global ? " " : " not ") + "set and does not equal the origin modifier");
+
+ if (a.multiline != b.multiline)
+ mismatchValues.push("expected modifier m was" + (b.multiline ? " " : " not ") + "set and does not equal the origin modifier");
+
+ if (a.sticky != b.sticky)
+ mismatchValues.push("expected modifier y was" + (b.sticky ? " " : " not ") + "set and does not equal the origin modifier");
+
+ return (mismatchValues.length === 0);
+};
+
+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.jasmineMatches) {
+ return a.jasmineMatches(b);
+ }
+
+ if (b.jasmineMatches) {
+ return b.jasmineMatches(a);
+ }
+
+ if (a instanceof jasmine.Matchers.ObjectContaining) {
+ return a.matches(b);
+ }
+
+ if (b instanceof jasmine.Matchers.ObjectContaining) {
+ 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 (a instanceof RegExp && b instanceof RegExp) {
+ return this.compareRegExps_(a, b, mismatchKeys, mismatchValues);
+ }
+
+ 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) {
+ if (!jasmine.CATCH_EXCEPTIONS) {
+ this.func.apply(this.spec);
+ }
+ else {
+ 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.toEqual() 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 compares the actual to NaN.
+ */
+jasmine.Matchers.prototype.toBeNaN = function() {
+ this.message = function() {
+ return [ "Expected " + jasmine.pp(this.actual) + " to be NaN." ];
+ };
+
+ return (this.actual !== this.actual);
+};
+
+/**
+ * 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() {
+ var invertedMessage = "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was.";
+ var positiveMessage = "";
+ if (this.actual.callCount === 0) {
+ positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.";
+ } else {
+ positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but actual calls were " + jasmine.pp(this.actual.argsForCall).replace(/^\[ | \]$/g, '')
+ }
+ return [positiveMessage, invertedMessage];
+ };
+
+ 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.toContain() 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, as number of decimal places
+ */
+jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) {
+ if (!(precision === 0)) {
+ precision = precision || 2;
+ }
+ return Math.abs(expected - this.actual) < (Math.pow(10, -precision) / 2);
+};
+
+/**
+ * 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.jasmineMatches = 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.jasmineToString = function() {
+ return '<jasmine.any(' + this.expectedClass + ')>';
+};
+
+jasmine.Matchers.ObjectContaining = function (sample) {
+ this.sample = sample;
+};
+
+jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) {
+ mismatchKeys = mismatchKeys || [];
+ mismatchValues = mismatchValues || [];
+
+ var env = jasmine.getEnv();
+
+ var hasKey = function(obj, keyName) {
+ return obj != null && obj[keyName] !== jasmine.undefined;
+ };
+
+ for (var property in this.sample) {
+ if (!hasKey(other, property) && hasKey(this.sample, property)) {
+ mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
+ }
+ else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) {
+ mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual.");
+ }
+ }
+
+ return (mismatchKeys.length === 0 && mismatchValues.length === 0);
+};
+
+jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () {
+ return "<jasmine.objectContaining(" + jasmine.pp(this.sample) + ")>";
+};
+// 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);
+ }
+};
+
+/**
+ * @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) {
+ 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.jasmineToString) {
+ this.emitScalar(value.jasmineToString());
+ } 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 (!obj.hasOwnProperty(property)) continue;
+ 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) {
+ if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) {
+ this.append("Array");
+ return;
+ }
+
+ 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) {
+ if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) {
+ this.append("Object");
+ return;
+ }
+
+ 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;
+
+ // parallel to blocks. each true value in this array means the block will
+ // get executed even if we abort
+ this.ensured = [];
+ this.blocks = [];
+ this.running = false;
+ this.index = 0;
+ this.offset = 0;
+ this.abort = false;
+};
+
+jasmine.Queue.prototype.addBefore = function(block, ensure) {
+ if (ensure === jasmine.undefined) {
+ ensure = false;
+ }
+
+ this.blocks.unshift(block);
+ this.ensured.unshift(ensure);
+};
+
+jasmine.Queue.prototype.add = function(block, ensure) {
+ if (ensure === jasmine.undefined) {
+ ensure = false;
+ }
+
+ this.blocks.push(block);
+ this.ensured.push(ensure);
+};
+
+jasmine.Queue.prototype.insertNext = function(block, ensure) {
+ if (ensure === jasmine.undefined) {
+ ensure = false;
+ }
+
+ this.ensured.splice((this.index + this.offset + 1), 0, ensure);
+ 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 && !this.ensured[self.index])) {
+ 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), true);
+ } 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), true);
+ }
+ 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), true);
+ }
+ }
+ for (i = 0; i < runner.after_.length; i++) {
+ this.queue.add(new jasmine.Block(this.env, runner.after_[i], this), true);
+ }
+};
+
+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);
+ }
+};
+
+jasmine.version_= {
+ "major": 1,
+ "minor": 3,
+ "build": 1,
+ "revision": 1354556913
+};
+
diff --git a/tests/lib/jasmine_favicon.png b/tests/lib/jasmine_favicon.png
new file mode 100644
index 0000000..218f3b4
Binary files /dev/null and b/tests/lib/jasmine_favicon.png differ
diff --git a/tests/lib/json2.js b/tests/lib/json2.js
new file mode 100644
index 0000000..ac58079
--- /dev/null
+++ b/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 ' '),
+ 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');
+ };
+ }
+}());
diff --git a/tests/native.html b/tests/native.html
new file mode 100644
index 0000000..82631ff
--- /dev/null
+++ b/tests/native.html
@@ -0,0 +1,58 @@
+<!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-matchers.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-number.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>
diff --git a/tests/spec/helpers-jasmine.js b/tests/spec/helpers-jasmine.js
new file mode 100644
index 0000000..66ace86
--- /dev/null
+++ b/tests/spec/helpers-jasmine.js
@@ -0,0 +1,2 @@
+/* globals require */
+require('../helpers/h-matchers');
diff --git a/tests/spec/s-array.js b/tests/spec/s-array.js
new file mode 100644
index 0000000..4de5872
--- /dev/null
+++ b/tests/spec/s-array.js
@@ -0,0 +1,1320 @@
+/* global describe, it, expect, beforeEach, jasmine, xit */
+
+var toStr = Object.prototype.toString;
+// var canDistinguishSparseFromUndefined = 0 in [undefined]; // IE 6 - 8 have a bug where this returns false.
+var hasStrictMode = (function () {
+ 'use strict';
+
+ return !this;
+}());
+
+describe('Array', function () {
+ var testSubject;
+ beforeEach(function () {
+ testSubject = [2, 3, undefined, true, 'hej', null, false, 0];
+ delete testSubject[1];
+ });
+ var createArrayLikeFromArray = 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 () {
+ 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!';
+ 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 a boxed object as list argument of callback', function () {
+ var listArg;
+ Array.prototype.forEach.call('foo', function (item, index, list) {
+ listArg = list;
+ });
+ expect(typeof listArg).toBe('object');
+ expect(toStr.call(listArg)).toBe('[object String]');
+ });
+ if (hasStrictMode) {
+ it('does not autobox the content in strict mode', function () {
+ var context;
+ [1].forEach(function () {
+ 'use strict';
+
+ context = this;
+ }, 'x');
+ expect(typeof context).toBe('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;
+ return numberOfRuns === 3;
+ });
+ 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;
+ return numberOfRuns === 3;
+ }, 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;
+ return numberOfRuns === 3;
+ });
+ 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;
+ return numberOfRuns === 3;
+ }, o);
+ expect(actual).toExactlyMatch(expected);
+ });
+ it('should have a boxed object as list argument of callback', function () {
+ var listArg;
+ Array.prototype.some.call('foo', function (item, index, list) {
+ listArg = list;
+ });
+ expect(typeof listArg).toBe('object');
+ expect(toStr.call(listArg)).toBe('[object String]');
+ });
+ });
+ 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;
+ return numberOfRuns !== 3;
+ });
+ 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;
+ return numberOfRuns !== 3;
+ }, 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;
+ return numberOfRuns !== 3;
+ });
+ 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;
+ return numberOfRuns !== 3;
+ }, o);
+ expect(actual).toExactlyMatch(expected);
+ });
+ it('should have a boxed object as list argument of callback', function () {
+ var listArg;
+ Array.prototype.every.call('foo', function (item, index, list) {
+ listArg = list;
+ });
+ expect(typeof listArg).toBe('object');
+ expect(toStr.call(listArg)).toBe('[object String]');
+ });
+ });
+
+ describe('indexOf', function () {
+ 'use strict';
+
+ var actual, expected;
+
+ 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).toBe(expected);
+ });
+ it('should not find the element', function () {
+ expected = -1;
+ actual = testSubject.indexOf('mus');
+ expect(actual).toBe(expected);
+ });
+ it('should find undefined as well', function () {
+ expected = -1;
+ actual = testSubject.indexOf(undefined);
+ expect(actual).not.toBe(expected);
+ });
+ it('should skip unset indexes', function () {
+ expected = 2;
+ actual = testSubject.indexOf(undefined);
+ expect(actual).toBe(expected);
+ });
+ it('should use a strict test', function () {
+ actual = testSubject.indexOf(null);
+ expect(actual).toBe(5);
+
+ actual = testSubject.indexOf('2');
+ expect(actual).toBe(-1);
+ });
+ it('should skip the first if fromIndex is set', function () {
+ expect(testSubject.indexOf(2, 2)).toBe(6);
+ expect(testSubject.indexOf(2, 0)).toBe(0);
+ expect(testSubject.indexOf(2, 6)).toBe(6);
+ });
+ it('should work with negative fromIndex', function () {
+ expect(testSubject.indexOf(2, -3)).toBe(6);
+ expect(testSubject.indexOf(2, -9)).toBe(0);
+ });
+ it('should work with fromIndex being greater than the length', function () {
+ expect(testSubject.indexOf(0, 20)).toBe(-1);
+ });
+ it('should work with fromIndex being negative and greater than the length', function () {
+ expect(testSubject.indexOf('hej', -20)).toBe(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).toBe(expected);
+ });
+ it('should not find the element (array-like)', function () {
+ expected = -1;
+ actual = indexOf.call(testAL, 'mus');
+ expect(actual).toBe(expected);
+ });
+ it('should find undefined as well (array-like)', function () {
+ expected = -1;
+ actual = indexOf.call(testAL, undefined);
+ expect(actual).not.toBe(expected);
+ });
+ it('should skip unset indexes (array-like)', function () {
+ expected = 2;
+ actual = indexOf.call(testAL, undefined);
+ expect(actual).toBe(expected);
+ });
+ it('should use a strict test (array-like)', function () {
+ actual = Array.prototype.indexOf.call(testAL, null);
+ expect(actual).toBe(5);
+
+ actual = Array.prototype.indexOf.call(testAL, '2');
+ expect(actual).toBe(-1);
+ });
+ it('should skip the first if fromIndex is set (array-like)', function () {
+ expect(indexOf.call(testAL, 2, 2)).toBe(6);
+ expect(indexOf.call(testAL, 2, 0)).toBe(0);
+ expect(indexOf.call(testAL, 2, 6)).toBe(6);
+ });
+ it('should work with negative fromIndex (array-like)', function () {
+ expect(indexOf.call(testAL, 2, -3)).toBe(6);
+ expect(indexOf.call(testAL, 2, -9)).toBe(0);
+ });
+ it('should work with fromIndex being greater than the length (array-like)', function () {
+ expect(indexOf.call(testAL, 0, 20)).toBe(-1);
+ });
+ it('should work with fromIndex being negative and greater than the length (array-like)', function () {
+ expect(indexOf.call(testAL, 'hej', -20)).toBe(4);
+ });
+ });
+ });
+ describe('lastIndexOf', function () {
+ 'use strict';
+
+ var actual, expected;
+
+ 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).toBe(expected);
+ });
+ it('should not find the element', function () {
+ expected = -1;
+ actual = testSubject.lastIndexOf('mus');
+ expect(actual).toBe(expected);
+ });
+ it('should find undefined as well', function () {
+ expected = -1;
+ actual = testSubject.lastIndexOf(undefined);
+ expect(actual).not.toBe(expected);
+ });
+ it('should skip unset indexes', function () {
+ expected = 2;
+ actual = testSubject.lastIndexOf(undefined);
+ expect(actual).toBe(expected);
+ });
+ it('should use a strict test', function () {
+ actual = testSubject.lastIndexOf(null);
+ expect(actual).toBe(5);
+
+ actual = testSubject.lastIndexOf('2');
+ expect(actual).toBe(-1);
+ });
+ it('should skip the first if fromIndex is set', function () {
+ expect(testSubject.lastIndexOf(2, 2)).toBe(0);
+ expect(testSubject.lastIndexOf(2, 0)).toBe(0);
+ expect(testSubject.lastIndexOf(2, 6)).toBe(6);
+ });
+ it('should work with negative fromIndex', function () {
+ expect(testSubject.lastIndexOf(2, -3)).toBe(6);
+ expect(testSubject.lastIndexOf(2, -9)).toBe(0);
+ });
+ it('should work with fromIndex being greater than the length', function () {
+ expect(testSubject.lastIndexOf(2, 20)).toBe(6);
+ });
+ it('should work with fromIndex being negative and greater than the length', function () {
+ expect(testSubject.lastIndexOf(2, -20)).toBe(-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).toBe(expected);
+ });
+ it('should not find the element (array-like)', function () {
+ expected = -1;
+ actual = lastIndexOf.call(testAL, 'mus');
+ expect(actual).toBe(expected);
+ });
+ it('should find undefined as well (array-like)', function () {
+ expected = -1;
+ actual = lastIndexOf.call(testAL, undefined);
+ expect(actual).not.toBe(expected);
+ });
+ it('should skip unset indexes (array-like)', function () {
+ expected = 2;
+ actual = lastIndexOf.call(testAL, undefined);
+ expect(actual).toBe(expected);
+ });
+ it('should use a strict test (array-like)', function () {
+ actual = lastIndexOf.call(testAL, null);
+ expect(actual).toBe(5);
+
+ actual = lastIndexOf.call(testAL, '2');
+ expect(actual).toBe(-1);
+ });
+ it('should skip the first if fromIndex is set', function () {
+ expect(lastIndexOf.call(testAL, 2, 2)).toBe(0);
+ expect(lastIndexOf.call(testAL, 2, 0)).toBe(0);
+ expect(lastIndexOf.call(testAL, 2, 6)).toBe(6);
+ });
+ it('should work with negative fromIndex', function () {
+ expect(lastIndexOf.call(testAL, 2, -3)).toBe(6);
+ expect(lastIndexOf.call(testAL, 2, -9)).toBe(0);
+ });
+ it('should work with fromIndex being greater than the length', function () {
+ expect(lastIndexOf.call(testAL, 2, 20)).toBe(6);
+ });
+ it('should work with fromIndex being negative and greater than the length', function () {
+ expect(lastIndexOf.call(testAL, 2, -20)).toBe(-1);
+ });
+ });
+ });
+
+ describe('filter', function () {
+ var filteredArray,
+ callback = function callback(o, i) {
+ 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 predicate = jasmine.createSpy('predicate');
+ var arr = ['1'];
+ arr.filter(predicate);
+ expect(predicate).toHaveBeenCalledWith('1', 0, arr);
+ });
+ it('should not affect elements added to the array after it has begun', function () {
+ var arr = [1, 2, 3];
+ var 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 predicate with the proper arguments', function () {
+ var predicate = jasmine.createSpy('predicate');
+ var arr = createArrayLikeFromArray(['1']);
+ Array.prototype.filter.call(arr, predicate);
+ expect(predicate).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;
+ arr.length += 1;
+ }
+ return true;
+ });
+ expect(arr).toEqual([1, 2, 3, 4, 5, 6]);
+ expect(i).toBe(3);
+ });
+ it('should skip non-set values', function () {
+ var passedValues = createArrayLikeFromArray([]);
+ testSubject = createArrayLikeFromArray([1, 2, 3, 4]);
+ delete testSubject[1];
+ Array.prototype.filter.call(testSubject, function (o, i) {
+ passedValues[i] = o;
+ passedValues.length = i + 1;
+ return true;
+ });
+ expect(passedValues).toEqual(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;
+ this.length = i + 1;
+ return true;
+ }, passedValues);
+ expect(passedValues).toEqual(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);
+ });
+ });
+ it('should have a boxed object as list argument of callback', function () {
+ var actual;
+ Array.prototype.filter.call('foo', function (item, index, list) {
+ actual = list;
+ });
+ expect(typeof actual).toBe('object');
+ expect(toStr.call(actual)).toBe('[object String]');
+ });
+ });
+ describe('map', function () {
+ var callback;
+ beforeEach(function () {
+ var i = 0;
+ callback = function () {
+ return i++;
+ };
+ });
+ describe('Array object', function () {
+ it('should call mapper with the right parameters', function () {
+ var mapper = jasmine.createSpy('mapper');
+ var array = [1];
+ array.map(mapper);
+ expect(mapper).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 mapper with the right parameters', function () {
+ var mapper = jasmine.createSpy('mapper');
+ var array = createArrayLikeFromArray([1]);
+ Array.prototype.map.call(array, mapper);
+ expect(mapper).toHaveBeenCalledWith(1, 0, array);
+ });
+ it('should set the context correctly', function () {
+ var context = {};
+ Array.prototype.map.call(testSubject, function (o, i) {
+ this[i] = o;
+ this.length = i + 1;
+ }, context);
+ expect(context).toEqual(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;
+ });
+ expect(arr).toEqual([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);
+ });
+ });
+ it('should have a boxed object as list argument of callback', function () {
+ var actual;
+ Array.prototype.map.call('foo', function (item, index, list) {
+ actual = list;
+ });
+ expect(typeof actual).toBe('object');
+ expect(toStr.call(actual)).toBe('[object String]');
+ });
+ });
+
+ 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 String(a || '') + String(b || '');
+ })).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 String(a || '') + String(b || '');
+ })).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);
+ });
+ });
+ it('should have a boxed object as list argument of callback', function () {
+ var actual;
+ Array.prototype.reduce.call('foo', function (accumulator, item, index, list) {
+ actual = list;
+ });
+ expect(typeof actual).toBe('object');
+ expect(toStr.call(actual)).toBe('[object String]');
+ });
+ });
+ 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 work as expected for empty arrays with an initial value', function () {
+ var spy = jasmine.createSpy(),
+ result;
+
+ result = [].reduceRight(spy, '');
+ expect(spy).not.toHaveBeenCalled();
+ expect(result).toBe('');
+ });
+ 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 String(a || '') + String(b || '');
+ })).toBe('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 String(a || '') + String(b || '');
+ })).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);
+ });
+ });
+ it('should have a boxed object as list argument of callback', function () {
+ var actual;
+ Array.prototype.reduceRight.call('foo', function (accumulator, item, index, list) {
+ actual = list;
+ });
+ expect(typeof actual).toBe('object');
+ expect(toStr.call(actual)).toBe('[object String]');
+ });
+ });
+
+ describe('isArray', function () {
+ it('should be true for Array', function () {
+ expect(Array.isArray([])).toBe(true);
+ });
+
+ it('should be false for primitives', function () {
+ var primitives = [
+ 'foo',
+ true,
+ false,
+ 42,
+ 0,
+ -0,
+ NaN,
+ Infinity,
+ -Infinity
+ ];
+ primitives.forEach(function (v) {
+ expect(Array.isArray(v)).toBe(false);
+ });
+ });
+
+ it('should fail for other objects', function () {
+ var objects = [
+ {},
+ /foo/,
+ arguments
+ ];
+ if (Object.create) {
+ objects.push(Object.create(null));
+ }
+
+ objects.forEach(function (v) {
+ expect(Array.isArray(v)).toBe(false);
+ });
+ });
+
+ /* globals document */
+ if (typeof document !== 'undefined') {
+ it('should be false for an HTML element', function () {
+ var el = document.getElementsByTagName('div');
+ expect(Array.isArray(el)).toBe(false);
+ });
+ }
+ });
+
+ describe('unshift', function () {
+ it('should return length', function () {
+ expect([].unshift(0)).toBe(1);
+ });
+ });
+
+ describe('splice', function () {
+ var b = ['b'];
+ var a = [1, 'a', b];
+ var test;
+
+ var makeArray = function (l, givenPrefix) {
+ var prefix = givenPrefix || '';
+ var length = l;
+ var arr = [];
+ while (length--) {
+ arr.unshift(prefix + Array(length + 1).join(' ') + length);
+ }
+ return arr;
+ };
+
+ beforeEach(function () {
+ test = a.slice(0);
+ });
+
+ it('has the right length', function () {
+ expect(Array.prototype.splice.length).toBe(2);
+ });
+
+ /* This test is disabled, because ES6 normalizes actual
+ * browser behavior, contradicting ES5.
+ */
+ xit('treats undefined deleteCount as 0', function () {
+ expect(test.splice(0).length).toBe(0);
+ expect(test.splice(0)).toEqual(test.splice(0, 0));
+ });
+
+ it('basic implementation test 1', function () {
+ expect(test.splice(0, 0)).toEqual([]);
+ });
+
+ it('basic implementation test 2', function () {
+ test.splice(0, 2);
+ expect(test).toEqual([b]);
+ });
+
+ it('should return right result 1', function () {
+ var array = [];
+
+ array.splice(0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20);
+ array.splice(1, 0, 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12', 'F13', 'F14', 'F15', 'F16', 'F17', 'F18', 'F19', 'F20', 'F21', 'F22', 'F23', 'F24', 'F25', 'F26');
+ array.splice(5, 0, 'XXX');
+
+ expect(array).toEqual([1, 'F1', 'F2', 'F3', 'F4', 'XXX', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12', 'F13', 'F14', 'F15', 'F16', 'F17', 'F18', 'F19', 'F20', 'F21', 'F22', 'F23', 'F24', 'F25', 'F26', 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]);
+ });
+
+ it('should return right result 2', function () {
+ var array = makeArray(6);
+
+ array.splice(array.length - 1, 1, '');
+ array.splice(0, 1, 1, 2, 3, 4);
+ array.splice(0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45);
+
+ array.splice(4, 0, '99999999999999');
+
+ expect(array).toEqual([1, 2, 3, 4, '99999999999999', 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 1, 2, 3, 4, ' 1', ' 2', ' 3', ' 4', '']);
+ });
+
+ it('should return right result 3', function () {
+ var array = [1, 2, 3];
+
+ array.splice(0, array.length);
+ array.splice(0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+ array.splice(1, 1, 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12', 'F13', 'F14', 'F15', 'F16', 'F17', 'F18', 'F19', 'F20', 'F21', 'F22', 'F23', 'F24', 'F25', 'F26');
+ array.splice(5, 1, 'YYY', 'XXX');
+ array.splice(0, 1);
+ array.splice(0, 2);
+ array.pop();
+ array.push.apply(array, makeArray(10, '-'));
+ array.splice(array.length - 2, 10);
+ array.splice();
+ array.splice(1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+ array.splice(1, 1, 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12', 'F13', 'F14', 'F15', 'F16', 'F17', 'F18', 'F19', 'F20', 'F21', 'F22', 'F23', 'F24', 'F25', 'F26', 1, 23, 4, 5, 6, 7, 8);
+ array.splice(30, 10);
+ array.splice(30, 1);
+ array.splice(30, 0);
+ array.splice(2, 5, 1, 2, 3, 'P', 'LLL', 'CCC', 'YYY', 'XXX');
+ array.push(1, 2, 3, 4, 5, 6);
+ array.splice(1, 6, 1, 2, 3, 4, 5, 6, 7, 8, 9, 4, 5, 6, 7, 8, 9);
+ array.splice(3, 7);
+ array.unshift(7, 8, 9, 10, 11);
+ array.pop();
+ array.splice(5, 2);
+ array.pop();
+ array.unshift.apply(array, makeArray(8, '~'));
+ array.pop();
+ array.splice(3, 1, 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12', 'F13', 'F14', 'F15', 'F16', 'F17', 'F18', 'F19', 'F20', 'F21', 'F22', 'F23', 'F24', 'F25', 'F26', 1, 23, 4, 5, 6, 7, 8);
+ array.splice(4, 5, 'P', 'LLL', 'CCC', 'YYY', 'XXX');
+
+ expect(array).toEqual(['~0', '~ 1', '~ 2', 'F1', 'P', 'LLL', 'CCC', 'YYY', 'XXX', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12', 'F13', 'F14', 'F15', 'F16', 'F17', 'F18', 'F19', 'F20', 'F21', 'F22', 'F23', 'F24', 'F25', 'F26', 1, 23, 4, 5, 6, 7, 8, '~ 4', '~ 5', '~ 6', '~ 7', 7, 8, 9, 10, 11, 2, 4, 5, 6, 7, 8, 9, 'CCC', 'YYY', 'XXX', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12', 'F13', 'F14', 'F15', 'F16', 'F17', 'F18', 'F19', 'F20', 'F21', 'F22', 'F23', 'F24', 'F25', 'F2 [...]
+ });
+
+ it('should do nothing if method called with no arguments', function () {
+ expect(test.splice()).toEqual([]);
+ expect(test).toEqual(a);
+ });
+
+ 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 work with objects - adding 1', function () {
+ var obj = {};
+ Array.prototype.splice.call(obj, 0, 0, 1, 2, 3);
+ expect(obj.length).toBe(3);
+ });
+
+ it('should work with objects - adding 2', function () {
+ var obj = { 0: 1, length: 1 };
+ Array.prototype.splice.call(obj, 1, 0, 2, 3);
+ expect(obj.length).toBe(3);
+ });
+
+ it('should work with objects - removing', function () {
+ var obj = { 0: 1, 1: 2, 2: 3, length: 3 };
+ Array.prototype.splice.call(obj, 0, 3);
+ expect(obj.length).toBe(0);
+ });
+
+ it('should work with objects - replacing', function () {
+ var obj = { 0: 99, length: 1 };
+ Array.prototype.splice.call(obj, 0, 1, 1, 2, 3);
+ expect(obj.length).toBe(3);
+ expect(obj[0]).toBe(1);
+ });
+
+ it('should not break on sparse arrays in Opera', function () {
+ // test from https://github.com/wikimedia/VisualEditor/blob/d468b00311e69c2095b9da360c5745153342a5c3/src/ve.utils.js#L182-L196
+ var n = 256;
+ var arr = [];
+ arr[n] = 'a';
+ arr.splice(n + 1, 0, 'b');
+ expect(arr[n]).toBe('a');
+ });
+
+ it('should not break on sparse arrays in Safari 7/8', function () {
+ // test from https://github.com/wikimedia/VisualEditor/blob/d468b00311e69c2095b9da360c5745153342a5c3/src/ve.utils.js#L182-L196
+ var justFine = new Array(1e5 - 1);
+ justFine[10] = 'x';
+ var tooBig = new Array(1e5);
+ tooBig[8] = 'x';
+
+ justFine.splice(1, 1);
+ expect(8 in justFine).toBe(false);
+ expect(justFine.indexOf('x')).toBe(9);
+ tooBig.splice(1, 1);
+ expect(6 in tooBig).toBe(false);
+ expect(tooBig.indexOf('x')).toBe(7);
+ });
+ });
+
+});
diff --git a/tests/spec/s-date.js b/tests/spec/s-date.js
new file mode 100644
index 0000000..c2ef3ce
--- /dev/null
+++ b/tests/spec/s-date.js
@@ -0,0 +1,247 @@
+/* global describe, it, xit, expect, beforeEach, jasmine */
+
+describe('Date', function () {
+ 'use strict';
+
+ var supportsDescriptors = Object.defineProperty && (function () {
+ try {
+ var obj = {};
+ Object.defineProperty(obj, 'x', { enumerable: false, value: obj });
+ for (var _ in obj) { return false; }
+ return obj.x === obj;
+ } catch (e) { /* this is ES3 */
+ return false;
+ }
+ }());
+ var ifSupportsDescriptorsIt = supportsDescriptors ? it : xit;
+ var has = Object.prototype.hasOwnProperty;
+
+ describe('now', function () {
+ it('should be the current time', function () {
+ var before = (new Date()).getTime();
+ var middle = Date.now();
+ var after = (new Date()).getTime();
+ expect(middle).not.toBeLessThan(before);
+ expect(middle).not.toBeGreaterThan(after);
+ });
+ });
+
+ describe('constructor', function () {
+ it('works with standard formats', function () {
+ // Chrome 19 Opera 12 Firefox 11 IE 9 Safari 5.1.1
+ expect(+new Date('2012-12-31T23:59:59.000Z')).toBe(1356998399000); // 1356998399000 1356998399000 1356998399000 1356998399000 1356998399000
+ expect(+new Date('2012-04-04T05:02:02.170Z')).toBe(1333515722170); // 1333515722170 1333515722170 1333515722170 1333515722170 1333515722170
+ expect(+new Date('2012-04-04T05:02:02.170999Z')).toBe(1333515722170); // 1333515722170 1333515722170 1333515722170 1333515722170 1333515722170
+ expect(+new Date('2012-04-04T05:02:02.17Z')).toBe(1333515722170); // 1333515722170 1333515722170 1333515722170 1333515722170 1333515722170
+ expect(+new Date('2012-04-04T05:02:02.1Z')).toBe(1333515722100); // 1333515722170 1333515722170 1333515722170 1333515722170 1333515722170
+ expect(+new Date('2012-04-04T24:00:00.000Z')).toBe(1333584000000); // NaN 1333584000000 1333584000000 1333584000000 1333584000000
+ expect(+new Date('2012-02-29T12:00:00.000Z')).toBe(1330516800000); // 1330516800000 1330516800000 1330516800000 1330516800000 1330516800000
+ expect(+new Date('2011-03-01T12:00:00.000Z')).toBe(1298980800000); // 1298980800000 1298980800000 1298980800000 1298980800000 1298980800000
+
+ // https://github.com/es-shims/es5-shim/issues/80 Safari bug with leap day
+ expect(new Date('2034-03-01T00:00:00.000Z') -
+ new Date('2034-02-27T23:59:59.999Z')).toBe(86400001); // 86400001 86400001 86400001 86400001 1
+
+ });
+
+ ifSupportsDescriptorsIt('is not enumerable', function () {
+ expect(Object.keys(new Date())).not.toContain('constructor');
+ });
+
+ it('works as a function', function () {
+ expect(Date(0)).toBe(String(Date(0)));
+ expect(Date(1441705534578)).toBe(String(Date(1441705534578)));
+ });
+ });
+
+ describe('parse', function () {
+ // TODO: Write the rest of the test.
+
+ ifSupportsDescriptorsIt('is not enumerable', function () {
+ expect(Object.getOwnPropertyDescriptor(Date, 'parse').enumerable).toBe(false);
+ });
+
+ it('should be an invalid date', 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:60.000Z')).toBeFalsy(); // NaN NaN NaN NaN 1356998400000
+ 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('2011-02-29T12:00:00.000Z')).toBeFalsy(); // 1298980800000 NaN NaN 1298980800000 NaN
+ });
+
+ it('should work', function () {
+ var dates = {
+ // Chrome 19 Opera 12 Firefox 11 IE 9 Safari 5.1.1 Safari 8
+ '2012-12-31T23:59:59.000Z': 1356998399000, // 1356998399000 1356998399000 1356998399000 1356998399000 1356998399000 1356998399000
+ '2012-04-04T05:02:02.170Z': 1333515722170, // 1333515722170 1333515722170 1333515722170 1333515722170 1333515722170 1333515722170
+ '2012-04-04T05:02:02.170999Z': 1333515722170, // 1333515722170 1333515722170 1333515722170 1333515722170 1333515722170 1333515722170.999
+ '2012-04-04T05:02:02.17Z': 1333515722170, // 1333515722170 1333515722170 1333515722170 1333515722170 1333515722170 1333515722170
+ '2012-04-04T05:02:02.1Z': 1333515722100, // 1333515722170 1333515722170 1333515722170 1333515722170 1333515722170 1333515722170
+ '2012-04-04T24:00:00.000Z': 1333584000000, // NaN 1333584000000 1333584000000 1333584000000 1333584000000 1333584000000
+ '2012-02-29T12:00:00.000Z': 1330516800000, // 1330516800000 1330516800000 1330516800000 1330516800000 1330516800000 1330516800000
+ '2011-03-01T12:00:00.000Z': 1298980800000 // 1298980800000 1298980800000 1298980800000 1298980800000 1298980800000 1298980800000
+ };
+ for (var str in dates) {
+ if (has.call(dates, str)) {
+ expect(Math.floor(Date.parse(str))).toBe(dates[str]);
+ }
+ }
+
+ // https://github.com/es-shims/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
+
+ });
+
+ it('should support extended years', function () {
+ // Chrome 19 Opera 12 Firefox 11 IE 9 Safari 5.1.1
+ expect(Date.parse('0000-01-01T00:00:00.000Z')).toBe(-621672192e5); // -621672192e5 -621672192e5 -621672192e5 -621672192e5 -621672192e5
+ expect(Date.parse('0001-01-01T00:00:00Z')).toBe(-621355968e5); // -621355968e5 -621355968e5 -621355968e5 8.64e15 -621355968e5
+ 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
+ expect(Date.parse('+033658-09-27T01:46:40.000Z')).toBe(1e15); // 1e15 NaN 1e15 1e15 9999999136e5
+ expect(Date.parse('-000001-01-01T00:00:00Z')).toBe(-621987552e5); // -621987552e5 NaN -621987552e5 -621987552e5 -621987552e5
+ expect(Date.parse('+002009-12-15T00:00:00Z')).toBe(12608352e5); // 12608352e5 NaN 12608352e5 12608352e5 12608352e5
+ });
+
+ it('works with timezone offsets', function () {
+ // Chrome 19 Opera 12 Firefox 11 IE 9 Safari 5.1.1
+ 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
+ });
+
+ it('should be able to coerce to a number', function () {
+ var actual = Number(new Date(1970, 0));
+ var expected = parseInt(actual, 10);
+ expect(actual).toBeDefined();
+ expect(actual).toEqual(expected);
+ expect(isNaN(actual)).toBeFalsy();
+ });
+
+ });
+
+ describe('toString', function () {
+ var actual;
+
+ beforeEach(function () {
+ actual = (new Date(1970, 0)).toString();
+ });
+
+ it('should show correct date info for ' + actual, function () {
+ expect(actual).toMatch(/1970/);
+ expect(actual).toMatch(/jan/i);
+ expect(actual).toMatch(/thu/i);
+ expect(actual).toMatch(/00:00:00/);
+ });
+ });
+
+ describe('valueOf', function () {
+ // Note that new Date(1970, 0).valueOf() is 0 in UTC timezone.
+ // Check check that it's a number (and an int), not that it's "truthy".
+ var actual;
+
+ beforeEach(function () {
+ actual = (new Date(1970, 0)).valueOf();
+ });
+ it('should give a numeric value', function () {
+ expect(typeof actual).toBe('number');
+ });
+ it('should not be NaN', function () {
+ expect(isNaN(actual)).toBe(false);
+ });
+ it('should give an int value', function () {
+ expect(actual).toBe(Math.floor(actual));
+ });
+ });
+
+ describe('getUTCDate', function () {
+ it('should return the right value for negative dates', function () {
+ // Opera 10.6/11.61/Opera 12 bug
+ expect(new Date(-3509827334573292).getUTCDate()).toBe(1);
+ });
+ });
+
+ describe('getUTCMonth', function () {
+ it('should return the right value for negative dates', function () {
+ // Opera 10.6/11.61/Opera 12 bug
+ expect(new Date(-3509827334573292).getUTCMonth()).toBe(0);
+ });
+
+ it('should return correct values', function () {
+ expect(new Date(8.64e15).getUTCMonth()).toBe(8);
+ expect(new Date(0).getUTCMonth()).toBe(0);
+ });
+ });
+
+ 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) {
+ /* invalid json */
+ expect(e).not.toEqual(jasmine.any(Error));
+ }
+ 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));
+ });
+ });
+
+});
diff --git a/tests/spec/s-function.js b/tests/spec/s-function.js
new file mode 100644
index 0000000..de84946
--- /dev/null
+++ b/tests/spec/s-function.js
@@ -0,0 +1,176 @@
+/* global describe, it, expect, beforeEach */
+
+describe('Function', function () {
+ 'use strict';
+
+ describe('apply', function () {
+ it('works with arraylike objects', function () {
+ var arrayLike = { length: 4, 0: 1, 2: 4, 3: true };
+ var expectedArray = [1, undefined, 4, true];
+ var actualArray = (function () {
+ return Array.prototype.slice.apply(arguments);
+ }.apply(null, arrayLike));
+ expect(actualArray).toEqual(expectedArray);
+ });
+ });
+
+ describe('bind', function () {
+ var actual;
+
+ var testSubject = {
+ push: function (o) {
+ this.a.push(o);
+ }
+ };
+
+ var func = 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('has the new instance\'s context as a constructor', function () {
+ var actualContext;
+ var expectedContext = { foo: 'bar' };
+ testSubject.Func = function () {
+ actualContext = this;
+ }.bind(expectedContext);
+ var result = new testSubject.Func();
+ expect(result).toBeTruthy();
+ expect(actualContext).not.toBe(expectedContext);
+ });
+ it('passes the correct arguments as a constructor', function () {
+ var expected = { name: 'Correct' };
+ testSubject.Func = function (arg) {
+ expect(this.hasOwnProperty('name')).toBe(false);
+ return arg;
+ }.bind({ name: 'Incorrect' });
+ var 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 () {
+ expect(this).not.toBe(oracle);
+ return oracle;
+ }.bind(null);
+ var result = new Subject();
+ expect(result).toBe(oracle);
+ });
+
+ it('returns the correct value if constructor returns primitive', function () {
+ var Subject = function (oracle) {
+ expect(this).not.toBe(oracle);
+ return oracle;
+ }.bind(null);
+
+ var primitives = ['asdf', null, true, 1];
+ for (var i = 0; i < primitives.length; ++i) {
+ expect(new Subject(primitives[i])).not.toBe(primitives[i]);
+ }
+
+ var objects = [[1, 2, 3], {}, function () {}];
+ for (var j = 0; j < objects.length; ++j) {
+ expect(new Subject(objects[j])).toBe(objects[j]);
+ }
+ });
+ 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);
+ });
+ it('sets a correct length without thisArg', function () {
+ var Subject = function (a, b, c) { return a + b + c; }.bind();
+ expect(Subject.length).toBe(3);
+ });
+ it('sets a correct length with thisArg', function () {
+ var Subject = function (a, b, c) { return a + b + c + this.d; }.bind({ d: 1 });
+ expect(Subject.length).toBe(3);
+ });
+ it('sets a correct length with thisArg and first argument', function () {
+ var Subject = function (a, b, c) { return a + b + c + this.d; }.bind({ d: 1 }, 1);
+ expect(Subject.length).toBe(2);
+ });
+ it('sets a correct length without thisArg and first argument', function () {
+ var Subject = function (a, b, c) { return a + b + c; }.bind(undefined, 1);
+ expect(Subject.length).toBe(2);
+ });
+ it('sets a correct length without thisArg and too many argument', function () {
+ var Subject = function (a, b, c) { return a + b + c; }.bind(undefined, 1, 2, 3, 4);
+ expect(Subject.length).toBe(0);
+ });
+ });
+});
diff --git a/tests/spec/s-global.js b/tests/spec/s-global.js
new file mode 100644
index 0000000..81f03c6
--- /dev/null
+++ b/tests/spec/s-global.js
@@ -0,0 +1,55 @@
+/* global describe, it, xit, expect */
+
+describe('global methods', function () {
+ 'use strict';
+
+ var foo = function foo() {};
+ var functionsHaveNames = foo.name === 'foo';
+ var ifFunctionsHaveNamesIt = functionsHaveNames ? it : xit;
+
+ describe('parseInt', function () {
+ ifFunctionsHaveNamesIt('has the right name', function () {
+ expect(parseInt.name).toBe('parseInt');
+ });
+
+ it('accepts a radix', function () {
+ for (var i = 2; i <= 36; ++i) {
+ expect(parseInt('10', i)).toBe(i);
+ }
+ });
+
+ it('defaults the radix to 10 when the number does not start with 0x or 0X', function () {
+ [
+ '01',
+ '08',
+ '10',
+ '42'
+ ].forEach(function (str) {
+ expect(parseInt(str)).toBe(parseInt(str, 10));
+ });
+ });
+
+ it('defaults the radix to 16 when the number starts with 0x or 0X', function () {
+ expect(parseInt('0x16')).toBe(parseInt('0x16', 16));
+ expect(parseInt('0X16')).toBe(parseInt('0X16', 16));
+ });
+
+ it('ignores leading whitespace', function () {
+ expect(parseInt(' 0x16')).toBe(parseInt('0x16', 16));
+ expect(parseInt(' 42')).toBe(parseInt('42', 10));
+ expect(parseInt(' 08')).toBe(parseInt('08', 10));
+
+ 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';
+ expect(parseInt(ws + '08')).toBe(parseInt('08', 10));
+ expect(parseInt(ws + '0x16')).toBe(parseInt('0x16', 16));
+ });
+
+ it('defaults the radix properly when not a true number', function () {
+ var fakeZero = { valueOf: function () { return 0; } };
+ expect(parseInt('08', fakeZero)).toBe(parseInt('08', 10));
+ expect(parseInt('0x16', fakeZero)).toBe(parseInt('0x16', 16));
+ });
+ });
+});
diff --git a/tests/spec/s-number.js b/tests/spec/s-number.js
new file mode 100644
index 0000000..9955486
--- /dev/null
+++ b/tests/spec/s-number.js
@@ -0,0 +1,37 @@
+/* global describe, it, expect */
+
+describe('Number', function () {
+ 'use strict';
+
+ describe('toFixed', function () {
+ it('should convert numbers correctly', function () {
+ expect((0.00008).toFixed(3)).toBe('0.000');
+ expect((0.9).toFixed(0)).toBe('1');
+ expect((1.255).toFixed(2)).toBe('1.25');
+ expect((1843654265.0774949).toFixed(5)).toBe('1843654265.07749');
+ expect((1000000000000000128).toFixed(0)).toBe('1000000000000000128');
+ });
+ });
+
+ describe('constants', function () {
+ it('should have MAX_VALUE', function () {
+ expect(Number.MAX_VALUE).toBe(1.7976931348623157e308);
+ });
+
+ it('should have MIN_VALUE', function () {
+ expect(Number.MIN_VALUE).toBe(5e-324);
+ });
+
+ it('should have NaN', function () {
+ expect(Number.NaN).not.toBe(Number.NaN);
+ });
+
+ it('should have POSITIVE_INFINITY', function () {
+ expect(Number.POSITIVE_INFINITY).toBe(Infinity);
+ });
+
+ it('should have NEGATIVE_INFINITY', function () {
+ expect(Number.NEGATIVE_INFINITY).toBe(-Infinity);
+ });
+ });
+});
diff --git a/tests/spec/s-object.js b/tests/spec/s-object.js
new file mode 100644
index 0000000..a638e5d
--- /dev/null
+++ b/tests/spec/s-object.js
@@ -0,0 +1,359 @@
+/* global describe, it, xit, expect, beforeEach, jasmine, window */
+
+var ifWindowIt = typeof window === 'undefined' ? xit : it;
+var extensionsPreventible = typeof Object.preventExtensions === 'function' && (function () {
+ var obj = {};
+ Object.preventExtensions(obj);
+ obj.a = 3;
+ return obj.a !== 3;
+}());
+var ifExtensionsPreventibleIt = extensionsPreventible ? it : xit;
+var canSeal = typeof Object.seal === 'function' && (function () {
+ var obj = { a: 3 };
+ Object.seal(obj);
+ delete obj.a;
+ return obj.a === 3;
+}());
+var ifCanSealIt = canSeal ? it : xit;
+var canFreeze = typeof Object.freeze === 'function' && (function () {
+ var obj = {};
+ Object.freeze(obj);
+ obj.a = 3;
+ return obj.a !== 3;
+}());
+var ifCanFreezeIt = canFreeze ? it : xit;
+
+describe('Object', function () {
+ 'use strict';
+
+ describe('.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);
+ });
+
+ describe('arguments objects', function () {
+ it('works with an arguments object', function () {
+ (function () {
+ expect(arguments.length).toBe(3);
+ expect(Object.keys(arguments).length).toBe(arguments.length);
+ expect(Object.keys(arguments)).toEqual(['0', '1', '2']);
+ }(1, 2, 3));
+ });
+
+ it('works with a legacy arguments object', function () {
+ var FakeArguments = function (args) {
+ args.forEach(function (arg, i) {
+ this[i] = arg;
+ }.bind(this));
+ };
+ FakeArguments.prototype.length = 3;
+ FakeArguments.prototype.callee = function () {};
+
+ var fakeOldArguments = new FakeArguments(['a', 'b', 'c']);
+ expect(Object.keys(fakeOldArguments)).toEqual(['0', '1', '2']);
+ });
+ });
+
+ 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);
+ });
+ });
+
+ // ES6 Object.keys does not require this throw
+ xit('should throw error for non object', function () {
+ var e = {};
+ expect(function () {
+ try {
+ Object.keys(42);
+ } catch (err) {
+ throw e;
+ }
+ }).toThrow(e);
+ });
+
+ describe('enumerating over non-enumerable properties', function () {
+ it('has no enumerable keys on a Function', function () {
+ var Foo = function () {};
+ expect(Object.keys(Foo.prototype)).toEqual([]);
+ });
+
+ it('has no enumerable keys on a boolean', function () {
+ expect(Object.keys(Boolean.prototype)).toEqual([]);
+ });
+
+ it('has no enumerable keys on an object', function () {
+ expect(Object.keys(Object.prototype)).toEqual([]);
+ });
+ });
+
+ it('works with boxed primitives', function () {
+ expect(Object.keys(Object('hello'))).toEqual(['0', '1', '2', '3', '4']);
+ });
+
+ it('works with boxed primitives with extra properties', function () {
+ var x = Object('x');
+ x.y = 1;
+ var actual = Object.keys(x);
+ var expected = ['0', 'y'];
+ actual.sort();
+ expected.sort();
+ expect(actual).toEqual(expected);
+ });
+
+ ifWindowIt('can serialize all objects on the `window`', function () {
+ var has = Object.prototype.hasOwnProperty;
+ var keys, exception;
+ var blacklistedKeys = ['window', 'console', 'parent', 'self', 'frames', 'frameElement'];
+ if (Object.defineProperty) {
+ Object.defineProperty(window, 'thrower', { configurable: true, get: function () { throw new RangeError('thrower!'); } });
+ }
+ for (var k in window) {
+ keys = exception = void 0;
+ if (blacklistedKeys.indexOf(k) === -1 && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') {
+ try {
+ keys = Object.keys(window[k]);
+ } catch (e) {
+ exception = e;
+ }
+ expect(Array.isArray(keys)).toEqual(true);
+ expect(exception).toBeUndefined();
+ }
+ }
+ delete window.thrower;
+ });
+ });
+
+ describe('Object.isExtensible', function () {
+ var obj = { };
+
+ it('should return true if object is extensible', function () {
+ expect(Object.isExtensible(obj)).toBe(true);
+ });
+
+ ifExtensionsPreventibleIt('should return false if object is not extensible', function () {
+ expect(Object.isExtensible(Object.preventExtensions(obj))).toBe(false);
+ });
+
+ ifCanSealIt('should return false if object is sealed', function () {
+ expect(Object.isExtensible(Object.seal(obj))).toBe(false);
+ });
+
+ ifCanFreezeIt('should return false if object is frozen', function () {
+ expect(Object.isExtensible(Object.freeze(obj))).toBe(false);
+ });
+
+ it('should throw error for non object', function () {
+ try {
+ // note: in ES6, this is expected to return false.
+ expect(Object.isExtensible(42)).toBe(false);
+ } catch (err) {
+ expect(err).toEqual(jasmine.any(TypeError));
+ }
+ });
+ });
+
+ describe('Object.defineProperty', function () {
+ var obj;
+
+ beforeEach(function () {
+ obj = {};
+
+ Object.defineProperty(obj, 'name', {
+ value: 'Testing',
+ configurable: true,
+ enumerable: true,
+ writable: true
+ });
+ });
+
+ it('should return the initial value', function () {
+ expect(obj.hasOwnProperty('name')).toBeTruthy();
+ expect(obj.name).toBe('Testing');
+ });
+
+ it('should be setable', function () {
+ obj.name = 'Other';
+ expect(obj.name).toBe('Other');
+ });
+
+ it('should return the parent initial value', function () {
+ var child = Object.create(obj, {});
+
+ expect(child.name).toBe('Testing');
+ expect(child.hasOwnProperty('name')).toBeFalsy();
+ });
+
+ it('should not override the parent value', function () {
+ var child = Object.create(obj, {});
+
+ Object.defineProperty(child, 'name', {
+ value: 'Other'
+ });
+
+ expect(obj.name).toBe('Testing');
+ expect(child.name).toBe('Other');
+ });
+
+ it('should throw error for non object', function () {
+ expect(function () {
+ Object.defineProperty(42, 'name', {});
+ }).toThrow();
+ });
+
+ it('should not throw error for empty descriptor', function () {
+ expect(function () {
+ Object.defineProperty({}, 'name', {});
+ }).not.toThrow();
+ });
+ });
+
+ describe('Object.getOwnPropertyDescriptor', function () {
+ it('should return undefined because the object does not own the property', function () {
+ var descr = Object.getOwnPropertyDescriptor({}, 'name');
+
+ expect(descr).toBeUndefined();
+ });
+
+ it('should return a data descriptor', function () {
+ var descr = Object.getOwnPropertyDescriptor({ name: 'Testing' }, 'name');
+
+ expect(descr).not.toBeUndefined();
+ expect(descr.value).toBe('Testing');
+ expect(descr.writable).toBe(true);
+ expect(descr.enumerable).toBe(true);
+ expect(descr.configurable).toBe(true);
+ });
+
+ it('should return undefined because the object does not own the property', function () {
+ var descr = Object.getOwnPropertyDescriptor(Object.create({ name: 'Testing' }, {}), 'name');
+
+ expect(descr).toBeUndefined();
+ });
+
+ it('should return a data descriptor', function () {
+ var obj = Object.create({}, {
+ name: {
+ value: 'Testing',
+ configurable: true,
+ enumerable: true,
+ writable: true
+ }
+ });
+
+ var descr = Object.getOwnPropertyDescriptor(obj, 'name');
+
+ expect(descr).not.toBeUndefined();
+ expect(descr.value).toBe('Testing');
+ expect(descr.writable).toBe(true);
+ expect(descr.enumerable).toBe(true);
+ expect(descr.configurable).toBe(true);
+ });
+
+ it('should throw error for non object', function () {
+ try {
+ // note: in ES6, we expect this to return undefined.
+ expect(Object.getOwnPropertyDescriptor(42, 'name')).toBeUndefined();
+ } catch (err) {
+ expect(err).toEqual(jasmine.any(TypeError));
+ }
+ });
+ });
+
+ describe('Object.getPrototypeOf', function () {
+ it('should return the [[Prototype]] of an object', function () {
+ var Foo = function () {};
+
+ var proto = Object.getPrototypeOf(new Foo());
+
+ expect(proto).toBe(Foo.prototype);
+ });
+
+ it('should shamone to the `Object.prototype` if `object.constructor` is not a function', function () {
+ var Foo = function () {};
+ Foo.prototype.constructor = 1;
+
+ var proto = Object.getPrototypeOf(new Foo()),
+ fnToString = Function.prototype.toString;
+
+ if (fnToString.call(Object.getPrototypeOf).indexOf('[native code]') < 0) {
+ expect(proto).toBe(Object.prototype);
+ } else {
+ expect(proto).toBe(Foo.prototype);
+ }
+ });
+
+ it('should throw error for non object', function () {
+ try {
+ expect(Object.getPrototypeOf(1)).toBe(Number.prototype); // ES6 behavior
+ } catch (err) {
+ expect(err).toEqual(jasmine.any(TypeError));
+ }
+ });
+
+ it('should return null on Object.create(null)', function () {
+ var obj = Object.create(null);
+
+ expect(Object.getPrototypeOf(obj)).toBe(null);
+ });
+ });
+
+ describe('Object.defineProperties', function () {
+ it('should define the constructor property', function () {
+ var target = {};
+ var newProperties = {
+ constructor: {
+ value: 'new constructor'
+ }
+ };
+ Object.defineProperties(target, newProperties);
+ expect(target.constructor).toEqual('new constructor');
+ });
+ });
+
+ describe('Object.create', function () {
+ it('should create objects with no properties when called as `Object.create(null)`', function () {
+ var obj = Object.create(null);
+
+ expect('hasOwnProperty' in obj).toBe(false);
+ expect('toString' in obj).toBe(false);
+ expect('constructor' in obj).toBe(false);
+
+ var protoIsEnumerable = false;
+ for (var k in obj) {
+ if (k === '__proto__') {
+ protoIsEnumerable = true;
+ }
+ }
+ expect(protoIsEnumerable).toBe(false);
+
+ expect(obj instanceof Object).toBe(false);
+ });
+ });
+});
diff --git a/tests/spec/s-string.js b/tests/spec/s-string.js
new file mode 100644
index 0000000..de62924
--- /dev/null
+++ b/tests/spec/s-string.js
@@ -0,0 +1,232 @@
+/* global describe, it, expect */
+
+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).toBe(13);
+ });
+
+ it('does not trim the zero-width space', function () {
+ expect('\u200b'.trim()).toBe('\u200b');
+ expect('\u200b'.trim().length).toBe(1);
+ });
+ });
+
+ describe('replace', function () {
+ it('returns undefined for non-capturing groups', function () {
+ var groups = [];
+ 'x'.replace(/x(.)?/g, function (m, group) {
+ groups.push(group); /* "" in FF, `undefined` in CH/WK/IE */
+ });
+ expect(groups.length).toBe(1);
+ expect(groups[0]).toBeUndefined();
+ });
+
+ it('should not fail in Firefox', function () {
+ expect(function () {
+ return '* alef\n* beth \n* gimel~0\n'.replace(
+ /(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
+ function (match, m1, m2, m3, m4) { return '<li>' + m4 + '</li>\n'; }
+ );
+ }).not.toThrow();
+ });
+ });
+
+ 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([]);
+ });
+
+ describe('Tests from Steven Levithan', function () {
+ it("''.split() results in ['']", function () {
+ expect(''.split()).toEqual(['']);
+ });
+ it("''.split(/./) results in ['']", function () {
+ expect(''.split(/./)).toEqual(['']);
+ });
+ it("''.split(/.?/) results in []", function () {
+ expect(''.split(/.?/)).toEqual([]);
+ });
+ it("''.split(/.??/) results in []", function () {
+ expect(''.split(/.??/)).toEqual([]);
+ });
+ it("'ab'.split(/a*/) results in ['', 'b']", function () {
+ expect('ab'.split(/a*/)).toEqual(['', 'b']);
+ });
+ it("'ab'.split(/a*?/) results in ['a', 'b']", function () {
+ expect('ab'.split(/a*?/)).toEqual(['a', 'b']);
+ });
+ it("'ab'.split(/(?:ab)/) results in ['', '']", function () {
+ expect('ab'.split(/(?:ab)/)).toEqual(['', '']);
+ });
+ it("'ab'.split(/(?:ab)*/) results in ['', '']", function () {
+ expect('ab'.split(/(?:ab)*/)).toEqual(['', '']);
+ });
+ it("'ab'.split(/(?:ab)*?/) results in ['a', 'b']", function () {
+ expect('ab'.split(/(?:ab)*?/)).toEqual(['a', 'b']);
+ });
+ it("'test'.split('') results in ['t', 'e', 's', 't']", function () {
+ expect('test'.split('')).toEqual(['t', 'e', 's', 't']);
+ });
+ it("'test'.split() results in ['test']", function () {
+ expect('test'.split()).toEqual(['test']);
+ });
+ it("'111'.split(1) results in ['', '', '', '']", function () {
+ expect('111'.split(1)).toEqual(['', '', '', '']);
+ });
+ it("'test'.split(/(?:)/, 2) results in ['t', 'e']", function () {
+ expect('test'.split(/(?:)/, 2)).toEqual(['t', 'e']);
+ });
+ it("'test'.split(/(?:)/, -1) results in ['t', 'e', 's', 't']", function () {
+ expect('test'.split(/(?:)/, -1)).toEqual(['t', 'e', 's', 't']);
+ });
+ it("'test'.split(/(?:)/, undefined) results in ['t', 'e', 's', 't']", function () {
+ expect('test'.split(/(?:)/, undefined)).toEqual(['t', 'e', 's', 't']);
+ });
+ it("'test'.split(/(?:)/, null) results in []", function () {
+ expect('test'.split(/(?:)/, null)).toEqual([]);
+ });
+ it("'test'.split(/(?:)/, NaN) results in []", function () {
+ expect('test'.split(/(?:)/, NaN)).toEqual([]);
+ });
+ it("'test'.split(/(?:)/, true) results in ['t']", function () {
+ expect('test'.split(/(?:)/, true)).toEqual(['t']);
+ });
+ it("'test'.split(/(?:)/, '2') results in ['t', 'e']", function () {
+ expect('test'.split(/(?:)/, '2')).toEqual(['t', 'e']);
+ });
+ it("'test'.split(/(?:)/, 'two') results in []", function () {
+ expect('test'.split(/(?:)/, 'two')).toEqual([]);
+ });
+ it("'a'.split(/-/) results in ['a']", function () {
+ expect('a'.split(/-/)).toEqual(['a']);
+ });
+ it("'a'.split(/-?/) results in ['a']", function () {
+ expect('a'.split(/-?/)).toEqual(['a']);
+ });
+ it("'a'.split(/-??/) results in ['a']", function () {
+ expect('a'.split(/-??/)).toEqual(['a']);
+ });
+ it("'a'.split(/a/) results in ['', '']", function () {
+ expect('a'.split(/a/)).toEqual(['', '']);
+ });
+ it("'a'.split(/a?/) results in ['', '']", function () {
+ expect('a'.split(/a?/)).toEqual(['', '']);
+ });
+ it("'a'.split(/a??/) results in ['a']", function () {
+ expect('a'.split(/a??/)).toEqual(['a']);
+ });
+ it("'ab'.split(/-/) results in ['ab']", function () {
+ expect('ab'.split(/-/)).toEqual(['ab']);
+ });
+ it("'ab'.split(/-?/) results in ['a', 'b']", function () {
+ expect('ab'.split(/-?/)).toEqual(['a', 'b']);
+ });
+ it("'ab'.split(/-??/) results in ['a', 'b']", function () {
+ expect('ab'.split(/-??/)).toEqual(['a', 'b']);
+ });
+ it("'a-b'.split(/-/) results in ['a', 'b']", function () {
+ expect('a-b'.split(/-/)).toEqual(['a', 'b']);
+ });
+ it("'a-b'.split(/-?/) results in ['a', 'b']", function () {
+ expect('a-b'.split(/-?/)).toEqual(['a', 'b']);
+ });
+ it("'a-b'.split(/-??/) results in ['a', '-', 'b']", function () {
+ expect('a-b'.split(/-??/)).toEqual(['a', '-', 'b']);
+ });
+ it("'a--b'.split(/-/) results in ['a', '', 'b']", function () {
+ expect('a--b'.split(/-/)).toEqual(['a', '', 'b']);
+ });
+ it("'a--b'.split(/-?/) results in ['a', '', 'b']", function () {
+ expect('a--b'.split(/-?/)).toEqual(['a', '', 'b']);
+ });
+ it("'a--b'.split(/-??/) results in ['a', '-', '-', 'b']", function () {
+ expect('a--b'.split(/-??/)).toEqual(['a', '-', '-', 'b']);
+ });
+ it("''.split(/()()/) results in []", function () {
+ expect(''.split(/()()/)).toEqual([]);
+ });
+ it("'.'.split(/()()/) results in ['.']", function () {
+ expect('.'.split(/()()/)).toEqual(['.']);
+ });
+ it("'.'.split(/(.?)(.?)/) results in ['', '.', '', '']", function () {
+ expect('.'.split(/(.?)(.?)/)).toEqual(['', '.', '', '']);
+ });
+ it("'.'.split(/(.??)(.??)/) results in ['.']", function () {
+ expect('.'.split(/(.??)(.??)/)).toEqual(['.']);
+ });
+ it("'.'.split(/(.)?(.)?/) results in ['', '.', undefined, '']", function () {
+ expect('.'.split(/(.)?(.)?/)).toEqual(['', '.', undefined, '']);
+ });
+ it("'A<B>bold</B>and<CODE>coded</CODE>'.split(/<(\\/)?([^<>]+)>/) results in ['A', undefined, 'B', 'bold', '/', 'B', 'and', undefined, 'CODE', 'coded', '/', 'CODE', '']", function () {
+ expect('A<B>bold</B>and<CODE>coded</CODE>'.split(/<(\/)?([^<>]+)>/)).toEqual(['A', undefined, 'B', 'bold', '/', 'B', 'and', undefined, 'CODE', 'coded', '/', 'CODE', '']);
+ });
+ it("'tesst'.split(/(s)*/) results in ['t', undefined, 'e', 's', 't']", function () {
+ expect('tesst'.split(/(s)*/)).toEqual(['t', undefined, 'e', 's', 't']);
+ });
+ it("'tesst'.split(/(s)*?/) results in ['t', undefined, 'e', undefined, 's', undefined, 's', undefined, 't']", function () {
+ expect('tesst'.split(/(s)*?/)).toEqual(['t', undefined, 'e', undefined, 's', undefined, 's', undefined, 't']);
+ });
+ it("'tesst'.split(/(s*)/) results in ['t', '', 'e', 'ss', 't']", function () {
+ expect('tesst'.split(/(s*)/)).toEqual(['t', '', 'e', 'ss', 't']);
+ });
+ it("'tesst'.split(/(s*?)/) results in ['t', '', 'e', '', 's', '', 's', '', 't']", function () {
+ expect('tesst'.split(/(s*?)/)).toEqual(['t', '', 'e', '', 's', '', 's', '', 't']);
+ });
+ it("'tesst'.split(/(?:s)*/) results in ['t', 'e', 't']", function () {
+ expect('tesst'.split(/(?:s)*/)).toEqual(['t', 'e', 't']);
+ });
+ it("'tesst'.split(/(?=s+)/) results in ['te', 's', 'st']", function () {
+ expect('tesst'.split(/(?=s+)/)).toEqual(['te', 's', 'st']);
+ });
+ it("'test'.split('t') results in ['', 'es', '']", function () {
+ expect('test'.split('t')).toEqual(['', 'es', '']);
+ });
+ it("'test'.split('es') results in ['t', 't']", function () {
+ expect('test'.split('es')).toEqual(['t', 't']);
+ });
+ it("'test'.split(/t/) results in ['', 'es', '']", function () {
+ expect('test'.split(/t/)).toEqual(['', 'es', '']);
+ });
+ it("'test'.split(/es/) results in ['t', 't']", function () {
+ expect('test'.split(/es/)).toEqual(['t', 't']);
+ });
+ it("'test'.split(/(t)/) results in ['', 't', 'es', 't', '']", function () {
+ expect('test'.split(/(t)/)).toEqual(['', 't', 'es', 't', '']);
+ });
+ it("'test'.split(/(es)/) results in ['t', 'es', 't']", function () {
+ expect('test'.split(/(es)/)).toEqual(['t', 'es', 't']);
+ });
+ it("'test'.split(/(t)(e)(s)(t)/) results in ['', 't', 'e', 's', 't', '']", function () {
+ expect('test'.split(/(t)(e)(s)(t)/)).toEqual(['', 't', 'e', 's', 't', '']);
+ });
+ it("'.'.split(/(((.((.??)))))/) results in ['', '.', '.', '.', '', '', '']", function () {
+ expect('.'.split(/(((.((.??)))))/)).toEqual(['', '.', '.', '.', '', '', '']);
+ });
+ it("'.'.split(/(((((.??)))))/) results in ['.']", function () {
+ expect('.'.split(/(((((.??)))))/)).toEqual(['.']);
+ });
+ it("'a b c d'.split(/ /, -(Math.pow(2, 32) - 1)) results in ['a']", function () {
+ expect('a b c d'.split(/ /, -(Math.pow(2, 32) - 1))).toEqual(['a']);
+ });
+ it("'a b c d'.split(/ /, Math.pow(2, 32) + 1) results in ['a']", function () {
+ expect('a b c d'.split(/ /, Math.pow(2, 32) + 1)).toEqual(['a']);
+ });
+ it("'a b c d'.split(/ /, Infinity) results in []", function () {
+ expect('a b c d'.split(/ /, Infinity)).toEqual([]);
+ });
+ });
+ });
+});
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-es5-shim.git
More information about the Pkg-javascript-commits
mailing list