0
0
mirror of https://github.com/twbs/bootstrap.git synced 2024-11-29 11:24:18 +01:00

Merge branch 'v4-dev' of https://github.com/twbs/bootstrap into v4-dev

This commit is contained in:
Mark Otto 2017-02-07 18:04:44 -08:00
commit c56a651b7d
6 changed files with 4994 additions and 4826 deletions

View File

@ -1,8 +1,8 @@
source 'https://rubygems.org' source 'https://rubygems.org'
group :development, :test do group :development, :test do
gem 'jekyll', '~> 3.3.1' gem 'jekyll', '~> 3.4.0'
gem 'jekyll-redirect-from', '~> 0.12.0' gem 'jekyll-redirect-from', '~> 0.12.1'
gem 'jekyll-sitemap', '~> 0.12.0' gem 'jekyll-sitemap', '~> 1.0.0'
gem 'scss_lint', '~> 0.51.0' gem 'scss_lint', '~> 0.52.0'
end end

View File

@ -4,9 +4,9 @@ GEM
addressable (2.5.0) addressable (2.5.0)
public_suffix (~> 2.0, >= 2.0.2) public_suffix (~> 2.0, >= 2.0.2)
colorator (1.1.0) colorator (1.1.0)
ffi (1.9.14) ffi (1.9.17)
forwardable-extended (2.6.0) forwardable-extended (2.6.0)
jekyll (3.3.1) jekyll (3.4.0)
addressable (~> 2.4) addressable (~> 2.4)
colorator (~> 1.0) colorator (~> 1.0)
jekyll-sass-converter (~> 1.0) jekyll-sass-converter (~> 1.0)
@ -17,15 +17,15 @@ GEM
pathutil (~> 0.9) pathutil (~> 0.9)
rouge (~> 1.7) rouge (~> 1.7)
safe_yaml (~> 1.0) safe_yaml (~> 1.0)
jekyll-redirect-from (0.12.0) jekyll-redirect-from (0.12.1)
jekyll (~> 3.3) jekyll (~> 3.3)
jekyll-sass-converter (1.5.0) jekyll-sass-converter (1.5.0)
sass (~> 3.4) sass (~> 3.4)
jekyll-sitemap (0.12.0) jekyll-sitemap (1.0.0)
jekyll (~> 3.3) jekyll (~> 3.3)
jekyll-watch (1.5.0) jekyll-watch (1.5.0)
listen (~> 3.0, < 3.1) listen (~> 3.0, < 3.1)
kramdown (1.13.1) kramdown (1.13.2)
liquid (3.0.6) liquid (3.0.6)
listen (3.0.8) listen (3.0.8)
rb-fsevent (~> 0.9, >= 0.9.4) rb-fsevent (~> 0.9, >= 0.9.4)
@ -36,12 +36,12 @@ GEM
public_suffix (2.0.5) public_suffix (2.0.5)
rake (12.0.0) rake (12.0.0)
rb-fsevent (0.9.8) rb-fsevent (0.9.8)
rb-inotify (0.9.7) rb-inotify (0.9.8)
ffi (>= 0.5.0) ffi (>= 0.5.0)
rouge (1.11.1) rouge (1.11.1)
safe_yaml (1.0.4) safe_yaml (1.0.4)
sass (3.4.23) sass (3.4.23)
scss_lint (0.51.0) scss_lint (0.52.0)
rake (>= 0.9, < 13) rake (>= 0.9, < 13)
sass (~> 3.4.20) sass (~> 3.4.20)
@ -49,10 +49,10 @@ PLATFORMS
ruby ruby
DEPENDENCIES DEPENDENCIES
jekyll (~> 3.3.1) jekyll (~> 3.4.0)
jekyll-redirect-from (~> 0.12.0) jekyll-redirect-from (~> 0.12.1)
jekyll-sitemap (~> 0.12.0) jekyll-sitemap (~> 1.0.0)
scss_lint (~> 0.51.0) scss_lint (~> 0.52.0)
BUNDLED WITH BUNDLED WITH
1.13.6 1.13.6

View File

@ -16,7 +16,7 @@
}, },
"acorn": { "acorn": {
"version": "4.0.4", "version": "4.0.4",
"from": "acorn@>=4.0.1 <5.0.0", "from": "acorn@4.0.4",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.4.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.4.tgz",
"dev": true "dev": true
}, },
@ -49,15 +49,15 @@
} }
}, },
"ajv": { "ajv": {
"version": "4.10.3", "version": "4.11.2",
"from": "ajv@>=4.7.0 <5.0.0", "from": "ajv@>=4.7.0 <5.0.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-4.10.3.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.2.tgz",
"dev": true "dev": true
}, },
"ajv-keywords": { "ajv-keywords": {
"version": "1.5.0", "version": "1.5.1",
"from": "ajv-keywords@>=1.0.0 <2.0.0", "from": "ajv-keywords@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.0.tgz", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz",
"dev": true "dev": true
}, },
"align-text": { "align-text": {
@ -66,12 +66,6 @@
"resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
"dev": true "dev": true
}, },
"amdefine": {
"version": "1.0.1",
"from": "amdefine@>=0.0.4",
"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
"dev": true
},
"ansi-escapes": { "ansi-escapes": {
"version": "1.4.0", "version": "1.4.0",
"from": "ansi-escapes@>=1.1.0 <2.0.0", "from": "ansi-escapes@>=1.1.0 <2.0.0",
@ -79,9 +73,9 @@
"dev": true "dev": true
}, },
"ansi-regex": { "ansi-regex": {
"version": "2.0.0", "version": "2.1.1",
"from": "ansi-regex@>=2.0.0 <3.0.0", "from": "ansi-regex@>=2.0.0 <3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"dev": true "dev": true
}, },
"ansi-styles": { "ansi-styles": {
@ -139,15 +133,13 @@
"version": "2.0.0", "version": "2.0.0",
"from": "arr-diff@>=2.0.0 <3.0.0", "from": "arr-diff@>=2.0.0 <3.0.0",
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
"dev": true, "dev": true
"optional": true
}, },
"arr-flatten": { "arr-flatten": {
"version": "1.0.1", "version": "1.0.1",
"from": "arr-flatten@>=1.0.1 <2.0.0", "from": "arr-flatten@>=1.0.1 <2.0.0",
"resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.0.1.tgz", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.0.1.tgz",
"dev": true, "dev": true
"optional": true
}, },
"array-differ": { "array-differ": {
"version": "1.0.0", "version": "1.0.0",
@ -161,12 +153,6 @@
"resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
"dev": true "dev": true
}, },
"array-index": {
"version": "1.0.0",
"from": "array-index@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/array-index/-/array-index-1.0.0.tgz",
"dev": true
},
"array-union": { "array-union": {
"version": "1.0.2", "version": "1.0.2",
"from": "array-union@>=1.0.1 <2.0.0", "from": "array-union@>=1.0.1 <2.0.0",
@ -183,8 +169,7 @@
"version": "0.2.1", "version": "0.2.1",
"from": "array-unique@>=0.2.1 <0.3.0", "from": "array-unique@>=0.2.1 <0.3.0",
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
"dev": true, "dev": true
"optional": true
}, },
"arrify": { "arrify": {
"version": "1.0.1", "version": "1.0.1",
@ -192,6 +177,12 @@
"resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
"dev": true "dev": true
}, },
"asap": {
"version": "2.0.5",
"from": "asap@>=2.0.3 <2.1.0",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.5.tgz",
"dev": true
},
"asn1": { "asn1": {
"version": "0.2.3", "version": "0.2.3",
"from": "asn1@>=0.2.3 <0.3.0", "from": "asn1@>=0.2.3 <0.3.0",
@ -230,9 +221,9 @@
"dev": true "dev": true
}, },
"autoprefixer": { "autoprefixer": {
"version": "6.6.1", "version": "6.7.2",
"from": "autoprefixer@>=6.6.1 <7.0.0", "from": "autoprefixer@>=6.7.2 <7.0.0",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.6.1.tgz", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.2.tgz",
"dev": true "dev": true
}, },
"aws-sign2": { "aws-sign2": {
@ -248,15 +239,15 @@
"dev": true "dev": true
}, },
"babel-code-frame": { "babel-code-frame": {
"version": "6.20.0", "version": "6.22.0",
"from": "babel-code-frame@>=6.16.0 <7.0.0", "from": "babel-code-frame@>=6.16.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.20.0.tgz", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-core": { "babel-core": {
"version": "6.21.0", "version": "6.22.1",
"from": "babel-core@>=6.0.12 <7.0.0", "from": "babel-core@>=6.0.12 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.21.0.tgz", "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.22.1.tgz",
"dev": true "dev": true
}, },
"babel-eslint": { "babel-eslint": {
@ -266,9 +257,9 @@
"dev": true "dev": true
}, },
"babel-generator": { "babel-generator": {
"version": "6.21.0", "version": "6.22.0",
"from": "babel-generator@>=6.21.0 <7.0.0", "from": "babel-generator@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.21.0.tgz", "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.22.0.tgz",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"jsesc": { "jsesc": {
@ -280,141 +271,141 @@
} }
}, },
"babel-helper-call-delegate": { "babel-helper-call-delegate": {
"version": "6.18.0", "version": "6.22.0",
"from": "babel-helper-call-delegate@>=6.18.0 <7.0.0", "from": "babel-helper-call-delegate@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.18.0.tgz", "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-helper-define-map": { "babel-helper-define-map": {
"version": "6.18.0", "version": "6.22.0",
"from": "babel-helper-define-map@>=6.18.0 <7.0.0", "from": "babel-helper-define-map@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.18.0.tgz", "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-helper-function-name": { "babel-helper-function-name": {
"version": "6.18.0", "version": "6.22.0",
"from": "babel-helper-function-name@>=6.18.0 <7.0.0", "from": "babel-helper-function-name@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.18.0.tgz", "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-helper-get-function-arity": { "babel-helper-get-function-arity": {
"version": "6.18.0", "version": "6.22.0",
"from": "babel-helper-get-function-arity@>=6.18.0 <7.0.0", "from": "babel-helper-get-function-arity@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.18.0.tgz", "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-helper-hoist-variables": { "babel-helper-hoist-variables": {
"version": "6.18.0", "version": "6.22.0",
"from": "babel-helper-hoist-variables@>=6.18.0 <7.0.0", "from": "babel-helper-hoist-variables@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.18.0.tgz", "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-helper-optimise-call-expression": { "babel-helper-optimise-call-expression": {
"version": "6.18.0", "version": "6.22.0",
"from": "babel-helper-optimise-call-expression@>=6.18.0 <7.0.0", "from": "babel-helper-optimise-call-expression@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.18.0.tgz", "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-helper-regex": { "babel-helper-regex": {
"version": "6.18.0", "version": "6.22.0",
"from": "babel-helper-regex@>=6.8.0 <7.0.0", "from": "babel-helper-regex@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.18.0.tgz", "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-helper-replace-supers": { "babel-helper-replace-supers": {
"version": "6.18.0", "version": "6.22.0",
"from": "babel-helper-replace-supers@>=6.18.0 <7.0.0", "from": "babel-helper-replace-supers@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.18.0.tgz", "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-helpers": { "babel-helpers": {
"version": "6.16.0", "version": "6.22.0",
"from": "babel-helpers@>=6.16.0 <7.0.0", "from": "babel-helpers@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.16.0.tgz", "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-messages": { "babel-messages": {
"version": "6.8.0", "version": "6.22.0",
"from": "babel-messages@>=6.8.0 <7.0.0", "from": "babel-messages@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.8.0.tgz", "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-check-es2015-constants": { "babel-plugin-check-es2015-constants": {
"version": "6.8.0", "version": "6.22.0",
"from": "babel-plugin-check-es2015-constants@>=6.3.13 <7.0.0", "from": "babel-plugin-check-es2015-constants@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.8.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-arrow-functions": { "babel-plugin-transform-es2015-arrow-functions": {
"version": "6.8.0", "version": "6.22.0",
"from": "babel-plugin-transform-es2015-arrow-functions@>=6.3.13 <7.0.0", "from": "babel-plugin-transform-es2015-arrow-functions@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.8.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-block-scoped-functions": { "babel-plugin-transform-es2015-block-scoped-functions": {
"version": "6.8.0", "version": "6.22.0",
"from": "babel-plugin-transform-es2015-block-scoped-functions@>=6.3.13 <7.0.0", "from": "babel-plugin-transform-es2015-block-scoped-functions@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.8.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-block-scoping": { "babel-plugin-transform-es2015-block-scoping": {
"version": "6.21.0", "version": "6.22.0",
"from": "babel-plugin-transform-es2015-block-scoping@>=6.18.0 <7.0.0", "from": "babel-plugin-transform-es2015-block-scoping@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.21.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-classes": { "babel-plugin-transform-es2015-classes": {
"version": "6.18.0", "version": "6.22.0",
"from": "babel-plugin-transform-es2015-classes@>=6.18.0 <7.0.0", "from": "babel-plugin-transform-es2015-classes@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.18.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-computed-properties": { "babel-plugin-transform-es2015-computed-properties": {
"version": "6.8.0", "version": "6.22.0",
"from": "babel-plugin-transform-es2015-computed-properties@>=6.3.13 <7.0.0", "from": "babel-plugin-transform-es2015-computed-properties@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.8.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-destructuring": { "babel-plugin-transform-es2015-destructuring": {
"version": "6.19.0", "version": "6.22.0",
"from": "babel-plugin-transform-es2015-destructuring@>=6.18.0 <7.0.0", "from": "babel-plugin-transform-es2015-destructuring@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.19.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-duplicate-keys": { "babel-plugin-transform-es2015-duplicate-keys": {
"version": "6.8.0", "version": "6.22.0",
"from": "babel-plugin-transform-es2015-duplicate-keys@>=6.6.0 <7.0.0", "from": "babel-plugin-transform-es2015-duplicate-keys@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.8.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-for-of": { "babel-plugin-transform-es2015-for-of": {
"version": "6.18.0", "version": "6.22.0",
"from": "babel-plugin-transform-es2015-for-of@>=6.18.0 <7.0.0", "from": "babel-plugin-transform-es2015-for-of@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.18.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-function-name": { "babel-plugin-transform-es2015-function-name": {
"version": "6.9.0", "version": "6.22.0",
"from": "babel-plugin-transform-es2015-function-name@>=6.9.0 <7.0.0", "from": "babel-plugin-transform-es2015-function-name@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.9.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-literals": { "babel-plugin-transform-es2015-literals": {
"version": "6.8.0", "version": "6.22.0",
"from": "babel-plugin-transform-es2015-literals@>=6.3.13 <7.0.0", "from": "babel-plugin-transform-es2015-literals@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.8.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-modules-amd": { "babel-plugin-transform-es2015-modules-amd": {
"version": "6.18.0", "version": "6.22.0",
"from": "babel-plugin-transform-es2015-modules-amd@>=6.18.0 <7.0.0", "from": "babel-plugin-transform-es2015-modules-amd@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.18.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-modules-commonjs": { "babel-plugin-transform-es2015-modules-commonjs": {
"version": "6.18.0", "version": "6.22.0",
"from": "babel-plugin-transform-es2015-modules-commonjs@>=6.18.0 <7.0.0", "from": "babel-plugin-transform-es2015-modules-commonjs@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.18.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-modules-strip": { "babel-plugin-transform-es2015-modules-strip": {
@ -424,117 +415,117 @@
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-modules-systemjs": { "babel-plugin-transform-es2015-modules-systemjs": {
"version": "6.19.0", "version": "6.22.0",
"from": "babel-plugin-transform-es2015-modules-systemjs@>=6.18.0 <7.0.0", "from": "babel-plugin-transform-es2015-modules-systemjs@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.19.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-modules-umd": { "babel-plugin-transform-es2015-modules-umd": {
"version": "6.18.0", "version": "6.22.0",
"from": "babel-plugin-transform-es2015-modules-umd@>=6.18.0 <7.0.0", "from": "babel-plugin-transform-es2015-modules-umd@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.18.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-object-super": { "babel-plugin-transform-es2015-object-super": {
"version": "6.8.0", "version": "6.22.0",
"from": "babel-plugin-transform-es2015-object-super@>=6.3.13 <7.0.0", "from": "babel-plugin-transform-es2015-object-super@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.8.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-parameters": { "babel-plugin-transform-es2015-parameters": {
"version": "6.21.0", "version": "6.22.0",
"from": "babel-plugin-transform-es2015-parameters@>=6.18.0 <7.0.0", "from": "babel-plugin-transform-es2015-parameters@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.21.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-shorthand-properties": { "babel-plugin-transform-es2015-shorthand-properties": {
"version": "6.18.0", "version": "6.22.0",
"from": "babel-plugin-transform-es2015-shorthand-properties@>=6.18.0 <7.0.0", "from": "babel-plugin-transform-es2015-shorthand-properties@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.18.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-spread": { "babel-plugin-transform-es2015-spread": {
"version": "6.8.0", "version": "6.22.0",
"from": "babel-plugin-transform-es2015-spread@>=6.3.13 <7.0.0", "from": "babel-plugin-transform-es2015-spread@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.8.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-sticky-regex": { "babel-plugin-transform-es2015-sticky-regex": {
"version": "6.8.0", "version": "6.22.0",
"from": "babel-plugin-transform-es2015-sticky-regex@>=6.3.13 <7.0.0", "from": "babel-plugin-transform-es2015-sticky-regex@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.8.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-template-literals": { "babel-plugin-transform-es2015-template-literals": {
"version": "6.8.0", "version": "6.22.0",
"from": "babel-plugin-transform-es2015-template-literals@>=6.6.0 <7.0.0", "from": "babel-plugin-transform-es2015-template-literals@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.8.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-typeof-symbol": { "babel-plugin-transform-es2015-typeof-symbol": {
"version": "6.18.0", "version": "6.22.0",
"from": "babel-plugin-transform-es2015-typeof-symbol@>=6.18.0 <7.0.0", "from": "babel-plugin-transform-es2015-typeof-symbol@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.18.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-es2015-unicode-regex": { "babel-plugin-transform-es2015-unicode-regex": {
"version": "6.11.0", "version": "6.22.0",
"from": "babel-plugin-transform-es2015-unicode-regex@>=6.3.13 <7.0.0", "from": "babel-plugin-transform-es2015-unicode-regex@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.11.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-regenerator": { "babel-plugin-transform-regenerator": {
"version": "6.21.0", "version": "6.22.0",
"from": "babel-plugin-transform-regenerator@>=6.16.0 <7.0.0", "from": "babel-plugin-transform-regenerator@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.21.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-plugin-transform-strict-mode": { "babel-plugin-transform-strict-mode": {
"version": "6.18.0", "version": "6.22.0",
"from": "babel-plugin-transform-strict-mode@>=6.18.0 <7.0.0", "from": "babel-plugin-transform-strict-mode@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.18.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-preset-es2015": { "babel-preset-es2015": {
"version": "6.18.0", "version": "6.22.0",
"from": "babel-preset-es2015@>=6.18.0 <7.0.0", "from": "babel-preset-es2015@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.18.0.tgz", "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-register": { "babel-register": {
"version": "6.18.0", "version": "6.22.0",
"from": "babel-register@>=6.18.0 <7.0.0", "from": "babel-register@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.18.0.tgz", "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-runtime": { "babel-runtime": {
"version": "6.20.0", "version": "6.22.0",
"from": "babel-runtime@>=6.20.0 <7.0.0", "from": "babel-runtime@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.20.0.tgz", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-template": { "babel-template": {
"version": "6.16.0", "version": "6.22.0",
"from": "babel-template@>=6.15.0 <7.0.0", "from": "babel-template@>=6.22.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.16.0.tgz", "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.22.0.tgz",
"dev": true "dev": true
}, },
"babel-traverse": { "babel-traverse": {
"version": "6.21.0", "version": "6.22.1",
"from": "babel-traverse@>=6.15.0 <7.0.0", "from": "babel-traverse@>=6.15.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.21.0.tgz", "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.22.1.tgz",
"dev": true "dev": true
}, },
"babel-types": { "babel-types": {
"version": "6.21.0", "version": "6.22.0",
"from": "babel-types@>=6.15.0 <7.0.0", "from": "babel-types@>=6.15.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.21.0.tgz", "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.22.0.tgz",
"dev": true "dev": true
}, },
"babylon": { "babylon": {
"version": "6.14.1", "version": "6.15.0",
"from": "babylon@>=6.13.0 <7.0.0", "from": "babylon@>=6.13.0 <7.0.0",
"resolved": "https://registry.npmjs.org/babylon/-/babylon-6.14.1.tgz", "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.15.0.tgz",
"dev": true "dev": true
}, },
"balanced-match": { "balanced-match": {
@ -556,9 +547,9 @@
"dev": true "dev": true
}, },
"bcrypt-pbkdf": { "bcrypt-pbkdf": {
"version": "1.0.0", "version": "1.0.1",
"from": "bcrypt-pbkdf@>=1.0.0 <2.0.0", "from": "bcrypt-pbkdf@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.0.tgz", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
"dev": true, "dev": true,
"optional": true "optional": true
}, },
@ -641,13 +632,12 @@
"version": "1.8.5", "version": "1.8.5",
"from": "braces@>=1.8.2 <2.0.0", "from": "braces@>=1.8.2 <2.0.0",
"resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
"dev": true, "dev": true
"optional": true
}, },
"browserslist": { "browserslist": {
"version": "1.5.1", "version": "1.7.1",
"from": "browserslist@>=1.5.1 <1.6.0", "from": "browserslist@>=1.7.1 <2.0.0",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.5.1.tgz", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.1.tgz",
"dev": true "dev": true
}, },
"buffer-crc32": { "buffer-crc32": {
@ -668,6 +658,26 @@
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
"dev": true "dev": true
}, },
"bulk-require": {
"version": "1.0.0",
"from": "bulk-require@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/bulk-require/-/bulk-require-1.0.0.tgz",
"dev": true,
"dependencies": {
"glob": {
"version": "3.2.11",
"from": "glob@>=3.2.7 <3.3.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
"dev": true
},
"minimatch": {
"version": "0.3.0",
"from": "minimatch@>=0.3.0 <0.4.0",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
"dev": true
}
}
},
"bytes": { "bytes": {
"version": "2.2.0", "version": "2.2.0",
"from": "bytes@2.2.0", "from": "bytes@2.2.0",
@ -699,9 +709,9 @@
"dev": true "dev": true
}, },
"caniuse-db": { "caniuse-db": {
"version": "1.0.30000604", "version": "1.0.30000619",
"from": "caniuse-db@>=1.0.30000604 <2.0.0", "from": "caniuse-db@>=1.0.30000618 <2.0.0",
"resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000604.tgz", "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000619.tgz",
"dev": true "dev": true
}, },
"caseless": { "caseless": {
@ -743,19 +753,23 @@
"resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.1.tgz", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.1.tgz",
"dev": true "dev": true
}, },
"cjson": {
"version": "0.5.0",
"from": "cjson@>=0.5.0 <0.6.0",
"resolved": "https://registry.npmjs.org/cjson/-/cjson-0.5.0.tgz",
"dev": true
},
"clean-css": { "clean-css": {
"version": "3.4.23", "version": "4.0.4",
"from": "clean-css@>=3.4.23 <4.0.0", "from": "clean-css@>=4.0.0 <5.0.0",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.23.tgz", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.0.4.tgz",
"dev": true, "dev": true
"dependencies": { },
"source-map": { "clean-css-cli": {
"version": "0.4.4", "version": "4.0.0",
"from": "source-map@>=0.4.0 <0.5.0", "from": "clean-css-cli@>=4.0.0 <5.0.0",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "resolved": "https://registry.npmjs.org/clean-css-cli/-/clean-css-cli-4.0.0.tgz",
"dev": true "dev": true
}
}
}, },
"cli": { "cli": {
"version": "0.6.6", "version": "0.6.6",
@ -826,9 +840,9 @@
"dev": true "dev": true
}, },
"commander": { "commander": {
"version": "2.8.1", "version": "2.9.0",
"from": "commander@>=2.8.0 <2.9.0", "from": "commander@>=2.9.0 <3.0.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
"dev": true "dev": true
}, },
"compress-commons": { "compress-commons": {
@ -911,10 +925,16 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"dev": true "dev": true
}, },
"crc": {
"version": "3.4.4",
"from": "crc@>=3.4.4 <4.0.0",
"resolved": "https://registry.npmjs.org/crc/-/crc-3.4.4.tgz",
"dev": true
},
"crc32-stream": { "crc32-stream": {
"version": "1.0.0", "version": "1.0.1",
"from": "crc32-stream@>=1.0.0 <2.0.0", "from": "crc32-stream@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-1.0.0.tgz", "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-1.0.1.tgz",
"dev": true "dev": true
}, },
"cross-spawn": { "cross-spawn": {
@ -1035,6 +1055,12 @@
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"dev": true "dev": true
}, },
"detect-file": {
"version": "0.1.0",
"from": "detect-file@>=0.1.0 <0.2.0",
"resolved": "https://registry.npmjs.org/detect-file/-/detect-file-0.1.0.tgz",
"dev": true
},
"detect-indent": { "detect-indent": {
"version": "4.0.0", "version": "4.0.0",
"from": "detect-indent@>=4.0.0 <5.0.0", "from": "detect-indent@>=4.0.0 <5.0.0",
@ -1098,6 +1124,12 @@
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"dev": true "dev": true
}, },
"electron-to-chromium": {
"version": "1.2.1",
"from": "electron-to-chromium@>=1.2.1 <2.0.0",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.2.1.tgz",
"dev": true
},
"encodeurl": { "encodeurl": {
"version": "1.0.1", "version": "1.0.1",
"from": "encodeurl@>=1.0.1 <1.1.0", "from": "encodeurl@>=1.0.1 <1.1.0",
@ -1197,15 +1229,15 @@
"dev": true "dev": true
}, },
"eslint": { "eslint": {
"version": "3.12.2", "version": "3.15.0",
"from": "eslint@>=3.12.2 <4.0.0", "from": "eslint@>=3.15.0 <4.0.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-3.12.2.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.15.0.tgz",
"dev": true "dev": true
}, },
"espree": { "espree": {
"version": "3.3.2", "version": "3.4.0",
"from": "espree@>=3.3.1 <4.0.0", "from": "espree@>=3.4.0 <4.0.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-3.3.2.tgz", "resolved": "https://registry.npmjs.org/espree/-/espree-3.4.0.tgz",
"dev": true "dev": true
}, },
"esprima": { "esprima": {
@ -1274,15 +1306,19 @@
"version": "0.1.5", "version": "0.1.5",
"from": "expand-brackets@>=0.1.4 <0.2.0", "from": "expand-brackets@>=0.1.4 <0.2.0",
"resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
"dev": true, "dev": true
"optional": true
}, },
"expand-range": { "expand-range": {
"version": "1.8.2", "version": "1.8.2",
"from": "expand-range@>=1.8.1 <2.0.0", "from": "expand-range@>=1.8.1 <2.0.0",
"resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz",
"dev": true, "dev": true
"optional": true },
"expand-tilde": {
"version": "1.2.2",
"from": "expand-tilde@>=1.2.2 <2.0.0",
"resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz",
"dev": true
}, },
"extend": { "extend": {
"version": "3.0.0", "version": "3.0.0",
@ -1294,8 +1330,7 @@
"version": "0.3.2", "version": "0.3.2",
"from": "extglob@>=0.3.1 <0.4.0", "from": "extglob@>=0.3.1 <0.4.0",
"resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
"dev": true, "dev": true
"optional": true
}, },
"extract-zip": { "extract-zip": {
"version": "1.5.0", "version": "1.5.0",
@ -1395,15 +1430,13 @@
"version": "2.0.0", "version": "2.0.0",
"from": "filename-regex@>=2.0.0 <3.0.0", "from": "filename-regex@>=2.0.0 <3.0.0",
"resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.0.tgz", "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.0.tgz",
"dev": true, "dev": true
"optional": true
}, },
"fill-range": { "fill-range": {
"version": "2.2.3", "version": "2.2.3",
"from": "fill-range@>=2.1.0 <3.0.0", "from": "fill-range@>=2.1.0 <3.0.0",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz",
"dev": true, "dev": true
"optional": true
}, },
"finalhandler": { "finalhandler": {
"version": "0.5.0", "version": "0.5.0",
@ -1445,6 +1478,18 @@
} }
} }
}, },
"fined": {
"version": "1.0.2",
"from": "fined@>=1.0.1 <2.0.0",
"resolved": "https://registry.npmjs.org/fined/-/fined-1.0.2.tgz",
"dev": true
},
"flagged-respawn": {
"version": "0.3.2",
"from": "flagged-respawn@>=0.3.2 <0.4.0",
"resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-0.3.2.tgz",
"dev": true
},
"flat-cache": { "flat-cache": {
"version": "1.2.2", "version": "1.2.2",
"from": "flat-cache@>=1.2.1 <2.0.0", "from": "flat-cache@>=1.2.1 <2.0.0",
@ -1455,15 +1500,13 @@
"version": "0.1.6", "version": "0.1.6",
"from": "for-in@>=0.1.5 <0.2.0", "from": "for-in@>=0.1.5 <0.2.0",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.6.tgz", "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.6.tgz",
"dev": true, "dev": true
"optional": true
}, },
"for-own": { "for-own": {
"version": "0.1.4", "version": "0.1.4",
"from": "for-own@>=0.1.4 <0.2.0", "from": "for-own@>=0.1.4 <0.2.0",
"resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.4.tgz", "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.4.tgz",
"dev": true, "dev": true
"optional": true
}, },
"forever-agent": { "forever-agent": {
"version": "0.6.1", "version": "0.6.1",
@ -1483,6 +1526,12 @@
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz",
"dev": true "dev": true
}, },
"fs-exists-sync": {
"version": "0.1.0",
"from": "fs-exists-sync@>=0.1.0 <0.2.0",
"resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz",
"dev": true
},
"fs-extra": { "fs-extra": {
"version": "1.0.0", "version": "1.0.0",
"from": "fs-extra@>=1.0.0 <1.1.0", "from": "fs-extra@>=1.0.0 <1.1.0",
@ -1508,10 +1557,18 @@
"dev": true "dev": true
}, },
"gauge": { "gauge": {
"version": "2.6.0", "version": "2.7.2",
"from": "gauge@>=2.6.0 <2.7.0", "from": "gauge@>=2.7.1 <2.8.0",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.6.0.tgz", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.2.tgz",
"dev": true "dev": true,
"dependencies": {
"supports-color": {
"version": "0.2.0",
"from": "supports-color@>=0.2.0 <0.3.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz",
"dev": true
}
}
}, },
"gaze": { "gaze": {
"version": "1.1.2", "version": "1.1.2",
@ -1581,6 +1638,18 @@
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
"dev": true "dev": true
}, },
"global-modules": {
"version": "0.2.3",
"from": "global-modules@>=0.2.3 <0.3.0",
"resolved": "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz",
"dev": true
},
"global-prefix": {
"version": "0.1.5",
"from": "global-prefix@>=0.1.4 <0.2.0",
"resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-0.1.5.tgz",
"dev": true
},
"globals": { "globals": {
"version": "9.14.0", "version": "9.14.0",
"from": "globals@>=9.0.0 <10.0.0", "from": "globals@>=9.0.0 <10.0.0",
@ -1690,9 +1759,9 @@
"dev": true "dev": true
}, },
"grunt-contrib-compress": { "grunt-contrib-compress": {
"version": "1.3.0", "version": "1.4.1",
"from": "grunt-contrib-compress@>=1.3.0 <2.0.0", "from": "grunt-contrib-compress@>=1.4.1 <2.0.0",
"resolved": "https://registry.npmjs.org/grunt-contrib-compress/-/grunt-contrib-compress-1.3.0.tgz", "resolved": "https://registry.npmjs.org/grunt-contrib-compress/-/grunt-contrib-compress-1.4.1.tgz",
"dev": true "dev": true
}, },
"grunt-contrib-concat": { "grunt-contrib-concat": {
@ -1739,24 +1808,10 @@
"resolved": "https://registry.npmjs.org/grunt-exec/-/grunt-exec-1.0.1.tgz", "resolved": "https://registry.npmjs.org/grunt-exec/-/grunt-exec-1.0.1.tgz",
"dev": true "dev": true
}, },
"grunt-html": {
"version": "8.1.0",
"from": "grunt-html@>=8.1.0 <9.0.0",
"resolved": "https://registry.npmjs.org/grunt-html/-/grunt-html-8.1.0.tgz",
"dev": true,
"dependencies": {
"async": {
"version": "2.1.2",
"from": "async@2.1.2",
"resolved": "https://registry.npmjs.org/async/-/async-2.1.2.tgz",
"dev": true
}
}
},
"grunt-jekyll": { "grunt-jekyll": {
"version": "0.4.4", "version": "0.4.6",
"from": "grunt-jekyll@>=0.4.4 <0.5.0", "from": "grunt-jekyll@>=0.4.6 <0.5.0",
"resolved": "https://registry.npmjs.org/grunt-jekyll/-/grunt-jekyll-0.4.4.tgz", "resolved": "https://registry.npmjs.org/grunt-jekyll/-/grunt-jekyll-0.4.6.tgz",
"dev": true "dev": true
}, },
"grunt-known-options": { "grunt-known-options": {
@ -1837,15 +1892,7 @@
"version": "2.0.6", "version": "2.0.6",
"from": "har-validator@>=2.0.6 <2.1.0", "from": "har-validator@>=2.0.6 <2.1.0",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz",
"dev": true, "dev": true
"dependencies": {
"commander": {
"version": "2.9.0",
"from": "commander@>=2.9.0 <3.0.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
"dev": true
}
}
}, },
"has-ansi": { "has-ansi": {
"version": "2.0.0", "version": "2.0.0",
@ -1853,12 +1900,6 @@
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
"dev": true "dev": true
}, },
"has-color": {
"version": "0.1.7",
"from": "has-color@>=0.1.7 <0.2.0",
"resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz",
"dev": true
},
"has-flag": { "has-flag": {
"version": "1.0.0", "version": "1.0.0",
"from": "has-flag@>=1.0.0 <2.0.0", "from": "has-flag@>=1.0.0 <2.0.0",
@ -1895,6 +1936,12 @@
"resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
"dev": true "dev": true
}, },
"homedir-polyfill": {
"version": "1.0.1",
"from": "homedir-polyfill@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz",
"dev": true
},
"hooker": { "hooker": {
"version": "0.2.3", "version": "0.2.3",
"from": "hooker@>=0.2.3 <0.3.0", "from": "hooker@>=0.2.3 <0.3.0",
@ -1936,9 +1983,27 @@
"from": "glob@5.0.15", "from": "glob@5.0.15",
"resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
"dev": true "dev": true
},
"strip-json-comments": {
"version": "1.0.4",
"from": "strip-json-comments@1.0.4",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz",
"dev": true
} }
} }
}, },
"htmllint": {
"version": "0.6.0",
"from": "htmllint@>=0.6.0 <0.7.0",
"resolved": "https://registry.npmjs.org/htmllint/-/htmllint-0.6.0.tgz",
"dev": true
},
"htmllint-cli": {
"version": "0.0.6",
"from": "htmllint-cli@0.0.6",
"resolved": "https://registry.npmjs.org/htmllint-cli/-/htmllint-cli-0.0.6.tgz",
"dev": true
},
"htmlparser2": { "htmlparser2": {
"version": "3.8.3", "version": "3.8.3",
"from": "htmlparser2@>=3.8.0 <3.9.0", "from": "htmlparser2@>=3.8.0 <3.9.0",
@ -1990,11 +2055,18 @@
"dev": true "dev": true
}, },
"ignore": { "ignore": {
"version": "3.2.0", "version": "3.2.2",
"from": "ignore@>=3.2.0 <4.0.0", "from": "ignore@>=3.2.0 <4.0.0",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-3.2.0.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.2.2.tgz",
"dev": true "dev": true
}, },
"iltorb": {
"version": "1.0.13",
"from": "iltorb@>=1.0.13 <2.0.0",
"resolved": "https://registry.npmjs.org/iltorb/-/iltorb-1.0.13.tgz",
"dev": true,
"optional": true
},
"imurmurhash": { "imurmurhash": {
"version": "0.1.4", "version": "0.1.4",
"from": "imurmurhash@>=0.1.4 <0.2.0", "from": "imurmurhash@>=0.1.4 <0.2.0",
@ -2025,6 +2097,12 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"dev": true "dev": true
}, },
"ini": {
"version": "1.3.4",
"from": "ini@>=1.3.4 <2.0.0",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz",
"dev": true
},
"inquirer": { "inquirer": {
"version": "0.12.0", "version": "0.12.0",
"from": "inquirer@>=0.12.0 <0.13.0", "from": "inquirer@>=0.12.0 <0.13.0",
@ -2049,6 +2127,12 @@
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
"dev": true "dev": true
}, },
"is-absolute": {
"version": "0.2.6",
"from": "is-absolute@>=0.2.3 <0.3.0",
"resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.2.6.tgz",
"dev": true
},
"is-arrayish": { "is-arrayish": {
"version": "0.2.1", "version": "0.2.1",
"from": "is-arrayish@>=0.2.1 <0.3.0", "from": "is-arrayish@>=0.2.1 <0.3.0",
@ -2084,15 +2168,13 @@
"version": "0.1.3", "version": "0.1.3",
"from": "is-equal-shallow@>=0.1.3 <0.2.0", "from": "is-equal-shallow@>=0.1.3 <0.2.0",
"resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz",
"dev": true, "dev": true
"optional": true
}, },
"is-extendable": { "is-extendable": {
"version": "0.1.1", "version": "0.1.1",
"from": "is-extendable@>=0.1.1 <0.2.0", "from": "is-extendable@>=0.1.1 <0.2.0",
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
"dev": true, "dev": true
"optional": true
}, },
"is-extglob": { "is-extglob": {
"version": "1.0.0", "version": "1.0.0",
@ -2152,8 +2234,7 @@
"version": "0.1.1", "version": "0.1.1",
"from": "is-posix-bracket@>=0.1.0 <0.2.0", "from": "is-posix-bracket@>=0.1.0 <0.2.0",
"resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz",
"dev": true, "dev": true
"optional": true
}, },
"is-primitive": { "is-primitive": {
"version": "2.0.0", "version": "2.0.0",
@ -2167,6 +2248,12 @@
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
"dev": true "dev": true
}, },
"is-relative": {
"version": "0.2.1",
"from": "is-relative@>=0.2.1 <0.3.0",
"resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.2.1.tgz",
"dev": true
},
"is-resolvable": { "is-resolvable": {
"version": "1.0.0", "version": "1.0.0",
"from": "is-resolvable@>=1.0.0 <2.0.0", "from": "is-resolvable@>=1.0.0 <2.0.0",
@ -2191,12 +2278,24 @@
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"dev": true "dev": true
}, },
"is-unc-path": {
"version": "0.1.2",
"from": "is-unc-path@>=0.1.1 <0.2.0",
"resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-0.1.2.tgz",
"dev": true
},
"is-utf8": { "is-utf8": {
"version": "0.2.1", "version": "0.2.1",
"from": "is-utf8@>=0.2.0 <0.3.0", "from": "is-utf8@>=0.2.0 <0.3.0",
"resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
"dev": true "dev": true
}, },
"is-windows": {
"version": "0.2.0",
"from": "is-windows@>=0.2.0 <0.3.0",
"resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz",
"dev": true
},
"isarray": { "isarray": {
"version": "1.0.0", "version": "1.0.0",
"from": "isarray@>=1.0.0 <1.1.0", "from": "isarray@>=1.0.0 <1.1.0",
@ -2213,8 +2312,7 @@
"version": "2.1.0", "version": "2.1.0",
"from": "isobject@>=2.0.0 <3.0.0", "from": "isobject@>=2.0.0 <3.0.0",
"resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
"dev": true, "dev": true
"optional": true
}, },
"isstream": { "isstream": {
"version": "0.1.2", "version": "0.1.2",
@ -2222,6 +2320,12 @@
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"dev": true "dev": true
}, },
"jju": {
"version": "1.3.0",
"from": "jju@>=1.1.0 <2.0.0",
"resolved": "https://registry.npmjs.org/jju/-/jju-1.3.0.tgz",
"dev": true
},
"jodid25519": { "jodid25519": {
"version": "1.0.2", "version": "1.0.2",
"from": "jodid25519@>=1.0.0 <2.0.0", "from": "jodid25519@>=1.0.0 <2.0.0",
@ -2241,9 +2345,9 @@
"dev": true "dev": true
}, },
"js-tokens": { "js-tokens": {
"version": "2.0.0", "version": "3.0.1",
"from": "js-tokens@>=2.0.0 <3.0.0", "from": "js-tokens@>=3.0.0 <4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-2.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.1.tgz",
"dev": true "dev": true
}, },
"js-yaml": { "js-yaml": {
@ -2288,9 +2392,21 @@
"from": "shelljs@>=0.3.0 <0.4.0", "from": "shelljs@>=0.3.0 <0.4.0",
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz",
"dev": true "dev": true
},
"strip-json-comments": {
"version": "1.0.4",
"from": "strip-json-comments@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz",
"dev": true
} }
} }
}, },
"json-parse-helpfulerror": {
"version": "1.0.3",
"from": "json-parse-helpfulerror@>=1.0.3 <2.0.0",
"resolved": "https://registry.npmjs.org/json-parse-helpfulerror/-/json-parse-helpfulerror-1.0.3.tgz",
"dev": true
},
"json-schema": { "json-schema": {
"version": "0.2.3", "version": "0.2.3",
"from": "json-schema@0.2.3", "from": "json-schema@0.2.3",
@ -2381,6 +2497,20 @@
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
"dev": true "dev": true
}, },
"liftoff": {
"version": "2.3.0",
"from": "liftoff@>=2.3.0 <3.0.0",
"resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.3.0.tgz",
"dev": true,
"dependencies": {
"findup-sync": {
"version": "0.4.3",
"from": "findup-sync@>=0.4.2 <0.5.0",
"resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.4.3.tgz",
"dev": true
}
}
},
"livereload-js": { "livereload-js": {
"version": "2.2.2", "version": "2.2.2",
"from": "livereload-js@>=2.2.0 <3.0.0", "from": "livereload-js@>=2.2.0 <3.0.0",
@ -2419,18 +2549,54 @@
"resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
"dev": true "dev": true
}, },
"lodash.assignwith": {
"version": "4.2.0",
"from": "lodash.assignwith@>=4.0.7 <5.0.0",
"resolved": "https://registry.npmjs.org/lodash.assignwith/-/lodash.assignwith-4.2.0.tgz",
"dev": true
},
"lodash.clonedeep": { "lodash.clonedeep": {
"version": "4.5.0", "version": "4.5.0",
"from": "lodash.clonedeep@>=4.3.2 <5.0.0", "from": "lodash.clonedeep@>=4.3.2 <5.0.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
"dev": true "dev": true
}, },
"lodash.isempty": {
"version": "4.4.0",
"from": "lodash.isempty@>=4.2.1 <5.0.0",
"resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz",
"dev": true
},
"lodash.isplainobject": {
"version": "4.0.6",
"from": "lodash.isplainobject@>=4.0.4 <5.0.0",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"dev": true
},
"lodash.isstring": {
"version": "4.0.1",
"from": "lodash.isstring@>=4.0.1 <5.0.0",
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
"dev": true
},
"lodash.mapvalues": {
"version": "4.6.0",
"from": "lodash.mapvalues@>=4.4.0 <5.0.0",
"resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz",
"dev": true
},
"lodash.mergewith": { "lodash.mergewith": {
"version": "4.6.0", "version": "4.6.0",
"from": "lodash.mergewith@>=4.6.0 <5.0.0", "from": "lodash.mergewith@>=4.6.0 <5.0.0",
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz", "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz",
"dev": true "dev": true
}, },
"lodash.pick": {
"version": "4.4.0",
"from": "lodash.pick@>=4.2.1 <5.0.0",
"resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
"dev": true
},
"lodash.pickby": { "lodash.pickby": {
"version": "4.6.0", "version": "4.6.0",
"from": "lodash.pickby@>=4.6.0 <5.0.0", "from": "lodash.pickby@>=4.6.0 <5.0.0",
@ -2444,9 +2610,9 @@
"dev": true "dev": true
}, },
"loose-envify": { "loose-envify": {
"version": "1.3.0", "version": "1.3.1",
"from": "loose-envify@>=1.0.0 <2.0.0", "from": "loose-envify@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.0.tgz", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
"dev": true "dev": true
}, },
"loud-rejection": { "loud-rejection": {
@ -2461,6 +2627,12 @@
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz",
"dev": true "dev": true
}, },
"map-cache": {
"version": "0.2.2",
"from": "map-cache@>=0.2.0 <0.3.0",
"resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
"dev": true
},
"map-obj": { "map-obj": {
"version": "1.0.1", "version": "1.0.1",
"from": "map-obj@>=1.0.1 <2.0.0", "from": "map-obj@>=1.0.1 <2.0.0",
@ -2489,10 +2661,9 @@
}, },
"micromatch": { "micromatch": {
"version": "2.3.11", "version": "2.3.11",
"from": "micromatch@>=2.1.5 <3.0.0", "from": "micromatch@>=2.3.7 <3.0.0",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
"dev": true, "dev": true
"optional": true
}, },
"mime": { "mime": {
"version": "1.3.4", "version": "1.3.4",
@ -2501,15 +2672,15 @@
"dev": true "dev": true
}, },
"mime-db": { "mime-db": {
"version": "1.25.0", "version": "1.26.0",
"from": "mime-db@>=1.25.0 <1.26.0", "from": "mime-db@>=1.26.0 <1.27.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.25.0.tgz", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.26.0.tgz",
"dev": true "dev": true
}, },
"mime-types": { "mime-types": {
"version": "2.1.13", "version": "2.1.14",
"from": "mime-types@>=2.1.11 <2.2.0", "from": "mime-types@>=2.1.11 <2.2.0",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.13.tgz", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.14.tgz",
"dev": true "dev": true
}, },
"minimatch": { "minimatch": {
@ -2569,9 +2740,9 @@
"dev": true "dev": true
}, },
"nan": { "nan": {
"version": "2.5.0", "version": "2.5.1",
"from": "nan@>=2.3.2 <3.0.0", "from": "nan@>=2.4.0 <3.0.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.5.0.tgz", "resolved": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz",
"dev": true "dev": true
}, },
"natural-compare": { "natural-compare": {
@ -2593,23 +2764,15 @@
"dev": true "dev": true
}, },
"node-gyp": { "node-gyp": {
"version": "3.4.0", "version": "3.5.0",
"from": "node-gyp@>=3.3.1 <4.0.0", "from": "node-gyp@>=3.3.1 <4.0.0",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.4.0.tgz", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.5.0.tgz",
"dev": true, "dev": true
"dependencies": {
"npmlog": {
"version": "3.1.2",
"from": "npmlog@>=0.0.0 <1.0.0||>=1.0.0 <2.0.0||>=2.0.0 <3.0.0||>=3.0.0 <4.0.0",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-3.1.2.tgz",
"dev": true
}
}
}, },
"node-sass": { "node-sass": {
"version": "4.1.1", "version": "4.5.0",
"from": "node-sass@>=4.1.1 <5.0.0", "from": "node-sass@>=4.5.0 <5.0.0",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.1.1.tgz", "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.5.0.tgz",
"dev": true "dev": true
}, },
"nopt": { "nopt": {
@ -2640,21 +2803,7 @@
"version": "4.0.2", "version": "4.0.2",
"from": "npmlog@>=4.0.0 <5.0.0", "from": "npmlog@>=4.0.0 <5.0.0",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.0.2.tgz", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.0.2.tgz",
"dev": true, "dev": true
"dependencies": {
"gauge": {
"version": "2.7.2",
"from": "gauge@>=2.7.1 <2.8.0",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.2.tgz",
"dev": true
},
"supports-color": {
"version": "0.2.0",
"from": "supports-color@>=0.2.0 <0.3.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz",
"dev": true
}
}
}, },
"num2fraction": { "num2fraction": {
"version": "1.2.2", "version": "1.2.2",
@ -2675,17 +2824,16 @@
"dev": true "dev": true
}, },
"object-assign": { "object-assign": {
"version": "4.1.0", "version": "4.1.1",
"from": "object-assign@>=4.0.1 <5.0.0", "from": "object-assign@>=4.0.1 <5.0.0",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"dev": true "dev": true
}, },
"object.omit": { "object.omit": {
"version": "2.0.1", "version": "2.0.1",
"from": "object.omit@>=2.0.0 <3.0.0", "from": "object.omit@>=2.0.0 <3.0.0",
"resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz",
"dev": true, "dev": true
"optional": true
}, },
"on-finished": { "on-finished": {
"version": "2.3.0", "version": "2.3.0",
@ -2753,6 +2901,12 @@
"resolved": "https://registry.npmjs.org/package/-/package-1.0.1.tgz", "resolved": "https://registry.npmjs.org/package/-/package-1.0.1.tgz",
"dev": true "dev": true
}, },
"parse-filepath": {
"version": "1.0.1",
"from": "parse-filepath@>=1.0.1 <2.0.0",
"resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.1.tgz",
"dev": true
},
"parse-glob": { "parse-glob": {
"version": "3.0.4", "version": "3.0.4",
"from": "parse-glob@3.0.4", "from": "parse-glob@3.0.4",
@ -2771,6 +2925,12 @@
"resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-1.0.1.tgz", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-1.0.1.tgz",
"dev": true "dev": true
}, },
"parse-passwd": {
"version": "1.0.0",
"from": "parse-passwd@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
"dev": true
},
"parserlib": { "parserlib": {
"version": "0.2.5", "version": "0.2.5",
"from": "parserlib@>=0.2.2 <0.3.0", "from": "parserlib@>=0.2.2 <0.3.0",
@ -2783,12 +2943,6 @@
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz",
"dev": true "dev": true
}, },
"path-array": {
"version": "1.0.1",
"from": "path-array@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/path-array/-/path-array-1.0.1.tgz",
"dev": true
},
"path-exists": { "path-exists": {
"version": "2.1.0", "version": "2.1.0",
"from": "path-exists@>=2.0.0 <3.0.0", "from": "path-exists@>=2.0.0 <3.0.0",
@ -2807,6 +2961,18 @@
"resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
"dev": true "dev": true
}, },
"path-root": {
"version": "0.1.1",
"from": "path-root@>=0.1.1 <0.2.0",
"resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz",
"dev": true
},
"path-root-regex": {
"version": "0.1.2",
"from": "path-root-regex@>=0.1.0 <0.2.0",
"resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz",
"dev": true
},
"path-type": { "path-type": {
"version": "1.1.0", "version": "1.1.0",
"from": "path-type@>=1.0.0 <2.0.0", "from": "path-type@>=1.0.0 <2.0.0",
@ -2868,9 +3034,9 @@
"dev": true "dev": true
}, },
"postcss": { "postcss": {
"version": "5.2.8", "version": "5.2.11",
"from": "postcss@>=5.2.8 <6.0.0", "from": "postcss@>=5.2.11 <6.0.0",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.8.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.11.tgz",
"dev": true "dev": true
}, },
"postcss-cli": { "postcss-cli": {
@ -2879,6 +3045,12 @@
"resolved": "https://registry.npmjs.org/postcss-cli/-/postcss-cli-2.6.0.tgz", "resolved": "https://registry.npmjs.org/postcss-cli/-/postcss-cli-2.6.0.tgz",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"camelcase": {
"version": "3.0.0",
"from": "camelcase@>=3.0.0 <4.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
"dev": true
},
"glob": { "glob": {
"version": "6.0.4", "version": "6.0.4",
"from": "glob@>=6.0.1 <7.0.0", "from": "glob@>=6.0.1 <7.0.0",
@ -2890,6 +3062,18 @@
"from": "globby@>=4.1.0 <5.0.0", "from": "globby@>=4.1.0 <5.0.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-4.1.0.tgz", "resolved": "https://registry.npmjs.org/globby/-/globby-4.1.0.tgz",
"dev": true "dev": true
},
"yargs": {
"version": "4.8.1",
"from": "yargs@>=4.7.1 <5.0.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz",
"dev": true
},
"yargs-parser": {
"version": "2.4.1",
"from": "yargs-parser@>=2.4.1 <3.0.0",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz",
"dev": true
} }
} }
}, },
@ -2915,8 +3099,7 @@
"version": "0.2.0", "version": "0.2.0",
"from": "preserve@>=0.2.0 <0.3.0", "from": "preserve@>=0.2.0 <0.3.0",
"resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz",
"dev": true, "dev": true
"optional": true
}, },
"pretty-bytes": { "pretty-bytes": {
"version": "3.0.1", "version": "3.0.1",
@ -2948,6 +3131,12 @@
"resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz",
"dev": true "dev": true
}, },
"promise": {
"version": "7.1.1",
"from": "promise@>=7.1.1 <8.0.0",
"resolved": "https://registry.npmjs.org/promise/-/promise-7.1.1.tgz",
"dev": true
},
"pseudomap": { "pseudomap": {
"version": "1.0.2", "version": "1.0.2",
"from": "pseudomap@>=1.0.1 <2.0.0", "from": "pseudomap@>=1.0.1 <2.0.0",
@ -2976,8 +3165,7 @@
"version": "1.1.6", "version": "1.1.6",
"from": "randomatic@>=1.1.3 <2.0.0", "from": "randomatic@>=1.1.3 <2.0.0",
"resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.6.tgz", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.6.tgz",
"dev": true, "dev": true
"optional": true
}, },
"range-parser": { "range-parser": {
"version": "1.2.0", "version": "1.2.0",
@ -3076,8 +3264,7 @@
"version": "0.4.3", "version": "0.4.3",
"from": "regex-cache@>=0.4.2 <0.5.0", "from": "regex-cache@>=0.4.2 <0.5.0",
"resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz", "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz",
"dev": true, "dev": true
"optional": true
}, },
"regexpu-core": { "regexpu-core": {
"version": "2.0.0", "version": "2.0.0",
@ -3157,6 +3344,12 @@
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.2.0.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.2.0.tgz",
"dev": true "dev": true
}, },
"resolve-dir": {
"version": "0.1.1",
"from": "resolve-dir@>=0.1.0 <0.2.0",
"resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-0.1.1.tgz",
"dev": true
},
"resolve-from": { "resolve-from": {
"version": "1.0.1", "version": "1.0.1",
"from": "resolve-from@>=1.0.0 <2.0.0", "from": "resolve-from@>=1.0.0 <2.0.0",
@ -3211,7 +3404,27 @@
"version": "2.1.2", "version": "2.1.2",
"from": "sass-graph@>=2.1.1 <3.0.0", "from": "sass-graph@>=2.1.1 <3.0.0",
"resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.1.2.tgz", "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.1.2.tgz",
"dev": true "dev": true,
"dependencies": {
"camelcase": {
"version": "3.0.0",
"from": "camelcase@>=3.0.0 <4.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
"dev": true
},
"yargs": {
"version": "4.8.1",
"from": "yargs@>=4.7.1 <5.0.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz",
"dev": true
},
"yargs-parser": {
"version": "2.4.1",
"from": "yargs-parser@>=2.4.1 <3.0.0",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz",
"dev": true
}
}
}, },
"sauce-tunnel": { "sauce-tunnel": {
"version": "2.5.0", "version": "2.5.0",
@ -3232,22 +3445,24 @@
"dev": true "dev": true
}, },
"send": { "send": {
"version": "0.14.1", "version": "0.14.2",
"from": "send@0.14.1", "from": "send@0.14.2",
"resolved": "https://registry.npmjs.org/send/-/send-0.14.1.tgz", "resolved": "https://registry.npmjs.org/send/-/send-0.14.2.tgz",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"debug": { "debug": {
"version": "2.2.0", "version": "2.2.0",
"from": "debug@>=2.2.0 <2.3.0", "from": "debug@>=2.2.0 <2.3.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
"dev": true "dev": true,
}, "dependencies": {
"ms": { "ms": {
"version": "0.7.1", "version": "0.7.1",
"from": "ms@0.7.1", "from": "ms@0.7.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
"dev": true "dev": true
}
}
} }
} }
}, },
@ -3272,14 +3487,14 @@
} }
}, },
"serve-static": { "serve-static": {
"version": "1.11.1", "version": "1.11.2",
"from": "serve-static@>=1.10.0 <2.0.0", "from": "serve-static@>=1.10.0 <2.0.0",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.11.1.tgz", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.11.2.tgz",
"dev": true "dev": true
}, },
"set-blocking": { "set-blocking": {
"version": "2.0.0", "version": "2.0.0",
"from": "set-blocking@>=2.0.0 <2.1.0", "from": "set-blocking@>=2.0.0 <3.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"dev": true "dev": true
}, },
@ -3297,15 +3512,15 @@
"dev": true "dev": true
}, },
"shelljs": { "shelljs": {
"version": "0.7.5", "version": "0.7.6",
"from": "shelljs@>=0.7.5 <0.8.0", "from": "shelljs@>=0.7.6 <0.8.0",
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.5.tgz", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.6.tgz",
"dev": true "dev": true
}, },
"shx": { "shx": {
"version": "0.2.1", "version": "0.2.2",
"from": "shx@>=0.2.1 <0.3.0", "from": "shx@>=0.2.2 <0.3.0",
"resolved": "https://registry.npmjs.org/shx/-/shx-0.2.1.tgz", "resolved": "https://registry.npmjs.org/shx/-/shx-0.2.2.tgz",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"minimist": { "minimist": {
@ -3353,9 +3568,9 @@
"dev": true "dev": true
}, },
"source-map-support": { "source-map-support": {
"version": "0.4.8", "version": "0.4.11",
"from": "source-map-support@>=0.4.2 <0.5.0", "from": "source-map-support@>=0.4.2 <0.5.0",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.8.tgz", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.11.tgz",
"dev": true "dev": true
}, },
"spdx-correct": { "spdx-correct": {
@ -3389,9 +3604,9 @@
"dev": true "dev": true
}, },
"sshpk": { "sshpk": {
"version": "1.10.1", "version": "1.10.2",
"from": "sshpk@>=1.7.0 <2.0.0", "from": "sshpk@>=1.7.0 <2.0.0",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.10.1.tgz", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.10.2.tgz",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"assert-plus": { "assert-plus": {
@ -3457,15 +3672,15 @@
"dev": true "dev": true
}, },
"strip-json-comments": { "strip-json-comments": {
"version": "1.0.4", "version": "2.0.1",
"from": "strip-json-comments@>=1.0.1 <1.1.0", "from": "strip-json-comments@>=2.0.1 <2.1.0",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"dev": true "dev": true
}, },
"supports-color": { "supports-color": {
"version": "3.1.2", "version": "3.2.3",
"from": "supports-color@>=3.1.2 <4.0.0", "from": "supports-color@>=3.2.3 <4.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
"dev": true "dev": true
}, },
"table": { "table": {
@ -3568,9 +3783,9 @@
} }
}, },
"tmp": { "tmp": {
"version": "0.0.28", "version": "0.0.31",
"from": "tmp@>=0.0.28 <0.0.29", "from": "tmp@>=0.0.31 <0.0.32",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.28.tgz", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz",
"dev": true "dev": true
}, },
"to-fast-properties": { "to-fast-properties": {
@ -3678,6 +3893,12 @@
"resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
"dev": true "dev": true
}, },
"unc-path-regex": {
"version": "0.1.2",
"from": "unc-path-regex@>=0.1.0 <0.2.0",
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
"dev": true
},
"underscore.string": { "underscore.string": {
"version": "3.2.3", "version": "3.2.3",
"from": "underscore.string@>=3.2.3 <3.3.0", "from": "underscore.string@>=3.2.3 <3.3.0",
@ -3726,12 +3947,6 @@
"resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz",
"dev": true "dev": true
}, },
"vnu-jar": {
"version": "16.6.29",
"from": "vnu-jar@16.6.29",
"resolved": "https://registry.npmjs.org/vnu-jar/-/vnu-jar-16.6.29.tgz",
"dev": true
},
"walkdir": { "walkdir": {
"version": "0.0.11", "version": "0.0.11",
"from": "walkdir@>=0.0.11 <0.0.12", "from": "walkdir@>=0.0.11 <0.0.12",
@ -3829,15 +4044,23 @@
"dev": true "dev": true
}, },
"yargs": { "yargs": {
"version": "4.8.1", "version": "6.6.0",
"from": "yargs@>=4.7.1 <5.0.0", "from": "yargs@>=6.6.0 <7.0.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz",
"dev": true "dev": true,
"dependencies": {
"camelcase": {
"version": "3.0.0",
"from": "camelcase@>=3.0.0 <4.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
"dev": true
}
}
}, },
"yargs-parser": { "yargs-parser": {
"version": "2.4.1", "version": "4.2.1",
"from": "yargs-parser@>=2.4.1 <3.0.0", "from": "yargs-parser@>=4.2.0 <5.0.0",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"camelcase": { "camelcase": {
@ -3855,9 +4078,9 @@
"dev": true "dev": true
}, },
"zip-stream": { "zip-stream": {
"version": "1.1.0", "version": "1.1.1",
"from": "zip-stream@>=1.1.0 <2.0.0", "from": "zip-stream@>=1.1.0 <2.0.0",
"resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-1.1.0.tgz", "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-1.1.1.tgz",
"dev": true "dev": true
} }
} }

View File

@ -1,12 +1,12 @@
/*! /*!
* QUnit 2.0.1 * QUnit 2.1.1
* https://qunitjs.com/ * https://qunitjs.com/
* *
* Copyright jQuery Foundation and other contributors * Copyright jQuery Foundation and other contributors
* Released under the MIT license * Released under the MIT license
* https://jquery.org/license * https://jquery.org/license
* *
* Date: 2016-07-23T19:39Z * Date: 2017-01-06T01:52Z
*/ */
/** Font Family and Sizes */ /** Font Family and Sizes */
@ -226,7 +226,8 @@
#qunit-tests li.running, #qunit-tests li.running,
#qunit-tests li.pass, #qunit-tests li.pass,
#qunit-tests li.fail, #qunit-tests li.fail,
#qunit-tests li.skipped { #qunit-tests li.skipped,
#qunit-tests li.aborted {
display: list-item; display: list-item;
} }
@ -374,6 +375,9 @@
#qunit-banner.qunit-fail { background-color: #EE5757; } #qunit-banner.qunit-fail { background-color: #EE5757; }
/*** Aborted tests */
#qunit-tests .aborted { color: #000; background-color: orange; }
/*** Skipped tests */ /*** Skipped tests */
#qunit-tests .skipped { #qunit-tests .skipped {
@ -393,16 +397,28 @@
/** Result */ /** Result */
#qunit-testresult { #qunit-testresult {
padding: 0.5em 1em 0.5em 1em;
color: #2B81AF; color: #2B81AF;
background-color: #D2E0E6; background-color: #D2E0E6;
border-bottom: 1px solid #FFF; border-bottom: 1px solid #FFF;
} }
#qunit-testresult .clearfix {
height: 0;
clear: both;
}
#qunit-testresult .module-name { #qunit-testresult .module-name {
font-weight: 700; font-weight: 700;
} }
#qunit-testresult-display {
padding: 0.5em 1em 0.5em 1em;
width: 85%;
float:left;
}
#qunit-testresult-controls {
padding: 0.5em 1em 0.5em 1em;
width: 10%;
float:left;
}
/** Fixture */ /** Fixture */

8785
js/tests/vendor/qunit.js vendored
View File

@ -1,4437 +1,4366 @@
/*! /*!
* QUnit 2.0.1 * QUnit 2.1.1
* https://qunitjs.com/ * https://qunitjs.com/
* *
* Copyright jQuery Foundation and other contributors * Copyright jQuery Foundation and other contributors
* Released under the MIT license * Released under the MIT license
* https://jquery.org/license * https://jquery.org/license
* *
* Date: 2016-07-23T19:39Z * Date: 2017-01-06T01:52Z
*/ */
(function (global$1) {
'use strict';
( function( global ) { global$1 = 'default' in global$1 ? global$1['default'] : global$1;
var QUnit = {}; var window = global$1.window;
var console = global$1.console;
var Date = global.Date; var setTimeout = global$1.setTimeout;
var now = Date.now || function() { var clearTimeout = global$1.clearTimeout;
return new Date().getTime();
}; var document = window && window.document;
var navigator = window && window.navigator;
var setTimeout = global.setTimeout; var sessionStorage = window && window.sessionStorage;
var clearTimeout = global.clearTimeout;
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
// Store a local window from the global to allow direct references. return typeof obj;
var window = global.window; } : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
var defined = { };
document: window && window.document !== undefined,
setTimeout: setTimeout !== undefined,
sessionStorage: ( function() {
var x = "qunit-test-string";
try {
sessionStorage.setItem( x, x );
sessionStorage.removeItem( x );
return true;
} catch ( e ) {
return false;
}
}() ) var classCallCheck = function (instance, Constructor) {
}; if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
var fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" ); }
var globalStartCalled = false; };
var runStarted = false;
var createClass = function () {
var autorun = false; function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var toString = Object.prototype.toString, var descriptor = props[i];
hasOwn = Object.prototype.hasOwnProperty; descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
// Returns a new Array with the elements that are in a but not in b if ("value" in descriptor) descriptor.writable = true;
function diff( a, b ) { Object.defineProperty(target, descriptor.key, descriptor);
var i, j, }
result = a.slice(); }
for ( i = 0; i < result.length; i++ ) { return function (Constructor, protoProps, staticProps) {
for ( j = 0; j < b.length; j++ ) { if (protoProps) defineProperties(Constructor.prototype, protoProps);
if ( result[ i ] === b[ j ] ) { if (staticProps) defineProperties(Constructor, staticProps);
result.splice( i, 1 ); return Constructor;
i--; };
break; }();
}
}
}
return result;
}
// From jquery.js
function inArray( elem, array ) {
if ( array.indexOf ) {
return array.indexOf( elem );
}
for ( var i = 0, length = array.length; i < length; i++ ) {
if ( array[ i ] === elem ) {
return i;
}
}
return -1;
}
/**
* Makes a clone of an object using only Array or Object as base,
* and copies over the own enumerable properties.
*
* @param {Object} obj
* @return {Object} New object with only the own properties (recursively).
*/
function objectValues ( obj ) {
var key, val,
vals = QUnit.is( "array", obj ) ? [] : {};
for ( key in obj ) {
if ( hasOwn.call( obj, key ) ) {
val = obj[ key ];
vals[ key ] = val === Object( val ) ? objectValues( val ) : val;
}
}
return vals;
}
function extend( a, b, undefOnly ) {
for ( var prop in b ) { var toConsumableArray = function (arr) {
if ( hasOwn.call( b, prop ) ) { if (Array.isArray(arr)) {
if ( b[ prop ] === undefined ) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
delete a[ prop ];
} else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) { return arr2;
a[ prop ] = b[ prop ]; } else {
} return Array.from(arr);
} }
} };
return a; var toString = Object.prototype.toString;
} var hasOwn = Object.prototype.hasOwnProperty;
var now = Date.now || function () {
function objectType( obj ) { return new Date().getTime();
if ( typeof obj === "undefined" ) { };
return "undefined";
} var defined = {
document: window && window.document !== undefined,
// Consider: typeof null === object setTimeout: setTimeout !== undefined
if ( obj === null ) { };
return "null";
} // Returns a new Array with the elements that are in a but not in b
function diff(a, b) {
var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ), var i,
type = match && match[ 1 ]; j,
result = a.slice();
switch ( type ) {
case "Number": for (i = 0; i < result.length; i++) {
if ( isNaN( obj ) ) { for (j = 0; j < b.length; j++) {
return "nan"; if (result[i] === b[j]) {
} result.splice(i, 1);
return "number"; i--;
case "String": break;
case "Boolean": }
case "Array": }
case "Set": }
case "Map": return result;
case "Date": }
case "RegExp":
case "Function": // From jquery.js
case "Symbol": function inArray(elem, array) {
return type.toLowerCase(); if (array.indexOf) {
} return array.indexOf(elem);
if ( typeof obj === "object" ) { }
return "object";
} for (var i = 0, length = array.length; i < length; i++) {
} if (array[i] === elem) {
return i;
// Safe object type checking }
function is( type, obj ) { }
return QUnit.objectType( obj ) === type;
} return -1;
}
// Doesn't support IE9, it will return undefined on these browsers
// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack /**
function extractStacktrace( e, offset ) { * Makes a clone of an object using only Array or Object as base,
offset = offset === undefined ? 4 : offset; * and copies over the own enumerable properties.
*
var stack, include, i; * @param {Object} obj
* @return {Object} New object with only the own properties (recursively).
if ( e.stack ) { */
stack = e.stack.split( "\n" ); function objectValues(obj) {
if ( /^error$/i.test( stack[ 0 ] ) ) { var key,
stack.shift(); val,
} vals = is("array", obj) ? [] : {};
if ( fileName ) { for (key in obj) {
include = []; if (hasOwn.call(obj, key)) {
for ( i = offset; i < stack.length; i++ ) { val = obj[key];
if ( stack[ i ].indexOf( fileName ) !== -1 ) { vals[key] = val === Object(val) ? objectValues(val) : val;
break; }
} }
include.push( stack[ i ] ); return vals;
} }
if ( include.length ) {
return include.join( "\n" ); function extend(a, b, undefOnly) {
} for (var prop in b) {
} if (hasOwn.call(b, prop)) {
return stack[ offset ]; if (b[prop] === undefined) {
} delete a[prop];
} } else if (!(undefOnly && typeof a[prop] !== "undefined")) {
a[prop] = b[prop];
function sourceFromStacktrace( offset ) { }
var error = new Error(); }
}
// Support: Safari <=7 only, IE <=10 - 11 only
// Not all browsers generate the `stack` property for `new Error()`, see also #636 return a;
if ( !error.stack ) { }
try {
throw error; function objectType(obj) {
} catch ( err ) { if (typeof obj === "undefined") {
error = err; return "undefined";
} }
}
// Consider: typeof null === object
return extractStacktrace( error, offset ); if (obj === null) {
} return "null";
}
/**
* Config object: Maintain internal state var match = toString.call(obj).match(/^\[object\s(.*)\]$/),
* Later exposed as QUnit.config type = match && match[1];
* `config` initialized at top of scope
*/ switch (type) {
var config = { case "Number":
if (isNaN(obj)) {
// The queue of tests to run return "nan";
queue: [], }
return "number";
// Block until document ready case "String":
blocking: true, case "Boolean":
case "Array":
// By default, run previously failed tests first case "Set":
// very useful in combination with "Hide passed tests" checked case "Map":
reorder: true, case "Date":
case "RegExp":
// By default, modify document.title when suite is done case "Function":
altertitle: true, case "Symbol":
return type.toLowerCase();
// HTML Reporter: collapse every test except the first failing test }
// If false, all failing tests will be expanded
collapse: true, if ((typeof obj === "undefined" ? "undefined" : _typeof(obj)) === "object") {
return "object";
// By default, scroll to top of the page when suite is done }
scrolltop: true, }
// Depth up-to which object will be dumped // Safe object type checking
maxDepth: 5, function is(type, obj) {
return objectType(obj) === type;
// When enabled, all tests must call expect() }
requireExpects: false,
// Test for equality any JavaScript type.
// Placeholder for user-configurable form-exposed URL parameters // Author: Philippe Rathé <prathe@gmail.com>
urlConfig: [], var equiv = (function () {
// Set of all modules. // Stack to decide between skip/abort functions
modules: [], var callers = [];
// Stack of nested modules // Stack to avoiding loops from circular referencing
moduleStack: [], var parents = [];
var parentsB = [];
// The first unnamed module
currentModule: { var getProto = Object.getPrototypeOf || function (obj) {
name: "", return obj.__proto__;
tests: [] };
},
function useStrictEquality(b, a) {
callbacks: {}
}; // To catch short annotation VS 'new' annotation of a declaration. e.g.:
// `var i = 1;`
// Push a loose unnamed module to the modules collection // `var j = new Number(1);`
config.modules.push( config.currentModule ); if ((typeof a === "undefined" ? "undefined" : _typeof(a)) === "object") {
a = a.valueOf();
// Register logging callbacks }
function registerLoggingCallbacks( obj ) { if ((typeof b === "undefined" ? "undefined" : _typeof(b)) === "object") {
var i, l, key, b = b.valueOf();
callbackNames = [ "begin", "done", "log", "testStart", "testDone", }
"moduleStart", "moduleDone" ];
return a === b;
function registerLoggingCallback( key ) { }
var loggingCallback = function( callback ) {
if ( objectType( callback ) !== "function" ) { function compareConstructors(a, b) {
throw new Error( var protoA = getProto(a);
"QUnit logging methods require a callback function as their first parameters." var protoB = getProto(b);
);
} // Comparing constructors is more strict than using `instanceof`
if (a.constructor === b.constructor) {
config.callbacks[ key ].push( callback ); return true;
}; }
return loggingCallback; // Ref #851
} // If the obj prototype descends from a null constructor, treat it
// as a null prototype.
for ( i = 0, l = callbackNames.length; i < l; i++ ) { if (protoA && protoA.constructor === null) {
key = callbackNames[ i ]; protoA = null;
}
// Initialize key collection of logging callback if (protoB && protoB.constructor === null) {
if ( objectType( config.callbacks[ key ] ) === "undefined" ) { protoB = null;
config.callbacks[ key ] = []; }
}
// Allow objects with no prototype to be equivalent to
obj[ key ] = registerLoggingCallback( key ); // objects with Object as their constructor.
} if (protoA === null && protoB === Object.prototype || protoB === null && protoA === Object.prototype) {
} return true;
}
function runLoggingCallbacks( key, args ) {
var i, l, callbacks; return false;
}
callbacks = config.callbacks[ key ];
for ( i = 0, l = callbacks.length; i < l; i++ ) { function getRegExpFlags(regexp) {
callbacks[ i ]( args ); return "flags" in regexp ? regexp.flags : regexp.toString().match(/[gimuy]*$/)[0];
} }
}
var callbacks = {
( function() { "string": useStrictEquality,
if ( !defined.document ) { "boolean": useStrictEquality,
return; "number": useStrictEquality,
} "null": useStrictEquality,
"undefined": useStrictEquality,
// `onErrorFnPrev` initialized at top of scope "symbol": useStrictEquality,
// Preserve other handlers "date": useStrictEquality,
var onErrorFnPrev = window.onerror;
"nan": function nan() {
// Cover uncaught exceptions return true;
// Returning true will suppress the default browser handler, },
// returning false will let it run.
window.onerror = function( error, filePath, linerNr ) { "regexp": function regexp(b, a) {
var ret = false; return a.source === b.source &&
if ( onErrorFnPrev ) {
ret = onErrorFnPrev( error, filePath, linerNr ); // Include flags in the comparison
} getRegExpFlags(a) === getRegExpFlags(b);
},
// Treat return value as window.onerror itself does,
// Only do our handling if not suppressed. // - skip when the property is a method of an instance (OOP)
if ( ret !== true ) { // - abort otherwise,
if ( QUnit.config.current ) { // initial === would have catch identical references anyway
if ( QUnit.config.current.ignoreGlobalErrors ) { "function": function _function(b, a) {
return true;
} var caller = callers[callers.length - 1];
QUnit.pushFailure( error, filePath + ":" + linerNr ); return caller !== Object && typeof caller !== "undefined" && a.toString() === b.toString();
} else { },
QUnit.test( "global failure", extend( function() {
QUnit.pushFailure( error, filePath + ":" + linerNr ); "array": function array(b, a) {
}, { validTest: true } ) ); var i, j, len, loop, aCircular, bCircular;
}
return false; len = a.length;
} if (len !== b.length) {
return ret; // Safe and faster
}; return false;
}() ); }
// Figure out if we're running the tests from a server or not // Track reference to avoid circular references
QUnit.isLocal = !( defined.document && window.location.protocol !== "file:" ); parents.push(a);
parentsB.push(b);
// Expose the current QUnit version for (i = 0; i < len; i++) {
QUnit.version = "2.0.1"; loop = false;
for (j = 0; j < parents.length; j++) {
extend( QUnit, { aCircular = parents[j] === a[i];
bCircular = parentsB[j] === b[i];
// Call on start of module test to prepend name to all tests if (aCircular || bCircular) {
module: function( name, testEnvironment, executeNow ) { if (a[i] === b[i] || aCircular && bCircular) {
var module, moduleFns; loop = true;
var currentModule = config.currentModule; } else {
parents.pop();
if ( arguments.length === 2 ) { parentsB.pop();
if ( objectType( testEnvironment ) === "function" ) { return false;
executeNow = testEnvironment; }
testEnvironment = undefined; }
} }
} if (!loop && !innerEquiv(a[i], b[i])) {
parents.pop();
module = createModule(); parentsB.pop();
return false;
if ( testEnvironment && ( testEnvironment.setup || testEnvironment.teardown ) ) { }
console.warn( }
"Module's `setup` and `teardown` are not hooks anymore on QUnit 2.0, use " + parents.pop();
"`beforeEach` and `afterEach` instead\n" + parentsB.pop();
"Details in our upgrade guide at https://qunitjs.com/upgrade-guide-2.x/" return true;
); },
}
"set": function set$$1(b, a) {
moduleFns = { var innerEq,
before: setHook( module, "before" ), outerEq = true;
beforeEach: setHook( module, "beforeEach" ),
afterEach: setHook( module, "afterEach" ), if (a.size !== b.size) {
after: setHook( module, "after" ) return false;
}; }
if ( objectType( executeNow ) === "function" ) { a.forEach(function (aVal) {
config.moduleStack.push( module ); innerEq = false;
setCurrentModule( module );
executeNow.call( module.testEnvironment, moduleFns ); b.forEach(function (bVal) {
config.moduleStack.pop(); if (innerEquiv(bVal, aVal)) {
module = module.parentModule || currentModule; innerEq = true;
} }
});
setCurrentModule( module );
if (!innerEq) {
function createModule() { outerEq = false;
var parentModule = config.moduleStack.length ? }
config.moduleStack.slice( -1 )[ 0 ] : null; });
var moduleName = parentModule !== null ?
[ parentModule.name, name ].join( " > " ) : name; return outerEq;
var module = { },
name: moduleName,
parentModule: parentModule, "map": function map(b, a) {
tests: [], var innerEq,
moduleId: generateHash( moduleName ), outerEq = true;
testsRun: 0
}; if (a.size !== b.size) {
return false;
var env = {}; }
if ( parentModule ) {
parentModule.childModule = module; a.forEach(function (aVal, aKey) {
extend( env, parentModule.testEnvironment ); innerEq = false;
delete env.beforeEach;
delete env.afterEach; b.forEach(function (bVal, bKey) {
} if (innerEquiv([bVal, bKey], [aVal, aKey])) {
extend( env, testEnvironment ); innerEq = true;
module.testEnvironment = env; }
});
config.modules.push( module );
return module; if (!innerEq) {
} outerEq = false;
}
function setCurrentModule( module ) { });
config.currentModule = module;
} return outerEq;
},
},
"object": function object(b, a) {
test: test, var i, j, loop, aCircular, bCircular;
skip: skip, // Default to true
var eq = true;
only: only, var aProperties = [];
var bProperties = [];
start: function( count ) {
var globalStartAlreadyCalled = globalStartCalled; if (compareConstructors(a, b) === false) {
return false;
if ( !config.current ) { }
globalStartCalled = true;
// Stack constructor before traversing properties
if ( runStarted ) { callers.push(a.constructor);
throw new Error( "Called start() while test already started running" );
} else if ( globalStartAlreadyCalled || count > 1 ) { // Track reference to avoid circular references
throw new Error( "Called start() outside of a test context too many times" ); parents.push(a);
} else if ( config.autostart ) { parentsB.push(b);
throw new Error( "Called start() outside of a test context when " +
"QUnit.config.autostart was true" ); // Be strict: don't ensure hasOwnProperty and go deep
} else if ( !config.pageLoaded ) { for (i in a) {
loop = false;
// The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it for (j = 0; j < parents.length; j++) {
config.autostart = true; aCircular = parents[j] === a[i];
return; bCircular = parentsB[j] === b[i];
} if (aCircular || bCircular) {
} else { if (a[i] === b[i] || aCircular && bCircular) {
throw new Error( loop = true;
"QUnit.start cannot be called inside a test context. This feature is removed in " + } else {
"QUnit 2.0. For async tests, use QUnit.test() with assert.async() instead.\n" + eq = false;
"Details in our upgrade guide at https://qunitjs.com/upgrade-guide-2.x/" break;
); }
} }
}
scheduleBegin(); aProperties.push(i);
}, if (!loop && !innerEquiv(a[i], b[i])) {
eq = false;
config: config, break;
}
is: is, }
objectType: objectType, parents.pop();
parentsB.pop();
extend: extend,
// Unstack, we are done
load: function() { callers.pop();
config.pageLoaded = true;
for (i in b) {
// Initialize the configuration options
extend( config, { // Collect b's properties
stats: { all: 0, bad: 0 }, bProperties.push(i);
moduleStats: { all: 0, bad: 0 }, }
started: 0,
updateRate: 1000, // Ensures identical properties name
autostart: true, return eq && innerEquiv(aProperties.sort(), bProperties.sort());
filter: "" }
}, true ); };
if ( !runStarted ) { function typeEquiv(a, b) {
config.blocking = false; var type = objectType(a);
return objectType(b) === type && callbacks[type](b, a);
if ( config.autostart ) { }
scheduleBegin();
} // The real equiv function
} function innerEquiv(a, b) {
},
// We're done when there's nothing more to compare
stack: function( offset ) { if (arguments.length < 2) {
offset = ( offset || 0 ) + 2; return true;
return sourceFromStacktrace( offset ); }
}
} ); // Require type-specific equality
return (a === b || typeEquiv(a, b)) && (
registerLoggingCallbacks( QUnit );
// ...across all consecutive argument pairs
function scheduleBegin() { arguments.length === 2 || innerEquiv.apply(this, [].slice.call(arguments, 1)));
}
runStarted = true;
return innerEquiv;
// Add a slight delay to allow definition of more modules and tests. })();
if ( defined.setTimeout ) {
setTimeout( function() { /**
begin(); * Config object: Maintain internal state
}, 13 ); * Later exposed as QUnit.config
} else { * `config` initialized at top of scope
begin(); */
} var config = {
}
// The queue of tests to run
function begin() { queue: [],
var i, l,
modulesLog = []; // Block until document ready
blocking: true,
// If the test run hasn't officially begun yet
if ( !config.started ) { // By default, run previously failed tests first
// very useful in combination with "Hide passed tests" checked
// Record the time of the test run's beginning reorder: true,
config.started = now();
// By default, modify document.title when suite is done
// Delete the loose unnamed module if unused. altertitle: true,
if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) {
config.modules.shift(); // HTML Reporter: collapse every test except the first failing test
} // If false, all failing tests will be expanded
collapse: true,
// Avoid unnecessary information by not logging modules' test environments
for ( i = 0, l = config.modules.length; i < l; i++ ) { // By default, scroll to top of the page when suite is done
modulesLog.push( { scrolltop: true,
name: config.modules[ i ].name,
tests: config.modules[ i ].tests // Depth up-to which object will be dumped
} ); maxDepth: 5,
}
// When enabled, all tests must call expect()
// The test run is officially beginning now requireExpects: false,
runLoggingCallbacks( "begin", {
totalTests: Test.count, // Placeholder for user-configurable form-exposed URL parameters
modules: modulesLog urlConfig: [],
} );
} // Set of all modules.
modules: [],
config.blocking = false;
process( true ); // Stack of nested modules
} moduleStack: [],
function process( last ) { // The first unnamed module
function next() { currentModule: {
process( last ); name: "",
} tests: [],
var start = now(); childModules: [],
config.depth = ( config.depth || 0 ) + 1; testsRun: 0
},
while ( config.queue.length && !config.blocking ) {
if ( !defined.setTimeout || config.updateRate <= 0 || callbacks: {},
( ( now() - start ) < config.updateRate ) ) {
if ( config.current ) { // The storage module to use for reordering tests
storage: sessionStorage
// Reset async tracking for each phase of the Test lifecycle };
config.current.usedAsync = false;
} // take a predefined QUnit.config and extend the defaults
config.queue.shift()(); var globalConfig = window && window.QUnit && window.QUnit.config;
} else {
setTimeout( next, 13 ); // only extend the global config if there is no QUnit overload
break; if (window && window.QUnit && !window.QUnit.version) {
} extend(config, globalConfig);
} }
config.depth--;
if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) { // Push a loose unnamed module to the modules collection
done(); config.modules.push(config.currentModule);
}
} // Based on jsDump by Ariel Flesler
// http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
function done() { var dump = (function () {
var runtime, passed; function quote(str) {
return "\"" + str.toString().replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\"";
autorun = true; }
function literal(o) {
// Log the last module results return o + "";
if ( config.previousModule ) { }
runLoggingCallbacks( "moduleDone", { function join(pre, arr, post) {
name: config.previousModule.name, var s = dump.separator(),
tests: config.previousModule.tests, base = dump.indent(),
failed: config.moduleStats.bad, inner = dump.indent(1);
passed: config.moduleStats.all - config.moduleStats.bad, if (arr.join) {
total: config.moduleStats.all, arr = arr.join("," + s + inner);
runtime: now() - config.moduleStats.started }
} ); if (!arr) {
} return pre + post;
delete config.previousModule; }
return [pre, inner + arr, base + post].join(s);
runtime = now() - config.started; }
passed = config.stats.all - config.stats.bad; function array(arr, stack) {
var i = arr.length,
runLoggingCallbacks( "done", { ret = new Array(i);
failed: config.stats.bad,
passed: passed, if (dump.maxDepth && dump.depth > dump.maxDepth) {
total: config.stats.all, return "[object Array]";
runtime: runtime }
} );
} this.up();
while (i--) {
function setHook( module, hookName ) { ret[i] = this.parse(arr[i], undefined, stack);
if ( module.testEnvironment === undefined ) { }
module.testEnvironment = {}; this.down();
} return join("[", ret, "]");
}
return function( callback ) {
module.testEnvironment[ hookName ] = callback; function isArray(obj) {
}; return (
}
//Native Arrays
var unitSampler, toString.call(obj) === "[object Array]" ||
focused = false,
priorityCount = 0; // NodeList objects
typeof obj.length === "number" && obj.item !== undefined && (obj.length ? obj.item(0) === obj[0] : obj.item(0) === null && obj[0] === undefined)
function Test( settings ) { );
var i, l; }
++Test.count; var reName = /^function (\w+)/,
dump = {
this.expected = null;
extend( this, settings ); // The objType is used mostly internally, you can fix a (custom) type in advance
this.assertions = []; parse: function parse(obj, objType, stack) {
this.semaphore = 0; stack = stack || [];
this.usedAsync = false; var res,
this.module = config.currentModule; parser,
this.stack = sourceFromStacktrace( 3 ); parserType,
inStack = inArray(obj, stack);
// Register unique strings
for ( i = 0, l = this.module.tests; i < l.length; i++ ) { if (inStack !== -1) {
if ( this.module.tests[ i ].name === this.testName ) { return "recursion(" + (inStack - stack.length) + ")";
this.testName += " "; }
}
} objType = objType || this.typeOf(obj);
parser = this.parsers[objType];
this.testId = generateHash( this.module.name, this.testName ); parserType = typeof parser === "undefined" ? "undefined" : _typeof(parser);
this.module.tests.push( { if (parserType === "function") {
name: this.testName, stack.push(obj);
testId: this.testId res = parser.call(this, obj, stack);
} ); stack.pop();
return res;
if ( settings.skip ) { }
return parserType === "string" ? parser : this.parsers.error;
// Skipped tests will fully ignore any sent callback },
this.callback = function() {}; typeOf: function typeOf(obj) {
this.async = false; var type;
this.expected = 0;
} else { if (obj === null) {
this.assert = new Assert( this ); type = "null";
} } else if (typeof obj === "undefined") {
} type = "undefined";
} else if (is("regexp", obj)) {
Test.count = 0; type = "regexp";
} else if (is("date", obj)) {
Test.prototype = { type = "date";
before: function() { } else if (is("function", obj)) {
if ( type = "function";
} else if (obj.setInterval !== undefined && obj.document !== undefined && obj.nodeType === undefined) {
// Emit moduleStart when we're switching from one module to another type = "window";
this.module !== config.previousModule || } else if (obj.nodeType === 9) {
type = "document";
// They could be equal (both undefined) but if the previousModule property doesn't } else if (obj.nodeType) {
// yet exist it means this is the first test in a suite that isn't wrapped in a type = "node";
// module, in which case we'll just emit a moduleStart event for 'undefined'. } else if (isArray(obj)) {
// Without this, reporters can get testStart before moduleStart which is a problem. type = "array";
!hasOwn.call( config, "previousModule" ) } else if (obj.constructor === Error.prototype.constructor) {
) { type = "error";
if ( hasOwn.call( config, "previousModule" ) ) { } else {
runLoggingCallbacks( "moduleDone", { type = typeof obj === "undefined" ? "undefined" : _typeof(obj);
name: config.previousModule.name, }
tests: config.previousModule.tests, return type;
failed: config.moduleStats.bad, },
passed: config.moduleStats.all - config.moduleStats.bad,
total: config.moduleStats.all, separator: function separator() {
runtime: now() - config.moduleStats.started if (this.multiline) {
} ); return this.HTML ? "<br />" : "\n";
} } else {
config.previousModule = this.module; return this.HTML ? "&#160;" : " ";
config.moduleStats = { all: 0, bad: 0, started: now() }; }
runLoggingCallbacks( "moduleStart", { },
name: this.module.name,
tests: this.module.tests // Extra can be a number, shortcut for increasing-calling-decreasing
} ); indent: function indent(extra) {
} if (!this.multiline) {
return "";
config.current = this; }
var chr = this.indentChar;
if ( this.module.testEnvironment ) { if (this.HTML) {
delete this.module.testEnvironment.before; chr = chr.replace(/\t/g, " ").replace(/ /g, "&#160;");
delete this.module.testEnvironment.beforeEach; }
delete this.module.testEnvironment.afterEach; return new Array(this.depth + (extra || 0)).join(chr);
delete this.module.testEnvironment.after; },
} up: function up(a) {
this.testEnvironment = extend( {}, this.module.testEnvironment ); this.depth += a || 1;
},
this.started = now(); down: function down(a) {
runLoggingCallbacks( "testStart", { this.depth -= a || 1;
name: this.testName, },
module: this.module.name, setParser: function setParser(name, parser) {
testId: this.testId this.parsers[name] = parser;
} ); },
if ( !config.pollution ) { // The next 3 are exposed so you can use them
saveGlobal(); quote: quote,
} literal: literal,
}, join: join,
depth: 1,
run: function() { maxDepth: config.maxDepth,
var promise;
// This is the list of parsers, to modify them, use dump.setParser
config.current = this; parsers: {
window: "[Window]",
this.callbackStarted = now(); document: "[Document]",
error: function error(_error) {
if ( config.notrycatch ) { return "Error(\"" + _error.message + "\")";
runTest( this ); },
return; unknown: "[Unknown]",
} "null": "null",
"undefined": "undefined",
try { "function": function _function(fn) {
runTest( this ); var ret = "function",
} catch ( e ) {
this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " +
this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) ); // Functions never have name in IE
name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];
// Else next test will carry the responsibility
saveGlobal(); if (name) {
ret += " " + name;
// Restart the tests if they're blocking }
if ( config.blocking ) { ret += "(";
internalRecover( this );
} ret = [ret, dump.parse(fn, "functionArgs"), "){"].join("");
} return join(ret, dump.parse(fn, "functionCode"), "}");
},
function runTest( test ) { array: array,
promise = test.callback.call( test.testEnvironment, test.assert ); nodelist: array,
test.resolvePromise( promise ); "arguments": array,
} object: function object(map, stack) {
}, var keys,
key,
after: function() { val,
checkPollution(); i,
}, nonEnumerableProperties,
ret = [];
queueHook: function( hook, hookName, hookOwner ) {
var promise, if (dump.maxDepth && dump.depth > dump.maxDepth) {
test = this; return "[object Object]";
return function runHook() { }
if ( hookName === "before" ) {
if ( hookOwner.testsRun !== 0 ) { dump.up();
return; keys = [];
} for (key in map) {
keys.push(key);
test.preserveEnvironment = true; }
}
// Some properties are not always enumerable on Error objects.
if ( hookName === "after" && hookOwner.testsRun !== numberOfTests( hookOwner ) - 1 ) { nonEnumerableProperties = ["message", "name"];
return; for (i in nonEnumerableProperties) {
} key = nonEnumerableProperties[i];
if (key in map && inArray(key, keys) < 0) {
config.current = test; keys.push(key);
if ( config.notrycatch ) { }
callHook(); }
return; keys.sort();
} for (i = 0; i < keys.length; i++) {
try { key = keys[i];
callHook(); val = map[key];
} catch ( error ) { ret.push(dump.parse(key, "key") + ": " + dump.parse(val, undefined, stack));
test.pushFailure( hookName + " failed on " + test.testName + ": " + }
( error.message || error ), extractStacktrace( error, 0 ) ); dump.down();
} return join("{", ret, "}");
},
function callHook() { node: function node(_node) {
promise = hook.call( test.testEnvironment, test.assert ); var len,
test.resolvePromise( promise, hookName ); i,
} val,
}; open = dump.HTML ? "&lt;" : "<",
}, close = dump.HTML ? "&gt;" : ">",
tag = _node.nodeName.toLowerCase(),
// Currently only used for module level hooks, can be used to add global level ones ret = open + tag,
hooks: function( handler ) { attrs = _node.attributes;
var hooks = [];
if (attrs) {
function processHooks( test, module ) { for (i = 0, len = attrs.length; i < len; i++) {
if ( module.parentModule ) { val = attrs[i].nodeValue;
processHooks( test, module.parentModule );
} // IE6 includes all attributes in .attributes, even ones not explicitly
if ( module.testEnvironment && // set. Those have values like undefined, null, 0, false, "" or
QUnit.objectType( module.testEnvironment[ handler ] ) === "function" ) { // "inherit".
hooks.push( test.queueHook( module.testEnvironment[ handler ], handler, module ) ); if (val && val !== "inherit") {
} ret += " " + attrs[i].nodeName + "=" + dump.parse(val, "attribute");
} }
}
// Hooks are ignored on skipped tests }
if ( !this.skip ) { ret += close;
processHooks( this, this.module );
} // Show content of TextNode or CDATASection
return hooks; if (_node.nodeType === 3 || _node.nodeType === 4) {
}, ret += _node.nodeValue;
}
finish: function() {
config.current = this; return ret + open + "/" + tag + close;
if ( config.requireExpects && this.expected === null ) { },
this.pushFailure( "Expected number of assertions to be defined, but expect() was " +
"not called.", this.stack ); // Function calls it internally, it's the arguments part of the function
} else if ( this.expected !== null && this.expected !== this.assertions.length ) { functionArgs: function functionArgs(fn) {
this.pushFailure( "Expected " + this.expected + " assertions, but " + var args,
this.assertions.length + " were run", this.stack ); l = fn.length;
} else if ( this.expected === null && !this.assertions.length ) {
this.pushFailure( "Expected at least one assertion, but none were run - call " + if (!l) {
"expect(0) to accept zero assertions.", this.stack ); return "";
} }
var i, args = new Array(l);
skipped = !!this.skip, while (l--) {
bad = 0;
// 97 is 'a'
this.runtime = now() - this.started; args[l] = String.fromCharCode(97 + l);
}
config.stats.all += this.assertions.length; return " " + args.join(", ") + " ";
config.moduleStats.all += this.assertions.length; },
for ( i = 0; i < this.assertions.length; i++ ) { // Object calls it internally, the key part of an item in a map
if ( !this.assertions[ i ].result ) { key: quote,
bad++;
config.stats.bad++; // Function calls it internally, it's the content of the function
config.moduleStats.bad++; functionCode: "[code]",
}
} // Node calls it internally, it's a html attribute value
attribute: quote,
notifyTestsRan( this.module ); string: quote,
runLoggingCallbacks( "testDone", { date: quote,
name: this.testName, regexp: literal,
module: this.module.name, number: literal,
skipped: skipped, "boolean": literal,
failed: bad, symbol: function symbol(sym) {
passed: this.assertions.length - bad, return sym.toString();
total: this.assertions.length, }
runtime: skipped ? 0 : this.runtime, },
// HTML Reporter use // If true, entities are escaped ( <, >, \t, space and \n )
assertions: this.assertions, HTML: false,
testId: this.testId,
// Indentation unit
// Source of Test indentChar: " ",
source: this.stack
} ); // If true, items in a collection, are separated by a \n, else just a space.
multiline: true
config.current = undefined; };
},
return dump;
preserveTestEnvironment: function() { })();
if ( this.preserveEnvironment ) {
this.module.testEnvironment = this.testEnvironment; // Register logging callbacks
this.testEnvironment = extend( {}, this.module.testEnvironment ); function registerLoggingCallbacks(obj) {
} var i,
}, l,
key,
queue: function() { callbackNames = ["begin", "done", "log", "testStart", "testDone", "moduleStart", "moduleDone"];
var priority,
test = this; function registerLoggingCallback(key) {
var loggingCallback = function loggingCallback(callback) {
if ( !this.valid() ) { if (objectType(callback) !== "function") {
return; throw new Error("QUnit logging methods require a callback function as their first parameters.");
} }
function run() { config.callbacks[key].push(callback);
};
// Each of these can by async
synchronize( [ return loggingCallback;
function() { }
test.before();
}, for (i = 0, l = callbackNames.length; i < l; i++) {
key = callbackNames[i];
test.hooks( "before" ),
// Initialize key collection of logging callback
function() { if (objectType(config.callbacks[key]) === "undefined") {
test.preserveTestEnvironment(); config.callbacks[key] = [];
}, }
test.hooks( "beforeEach" ), obj[key] = registerLoggingCallback(key);
}
function() { }
test.run();
}, function runLoggingCallbacks(key, args) {
var i, l, callbacks;
test.hooks( "afterEach" ).reverse(),
test.hooks( "after" ).reverse(), callbacks = config.callbacks[key];
for (i = 0, l = callbacks.length; i < l; i++) {
function() { callbacks[i](args);
test.after(); }
}, }
function() { // Doesn't support IE9, it will return undefined on these browsers
test.finish(); // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
} var fileName = (sourceFromStacktrace(0) || "").replace(/(:\d+)+\)?/, "").replace(/.+\//, "");
] );
} function extractStacktrace(e, offset) {
offset = offset === undefined ? 4 : offset;
// Prioritize previously failed tests, detected from sessionStorage
priority = QUnit.config.reorder && defined.sessionStorage && var stack, include, i;
+sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName );
if (e && e.stack) {
return synchronize( run, priority, config.seed ); stack = e.stack.split("\n");
}, if (/^error$/i.test(stack[0])) {
stack.shift();
pushResult: function( resultInfo ) { }
if (fileName) {
// Destructure of resultInfo = { result, actual, expected, message, negative } include = [];
var source, for (i = offset; i < stack.length; i++) {
details = { if (stack[i].indexOf(fileName) !== -1) {
module: this.module.name, break;
name: this.testName, }
result: resultInfo.result, include.push(stack[i]);
message: resultInfo.message, }
actual: resultInfo.actual, if (include.length) {
expected: resultInfo.expected, return include.join("\n");
testId: this.testId, }
negative: resultInfo.negative || false, }
runtime: now() - this.started return stack[offset];
}; }
}
if ( !resultInfo.result ) {
source = sourceFromStacktrace(); function sourceFromStacktrace(offset) {
var error = new Error();
if ( source ) {
details.source = source; // Support: Safari <=7 only, IE <=10 - 11 only
} // Not all browsers generate the `stack` property for `new Error()`, see also #636
} if (!error.stack) {
try {
runLoggingCallbacks( "log", details ); throw error;
} catch (err) {
this.assertions.push( { error = err;
result: !!resultInfo.result, }
message: resultInfo.message }
} );
}, return extractStacktrace(error, offset);
}
pushFailure: function( message, source, actual ) {
if ( !( this instanceof Test ) ) { var unitSampler;
throw new Error( "pushFailure() assertion outside test context, was " + var focused = false;
sourceFromStacktrace( 2 ) ); var priorityCount = 0;
}
function Test(settings) {
var details = { var i, l;
module: this.module.name,
name: this.testName, ++Test.count;
result: false,
message: message || "error", this.expected = null;
actual: actual || null, extend(this, settings);
testId: this.testId, this.assertions = [];
runtime: now() - this.started this.semaphore = 0;
}; this.usedAsync = false;
this.module = config.currentModule;
if ( source ) { this.stack = sourceFromStacktrace(3);
details.source = source;
} // Register unique strings
for (i = 0, l = this.module.tests; i < l.length; i++) {
runLoggingCallbacks( "log", details ); if (this.module.tests[i].name === this.testName) {
this.testName += " ";
this.assertions.push( { }
result: false, }
message: message
} ); this.testId = generateHash(this.module.name, this.testName);
},
this.module.tests.push({
resolvePromise: function( promise, phase ) { name: this.testName,
var then, resume, message, testId: this.testId
test = this; });
if ( promise != null ) {
then = promise.then; if (settings.skip) {
if ( QUnit.objectType( then ) === "function" ) {
resume = internalStop( test ); // Skipped tests will fully ignore any sent callback
then.call( this.callback = function () {};
promise, this.async = false;
function() { resume(); }, this.expected = 0;
function( error ) { } else {
message = "Promise rejected " + this.assert = new Assert(this);
( !phase ? "during" : phase.replace( /Each$/, "" ) ) + }
" " + test.testName + ": " + ( error.message || error ); }
test.pushFailure( message, extractStacktrace( error, 0 ) );
Test.count = 0;
// Else next test will carry the responsibility
saveGlobal(); function getNotStartedModules(startModule) {
var module = startModule,
// Unblock modules = [];
resume();
} while (module && module.testsRun === 0) {
); modules.push(module);
} module = module.parentModule;
} }
},
return modules;
valid: function() { }
var filter = config.filter,
regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec( filter ), Test.prototype = {
module = config.module && config.module.toLowerCase(), before: function before() {
fullName = ( this.module.name + ": " + this.testName ); var i,
startModule,
function moduleChainNameMatch( testModule ) { module = this.module,
var testModuleName = testModule.name ? testModule.name.toLowerCase() : null; notStartedModules = getNotStartedModules(module);
if ( testModuleName === module ) {
return true; for (i = notStartedModules.length - 1; i >= 0; i--) {
} else if ( testModule.parentModule ) { startModule = notStartedModules[i];
return moduleChainNameMatch( testModule.parentModule ); startModule.stats = { all: 0, bad: 0, started: now() };
} else { runLoggingCallbacks("moduleStart", {
return false; name: startModule.name,
} tests: startModule.tests
} });
}
function moduleChainIdMatch( testModule ) {
return inArray( testModule.moduleId, config.moduleId ) > -1 || config.current = this;
testModule.parentModule && moduleChainIdMatch( testModule.parentModule );
} if (module.testEnvironment) {
delete module.testEnvironment.before;
// Internally-generated tests are always valid delete module.testEnvironment.beforeEach;
if ( this.callback && this.callback.validTest ) { delete module.testEnvironment.afterEach;
return true; delete module.testEnvironment.after;
} }
this.testEnvironment = extend({}, module.testEnvironment);
if ( config.moduleId && config.moduleId.length > 0 &&
!moduleChainIdMatch( this.module ) ) { this.started = now();
runLoggingCallbacks("testStart", {
return false; name: this.testName,
} module: module.name,
testId: this.testId,
if ( config.testId && config.testId.length > 0 && previousFailure: this.previousFailure
inArray( this.testId, config.testId ) < 0 ) { });
return false; if (!config.pollution) {
} saveGlobal();
}
if ( module && !moduleChainNameMatch( this.module ) ) { },
return false;
} run: function run() {
var promise;
if ( !filter ) {
return true; config.current = this;
}
this.callbackStarted = now();
return regexFilter ?
this.regexFilter( !!regexFilter[ 1 ], regexFilter[ 2 ], regexFilter[ 3 ], fullName ) : if (config.notrycatch) {
this.stringFilter( filter, fullName ); runTest(this);
}, return;
}
regexFilter: function( exclude, pattern, flags, fullName ) {
var regex = new RegExp( pattern, flags ); try {
var match = regex.test( fullName ); runTest(this);
} catch (e) {
return match !== exclude; this.pushFailure("Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + (e.message || e), extractStacktrace(e, 0));
},
// Else next test will carry the responsibility
stringFilter: function( filter, fullName ) { saveGlobal();
filter = filter.toLowerCase();
fullName = fullName.toLowerCase(); // Restart the tests if they're blocking
if (config.blocking) {
var include = filter.charAt( 0 ) !== "!"; internalRecover(this);
if ( !include ) { }
filter = filter.slice( 1 ); }
}
function runTest(test) {
// If the filter matches, we need to honour include promise = test.callback.call(test.testEnvironment, test.assert);
if ( fullName.indexOf( filter ) !== -1 ) { test.resolvePromise(promise);
return include; }
} },
// Otherwise, do the opposite after: function after() {
return !include; checkPollution();
} },
};
queueHook: function queueHook(hook, hookName, hookOwner) {
QUnit.pushFailure = function() { var promise,
if ( !QUnit.config.current ) { test = this;
throw new Error( "pushFailure() assertion outside test context, in " + return function runHook() {
sourceFromStacktrace( 2 ) ); if (hookName === "before") {
} if (hookOwner.testsRun !== 0) {
return;
// Gets current test obj }
var currentTest = QUnit.config.current;
test.preserveEnvironment = true;
return currentTest.pushFailure.apply( currentTest, arguments ); }
};
if (hookName === "after" && hookOwner.testsRun !== numberOfTests(hookOwner) - 1) {
// Based on Java's String.hashCode, a simple but not return;
// rigorously collision resistant hashing function }
function generateHash( module, testName ) {
var hex, config.current = test;
i = 0, if (config.notrycatch) {
hash = 0, callHook();
str = module + "\x1C" + testName, return;
len = str.length; }
try {
for ( ; i < len; i++ ) { callHook();
hash = ( ( hash << 5 ) - hash ) + str.charCodeAt( i ); } catch (error) {
hash |= 0; test.pushFailure(hookName + " failed on " + test.testName + ": " + (error.message || error), extractStacktrace(error, 0));
} }
// Convert the possibly negative integer hash code into an 8 character hex string, which isn't function callHook() {
// strictly necessary but increases user understanding that the id is a SHA-like hash promise = hook.call(test.testEnvironment, test.assert);
hex = ( 0x100000000 + hash ).toString( 16 ); test.resolvePromise(promise, hookName);
if ( hex.length < 8 ) { }
hex = "0000000" + hex; };
} },
return hex.slice( -8 ); // Currently only used for module level hooks, can be used to add global level ones
} hooks: function hooks(handler) {
var hooks = [];
function synchronize( callback, priority, seed ) {
var last = !priority, function processHooks(test, module) {
index; if (module.parentModule) {
processHooks(test, module.parentModule);
if ( QUnit.objectType( callback ) === "array" ) { }
while ( callback.length ) { if (module.testEnvironment && objectType(module.testEnvironment[handler]) === "function") {
synchronize( callback.shift() ); hooks.push(test.queueHook(module.testEnvironment[handler], handler, module));
} }
return; }
}
// Hooks are ignored on skipped tests
if ( priority ) { if (!this.skip) {
config.queue.splice( priorityCount++, 0, callback ); processHooks(this, this.module);
} else if ( seed ) { }
if ( !unitSampler ) { return hooks;
unitSampler = unitSamplerGenerator( seed ); },
}
finish: function finish() {
// Insert into a random position after all priority items config.current = this;
index = Math.floor( unitSampler() * ( config.queue.length - priorityCount + 1 ) ); if (config.requireExpects && this.expected === null) {
config.queue.splice( priorityCount + index, 0, callback ); this.pushFailure("Expected number of assertions to be defined, but expect() was " + "not called.", this.stack);
} else { } else if (this.expected !== null && this.expected !== this.assertions.length) {
config.queue.push( callback ); this.pushFailure("Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack);
} } else if (this.expected === null && !this.assertions.length) {
this.pushFailure("Expected at least one assertion, but none were run - call " + "expect(0) to accept zero assertions.", this.stack);
if ( autorun && !config.blocking ) { }
process( last );
} var i,
} module = this.module,
moduleName = module.name,
function unitSamplerGenerator( seed ) { testName = this.testName,
skipped = !!this.skip,
// 32-bit xorshift, requires only a nonzero seed bad = 0,
// http://excamera.com/sphinx/article-xorshift.html storage = config.storage;
var sample = parseInt( generateHash( seed ), 16 ) || -1;
return function() { this.runtime = now() - this.started;
sample ^= sample << 13;
sample ^= sample >>> 17; config.stats.all += this.assertions.length;
sample ^= sample << 5; module.stats.all += this.assertions.length;
// ECMAScript has no unsigned number type for (i = 0; i < this.assertions.length; i++) {
if ( sample < 0 ) { if (!this.assertions[i].result) {
sample += 0x100000000; bad++;
} config.stats.bad++;
module.stats.bad++;
return sample / 0x100000000; }
}; }
}
notifyTestsRan(module);
function saveGlobal() {
config.pollution = []; // Store result when possible
if (storage) {
if ( config.noglobals ) { if (bad) {
for ( var key in global ) { storage.setItem("qunit-test-" + moduleName + "-" + testName, bad);
if ( hasOwn.call( global, key ) ) { } else {
storage.removeItem("qunit-test-" + moduleName + "-" + testName);
// In Opera sometimes DOM element ids show up here, ignore them }
if ( /^qunit-test-output/.test( key ) ) { }
continue;
} runLoggingCallbacks("testDone", {
config.pollution.push( key ); name: testName,
} module: moduleName,
} skipped: skipped,
} failed: bad,
} passed: this.assertions.length - bad,
total: this.assertions.length,
function checkPollution() { runtime: skipped ? 0 : this.runtime,
var newGlobals,
deletedGlobals, // HTML Reporter use
old = config.pollution; assertions: this.assertions,
testId: this.testId,
saveGlobal();
// Source of Test
newGlobals = diff( config.pollution, old ); source: this.stack
if ( newGlobals.length > 0 ) { });
QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) );
} if (module.testsRun === numberOfTests(module)) {
runLoggingCallbacks("moduleDone", {
deletedGlobals = diff( old, config.pollution ); name: module.name,
if ( deletedGlobals.length > 0 ) { tests: module.tests,
QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) ); failed: module.stats.bad,
} passed: module.stats.all - module.stats.bad,
} total: module.stats.all,
runtime: now() - module.stats.started
// Will be exposed as QUnit.test });
function test( testName, callback ) { }
if ( focused ) { return; }
config.current = undefined;
var newTest; },
newTest = new Test( { preserveTestEnvironment: function preserveTestEnvironment() {
testName: testName, if (this.preserveEnvironment) {
callback: callback this.module.testEnvironment = this.testEnvironment;
} ); this.testEnvironment = extend({}, this.module.testEnvironment);
}
newTest.queue(); },
}
queue: function queue() {
// Will be exposed as QUnit.skip var priority,
function skip( testName ) { previousFailCount,
if ( focused ) { return; } test = this;
var test = new Test( { if (!this.valid()) {
testName: testName, return;
skip: true }
} );
function run() {
test.queue();
} // Each of these can by async
synchronize([function () {
// Will be exposed as QUnit.only test.before();
function only( testName, callback ) { }, test.hooks("before"), function () {
var newTest; test.preserveTestEnvironment();
}, test.hooks("beforeEach"), function () {
if ( focused ) { return; } test.run();
}, test.hooks("afterEach").reverse(), test.hooks("after").reverse(), function () {
QUnit.config.queue.length = 0; test.after();
focused = true; }, function () {
test.finish();
newTest = new Test( { }]);
testName: testName, }
callback: callback
} ); previousFailCount = config.storage && +config.storage.getItem("qunit-test-" + this.module.name + "-" + this.testName);
newTest.queue(); // Prioritize previously failed tests, detected from storage
} priority = config.reorder && previousFailCount;
// Put a hold on processing and return a function that will release it. this.previousFailure = !!previousFailCount;
function internalStop( test ) {
var released = false; return synchronize(run, priority, config.seed);
},
test.semaphore += 1;
config.blocking = true; pushResult: function pushResult(resultInfo) {
// Set a recovery timeout, if so configured. // Destructure of resultInfo = { result, actual, expected, message, negative }
if ( config.testTimeout && defined.setTimeout ) { var source,
clearTimeout( config.timeout ); details = {
config.timeout = setTimeout( function() { module: this.module.name,
QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) ); name: this.testName,
internalRecover( test ); result: resultInfo.result,
}, config.testTimeout ); message: resultInfo.message,
} actual: resultInfo.actual,
expected: resultInfo.expected,
return function resume() { testId: this.testId,
if ( released ) { negative: resultInfo.negative || false,
return; runtime: now() - this.started
} };
released = true; if (!resultInfo.result) {
test.semaphore -= 1; source = sourceFromStacktrace();
internalStart( test );
}; if (source) {
} details.source = source;
}
// Forcefully release all processing holds. }
function internalRecover( test ) {
test.semaphore = 0; runLoggingCallbacks("log", details);
internalStart( test );
} this.assertions.push({
result: !!resultInfo.result,
// Release a processing hold, scheduling a resumption attempt if no holds remain. message: resultInfo.message
function internalStart( test ) { });
},
// If semaphore is non-numeric, throw error
if ( isNaN( test.semaphore ) ) { pushFailure: function pushFailure(message, source, actual) {
test.semaphore = 0; if (!(this instanceof Test)) {
throw new Error("pushFailure() assertion outside test context, was " + sourceFromStacktrace(2));
QUnit.pushFailure( }
"Invalid value on test.semaphore",
sourceFromStacktrace( 2 ) var details = {
); module: this.module.name,
return; name: this.testName,
} result: false,
message: message || "error",
// Don't start until equal number of stop-calls actual: actual || null,
if ( test.semaphore > 0 ) { testId: this.testId,
return; runtime: now() - this.started
} };
// Throw an Error if start is called more often than stop if (source) {
if ( test.semaphore < 0 ) { details.source = source;
test.semaphore = 0; }
QUnit.pushFailure( runLoggingCallbacks("log", details);
"Tried to restart test while already started (test's semaphore was 0 already)",
sourceFromStacktrace( 2 ) this.assertions.push({
); result: false,
return; message: message
} });
},
// Add a slight delay to allow more assertions etc.
if ( defined.setTimeout ) { resolvePromise: function resolvePromise(promise, phase) {
if ( config.timeout ) { var then,
clearTimeout( config.timeout ); resume,
} message,
config.timeout = setTimeout( function() { test = this;
if ( test.semaphore > 0 ) { if (promise != null) {
return; then = promise.then;
} if (objectType(then) === "function") {
resume = internalStop(test);
if ( config.timeout ) { then.call(promise, function () {
clearTimeout( config.timeout ); resume();
} }, function (error) {
message = "Promise rejected " + (!phase ? "during" : phase.replace(/Each$/, "")) + " \"" + test.testName + "\": " + (error && error.message || error);
begin(); test.pushFailure(message, extractStacktrace(error, 0));
}, 13 );
} else { // Else next test will carry the responsibility
begin(); saveGlobal();
}
} // Unblock
resume();
function numberOfTests( module ) { });
var count = module.tests.length; }
while ( module = module.childModule ) { }
count += module.tests.length; },
}
return count; valid: function valid() {
} var filter = config.filter,
regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec(filter),
function notifyTestsRan( module ) { module = config.module && config.module.toLowerCase(),
module.testsRun++; fullName = this.module.name + ": " + this.testName;
while ( module = module.parentModule ) {
module.testsRun++; function moduleChainNameMatch(testModule) {
} var testModuleName = testModule.name ? testModule.name.toLowerCase() : null;
} if (testModuleName === module) {
return true;
function Assert( testContext ) { } else if (testModule.parentModule) {
this.test = testContext; return moduleChainNameMatch(testModule.parentModule);
} } else {
return false;
// Assert helpers }
QUnit.assert = Assert.prototype = { }
// Specify the number of expected assertions to guarantee that failed test function moduleChainIdMatch(testModule) {
// (no assertions are run at all) don't slip through. return inArray(testModule.moduleId, config.moduleId) > -1 || testModule.parentModule && moduleChainIdMatch(testModule.parentModule);
expect: function( asserts ) { }
if ( arguments.length === 1 ) {
this.test.expected = asserts; // Internally-generated tests are always valid
} else { if (this.callback && this.callback.validTest) {
return this.test.expected; return true;
} }
},
if (config.moduleId && config.moduleId.length > 0 && !moduleChainIdMatch(this.module)) {
// Put a hold on processing and return a function that will release it a maximum of once.
async: function( count ) { return false;
var resume, }
test = this.test,
popped = false, if (config.testId && config.testId.length > 0 && inArray(this.testId, config.testId) < 0) {
acceptCallCount = count;
return false;
if ( typeof acceptCallCount === "undefined" ) { }
acceptCallCount = 1;
} if (module && !moduleChainNameMatch(this.module)) {
return false;
test.usedAsync = true; }
resume = internalStop( test );
if (!filter) {
return function done() { return true;
}
if ( popped ) {
test.pushFailure( "Too many calls to the `assert.async` callback", return regexFilter ? this.regexFilter(!!regexFilter[1], regexFilter[2], regexFilter[3], fullName) : this.stringFilter(filter, fullName);
sourceFromStacktrace( 2 ) ); },
return;
} regexFilter: function regexFilter(exclude, pattern, flags, fullName) {
acceptCallCount -= 1; var regex = new RegExp(pattern, flags);
if ( acceptCallCount > 0 ) { var match = regex.test(fullName);
return;
} return match !== exclude;
},
popped = true;
resume(); stringFilter: function stringFilter(filter, fullName) {
}; filter = filter.toLowerCase();
}, fullName = fullName.toLowerCase();
// Exports test.push() to the user API var include = filter.charAt(0) !== "!";
// Alias of pushResult. if (!include) {
push: function( result, actual, expected, message, negative ) { filter = filter.slice(1);
var currentAssert = this instanceof Assert ? this : QUnit.config.current.assert; }
return currentAssert.pushResult( {
result: result, // If the filter matches, we need to honour include
actual: actual, if (fullName.indexOf(filter) !== -1) {
expected: expected, return include;
message: message, }
negative: negative
} ); // Otherwise, do the opposite
}, return !include;
}
pushResult: function( resultInfo ) { };
// Destructure of resultInfo = { result, actual, expected, message, negative } function pushFailure() {
var assert = this, if (!config.current) {
currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current; throw new Error("pushFailure() assertion outside test context, in " + sourceFromStacktrace(2));
}
// Backwards compatibility fix.
// Allows the direct use of global exported assertions and QUnit.assert.* // Gets current test obj
// Although, it's use is not recommended as it can leak assertions var currentTest = config.current;
// to other tests from async tests, because we only get a reference to the current test,
// not exactly the test where assertion were intended to be called. return currentTest.pushFailure.apply(currentTest, arguments);
if ( !currentTest ) { }
throw new Error( "assertion outside test context, in " + sourceFromStacktrace( 2 ) );
} // Based on Java's String.hashCode, a simple but not
// rigorously collision resistant hashing function
if ( currentTest.usedAsync === true && currentTest.semaphore === 0 ) { function generateHash(module, testName) {
currentTest.pushFailure( "Assertion after the final `assert.async` was resolved", var hex,
sourceFromStacktrace( 2 ) ); i = 0,
hash = 0,
// Allow this assertion to continue running anyway... str = module + "\x1C" + testName,
} len = str.length;
if ( !( assert instanceof Assert ) ) { for (; i < len; i++) {
assert = currentTest.assert; hash = (hash << 5) - hash + str.charCodeAt(i);
} hash |= 0;
}
return assert.test.pushResult( resultInfo );
}, // Convert the possibly negative integer hash code into an 8 character hex string, which isn't
// strictly necessary but increases user understanding that the id is a SHA-like hash
ok: function( result, message ) { hex = (0x100000000 + hash).toString(16);
message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " + if (hex.length < 8) {
QUnit.dump.parse( result ) ); hex = "0000000" + hex;
this.pushResult( { }
result: !!result,
actual: result, return hex.slice(-8);
expected: true, }
message: message
} ); function synchronize(callback, priority, seed) {
}, var last = !priority,
index;
notOk: function( result, message ) {
message = message || ( !result ? "okay" : "failed, expected argument to be falsy, was: " + if (objectType(callback) === "array") {
QUnit.dump.parse( result ) ); while (callback.length) {
this.pushResult( { synchronize(callback.shift());
result: !result, }
actual: result, return;
expected: false, }
message: message
} ); if (priority) {
}, config.queue.splice(priorityCount++, 0, callback);
} else if (seed) {
equal: function( actual, expected, message ) { if (!unitSampler) {
/*jshint eqeqeq:false */ unitSampler = unitSamplerGenerator(seed);
this.pushResult( { }
result: expected == actual,
actual: actual, // Insert into a random position after all priority items
expected: expected, index = Math.floor(unitSampler() * (config.queue.length - priorityCount + 1));
message: message config.queue.splice(priorityCount + index, 0, callback);
} ); } else {
}, config.queue.push(callback);
}
notEqual: function( actual, expected, message ) {
/*jshint eqeqeq:false */ if (internalState.autorun && !config.blocking) {
this.pushResult( { process(last);
result: expected != actual, }
actual: actual, }
expected: expected,
message: message, function unitSamplerGenerator(seed) {
negative: true
} ); // 32-bit xorshift, requires only a nonzero seed
}, // http://excamera.com/sphinx/article-xorshift.html
var sample = parseInt(generateHash(seed), 16) || -1;
propEqual: function( actual, expected, message ) { return function () {
actual = objectValues( actual ); sample ^= sample << 13;
expected = objectValues( expected ); sample ^= sample >>> 17;
this.pushResult( { sample ^= sample << 5;
result: QUnit.equiv( actual, expected ),
actual: actual, // ECMAScript has no unsigned number type
expected: expected, if (sample < 0) {
message: message sample += 0x100000000;
} ); }
},
return sample / 0x100000000;
notPropEqual: function( actual, expected, message ) { };
actual = objectValues( actual ); }
expected = objectValues( expected );
this.pushResult( { function saveGlobal() {
result: !QUnit.equiv( actual, expected ), config.pollution = [];
actual: actual,
expected: expected, if (config.noglobals) {
message: message, for (var key in global$1) {
negative: true if (hasOwn.call(global$1, key)) {
} );
}, // In Opera sometimes DOM element ids show up here, ignore them
if (/^qunit-test-output/.test(key)) {
deepEqual: function( actual, expected, message ) { continue;
this.pushResult( { }
result: QUnit.equiv( actual, expected ), config.pollution.push(key);
actual: actual, }
expected: expected, }
message: message }
} ); }
},
function checkPollution() {
notDeepEqual: function( actual, expected, message ) { var newGlobals,
this.pushResult( { deletedGlobals,
result: !QUnit.equiv( actual, expected ), old = config.pollution;
actual: actual,
expected: expected, saveGlobal();
message: message,
negative: true newGlobals = diff(config.pollution, old);
} ); if (newGlobals.length > 0) {
}, pushFailure("Introduced global variable(s): " + newGlobals.join(", "));
}
strictEqual: function( actual, expected, message ) {
this.pushResult( { deletedGlobals = diff(old, config.pollution);
result: expected === actual, if (deletedGlobals.length > 0) {
actual: actual, pushFailure("Deleted global variable(s): " + deletedGlobals.join(", "));
expected: expected, }
message: message }
} );
}, // Will be exposed as QUnit.test
function test(testName, callback) {
notStrictEqual: function( actual, expected, message ) { if (focused) {
this.pushResult( { return;
result: expected !== actual, }
actual: actual,
expected: expected, var newTest;
message: message,
negative: true newTest = new Test({
} ); testName: testName,
}, callback: callback
});
"throws": function( block, expected, message ) {
var actual, expectedType, newTest.queue();
expectedOutput = expected, }
ok = false,
currentTest = ( this instanceof Assert && this.test ) || QUnit.config.current; // Will be exposed as QUnit.skip
function skip(testName) {
// 'expected' is optional unless doing string comparison if (focused) {
if ( QUnit.objectType( expected ) === "string" ) { return;
if ( message == null ) { }
message = expected;
expected = null; var test = new Test({
} else { testName: testName,
throw new Error( skip: true
"throws/raises does not accept a string value for the expected argument.\n" + });
"Use a non-string object value (e.g. regExp) instead if it's necessary." +
"Details in our upgrade guide at https://qunitjs.com/upgrade-guide-2.x/" test.queue();
); }
}
} // Will be exposed as QUnit.only
function only(testName, callback) {
currentTest.ignoreGlobalErrors = true; var newTest;
try {
block.call( currentTest.testEnvironment ); if (focused) {
} catch ( e ) { return;
actual = e; }
}
currentTest.ignoreGlobalErrors = false; config.queue.length = 0;
focused = true;
if ( actual ) {
expectedType = QUnit.objectType( expected ); newTest = new Test({
testName: testName,
// We don't want to validate thrown error callback: callback
if ( !expected ) { });
ok = true;
expectedOutput = null; newTest.queue();
}
// Expected is a regexp
} else if ( expectedType === "regexp" ) { // Put a hold on processing and return a function that will release it.
ok = expected.test( errorString( actual ) ); function internalStop(test) {
var released = false;
// Expected is a constructor, maybe an Error constructor
} else if ( expectedType === "function" && actual instanceof expected ) { test.semaphore += 1;
ok = true; config.blocking = true;
// Expected is an Error object // Set a recovery timeout, if so configured.
} else if ( expectedType === "object" ) { if (config.testTimeout && defined.setTimeout) {
ok = actual instanceof expected.constructor && clearTimeout(config.timeout);
actual.name === expected.name && config.timeout = setTimeout(function () {
actual.message === expected.message; pushFailure("Test timed out", sourceFromStacktrace(2));
internalRecover(test);
// Expected is a validation function which returns true if validation passed }, config.testTimeout);
} else if ( expectedType === "function" && expected.call( {}, actual ) === true ) { }
expectedOutput = null;
ok = true; return function resume() {
} if (released) {
} return;
}
currentTest.assert.pushResult( {
result: ok, released = true;
actual: actual, test.semaphore -= 1;
expected: expectedOutput, internalStart(test);
message: message };
} ); }
}
}; // Forcefully release all processing holds.
function internalRecover(test) {
// Provide an alternative to assert.throws(), for environments that consider throws a reserved word test.semaphore = 0;
// Known to us are: Closure Compiler, Narwhal internalStart(test);
( function() { }
/*jshint sub:true */
Assert.prototype.raises = Assert.prototype [ "throws" ]; //jscs:ignore requireDotNotation // Release a processing hold, scheduling a resumption attempt if no holds remain.
}() ); function internalStart(test) {
function errorString( error ) { // If semaphore is non-numeric, throw error
var name, message, if (isNaN(test.semaphore)) {
resultErrorString = error.toString(); test.semaphore = 0;
if ( resultErrorString.substring( 0, 7 ) === "[object" ) {
name = error.name ? error.name.toString() : "Error"; pushFailure("Invalid value on test.semaphore", sourceFromStacktrace(2));
message = error.message ? error.message.toString() : ""; return;
if ( name && message ) { }
return name + ": " + message;
} else if ( name ) { // Don't start until equal number of stop-calls
return name; if (test.semaphore > 0) {
} else if ( message ) { return;
return message; }
} else {
return "Error"; // Throw an Error if start is called more often than stop
} if (test.semaphore < 0) {
} else { test.semaphore = 0;
return resultErrorString;
} pushFailure("Tried to restart test while already started (test's semaphore was 0 already)", sourceFromStacktrace(2));
} return;
}
// Test for equality any JavaScript type.
// Author: Philippe Rathé <prathe@gmail.com> // Add a slight delay to allow more assertions etc.
QUnit.equiv = ( function() { if (defined.setTimeout) {
if (config.timeout) {
// Stack to decide between skip/abort functions clearTimeout(config.timeout);
var callers = []; }
config.timeout = setTimeout(function () {
// Stack to avoiding loops from circular referencing if (test.semaphore > 0) {
var parents = []; return;
var parentsB = []; }
var getProto = Object.getPrototypeOf || function( obj ) { if (config.timeout) {
clearTimeout(config.timeout);
/*jshint proto: true */ }
return obj.__proto__;
}; begin();
}, 13);
function useStrictEquality( b, a ) { } else {
begin();
// To catch short annotation VS 'new' annotation of a declaration. e.g.: }
// `var i = 1;` }
// `var j = new Number(1);`
if ( typeof a === "object" ) { function numberOfTests(module) {
a = a.valueOf(); var count = module.tests.length,
} modules = [].concat(toConsumableArray(module.childModules));
if ( typeof b === "object" ) {
b = b.valueOf(); // Do a breadth-first traversal of the child modules
} while (modules.length) {
var nextModule = modules.shift();
return a === b; count += nextModule.tests.length;
} modules.push.apply(modules, toConsumableArray(nextModule.childModules));
}
function compareConstructors( a, b ) {
var protoA = getProto( a ); return count;
var protoB = getProto( b ); }
// Comparing constructors is more strict than using `instanceof` function notifyTestsRan(module) {
if ( a.constructor === b.constructor ) { module.testsRun++;
return true; while (module = module.parentModule) {
} module.testsRun++;
}
// Ref #851 }
// If the obj prototype descends from a null constructor, treat it
// as a null prototype. var Assert = function () {
if ( protoA && protoA.constructor === null ) { function Assert(testContext) {
protoA = null; classCallCheck(this, Assert);
}
if ( protoB && protoB.constructor === null ) { this.test = testContext;
protoB = null; }
}
// Assert helpers
// Allow objects with no prototype to be equivalent to
// objects with Object as their constructor. // Specify the number of expected assertions to guarantee that failed test
if ( ( protoA === null && protoB === Object.prototype ) || // (no assertions are run at all) don't slip through.
( protoB === null && protoA === Object.prototype ) ) {
return true;
} createClass(Assert, [{
key: "expect",
return false; value: function expect(asserts) {
} if (arguments.length === 1) {
this.test.expected = asserts;
function getRegExpFlags( regexp ) { } else {
return "flags" in regexp ? regexp.flags : regexp.toString().match( /[gimuy]*$/ )[ 0 ]; return this.test.expected;
} }
}
var callbacks = {
"string": useStrictEquality, // Put a hold on processing and return a function that will release it a maximum of once.
"boolean": useStrictEquality,
"number": useStrictEquality, }, {
"null": useStrictEquality, key: "async",
"undefined": useStrictEquality, value: function async(count) {
"symbol": useStrictEquality, var test$$1 = this.test,
"date": useStrictEquality, popped = false,
acceptCallCount = count;
"nan": function() {
return true; if (typeof acceptCallCount === "undefined") {
}, acceptCallCount = 1;
}
"regexp": function( b, a ) {
return a.source === b.source && test$$1.usedAsync = true;
var resume = internalStop(test$$1);
// Include flags in the comparison
getRegExpFlags( a ) === getRegExpFlags( b ); return function done() {
}, if (popped) {
test$$1.pushFailure("Too many calls to the `assert.async` callback", sourceFromStacktrace(2));
// - skip when the property is a method of an instance (OOP) return;
// - abort otherwise, }
// initial === would have catch identical references anyway
"function": function() { acceptCallCount -= 1;
var caller = callers[ callers.length - 1 ]; if (acceptCallCount > 0) {
return caller !== Object && typeof caller !== "undefined"; return;
}, }
"array": function( b, a ) { popped = true;
var i, j, len, loop, aCircular, bCircular; resume();
};
len = a.length; }
if ( len !== b.length ) {
// Exports test.push() to the user API
// Safe and faster // Alias of pushResult.
return false;
} }, {
key: "push",
// Track reference to avoid circular references value: function push(result, actual, expected, message, negative) {
parents.push( a ); console.warn("assert.push is deprecated and will be removed in QUnit 3.0." + " Please use assert.pushResult instead (http://api.qunitjs.com/pushResult/).");
parentsB.push( b );
for ( i = 0; i < len; i++ ) { var currentAssert = this instanceof Assert ? this : config.current.assert;
loop = false; return currentAssert.pushResult({
for ( j = 0; j < parents.length; j++ ) { result: result,
aCircular = parents[ j ] === a[ i ]; actual: actual,
bCircular = parentsB[ j ] === b[ i ]; expected: expected,
if ( aCircular || bCircular ) { message: message,
if ( a[ i ] === b[ i ] || aCircular && bCircular ) { negative: negative
loop = true; });
} else { }
parents.pop(); }, {
parentsB.pop(); key: "pushResult",
return false; value: function pushResult(resultInfo) {
}
} // Destructure of resultInfo = { result, actual, expected, message, negative }
} var assert = this,
if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) { currentTest = assert instanceof Assert && assert.test || config.current;
parents.pop();
parentsB.pop(); // Backwards compatibility fix.
return false; // Allows the direct use of global exported assertions and QUnit.assert.*
} // Although, it's use is not recommended as it can leak assertions
} // to other tests from async tests, because we only get a reference to the current test,
parents.pop(); // not exactly the test where assertion were intended to be called.
parentsB.pop(); if (!currentTest) {
return true; throw new Error("assertion outside test context, in " + sourceFromStacktrace(2));
}, }
"set": function( b, a ) { if (currentTest.usedAsync === true && currentTest.semaphore === 0) {
var innerEq, currentTest.pushFailure("Assertion after the final `assert.async` was resolved", sourceFromStacktrace(2));
outerEq = true;
// Allow this assertion to continue running anyway...
if ( a.size !== b.size ) { }
return false;
} if (!(assert instanceof Assert)) {
assert = currentTest.assert;
a.forEach( function( aVal ) { }
innerEq = false;
return assert.test.pushResult(resultInfo);
b.forEach( function( bVal ) { }
if ( innerEquiv( bVal, aVal ) ) { }, {
innerEq = true; key: "ok",
} value: function ok(result, message) {
} ); if (!message) {
message = result ? "okay" : "failed, expected argument to be truthy, was: " + dump.parse(result);
if ( !innerEq ) { }
outerEq = false;
} this.pushResult({
} ); result: !!result,
actual: result,
return outerEq; expected: true,
}, message: message
});
"map": function( b, a ) { }
var innerEq, }, {
outerEq = true; key: "notOk",
value: function notOk(result, message) {
if ( a.size !== b.size ) { if (!message) {
return false; message = !result ? "okay" : "failed, expected argument to be falsy, was: " + dump.parse(result);
} }
a.forEach( function( aVal, aKey ) { this.pushResult({
innerEq = false; result: !result,
actual: result,
b.forEach( function( bVal, bKey ) { expected: false,
if ( innerEquiv( [ bVal, bKey ], [ aVal, aKey ] ) ) { message: message
innerEq = true; });
} }
} ); }, {
key: "equal",
if ( !innerEq ) { value: function equal(actual, expected, message) {
outerEq = false;
} // eslint-disable-next-line eqeqeq
} ); var result = expected == actual;
return outerEq; this.pushResult({
}, result: result,
actual: actual,
"object": function( b, a ) { expected: expected,
var i, j, loop, aCircular, bCircular; message: message
});
// Default to true }
var eq = true; }, {
var aProperties = []; key: "notEqual",
var bProperties = []; value: function notEqual(actual, expected, message) {
if ( compareConstructors( a, b ) === false ) { // eslint-disable-next-line eqeqeq
return false; var result = expected != actual;
}
this.pushResult({
// Stack constructor before traversing properties result: result,
callers.push( a.constructor ); actual: actual,
expected: expected,
// Track reference to avoid circular references message: message,
parents.push( a ); negative: true
parentsB.push( b ); });
}
// Be strict: don't ensure hasOwnProperty and go deep }, {
for ( i in a ) { key: "propEqual",
loop = false; value: function propEqual(actual, expected, message) {
for ( j = 0; j < parents.length; j++ ) { actual = objectValues(actual);
aCircular = parents[ j ] === a[ i ]; expected = objectValues(expected);
bCircular = parentsB[ j ] === b[ i ];
if ( aCircular || bCircular ) { this.pushResult({
if ( a[ i ] === b[ i ] || aCircular && bCircular ) { result: equiv(actual, expected),
loop = true; actual: actual,
} else { expected: expected,
eq = false; message: message
break; });
} }
} }, {
} key: "notPropEqual",
aProperties.push( i ); value: function notPropEqual(actual, expected, message) {
if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) { actual = objectValues(actual);
eq = false; expected = objectValues(expected);
break;
} this.pushResult({
} result: !equiv(actual, expected),
actual: actual,
parents.pop(); expected: expected,
parentsB.pop(); message: message,
negative: true
// Unstack, we are done });
callers.pop(); }
}, {
for ( i in b ) { key: "deepEqual",
value: function deepEqual(actual, expected, message) {
// Collect b's properties this.pushResult({
bProperties.push( i ); result: equiv(actual, expected),
} actual: actual,
expected: expected,
// Ensures identical properties name message: message
return eq && innerEquiv( aProperties.sort(), bProperties.sort() ); });
} }
}; }, {
key: "notDeepEqual",
function typeEquiv( a, b ) { value: function notDeepEqual(actual, expected, message) {
var type = QUnit.objectType( a ); this.pushResult({
return QUnit.objectType( b ) === type && callbacks[ type ]( b, a ); result: !equiv(actual, expected),
} actual: actual,
expected: expected,
// The real equiv function message: message,
function innerEquiv( a, b ) { negative: true
});
// We're done when there's nothing more to compare }
if ( arguments.length < 2 ) { }, {
return true; key: "strictEqual",
} value: function strictEqual(actual, expected, message) {
this.pushResult({
// Require type-specific equality result: expected === actual,
return ( a === b || typeEquiv( a, b ) ) && actual: actual,
expected: expected,
// ...across all consecutive argument pairs message: message
( arguments.length === 2 || innerEquiv.apply( this, [].slice.call( arguments, 1 ) ) ); });
} }
}, {
return innerEquiv; key: "notStrictEqual",
}() ); value: function notStrictEqual(actual, expected, message) {
this.pushResult({
// Based on jsDump by Ariel Flesler result: expected !== actual,
// http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html actual: actual,
QUnit.dump = ( function() { expected: expected,
function quote( str ) { message: message,
return "\"" + str.toString().replace( /\\/g, "\\\\" ).replace( /"/g, "\\\"" ) + "\""; negative: true
} });
function literal( o ) { }
return o + ""; }, {
} key: "throws",
function join( pre, arr, post ) { value: function throws(block, expected, message) {
var s = dump.separator(), var actual = void 0,
base = dump.indent(), result = false,
inner = dump.indent( 1 ); currentTest = this instanceof Assert && this.test || config.current;
if ( arr.join ) {
arr = arr.join( "," + s + inner ); // 'expected' is optional unless doing string comparison
} if (objectType(expected) === "string") {
if ( !arr ) { if (message == null) {
return pre + post; message = expected;
} expected = null;
return [ pre, inner + arr, base + post ].join( s ); } else {
} throw new Error("throws/raises does not accept a string value for the expected argument.\n" + "Use a non-string object value (e.g. regExp) instead if it's necessary.");
function array( arr, stack ) { }
var i = arr.length, }
ret = new Array( i );
currentTest.ignoreGlobalErrors = true;
if ( dump.maxDepth && dump.depth > dump.maxDepth ) { try {
return "[object Array]"; block.call(currentTest.testEnvironment);
} } catch (e) {
actual = e;
this.up(); }
while ( i-- ) { currentTest.ignoreGlobalErrors = false;
ret[ i ] = this.parse( arr[ i ], undefined, stack );
} if (actual) {
this.down(); var expectedType = objectType(expected);
return join( "[", ret, "]" );
} // We don't want to validate thrown error
if (!expected) {
function isArray( obj ) { result = true;
return ( expected = null;
//Native Arrays // Expected is a regexp
toString.call( obj ) === "[object Array]" || } else if (expectedType === "regexp") {
result = expected.test(errorString(actual));
// NodeList objects
( typeof obj.length === "number" && obj.item !== undefined ) && // Expected is a constructor, maybe an Error constructor
( obj.length ? } else if (expectedType === "function" && actual instanceof expected) {
obj.item( 0 ) === obj[ 0 ] : result = true;
( obj.item( 0 ) === null && obj[ 0 ] === undefined )
) // Expected is an Error object
); } else if (expectedType === "object") {
} result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message;
var reName = /^function (\w+)/, // Expected is a validation function which returns true if validation passed
dump = { } else if (expectedType === "function" && expected.call({}, actual) === true) {
expected = null;
// The objType is used mostly internally, you can fix a (custom) type in advance result = true;
parse: function( obj, objType, stack ) { }
stack = stack || []; }
var res, parser, parserType,
inStack = inArray( obj, stack ); currentTest.assert.pushResult({
result: result,
if ( inStack !== -1 ) { actual: actual,
return "recursion(" + ( inStack - stack.length ) + ")"; expected: expected,
} message: message
});
objType = objType || this.typeOf( obj ); }
parser = this.parsers[ objType ]; }]);
parserType = typeof parser; return Assert;
}();
if ( parserType === "function" ) {
stack.push( obj ); // Provide an alternative to assert.throws(), for environments that consider throws a reserved word
res = parser.call( this, obj, stack ); // Known to us are: Closure Compiler, Narwhal
stack.pop(); // eslint-disable-next-line dot-notation
return res;
}
return ( parserType === "string" ) ? parser : this.parsers.error; Assert.prototype.raises = Assert.prototype["throws"];
},
typeOf: function( obj ) { /**
var type; * Converts an error into a simple string for comparisons.
*
if ( obj === null ) { * @param {Error} error
type = "null"; * @return {String}
} else if ( typeof obj === "undefined" ) { */
type = "undefined"; function errorString(error) {
} else if ( QUnit.is( "regexp", obj ) ) { var resultErrorString = error.toString();
type = "regexp";
} else if ( QUnit.is( "date", obj ) ) { if (resultErrorString.substring(0, 7) === "[object") {
type = "date"; var name = error.name ? error.name.toString() : "Error";
} else if ( QUnit.is( "function", obj ) ) { var message = error.message ? error.message.toString() : "";
type = "function";
} else if ( obj.setInterval !== undefined && if (name && message) {
obj.document !== undefined && return name + ": " + message;
obj.nodeType === undefined ) { } else if (name) {
type = "window"; return name;
} else if ( obj.nodeType === 9 ) { } else if (message) {
type = "document"; return message;
} else if ( obj.nodeType ) { } else {
type = "node"; return "Error";
} else if ( isArray( obj ) ) { }
type = "array"; } else {
} else if ( obj.constructor === Error.prototype.constructor ) { return resultErrorString;
type = "error"; }
} else { }
type = typeof obj;
} /* global module, exports, define */
return type; function exportQUnit(QUnit) {
},
if (defined.document) {
separator: function() {
return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&#160;" : " "; // QUnit may be defined when it is preconfigured but then only QUnit and QUnit.config may be defined.
}, if (window.QUnit && window.QUnit.version) {
throw new Error("QUnit has already been defined.");
// Extra can be a number, shortcut for increasing-calling-decreasing }
indent: function( extra ) {
if ( !this.multiline ) { window.QUnit = QUnit;
return ""; }
}
var chr = this.indentChar; // For nodejs
if ( this.HTML ) { if (typeof module !== "undefined" && module && module.exports) {
chr = chr.replace( /\t/g, " " ).replace( / /g, "&#160;" ); module.exports = QUnit;
}
return new Array( this.depth + ( extra || 0 ) ).join( chr ); // For consistency with CommonJS environments' exports
}, module.exports.QUnit = QUnit;
up: function( a ) { }
this.depth += a || 1;
}, // For CommonJS with exports, but without module.exports, like Rhino
down: function( a ) { if (typeof exports !== "undefined" && exports) {
this.depth -= a || 1; exports.QUnit = QUnit;
}, }
setParser: function( name, parser ) {
this.parsers[ name ] = parser; if (typeof define === "function" && define.amd) {
}, define(function () {
return QUnit;
// The next 3 are exposed so you can use them });
quote: quote, QUnit.config.autostart = false;
literal: literal, }
join: join, }
depth: 1,
maxDepth: QUnit.config.maxDepth, (function () {
if (!defined.document) {
// This is the list of parsers, to modify them, use dump.setParser return;
parsers: { }
window: "[Window]",
document: "[Document]", // `onErrorFnPrev` initialized at top of scope
error: function( error ) { // Preserve other handlers
return "Error(\"" + error.message + "\")"; var onErrorFnPrev = window.onerror;
},
unknown: "[Unknown]", // Cover uncaught exceptions
"null": "null", // Returning true will suppress the default browser handler,
"undefined": "undefined", // returning false will let it run.
"function": function( fn ) { window.onerror = function (error, filePath, linerNr) {
var ret = "function", var ret = false;
if (onErrorFnPrev) {
// Functions never have name in IE ret = onErrorFnPrev(error, filePath, linerNr);
name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ]; }
if ( name ) { // Treat return value as window.onerror itself does,
ret += " " + name; // Only do our handling if not suppressed.
} if (ret !== true) {
ret += "("; if (config.current) {
if (config.current.ignoreGlobalErrors) {
ret = [ ret, dump.parse( fn, "functionArgs" ), "){" ].join( "" ); return true;
return join( ret, dump.parse( fn, "functionCode" ), "}" ); }
}, pushFailure(error, filePath + ":" + linerNr);
array: array, } else {
nodelist: array, test("global failure", extend(function () {
"arguments": array, pushFailure(error, filePath + ":" + linerNr);
object: function( map, stack ) { }, { validTest: true }));
var keys, key, val, i, nonEnumerableProperties, }
ret = []; return false;
}
if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
return "[object Object]"; return ret;
} };
})();
dump.up();
keys = []; var QUnit = {};
for ( key in map ) {
keys.push( key ); var globalStartCalled = false;
} var runStarted = false;
// Some properties are not always enumerable on Error objects. var internalState = {
nonEnumerableProperties = [ "message", "name" ]; autorun: false
for ( i in nonEnumerableProperties ) { };
key = nonEnumerableProperties[ i ];
if ( key in map && inArray( key, keys ) < 0 ) { // Figure out if we're running the tests from a server or not
keys.push( key ); QUnit.isLocal = !(defined.document && window.location.protocol !== "file:");
}
} // Expose the current QUnit version
keys.sort(); QUnit.version = "2.1.1";
for ( i = 0; i < keys.length; i++ ) {
key = keys[ i ]; extend(QUnit, {
val = map[ key ];
ret.push( dump.parse( key, "key" ) + ": " + // Call on start of module test to prepend name to all tests
dump.parse( val, undefined, stack ) ); module: function module(name, testEnvironment, executeNow) {
} var module, moduleFns;
dump.down(); var currentModule = config.currentModule;
return join( "{", ret, "}" );
}, if (arguments.length === 2) {
node: function( node ) { if (objectType(testEnvironment) === "function") {
var len, i, val, executeNow = testEnvironment;
open = dump.HTML ? "&lt;" : "<", testEnvironment = undefined;
close = dump.HTML ? "&gt;" : ">", }
tag = node.nodeName.toLowerCase(), }
ret = open + tag,
attrs = node.attributes; module = createModule();
if ( attrs ) { moduleFns = {
for ( i = 0, len = attrs.length; i < len; i++ ) { before: setHook(module, "before"),
val = attrs[ i ].nodeValue; beforeEach: setHook(module, "beforeEach"),
afterEach: setHook(module, "afterEach"),
// IE6 includes all attributes in .attributes, even ones not explicitly after: setHook(module, "after")
// set. Those have values like undefined, null, 0, false, "" or };
// "inherit".
if ( val && val !== "inherit" ) { if (objectType(executeNow) === "function") {
ret += " " + attrs[ i ].nodeName + "=" + config.moduleStack.push(module);
dump.parse( val, "attribute" ); setCurrentModule(module);
} executeNow.call(module.testEnvironment, moduleFns);
} config.moduleStack.pop();
} module = module.parentModule || currentModule;
ret += close; }
// Show content of TextNode or CDATASection setCurrentModule(module);
if ( node.nodeType === 3 || node.nodeType === 4 ) {
ret += node.nodeValue; function createModule() {
} var parentModule = config.moduleStack.length ? config.moduleStack.slice(-1)[0] : null;
var moduleName = parentModule !== null ? [parentModule.name, name].join(" > ") : name;
return ret + open + "/" + tag + close; var module = {
}, name: moduleName,
parentModule: parentModule,
// Function calls it internally, it's the arguments part of the function tests: [],
functionArgs: function( fn ) { moduleId: generateHash(moduleName),
var args, testsRun: 0,
l = fn.length; childModules: []
};
if ( !l ) {
return ""; var env = {};
} if (parentModule) {
parentModule.childModules.push(module);
args = new Array( l ); extend(env, parentModule.testEnvironment);
while ( l-- ) { delete env.beforeEach;
delete env.afterEach;
// 97 is 'a' }
args[ l ] = String.fromCharCode( 97 + l ); extend(env, testEnvironment);
} module.testEnvironment = env;
return " " + args.join( ", " ) + " ";
}, config.modules.push(module);
return module;
// Object calls it internally, the key part of an item in a map }
key: quote,
function setCurrentModule(module) {
// Function calls it internally, it's the content of the function config.currentModule = module;
functionCode: "[code]", }
},
// Node calls it internally, it's a html attribute value
attribute: quote, test: test,
string: quote,
date: quote, skip: skip,
regexp: literal,
number: literal, only: only,
"boolean": literal,
symbol: function( sym ) { start: function start(count) {
return sym.toString(); var globalStartAlreadyCalled = globalStartCalled;
}
}, if (!config.current) {
globalStartCalled = true;
// If true, entities are escaped ( <, >, \t, space and \n )
HTML: false, if (runStarted) {
throw new Error("Called start() while test already started running");
// Indentation unit } else if (globalStartAlreadyCalled || count > 1) {
indentChar: " ", throw new Error("Called start() outside of a test context too many times");
} else if (config.autostart) {
// If true, items in a collection, are separated by a \n, else just a space. throw new Error("Called start() outside of a test context when " + "QUnit.config.autostart was true");
multiline: true } else if (!defined.document && !config.pageLoaded) {
};
// Starts from Node even if .load was not previously called
return dump; QUnit.load();
}() ); } else if (!config.pageLoaded) {
// Back compat // The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it
QUnit.jsDump = QUnit.dump; config.autostart = true;
return;
function applyDeprecated( name ) { }
return function() { } else {
throw new Error( throw new Error("QUnit.start cannot be called inside a test context.");
name + " is removed in QUnit 2.0.\n" + }
"Details in our upgrade guide at https://qunitjs.com/upgrade-guide-2.x/"
); scheduleBegin();
}; },
}
config: config,
Object.keys( Assert.prototype ).forEach( function( key ) {
QUnit[ key ] = applyDeprecated( "`QUnit." + key + "`" ); is: is,
} );
objectType: objectType,
QUnit.asyncTest = function() {
throw new Error( extend: extend,
"asyncTest is removed in QUnit 2.0, use QUnit.test() with assert.async() instead.\n" +
"Details in our upgrade guide at https://qunitjs.com/upgrade-guide-2.x/" load: function load() {
); config.pageLoaded = true;
};
// Initialize the configuration options
QUnit.stop = function() { extend(config, {
throw new Error( stats: { all: 0, bad: 0 },
"QUnit.stop is removed in QUnit 2.0, use QUnit.test() with assert.async() instead.\n" + started: 0,
"Details in our upgrade guide at https://qunitjs.com/upgrade-guide-2.x/" updateRate: 1000,
); autostart: true,
}; filter: ""
}, true);
function resetThrower() {
throw new Error( if (!runStarted) {
"QUnit.reset is removed in QUnit 2.0 without replacement.\n" + config.blocking = false;
"Details in our upgrade guide at https://qunitjs.com/upgrade-guide-2.x/"
); if (config.autostart) {
} scheduleBegin();
}
Object.defineProperty( QUnit, "reset", { }
get: function() { },
return resetThrower;
}, stack: function stack(offset) {
set: resetThrower offset = (offset || 0) + 2;
} ); return sourceFromStacktrace(offset);
}
if ( defined.document ) { });
if ( window.QUnit ) {
throw new Error( "QUnit has already been defined." ); QUnit.pushFailure = pushFailure;
} QUnit.assert = Assert.prototype;
QUnit.equiv = equiv;
[ QUnit.dump = dump;
"test",
"module", registerLoggingCallbacks(QUnit);
"expect",
"start", function scheduleBegin() {
"ok",
"notOk", runStarted = true;
"equal",
"notEqual", // Add a slight delay to allow definition of more modules and tests.
"propEqual", if (defined.setTimeout) {
"notPropEqual", setTimeout(function () {
"deepEqual", begin();
"notDeepEqual", }, 13);
"strictEqual", } else {
"notStrictEqual", begin();
"throws", }
"raises" }
].forEach( function( key ) {
window[ key ] = applyDeprecated( "The global `" + key + "`" ); function begin() {
} ); var i,
l,
window.QUnit = QUnit; modulesLog = [];
}
// If the test run hasn't officially begun yet
// For nodejs if (!config.started) {
if ( typeof module !== "undefined" && module && module.exports ) {
module.exports = QUnit; // Record the time of the test run's beginning
config.started = now();
// For consistency with CommonJS environments' exports
module.exports.QUnit = QUnit; // Delete the loose unnamed module if unused.
} if (config.modules[0].name === "" && config.modules[0].tests.length === 0) {
config.modules.shift();
// For CommonJS with exports, but without module.exports, like Rhino }
if ( typeof exports !== "undefined" && exports ) {
exports.QUnit = QUnit; // Avoid unnecessary information by not logging modules' test environments
} for (i = 0, l = config.modules.length; i < l; i++) {
modulesLog.push({
if ( typeof define === "function" && define.amd ) { name: config.modules[i].name,
define( function() { tests: config.modules[i].tests
return QUnit; });
} ); }
QUnit.config.autostart = false;
} // The test run is officially beginning now
runLoggingCallbacks("begin", {
// Get a reference to the global object, like window in browsers totalTests: Test.count,
}( ( function() { modules: modulesLog
return this; });
}() ) ) ); }
( function() { config.blocking = false;
process(true);
if ( typeof window === "undefined" || !window.document ) { }
return;
} function process(last) {
function next() {
var config = QUnit.config, process(last);
hasOwn = Object.prototype.hasOwnProperty; }
var start = now();
// Stores fixture HTML for resetting later config.depth = (config.depth || 0) + 1;
function storeFixture() {
while (config.queue.length && !config.blocking) {
// Avoid overwriting user-defined values if (!defined.setTimeout || config.updateRate <= 0 || now() - start < config.updateRate) {
if ( hasOwn.call( config, "fixture" ) ) { if (config.current) {
return;
} // Reset async tracking for each phase of the Test lifecycle
config.current.usedAsync = false;
var fixture = document.getElementById( "qunit-fixture" ); }
if ( fixture ) { config.queue.shift()();
config.fixture = fixture.innerHTML; } else {
} setTimeout(next, 13);
} break;
}
QUnit.begin( storeFixture ); }
config.depth--;
// Resets the fixture DOM element if available. if (last && !config.blocking && !config.queue.length && config.depth === 0) {
function resetFixture() { done();
if ( config.fixture == null ) { }
return; }
}
function done() {
var fixture = document.getElementById( "qunit-fixture" ); var runtime,
if ( fixture ) { passed,
fixture.innerHTML = config.fixture; i,
} key,
} storage = config.storage;
QUnit.testStart( resetFixture ); internalState.autorun = true;
}() ); runtime = now() - config.started;
passed = config.stats.all - config.stats.bad;
( function() {
runLoggingCallbacks("done", {
// Only interact with URLs via window.location failed: config.stats.bad,
var location = typeof window !== "undefined" && window.location; passed: passed,
if ( !location ) { total: config.stats.all,
return; runtime: runtime
} });
var urlParams = getUrlParams(); // Clear own storage items if all tests passed
if (storage && config.stats.bad === 0) {
QUnit.urlParams = urlParams; for (i = storage.length - 1; i >= 0; i--) {
key = storage.key(i);
// Match module/test by inclusion in an array if (key.indexOf("qunit-test-") === 0) {
QUnit.config.moduleId = [].concat( urlParams.moduleId || [] ); storage.removeItem(key);
QUnit.config.testId = [].concat( urlParams.testId || [] ); }
}
// Exact case-insensitive match of the module name }
QUnit.config.module = urlParams.module; }
// Regular expression or case-insenstive substring match against "moduleName: testName" function setHook(module, hookName) {
QUnit.config.filter = urlParams.filter; if (module.testEnvironment === undefined) {
module.testEnvironment = {};
// Test order randomization }
if ( urlParams.seed === true ) {
return function (callback) {
// Generate a random seed if the option is specified without a value module.testEnvironment[hookName] = callback;
QUnit.config.seed = Math.random().toString( 36 ).slice( 2 ); };
} else if ( urlParams.seed ) { }
QUnit.config.seed = urlParams.seed;
} exportQUnit(QUnit);
// Add URL-parameter-mapped config values with UI form rendering data (function () {
QUnit.config.urlConfig.push(
{ if (typeof window === "undefined" || typeof document === "undefined") {
id: "hidepassed", return;
label: "Hide passed tests", }
tooltip: "Only show tests and assertions that fail. Stored as query-strings."
}, var config = QUnit.config,
{ hasOwn = Object.prototype.hasOwnProperty;
id: "noglobals",
label: "Check for Globals", // Stores fixture HTML for resetting later
tooltip: "Enabling this will test if any test introduces new properties on the " + function storeFixture() {
"global object (`window` in Browsers). Stored as query-strings."
}, // Avoid overwriting user-defined values
{ if (hasOwn.call(config, "fixture")) {
id: "notrycatch", return;
label: "No try-catch", }
tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " +
"exceptions in IE reasonable. Stored as query-strings." var fixture = document.getElementById("qunit-fixture");
} if (fixture) {
); config.fixture = fixture.innerHTML;
}
QUnit.begin( function() { }
var i, option,
urlConfig = QUnit.config.urlConfig; QUnit.begin(storeFixture);
for ( i = 0; i < urlConfig.length; i++ ) { // Resets the fixture DOM element if available.
function resetFixture() {
// Options can be either strings or objects with nonempty "id" properties if (config.fixture == null) {
option = QUnit.config.urlConfig[ i ]; return;
if ( typeof option !== "string" ) { }
option = option.id;
} var fixture = document.getElementById("qunit-fixture");
if (fixture) {
if ( QUnit.config[ option ] === undefined ) { fixture.innerHTML = config.fixture;
QUnit.config[ option ] = urlParams[ option ]; }
} }
}
} ); QUnit.testStart(resetFixture);
})();
function getUrlParams() {
var i, param, name, value; (function () {
var urlParams = {};
var params = location.search.slice( 1 ).split( "&" ); // Only interact with URLs via window.location
var length = params.length; var location = typeof window !== "undefined" && window.location;
if (!location) {
for ( i = 0; i < length; i++ ) { return;
if ( params[ i ] ) { }
param = params[ i ].split( "=" );
name = decodeQueryParam( param[ 0 ] ); var urlParams = getUrlParams();
// Allow just a key to turn on a flag, e.g., test.html?noglobals QUnit.urlParams = urlParams;
value = param.length === 1 ||
decodeQueryParam( param.slice( 1 ).join( "=" ) ) ; // Match module/test by inclusion in an array
if ( urlParams[ name ] ) { QUnit.config.moduleId = [].concat(urlParams.moduleId || []);
urlParams[ name ] = [].concat( urlParams[ name ], value ); QUnit.config.testId = [].concat(urlParams.testId || []);
} else {
urlParams[ name ] = value; // Exact case-insensitive match of the module name
} QUnit.config.module = urlParams.module;
}
} // Regular expression or case-insenstive substring match against "moduleName: testName"
QUnit.config.filter = urlParams.filter;
return urlParams;
} // Test order randomization
if (urlParams.seed === true) {
function decodeQueryParam( param ) {
return decodeURIComponent( param.replace( /\+/g, "%20" ) ); // Generate a random seed if the option is specified without a value
} QUnit.config.seed = Math.random().toString(36).slice(2);
} else if (urlParams.seed) {
// Don't load the HTML Reporter on non-browser environments QUnit.config.seed = urlParams.seed;
if ( typeof window === "undefined" || !window.document ) { }
return;
} // Add URL-parameter-mapped config values with UI form rendering data
QUnit.config.urlConfig.push({
QUnit.init = function() { id: "hidepassed",
throw new Error( label: "Hide passed tests",
"QUnit.init is removed in QUnit 2.0, use QUnit.test() with assert.async() instead.\n" + tooltip: "Only show tests and assertions that fail. Stored as query-strings."
"Details in our upgrade guide at https://qunitjs.com/upgrade-guide-2.x/" }, {
); id: "noglobals",
}; label: "Check for Globals",
tooltip: "Enabling this will test if any test introduces new properties on the " + "global object (`window` in Browsers). Stored as query-strings."
var config = QUnit.config, }, {
document = window.document, id: "notrycatch",
collapseNext = false, label: "No try-catch",
hasOwn = Object.prototype.hasOwnProperty, tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + "exceptions in IE reasonable. Stored as query-strings."
unfilteredUrl = setUrl( { filter: undefined, module: undefined, });
moduleId: undefined, testId: undefined } ),
defined = { QUnit.begin(function () {
sessionStorage: ( function() { var i,
var x = "qunit-test-string"; option,
try { urlConfig = QUnit.config.urlConfig;
sessionStorage.setItem( x, x );
sessionStorage.removeItem( x ); for (i = 0; i < urlConfig.length; i++) {
return true;
} catch ( e ) { // Options can be either strings or objects with nonempty "id" properties
return false; option = QUnit.config.urlConfig[i];
} if (typeof option !== "string") {
}() ) option = option.id;
}, }
modulesList = [];
if (QUnit.config[option] === undefined) {
// Escape text for attribute or text content. QUnit.config[option] = urlParams[option];
function escapeText( s ) { }
if ( !s ) { }
return ""; });
}
s = s + ""; function getUrlParams() {
var i, param, name, value;
// Both single quotes and double quotes (for attributes) var urlParams = Object.create(null);
return s.replace( /['"<>&]/g, function( s ) { var params = location.search.slice(1).split("&");
switch ( s ) { var length = params.length;
case "'":
return "&#039;"; for (i = 0; i < length; i++) {
case "\"": if (params[i]) {
return "&quot;"; param = params[i].split("=");
case "<": name = decodeQueryParam(param[0]);
return "&lt;";
case ">": // Allow just a key to turn on a flag, e.g., test.html?noglobals
return "&gt;"; value = param.length === 1 || decodeQueryParam(param.slice(1).join("="));
case "&": if (name in urlParams) {
return "&amp;"; urlParams[name] = [].concat(urlParams[name], value);
} } else {
} ); urlParams[name] = value;
} }
}
function addEvent( elem, type, fn ) { }
elem.addEventListener( type, fn, false );
} return urlParams;
}
function removeEvent( elem, type, fn ) {
elem.removeEventListener( type, fn, false ); function decodeQueryParam(param) {
} return decodeURIComponent(param.replace(/\+/g, "%20"));
}
function addEvents( elems, type, fn ) { })();
var i = elems.length;
while ( i-- ) { // Escape text for attribute or text content.
addEvent( elems[ i ], type, fn ); function escapeText(s) {
} if (!s) {
} return "";
}
function hasClass( elem, name ) { s = s + "";
return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0;
} // Both single quotes and double quotes (for attributes)
return s.replace(/['"<>&]/g, function (s) {
function addClass( elem, name ) { switch (s) {
if ( !hasClass( elem, name ) ) { case "'":
elem.className += ( elem.className ? " " : "" ) + name; return "&#039;";
} case "\"":
} return "&quot;";
case "<":
function toggleClass( elem, name, force ) { return "&lt;";
if ( force || typeof force === "undefined" && !hasClass( elem, name ) ) { case ">":
addClass( elem, name ); return "&gt;";
} else { case "&":
removeClass( elem, name ); return "&amp;";
} }
} });
}
function removeClass( elem, name ) {
var set = " " + elem.className + " "; (function () {
// Class name may appear multiple times // Don't load the HTML Reporter on non-browser environments
while ( set.indexOf( " " + name + " " ) >= 0 ) { if (typeof window === "undefined" || !window.document) {
set = set.replace( " " + name + " ", " " ); return;
} }
// Trim for prettiness var config = QUnit.config,
elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" ); document$$1 = window.document,
} collapseNext = false,
hasOwn = Object.prototype.hasOwnProperty,
function id( name ) { unfilteredUrl = setUrl({ filter: undefined, module: undefined,
return document.getElementById && document.getElementById( name ); moduleId: undefined, testId: undefined }),
} modulesList = [];
function interceptNavigation( ev ) { function addEvent(elem, type, fn) {
applyUrlParams(); elem.addEventListener(type, fn, false);
}
if ( ev && ev.preventDefault ) {
ev.preventDefault(); function removeEvent(elem, type, fn) {
} elem.removeEventListener(type, fn, false);
}
return false;
} function addEvents(elems, type, fn) {
var i = elems.length;
function getUrlConfigHtml() { while (i--) {
var i, j, val, addEvent(elems[i], type, fn);
escaped, escapedTooltip, }
selection = false, }
urlConfig = config.urlConfig,
urlConfigHtml = ""; function hasClass(elem, name) {
return (" " + elem.className + " ").indexOf(" " + name + " ") >= 0;
for ( i = 0; i < urlConfig.length; i++ ) { }
// Options can be either strings or objects with nonempty "id" properties function addClass(elem, name) {
val = config.urlConfig[ i ]; if (!hasClass(elem, name)) {
if ( typeof val === "string" ) { elem.className += (elem.className ? " " : "") + name;
val = { }
id: val, }
label: val
}; function toggleClass(elem, name, force) {
} if (force || typeof force === "undefined" && !hasClass(elem, name)) {
addClass(elem, name);
escaped = escapeText( val.id ); } else {
escapedTooltip = escapeText( val.tooltip ); removeClass(elem, name);
}
if ( !val.value || typeof val.value === "string" ) { }
urlConfigHtml += "<label for='qunit-urlconfig-" + escaped +
"' title='" + escapedTooltip + "'><input id='qunit-urlconfig-" + escaped + function removeClass(elem, name) {
"' name='" + escaped + "' type='checkbox'" + var set = " " + elem.className + " ";
( val.value ? " value='" + escapeText( val.value ) + "'" : "" ) +
( config[ val.id ] ? " checked='checked'" : "" ) + // Class name may appear multiple times
" title='" + escapedTooltip + "' />" + escapeText( val.label ) + "</label>"; while (set.indexOf(" " + name + " ") >= 0) {
} else { set = set.replace(" " + name + " ", " ");
urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + }
"' title='" + escapedTooltip + "'>" + val.label +
": </label><select id='qunit-urlconfig-" + escaped + // Trim for prettiness
"' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>"; elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, "");
}
if ( QUnit.is( "array", val.value ) ) {
for ( j = 0; j < val.value.length; j++ ) { function id(name) {
escaped = escapeText( val.value[ j ] ); return document$$1.getElementById && document$$1.getElementById(name);
urlConfigHtml += "<option value='" + escaped + "'" + }
( config[ val.id ] === val.value[ j ] ?
( selection = true ) && " selected='selected'" : "" ) + function abortTests() {
">" + escaped + "</option>"; var abortButton = id("qunit-abort-tests-button");
} if (abortButton) {
} else { abortButton.disabled = true;
for ( j in val.value ) { abortButton.innerHTML = "Aborting...";
if ( hasOwn.call( val.value, j ) ) { }
urlConfigHtml += "<option value='" + escapeText( j ) + "'" + QUnit.config.queue.length = 0;
( config[ val.id ] === j ? return false;
( selection = true ) && " selected='selected'" : "" ) + }
">" + escapeText( val.value[ j ] ) + "</option>";
} function interceptNavigation(ev) {
} applyUrlParams();
}
if ( config[ val.id ] && !selection ) { if (ev && ev.preventDefault) {
escaped = escapeText( config[ val.id ] ); ev.preventDefault();
urlConfigHtml += "<option value='" + escaped + }
"' selected='selected' disabled='disabled'>" + escaped + "</option>";
} return false;
urlConfigHtml += "</select>"; }
}
} function getUrlConfigHtml() {
var i,
return urlConfigHtml; j,
} val,
escaped,
// Handle "click" events on toolbar checkboxes and "change" for select menus. escapedTooltip,
// Updates the URL with the new state of `config.urlConfig` values. selection = false,
function toolbarChanged() { urlConfig = config.urlConfig,
var updatedUrl, value, tests, urlConfigHtml = "";
field = this,
params = {}; for (i = 0; i < urlConfig.length; i++) {
// Detect if field is a select menu or a checkbox // Options can be either strings or objects with nonempty "id" properties
if ( "selectedIndex" in field ) { val = config.urlConfig[i];
value = field.options[ field.selectedIndex ].value || undefined; if (typeof val === "string") {
} else { val = {
value = field.checked ? ( field.defaultValue || true ) : undefined; id: val,
} label: val
};
params[ field.name ] = value; }
updatedUrl = setUrl( params );
escaped = escapeText(val.id);
// Check if we can apply the change without a page refresh escapedTooltip = escapeText(val.tooltip);
if ( "hidepassed" === field.name && "replaceState" in window.history ) {
QUnit.urlParams[ field.name ] = value; if (!val.value || typeof val.value === "string") {
config[ field.name ] = value || false; urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'><input id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' type='checkbox'" + (val.value ? " value='" + escapeText(val.value) + "'" : "") + (config[val.id] ? " checked='checked'" : "") + " title='" + escapedTooltip + "' />" + escapeText(val.label) + "</label>";
tests = id( "qunit-tests" ); } else {
if ( tests ) { urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'>" + val.label + ": </label><select id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
toggleClass( tests, "hidepass", value || false );
} if (QUnit.is("array", val.value)) {
window.history.replaceState( null, "", updatedUrl ); for (j = 0; j < val.value.length; j++) {
} else { escaped = escapeText(val.value[j]);
window.location = updatedUrl; urlConfigHtml += "<option value='" + escaped + "'" + (config[val.id] === val.value[j] ? (selection = true) && " selected='selected'" : "") + ">" + escaped + "</option>";
} }
} } else {
for (j in val.value) {
function setUrl( params ) { if (hasOwn.call(val.value, j)) {
var key, arrValue, i, urlConfigHtml += "<option value='" + escapeText(j) + "'" + (config[val.id] === j ? (selection = true) && " selected='selected'" : "") + ">" + escapeText(val.value[j]) + "</option>";
querystring = "?", }
location = window.location; }
}
params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params ); if (config[val.id] && !selection) {
escaped = escapeText(config[val.id]);
for ( key in params ) { urlConfigHtml += "<option value='" + escaped + "' selected='selected' disabled='disabled'>" + escaped + "</option>";
}
// Skip inherited or undefined properties urlConfigHtml += "</select>";
if ( hasOwn.call( params, key ) && params[ key ] !== undefined ) { }
}
// Output a parameter for each value of this key (but usually just one)
arrValue = [].concat( params[ key ] ); return urlConfigHtml;
for ( i = 0; i < arrValue.length; i++ ) { }
querystring += encodeURIComponent( key );
if ( arrValue[ i ] !== true ) { // Handle "click" events on toolbar checkboxes and "change" for select menus.
querystring += "=" + encodeURIComponent( arrValue[ i ] ); // Updates the URL with the new state of `config.urlConfig` values.
} function toolbarChanged() {
querystring += "&"; var updatedUrl,
} value,
} tests,
} field = this,
return location.protocol + "//" + location.host + params = {};
location.pathname + querystring.slice( 0, -1 );
} // Detect if field is a select menu or a checkbox
if ("selectedIndex" in field) {
function applyUrlParams() { value = field.options[field.selectedIndex].value || undefined;
var i, } else {
selectedModules = [], value = field.checked ? field.defaultValue || true : undefined;
modulesList = id( "qunit-modulefilter-dropdown-list" ).getElementsByTagName( "input" ), }
filter = id( "qunit-filter-input" ).value;
params[field.name] = value;
for ( i = 0; i < modulesList.length; i++ ) { updatedUrl = setUrl(params);
if ( modulesList[ i ].checked ) {
selectedModules.push( modulesList[ i ].value ); // Check if we can apply the change without a page refresh
} if ("hidepassed" === field.name && "replaceState" in window.history) {
} QUnit.urlParams[field.name] = value;
config[field.name] = value || false;
window.location = setUrl( { tests = id("qunit-tests");
filter: ( filter === "" ) ? undefined : filter, if (tests) {
moduleId: ( selectedModules.length === 0 ) ? undefined : selectedModules, toggleClass(tests, "hidepass", value || false);
}
// Remove module and testId filter window.history.replaceState(null, "", updatedUrl);
module: undefined, } else {
testId: undefined window.location = updatedUrl;
} ); }
} }
function toolbarUrlConfigContainer() { function setUrl(params) {
var urlConfigContainer = document.createElement( "span" ); var key,
arrValue,
urlConfigContainer.innerHTML = getUrlConfigHtml(); i,
addClass( urlConfigContainer, "qunit-url-config" ); querystring = "?",
location = window.location;
addEvents( urlConfigContainer.getElementsByTagName( "input" ), "change", toolbarChanged );
addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged ); params = QUnit.extend(QUnit.extend({}, QUnit.urlParams), params);
return urlConfigContainer; for (key in params) {
}
// Skip inherited or undefined properties
function toolbarLooseFilter() { if (hasOwn.call(params, key) && params[key] !== undefined) {
var filter = document.createElement( "form" ),
label = document.createElement( "label" ), // Output a parameter for each value of this key (but usually just one)
input = document.createElement( "input" ), arrValue = [].concat(params[key]);
button = document.createElement( "button" ); for (i = 0; i < arrValue.length; i++) {
querystring += encodeURIComponent(key);
addClass( filter, "qunit-filter" ); if (arrValue[i] !== true) {
querystring += "=" + encodeURIComponent(arrValue[i]);
label.innerHTML = "Filter: "; }
querystring += "&";
input.type = "text"; }
input.value = config.filter || ""; }
input.name = "filter"; }
input.id = "qunit-filter-input"; return location.protocol + "//" + location.host + location.pathname + querystring.slice(0, -1);
}
button.innerHTML = "Go";
function applyUrlParams() {
label.appendChild( input ); var i,
selectedModules = [],
filter.appendChild( label ); modulesList = id("qunit-modulefilter-dropdown-list").getElementsByTagName("input"),
filter.appendChild( document.createTextNode( " " ) ); filter = id("qunit-filter-input").value;
filter.appendChild( button );
addEvent( filter, "submit", interceptNavigation ); for (i = 0; i < modulesList.length; i++) {
if (modulesList[i].checked) {
return filter; selectedModules.push(modulesList[i].value);
} }
}
function moduleListHtml () {
var i, checked, window.location = setUrl({
html = ""; filter: filter === "" ? undefined : filter,
moduleId: selectedModules.length === 0 ? undefined : selectedModules,
for ( i = 0; i < config.modules.length; i++ ) {
if ( config.modules[ i ].name !== "" ) { // Remove module and testId filter
checked = config.moduleId.indexOf( config.modules[ i ].moduleId ) > -1; module: undefined,
html += "<li><label class='clickable" + ( checked ? " checked" : "" ) + testId: undefined
"'><input type='checkbox' " + "value='" + config.modules[ i ].moduleId + "'" + });
( checked ? " checked='checked'" : "" ) + " />" + }
escapeText( config.modules[ i ].name ) + "</label></li>";
} function toolbarUrlConfigContainer() {
} var urlConfigContainer = document$$1.createElement("span");
return html; urlConfigContainer.innerHTML = getUrlConfigHtml();
} addClass(urlConfigContainer, "qunit-url-config");
function toolbarModuleFilter () { addEvents(urlConfigContainer.getElementsByTagName("input"), "change", toolbarChanged);
var allCheckbox, commit, reset, addEvents(urlConfigContainer.getElementsByTagName("select"), "change", toolbarChanged);
moduleFilter = document.createElement( "form" ),
label = document.createElement( "label" ), return urlConfigContainer;
moduleSearch = document.createElement( "input" ), }
dropDown = document.createElement( "div" ),
actions = document.createElement( "span" ), function abortTestsButton() {
dropDownList = document.createElement( "ul" ), var button = document$$1.createElement("button");
dirty = false; button.id = "qunit-abort-tests-button";
button.innerHTML = "Abort";
moduleSearch.id = "qunit-modulefilter-search"; addEvent(button, "click", abortTests);
addEvent( moduleSearch, "input", searchInput ); return button;
addEvent( moduleSearch, "input", searchFocus ); }
addEvent( moduleSearch, "focus", searchFocus );
addEvent( moduleSearch, "click", searchFocus ); function toolbarLooseFilter() {
var filter = document$$1.createElement("form"),
label.id = "qunit-modulefilter-search-container"; label = document$$1.createElement("label"),
label.innerHTML = "Module: "; input = document$$1.createElement("input"),
label.appendChild( moduleSearch ); button = document$$1.createElement("button");
actions.id = "qunit-modulefilter-actions"; addClass(filter, "qunit-filter");
actions.innerHTML =
"<button style='display:none'>Apply</button>" + label.innerHTML = "Filter: ";
"<button type='reset' style='display:none'>Reset</button>" +
"<label class='clickable" + input.type = "text";
( config.moduleId.length ? "" : " checked" ) + input.value = config.filter || "";
"'><input type='checkbox'" + ( config.moduleId.length ? "" : " checked='checked'" ) + input.name = "filter";
">All modules</label>"; input.id = "qunit-filter-input";
allCheckbox = actions.lastChild.firstChild;
commit = actions.firstChild; button.innerHTML = "Go";
reset = commit.nextSibling;
addEvent( commit, "click", applyUrlParams ); label.appendChild(input);
dropDownList.id = "qunit-modulefilter-dropdown-list"; filter.appendChild(label);
dropDownList.innerHTML = moduleListHtml(); filter.appendChild(document$$1.createTextNode(" "));
filter.appendChild(button);
dropDown.id = "qunit-modulefilter-dropdown"; addEvent(filter, "submit", interceptNavigation);
dropDown.style.display = "none";
dropDown.appendChild( actions ); return filter;
dropDown.appendChild( dropDownList ); }
addEvent( dropDown, "change", selectionChange );
selectionChange(); function moduleListHtml() {
var i,
moduleFilter.id = "qunit-modulefilter"; checked,
moduleFilter.appendChild( label ); html = "";
moduleFilter.appendChild( dropDown ) ;
addEvent( moduleFilter, "submit", interceptNavigation ); for (i = 0; i < config.modules.length; i++) {
addEvent( moduleFilter, "reset", function() { if (config.modules[i].name !== "") {
checked = config.moduleId.indexOf(config.modules[i].moduleId) > -1;
// Let the reset happen, then update styles html += "<li><label class='clickable" + (checked ? " checked" : "") + "'><input type='checkbox' " + "value='" + config.modules[i].moduleId + "'" + (checked ? " checked='checked'" : "") + " />" + escapeText(config.modules[i].name) + "</label></li>";
window.setTimeout( selectionChange ); }
} ); }
// Enables show/hide for the dropdown return html;
function searchFocus() { }
if ( dropDown.style.display !== "none" ) {
return; function toolbarModuleFilter() {
} var allCheckbox,
commit,
dropDown.style.display = "block"; reset,
addEvent( document, "click", hideHandler ); moduleFilter = document$$1.createElement("form"),
addEvent( document, "keydown", hideHandler ); label = document$$1.createElement("label"),
moduleSearch = document$$1.createElement("input"),
// Hide on Escape keydown or outside-container click dropDown = document$$1.createElement("div"),
function hideHandler( e ) { actions = document$$1.createElement("span"),
var inContainer = moduleFilter.contains( e.target ); dropDownList = document$$1.createElement("ul"),
dirty = false;
if ( e.keyCode === 27 || !inContainer ) {
if ( e.keyCode === 27 && inContainer ) { moduleSearch.id = "qunit-modulefilter-search";
moduleSearch.focus(); addEvent(moduleSearch, "input", searchInput);
} addEvent(moduleSearch, "input", searchFocus);
dropDown.style.display = "none"; addEvent(moduleSearch, "focus", searchFocus);
removeEvent( document, "click", hideHandler ); addEvent(moduleSearch, "click", searchFocus);
removeEvent( document, "keydown", hideHandler );
moduleSearch.value = ""; label.id = "qunit-modulefilter-search-container";
searchInput(); label.innerHTML = "Module: ";
} label.appendChild(moduleSearch);
}
} actions.id = "qunit-modulefilter-actions";
actions.innerHTML = "<button style='display:none'>Apply</button>" + "<button type='reset' style='display:none'>Reset</button>" + "<label class='clickable" + (config.moduleId.length ? "" : " checked") + "'><input type='checkbox'" + (config.moduleId.length ? "" : " checked='checked'") + ">All modules</label>";
// Processes module search box input allCheckbox = actions.lastChild.firstChild;
function searchInput() { commit = actions.firstChild;
var i, item, reset = commit.nextSibling;
searchText = moduleSearch.value.toLowerCase(), addEvent(commit, "click", applyUrlParams);
listItems = dropDownList.children;
dropDownList.id = "qunit-modulefilter-dropdown-list";
for ( i = 0; i < listItems.length; i++ ) { dropDownList.innerHTML = moduleListHtml();
item = listItems[ i ];
if ( !searchText || item.textContent.toLowerCase().indexOf( searchText ) > -1 ) { dropDown.id = "qunit-modulefilter-dropdown";
item.style.display = ""; dropDown.style.display = "none";
} else { dropDown.appendChild(actions);
item.style.display = "none"; dropDown.appendChild(dropDownList);
} addEvent(dropDown, "change", selectionChange);
} selectionChange();
}
moduleFilter.id = "qunit-modulefilter";
// Processes selection changes moduleFilter.appendChild(label);
function selectionChange( evt ) { moduleFilter.appendChild(dropDown);
var i, item, addEvent(moduleFilter, "submit", interceptNavigation);
checkbox = evt && evt.target || allCheckbox, addEvent(moduleFilter, "reset", function () {
modulesList = dropDownList.getElementsByTagName( "input" ),
selectedNames = []; // Let the reset happen, then update styles
window.setTimeout(selectionChange);
toggleClass( checkbox.parentNode, "checked", checkbox.checked ); });
dirty = false; // Enables show/hide for the dropdown
if ( checkbox.checked && checkbox !== allCheckbox ) { function searchFocus() {
allCheckbox.checked = false; if (dropDown.style.display !== "none") {
removeClass( allCheckbox.parentNode, "checked" ); return;
} }
for ( i = 0; i < modulesList.length; i++ ) {
item = modulesList[ i ]; dropDown.style.display = "block";
if ( !evt ) { addEvent(document$$1, "click", hideHandler);
toggleClass( item.parentNode, "checked", item.checked ); addEvent(document$$1, "keydown", hideHandler);
} else if ( checkbox === allCheckbox && checkbox.checked ) {
item.checked = false; // Hide on Escape keydown or outside-container click
removeClass( item.parentNode, "checked" ); function hideHandler(e) {
} var inContainer = moduleFilter.contains(e.target);
dirty = dirty || ( item.checked !== item.defaultChecked );
if ( item.checked ) { if (e.keyCode === 27 || !inContainer) {
selectedNames.push( item.parentNode.textContent ); if (e.keyCode === 27 && inContainer) {
} moduleSearch.focus();
} }
dropDown.style.display = "none";
commit.style.display = reset.style.display = dirty ? "" : "none"; removeEvent(document$$1, "click", hideHandler);
moduleSearch.placeholder = selectedNames.join( ", " ) || allCheckbox.parentNode.textContent; removeEvent(document$$1, "keydown", hideHandler);
moduleSearch.title = "Type to filter list. Current selection:\n" + moduleSearch.value = "";
( selectedNames.join( "\n" ) || allCheckbox.parentNode.textContent ); searchInput();
} }
}
return moduleFilter; }
}
// Processes module search box input
function appendToolbar() { function searchInput() {
var toolbar = id( "qunit-testrunner-toolbar" ); var i,
item,
if ( toolbar ) { searchText = moduleSearch.value.toLowerCase(),
toolbar.appendChild( toolbarUrlConfigContainer() ); listItems = dropDownList.children;
toolbar.appendChild( toolbarModuleFilter() );
toolbar.appendChild( toolbarLooseFilter() ); for (i = 0; i < listItems.length; i++) {
toolbar.appendChild( document.createElement( "div" ) ).className = "clearfix"; item = listItems[i];
} if (!searchText || item.textContent.toLowerCase().indexOf(searchText) > -1) {
} item.style.display = "";
} else {
function appendHeader() { item.style.display = "none";
var header = id( "qunit-header" ); }
}
if ( header ) { }
header.innerHTML = "<a href='" + escapeText( unfilteredUrl ) + "'>" + header.innerHTML +
"</a> "; // Processes selection changes
} function selectionChange(evt) {
} var i,
item,
function appendBanner() { checkbox = evt && evt.target || allCheckbox,
var banner = id( "qunit-banner" ); modulesList = dropDownList.getElementsByTagName("input"),
selectedNames = [];
if ( banner ) {
banner.className = ""; toggleClass(checkbox.parentNode, "checked", checkbox.checked);
}
} dirty = false;
if (checkbox.checked && checkbox !== allCheckbox) {
function appendTestResults() { allCheckbox.checked = false;
var tests = id( "qunit-tests" ), removeClass(allCheckbox.parentNode, "checked");
result = id( "qunit-testresult" ); }
for (i = 0; i < modulesList.length; i++) {
if ( result ) { item = modulesList[i];
result.parentNode.removeChild( result ); if (!evt) {
} toggleClass(item.parentNode, "checked", item.checked);
} else if (checkbox === allCheckbox && checkbox.checked) {
if ( tests ) { item.checked = false;
tests.innerHTML = ""; removeClass(item.parentNode, "checked");
result = document.createElement( "p" ); }
result.id = "qunit-testresult"; dirty = dirty || item.checked !== item.defaultChecked;
result.className = "result"; if (item.checked) {
tests.parentNode.insertBefore( result, tests ); selectedNames.push(item.parentNode.textContent);
result.innerHTML = "Running...<br />&#160;"; }
} }
}
commit.style.display = reset.style.display = dirty ? "" : "none";
function appendFilteredTest() { moduleSearch.placeholder = selectedNames.join(", ") || allCheckbox.parentNode.textContent;
var testId = QUnit.config.testId; moduleSearch.title = "Type to filter list. Current selection:\n" + (selectedNames.join("\n") || allCheckbox.parentNode.textContent);
if ( !testId || testId.length <= 0 ) { }
return "";
} return moduleFilter;
return "<div id='qunit-filteredTest'>Rerunning selected tests: " + }
escapeText( testId.join( ", " ) ) +
" <a id='qunit-clearFilter' href='" + function appendToolbar() {
escapeText( unfilteredUrl ) + var toolbar = id("qunit-testrunner-toolbar");
"'>Run all tests</a></div>";
} if (toolbar) {
toolbar.appendChild(toolbarUrlConfigContainer());
function appendUserAgent() { toolbar.appendChild(toolbarModuleFilter());
var userAgent = id( "qunit-userAgent" ); toolbar.appendChild(toolbarLooseFilter());
toolbar.appendChild(document$$1.createElement("div")).className = "clearfix";
if ( userAgent ) { }
userAgent.innerHTML = ""; }
userAgent.appendChild(
document.createTextNode( function appendHeader() {
"QUnit " + QUnit.version + "; " + navigator.userAgent var header = id("qunit-header");
)
); if (header) {
} header.innerHTML = "<a href='" + escapeText(unfilteredUrl) + "'>" + header.innerHTML + "</a> ";
} }
}
function appendInterface() {
var qunit = id( "qunit" ); function appendBanner() {
var banner = id("qunit-banner");
if ( qunit ) {
qunit.innerHTML = if (banner) {
"<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" + banner.className = "";
"<h2 id='qunit-banner'></h2>" + }
"<div id='qunit-testrunner-toolbar'></div>" + }
appendFilteredTest() +
"<h2 id='qunit-userAgent'></h2>" + function appendTestResults() {
"<ol id='qunit-tests'></ol>"; var tests = id("qunit-tests"),
} result = id("qunit-testresult"),
controls;
appendHeader();
appendBanner(); if (result) {
appendTestResults(); result.parentNode.removeChild(result);
appendUserAgent(); }
appendToolbar();
} if (tests) {
tests.innerHTML = "";
function appendTestsList( modules ) { result = document$$1.createElement("p");
var i, l, x, z, test, moduleObj; result.id = "qunit-testresult";
result.className = "result";
for ( i = 0, l = modules.length; i < l; i++ ) { tests.parentNode.insertBefore(result, tests);
moduleObj = modules[ i ]; result.innerHTML = "<div id=\"qunit-testresult-display\">Running...<br />&#160;</div>" + "<div id=\"qunit-testresult-controls\"></div>" + "<div class=\"clearfix\"></div>";
controls = id("qunit-testresult-controls");
for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) { }
test = moduleObj.tests[ x ];
if (controls) {
appendTest( test.name, test.testId, moduleObj.name ); controls.appendChild(abortTestsButton());
} }
} }
}
function appendFilteredTest() {
function appendTest( name, testId, moduleName ) { var testId = QUnit.config.testId;
var title, rerunTrigger, testBlock, assertList, if (!testId || testId.length <= 0) {
tests = id( "qunit-tests" ); return "";
}
if ( !tests ) { return "<div id='qunit-filteredTest'>Rerunning selected tests: " + escapeText(testId.join(", ")) + " <a id='qunit-clearFilter' href='" + escapeText(unfilteredUrl) + "'>Run all tests</a></div>";
return; }
}
function appendUserAgent() {
title = document.createElement( "strong" ); var userAgent = id("qunit-userAgent");
title.innerHTML = getNameHtml( name, moduleName );
if (userAgent) {
rerunTrigger = document.createElement( "a" ); userAgent.innerHTML = "";
rerunTrigger.innerHTML = "Rerun"; userAgent.appendChild(document$$1.createTextNode("QUnit " + QUnit.version + "; " + navigator.userAgent));
rerunTrigger.href = setUrl( { testId: testId } ); }
}
testBlock = document.createElement( "li" );
testBlock.appendChild( title ); function appendInterface() {
testBlock.appendChild( rerunTrigger ); var qunit = id("qunit");
testBlock.id = "qunit-test-output-" + testId;
if (qunit) {
assertList = document.createElement( "ol" ); qunit.innerHTML = "<h1 id='qunit-header'>" + escapeText(document$$1.title) + "</h1>" + "<h2 id='qunit-banner'></h2>" + "<div id='qunit-testrunner-toolbar'></div>" + appendFilteredTest() + "<h2 id='qunit-userAgent'></h2>" + "<ol id='qunit-tests'></ol>";
assertList.className = "qunit-assert-list"; }
testBlock.appendChild( assertList ); appendHeader();
appendBanner();
tests.appendChild( testBlock ); appendTestResults();
} appendUserAgent();
appendToolbar();
// HTML Reporter initialization and load }
QUnit.begin( function( details ) {
var i, moduleObj, tests; function appendTestsList(modules) {
var i, l, x, z, test, moduleObj;
// Sort modules by name for the picker
for ( i = 0; i < details.modules.length; i++ ) { for (i = 0, l = modules.length; i < l; i++) {
moduleObj = details.modules[ i ]; moduleObj = modules[i];
if ( moduleObj.name ) {
modulesList.push( moduleObj.name ); for (x = 0, z = moduleObj.tests.length; x < z; x++) {
} test = moduleObj.tests[x];
}
modulesList.sort( function( a, b ) { appendTest(test.name, test.testId, moduleObj.name);
return a.localeCompare( b ); }
} ); }
}
// Initialize QUnit elements
appendInterface(); function appendTest(name, testId, moduleName) {
appendTestsList( details.modules ); var title,
tests = id( "qunit-tests" ); rerunTrigger,
if ( tests && config.hidepassed ) { testBlock,
addClass( tests, "hidepass" ); assertList,
} tests = id("qunit-tests");
} );
if (!tests) {
QUnit.done( function( details ) { return;
var i, key, }
banner = id( "qunit-banner" ),
tests = id( "qunit-tests" ), title = document$$1.createElement("strong");
html = [ title.innerHTML = getNameHtml(name, moduleName);
"Tests completed in ",
details.runtime, rerunTrigger = document$$1.createElement("a");
" milliseconds.<br />", rerunTrigger.innerHTML = "Rerun";
"<span class='passed'>", rerunTrigger.href = setUrl({ testId: testId });
details.passed,
"</span> assertions of <span class='total'>", testBlock = document$$1.createElement("li");
details.total, testBlock.appendChild(title);
"</span> passed, <span class='failed'>", testBlock.appendChild(rerunTrigger);
details.failed, testBlock.id = "qunit-test-output-" + testId;
"</span> failed."
].join( "" ); assertList = document$$1.createElement("ol");
assertList.className = "qunit-assert-list";
if ( banner ) {
banner.className = details.failed ? "qunit-fail" : "qunit-pass"; testBlock.appendChild(assertList);
}
tests.appendChild(testBlock);
if ( tests ) { }
id( "qunit-testresult" ).innerHTML = html;
} // HTML Reporter initialization and load
QUnit.begin(function (details) {
if ( config.altertitle && document.title ) { var i, moduleObj, tests;
// Show ✖ for good, ✔ for bad suite result in title // Sort modules by name for the picker
// use escape sequences in case file gets loaded with non-utf-8-charset for (i = 0; i < details.modules.length; i++) {
document.title = [ moduleObj = details.modules[i];
( details.failed ? "\u2716" : "\u2714" ), if (moduleObj.name) {
document.title.replace( /^[\u2714\u2716] /i, "" ) modulesList.push(moduleObj.name);
].join( " " ); }
} }
modulesList.sort(function (a, b) {
// Clear own sessionStorage items if all tests passed return a.localeCompare(b);
if ( config.reorder && defined.sessionStorage && details.failed === 0 ) { });
for ( i = 0; i < sessionStorage.length; i++ ) {
key = sessionStorage.key( i++ ); // Initialize QUnit elements
if ( key.indexOf( "qunit-test-" ) === 0 ) { appendInterface();
sessionStorage.removeItem( key ); appendTestsList(details.modules);
} tests = id("qunit-tests");
} if (tests && config.hidepassed) {
} addClass(tests, "hidepass");
}
// Scroll back to top to show results });
if ( config.scrolltop && window.scrollTo ) {
window.scrollTo( 0, 0 ); QUnit.done(function (details) {
} var banner = id("qunit-banner"),
} ); tests = id("qunit-tests"),
abortButton = id("qunit-abort-tests-button"),
function getNameHtml( name, module ) { html = ["Tests completed in ", details.runtime, " milliseconds.<br />", "<span class='passed'>", details.passed, "</span> assertions of <span class='total'>", details.total, "</span> passed, <span class='failed'>", details.failed, "</span> failed."].join(""),
var nameHtml = ""; test,
assertLi,
if ( module ) { assertList;
nameHtml = "<span class='module-name'>" + escapeText( module ) + "</span>: ";
} // Update remaing tests to aborted
if (abortButton && abortButton.disabled) {
nameHtml += "<span class='test-name'>" + escapeText( name ) + "</span>"; html = "Tests aborted after " + details.runtime + " milliseconds.";
return nameHtml; for (var i = 0; i < tests.children.length; i++) {
} test = tests.children[i];
if (test.className === "" || test.className === "running") {
QUnit.testStart( function( details ) { test.className = "aborted";
var running, testBlock, bad; assertList = test.getElementsByTagName("ol")[0];
assertLi = document$$1.createElement("li");
testBlock = id( "qunit-test-output-" + details.testId ); assertLi.className = "fail";
if ( testBlock ) { assertLi.innerHTML = "Test aborted.";
testBlock.className = "running"; assertList.appendChild(assertLi);
} else { }
}
// Report later registered tests }
appendTest( details.name, details.testId, details.module );
} if (banner && (!abortButton || abortButton.disabled === false)) {
banner.className = details.failed ? "qunit-fail" : "qunit-pass";
running = id( "qunit-testresult" ); }
if ( running ) {
bad = QUnit.config.reorder && defined.sessionStorage && if (abortButton) {
+sessionStorage.getItem( "qunit-test-" + details.module + "-" + details.name ); abortButton.parentNode.removeChild(abortButton);
}
running.innerHTML = ( bad ?
"Rerunning previously failed test: <br />" : if (tests) {
"Running: <br />" ) + id("qunit-testresult-display").innerHTML = html;
getNameHtml( details.name, details.module ); }
}
if (config.altertitle && document$$1.title) {
} );
// Show ✖ for good, ✔ for bad suite result in title
function stripHtml( string ) { // use escape sequences in case file gets loaded with non-utf-8-charset
document$$1.title = [details.failed ? "\u2716" : "\u2714", document$$1.title.replace(/^[\u2714\u2716] /i, "")].join(" ");
// Strip tags, html entity and whitespaces }
return string.replace( /<\/?[^>]+(>|$)/g, "" ).replace( /\&quot;/g, "" ).replace( /\s+/g, "" );
} // Scroll back to top to show results
if (config.scrolltop && window.scrollTo) {
QUnit.log( function( details ) { window.scrollTo(0, 0);
var assertList, assertLi, }
message, expected, actual, diff, });
showDiff = false,
testItem = id( "qunit-test-output-" + details.testId ); function getNameHtml(name, module) {
var nameHtml = "";
if ( !testItem ) {
return; if (module) {
} nameHtml = "<span class='module-name'>" + escapeText(module) + "</span>: ";
}
message = escapeText( details.message ) || ( details.result ? "okay" : "failed" );
message = "<span class='test-message'>" + message + "</span>"; nameHtml += "<span class='test-name'>" + escapeText(name) + "</span>";
message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
return nameHtml;
// The pushFailure doesn't provide details.expected }
// when it calls, it's implicit to also not show expected and diff stuff
// Also, we need to check details.expected existence, as it can exist and be undefined QUnit.testStart(function (details) {
if ( !details.result && hasOwn.call( details, "expected" ) ) { var running, testBlock, bad;
if ( details.negative ) {
expected = "NOT " + QUnit.dump.parse( details.expected ); testBlock = id("qunit-test-output-" + details.testId);
} else { if (testBlock) {
expected = QUnit.dump.parse( details.expected ); testBlock.className = "running";
} } else {
actual = QUnit.dump.parse( details.actual ); // Report later registered tests
message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + appendTest(details.name, details.testId, details.module);
escapeText( expected ) + }
"</pre></td></tr>";
running = id("qunit-testresult-display");
if ( actual !== expected ) { if (running) {
bad = QUnit.config.reorder && details.previousFailure;
message += "<tr class='test-actual'><th>Result: </th><td><pre>" +
escapeText( actual ) + "</pre></td></tr>"; running.innerHTML = (bad ? "Rerunning previously failed test: <br />" : "Running: <br />") + getNameHtml(details.name, details.module);
}
// Don't show diff if actual or expected are booleans });
if ( !( /^(true|false)$/.test( actual ) ) &&
!( /^(true|false)$/.test( expected ) ) ) { function stripHtml(string) {
diff = QUnit.diff( expected, actual );
showDiff = stripHtml( diff ).length !== // Strip tags, html entity and whitespaces
stripHtml( expected ).length + return string.replace(/<\/?[^>]+(>|$)/g, "").replace(/\&quot;/g, "").replace(/\s+/g, "");
stripHtml( actual ).length; }
}
QUnit.log(function (details) {
// Don't show diff if expected and actual are totally different var assertList,
if ( showDiff ) { assertLi,
message += "<tr class='test-diff'><th>Diff: </th><td><pre>" + message,
diff + "</pre></td></tr>"; expected,
} actual,
} else if ( expected.indexOf( "[object Array]" ) !== -1 || diff,
expected.indexOf( "[object Object]" ) !== -1 ) { showDiff = false,
message += "<tr class='test-message'><th>Message: </th><td>" + testItem = id("qunit-test-output-" + details.testId);
"Diff suppressed as the depth of object is more than current max depth (" +
QUnit.config.maxDepth + ").<p>Hint: Use <code>QUnit.dump.maxDepth</code> to " + if (!testItem) {
" run with a higher max depth or <a href='" + return;
escapeText( setUrl( { maxDepth: -1 } ) ) + "'>" + }
"Rerun</a> without max depth.</p></td></tr>";
} else { message = escapeText(details.message) || (details.result ? "okay" : "failed");
message += "<tr class='test-message'><th>Message: </th><td>" + message = "<span class='test-message'>" + message + "</span>";
"Diff suppressed as the expected and actual results have an equivalent" + message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
" serialization</td></tr>";
} // The pushFailure doesn't provide details.expected
// when it calls, it's implicit to also not show expected and diff stuff
if ( details.source ) { // Also, we need to check details.expected existence, as it can exist and be undefined
message += "<tr class='test-source'><th>Source: </th><td><pre>" + if (!details.result && hasOwn.call(details, "expected")) {
escapeText( details.source ) + "</pre></td></tr>"; if (details.negative) {
} expected = "NOT " + QUnit.dump.parse(details.expected);
} else {
message += "</table>"; expected = QUnit.dump.parse(details.expected);
}
// This occurs when pushFailure is set and we have an extracted stack trace
} else if ( !details.result && details.source ) { actual = QUnit.dump.parse(details.actual);
message += "<table>" + message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + escapeText(expected) + "</pre></td></tr>";
"<tr class='test-source'><th>Source: </th><td><pre>" +
escapeText( details.source ) + "</pre></td></tr>" + if (actual !== expected) {
"</table>";
} message += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText(actual) + "</pre></td></tr>";
assertList = testItem.getElementsByTagName( "ol" )[ 0 ]; // Don't show diff if actual or expected are booleans
if (!/^(true|false)$/.test(actual) && !/^(true|false)$/.test(expected)) {
assertLi = document.createElement( "li" ); diff = QUnit.diff(expected, actual);
assertLi.className = details.result ? "pass" : "fail"; showDiff = stripHtml(diff).length !== stripHtml(expected).length + stripHtml(actual).length;
assertLi.innerHTML = message; }
assertList.appendChild( assertLi );
} ); // Don't show diff if expected and actual are totally different
if (showDiff) {
QUnit.testDone( function( details ) { message += "<tr class='test-diff'><th>Diff: </th><td><pre>" + diff + "</pre></td></tr>";
var testTitle, time, testItem, assertList, }
good, bad, testCounts, skipped, sourceName, } else if (expected.indexOf("[object Array]") !== -1 || expected.indexOf("[object Object]") !== -1) {
tests = id( "qunit-tests" ); message += "<tr class='test-message'><th>Message: </th><td>" + "Diff suppressed as the depth of object is more than current max depth (" + QUnit.config.maxDepth + ").<p>Hint: Use <code>QUnit.dump.maxDepth</code> to " + " run with a higher max depth or <a href='" + escapeText(setUrl({ maxDepth: -1 })) + "'>" + "Rerun</a> without max depth.</p></td></tr>";
} else {
if ( !tests ) { message += "<tr class='test-message'><th>Message: </th><td>" + "Diff suppressed as the expected and actual results have an equivalent" + " serialization</td></tr>";
return; }
}
if (details.source) {
testItem = id( "qunit-test-output-" + details.testId ); message += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + "</pre></td></tr>";
}
assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
message += "</table>";
good = details.passed;
bad = details.failed; // This occurs when pushFailure is set and we have an extracted stack trace
} else if (!details.result && details.source) {
// Store result when possible message += "<table>" + "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + "</pre></td></tr>" + "</table>";
if ( config.reorder && defined.sessionStorage ) { }
if ( bad ) {
sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad ); assertList = testItem.getElementsByTagName("ol")[0];
} else {
sessionStorage.removeItem( "qunit-test-" + details.module + "-" + details.name ); assertLi = document$$1.createElement("li");
} assertLi.className = details.result ? "pass" : "fail";
} assertLi.innerHTML = message;
assertList.appendChild(assertLi);
if ( bad === 0 ) { });
// Collapse the passing tests QUnit.testDone(function (details) {
addClass( assertList, "qunit-collapsed" ); var testTitle,
} else if ( bad && config.collapse && !collapseNext ) { time,
testItem,
// Skip collapsing the first failing test assertList,
collapseNext = true; good,
} else { bad,
testCounts,
// Collapse remaining tests skipped,
addClass( assertList, "qunit-collapsed" ); sourceName,
} tests = id("qunit-tests");
// The testItem.firstChild is the test name if (!tests) {
testTitle = testItem.firstChild; return;
}
testCounts = bad ?
"<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " : testItem = id("qunit-test-output-" + details.testId);
"";
assertList = testItem.getElementsByTagName("ol")[0];
testTitle.innerHTML += " <b class='counts'>(" + testCounts +
details.assertions.length + ")</b>"; good = details.passed;
bad = details.failed;
if ( details.skipped ) {
testItem.className = "skipped"; if (bad === 0) {
skipped = document.createElement( "em" );
skipped.className = "qunit-skipped-label"; // Collapse the passing tests
skipped.innerHTML = "skipped"; addClass(assertList, "qunit-collapsed");
testItem.insertBefore( skipped, testTitle ); } else if (config.collapse) {
} else { if (!collapseNext) {
addEvent( testTitle, "click", function() {
toggleClass( assertList, "qunit-collapsed" ); // Skip collapsing the first failing test
} ); collapseNext = true;
} else {
testItem.className = bad ? "fail" : "pass";
// Collapse remaining tests
time = document.createElement( "span" ); addClass(assertList, "qunit-collapsed");
time.className = "runtime"; }
time.innerHTML = details.runtime + " ms"; }
testItem.insertBefore( time, assertList );
} // The testItem.firstChild is the test name
testTitle = testItem.firstChild;
// Show the source of the test when showing assertions
if ( details.source ) { testCounts = bad ? "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " : "";
sourceName = document.createElement( "p" );
sourceName.innerHTML = "<strong>Source: </strong>" + details.source; testTitle.innerHTML += " <b class='counts'>(" + testCounts + details.assertions.length + ")</b>";
addClass( sourceName, "qunit-source" );
if ( bad === 0 ) { if (details.skipped) {
addClass( sourceName, "qunit-collapsed" ); testItem.className = "skipped";
} skipped = document$$1.createElement("em");
addEvent( testTitle, "click", function() { skipped.className = "qunit-skipped-label";
toggleClass( sourceName, "qunit-collapsed" ); skipped.innerHTML = "skipped";
} ); testItem.insertBefore(skipped, testTitle);
testItem.appendChild( sourceName ); } else {
} addEvent(testTitle, "click", function () {
} ); toggleClass(assertList, "qunit-collapsed");
});
// Avoid readyState issue with phantomjs
// Ref: #818 testItem.className = bad ? "fail" : "pass";
var notPhantom = ( function( p ) {
return !( p && p.version && p.version.major > 0 ); time = document$$1.createElement("span");
} )( window.phantom ); time.className = "runtime";
time.innerHTML = details.runtime + " ms";
if ( notPhantom && document.readyState === "complete" ) { testItem.insertBefore(time, assertList);
QUnit.load(); }
} else {
addEvent( window, "load", QUnit.load ); // Show the source of the test when showing assertions
} if (details.source) {
sourceName = document$$1.createElement("p");
/* sourceName.innerHTML = "<strong>Source: </strong>" + details.source;
* This file is a modified version of google-diff-match-patch's JavaScript implementation addClass(sourceName, "qunit-source");
* (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js), if (bad === 0) {
* modifications are licensed as more fully set forth in LICENSE.txt. addClass(sourceName, "qunit-collapsed");
* }
* The original source of google-diff-match-patch is attributable and licensed as follows: addEvent(testTitle, "click", function () {
* toggleClass(sourceName, "qunit-collapsed");
* Copyright 2006 Google Inc. });
* https://code.google.com/p/google-diff-match-patch/ testItem.appendChild(sourceName);
* }
* Licensed under the Apache License, Version 2.0 (the "License"); });
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // Avoid readyState issue with phantomjs
* // Ref: #818
* https://www.apache.org/licenses/LICENSE-2.0 var notPhantom = function (p) {
* return !(p && p.version && p.version.major > 0);
* Unless required by applicable law or agreed to in writing, software }(window.phantom);
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. if (notPhantom && document$$1.readyState === "complete") {
* See the License for the specific language governing permissions and QUnit.load();
* limitations under the License. } else {
* addEvent(window, "load", QUnit.load);
* More Info: }
* https://code.google.com/p/google-diff-match-patch/ })();
*
* Usage: QUnit.diff(expected, actual) /*
* * This file is a modified version of google-diff-match-patch's JavaScript implementation
*/ * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js),
QUnit.diff = ( function() { * modifications are licensed as more fully set forth in LICENSE.txt.
function DiffMatchPatch() { *
} * The original source of google-diff-match-patch is attributable and licensed as follows:
*
// DIFF FUNCTIONS * Copyright 2006 Google Inc.
* https://code.google.com/p/google-diff-match-patch/
/** *
* The data structure representing a diff is an array of tuples: * Licensed under the Apache License, Version 2.0 (the "License");
* [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] * you may not use this file except in compliance with the License.
* which means: delete 'Hello', add 'Goodbye' and keep ' world.' * You may obtain a copy of the License at
*/ *
var DIFF_DELETE = -1, * https://www.apache.org/licenses/LICENSE-2.0
DIFF_INSERT = 1, *
DIFF_EQUAL = 0; * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
/** * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* Find the differences between two texts. Simplifies the problem by stripping * See the License for the specific language governing permissions and
* any common prefix or suffix off the texts before diffing. * limitations under the License.
* @param {string} text1 Old string to be diffed. *
* @param {string} text2 New string to be diffed. * More Info:
* @param {boolean=} optChecklines Optional speedup flag. If present and false, * https://code.google.com/p/google-diff-match-patch/
* then don't run a line-level diff first to identify the changed areas. *
* Defaults to true, which does a faster, slightly less optimal diff. * Usage: QUnit.diff(expected, actual)
* @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples. *
*/ */
DiffMatchPatch.prototype.DiffMain = function( text1, text2, optChecklines ) { QUnit.diff = function () {
var deadline, checklines, commonlength, function DiffMatchPatch() {}
commonprefix, commonsuffix, diffs;
// DIFF FUNCTIONS
// The diff must be complete in up to 1 second.
deadline = ( new Date() ).getTime() + 1000; /**
* The data structure representing a diff is an array of tuples:
// Check for null inputs. * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
if ( text1 === null || text2 === null ) { * which means: delete 'Hello', add 'Goodbye' and keep ' world.'
throw new Error( "Null input. (DiffMain)" ); */
} var DIFF_DELETE = -1,
DIFF_INSERT = 1,
// Check for equality (speedup). DIFF_EQUAL = 0;
if ( text1 === text2 ) {
if ( text1 ) { /**
return [ * Find the differences between two texts. Simplifies the problem by stripping
[ DIFF_EQUAL, text1 ] * any common prefix or suffix off the texts before diffing.
]; * @param {string} text1 Old string to be diffed.
} * @param {string} text2 New string to be diffed.
return []; * @param {boolean=} optChecklines Optional speedup flag. If present and false,
} * then don't run a line-level diff first to identify the changed areas.
* Defaults to true, which does a faster, slightly less optimal diff.
if ( typeof optChecklines === "undefined" ) { * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
optChecklines = true; */
} DiffMatchPatch.prototype.DiffMain = function (text1, text2, optChecklines) {
var deadline, checklines, commonlength, commonprefix, commonsuffix, diffs;
checklines = optChecklines;
// The diff must be complete in up to 1 second.
// Trim off common prefix (speedup). deadline = new Date().getTime() + 1000;
commonlength = this.diffCommonPrefix( text1, text2 );
commonprefix = text1.substring( 0, commonlength ); // Check for null inputs.
text1 = text1.substring( commonlength ); if (text1 === null || text2 === null) {
text2 = text2.substring( commonlength ); throw new Error("Null input. (DiffMain)");
}
// Trim off common suffix (speedup).
commonlength = this.diffCommonSuffix( text1, text2 ); // Check for equality (speedup).
commonsuffix = text1.substring( text1.length - commonlength ); if (text1 === text2) {
text1 = text1.substring( 0, text1.length - commonlength ); if (text1) {
text2 = text2.substring( 0, text2.length - commonlength ); return [[DIFF_EQUAL, text1]];
}
// Compute the diff on the middle block. return [];
diffs = this.diffCompute( text1, text2, checklines, deadline ); }
// Restore the prefix and suffix. if (typeof optChecklines === "undefined") {
if ( commonprefix ) { optChecklines = true;
diffs.unshift( [ DIFF_EQUAL, commonprefix ] ); }
}
if ( commonsuffix ) { checklines = optChecklines;
diffs.push( [ DIFF_EQUAL, commonsuffix ] );
} // Trim off common prefix (speedup).
this.diffCleanupMerge( diffs ); commonlength = this.diffCommonPrefix(text1, text2);
return diffs; commonprefix = text1.substring(0, commonlength);
}; text1 = text1.substring(commonlength);
text2 = text2.substring(commonlength);
/**
* Reduce the number of edits by eliminating operationally trivial equalities. // Trim off common suffix (speedup).
* @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples. commonlength = this.diffCommonSuffix(text1, text2);
*/ commonsuffix = text1.substring(text1.length - commonlength);
DiffMatchPatch.prototype.diffCleanupEfficiency = function( diffs ) { text1 = text1.substring(0, text1.length - commonlength);
var changes, equalities, equalitiesLength, lastequality, text2 = text2.substring(0, text2.length - commonlength);
pointer, preIns, preDel, postIns, postDel;
changes = false; // Compute the diff on the middle block.
equalities = []; // Stack of indices where equalities are found. diffs = this.diffCompute(text1, text2, checklines, deadline);
equalitiesLength = 0; // Keeping our own length var is faster in JS.
/** @type {?string} */ // Restore the prefix and suffix.
lastequality = null; if (commonprefix) {
diffs.unshift([DIFF_EQUAL, commonprefix]);
// Always equal to diffs[equalities[equalitiesLength - 1]][1] }
pointer = 0; // Index of current position. if (commonsuffix) {
diffs.push([DIFF_EQUAL, commonsuffix]);
// Is there an insertion operation before the last equality. }
preIns = false; this.diffCleanupMerge(diffs);
return diffs;
// Is there a deletion operation before the last equality. };
preDel = false;
/**
// Is there an insertion operation after the last equality. * Reduce the number of edits by eliminating operationally trivial equalities.
postIns = false; * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
*/
// Is there a deletion operation after the last equality. DiffMatchPatch.prototype.diffCleanupEfficiency = function (diffs) {
postDel = false; var changes, equalities, equalitiesLength, lastequality, pointer, preIns, preDel, postIns, postDel;
while ( pointer < diffs.length ) { changes = false;
equalities = []; // Stack of indices where equalities are found.
// Equality found. equalitiesLength = 0; // Keeping our own length var is faster in JS.
if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) { /** @type {?string} */
if ( diffs[ pointer ][ 1 ].length < 4 && ( postIns || postDel ) ) { lastequality = null;
// Candidate found. // Always equal to diffs[equalities[equalitiesLength - 1]][1]
equalities[ equalitiesLength++ ] = pointer; pointer = 0; // Index of current position.
preIns = postIns;
preDel = postDel; // Is there an insertion operation before the last equality.
lastequality = diffs[ pointer ][ 1 ]; preIns = false;
} else {
// Is there a deletion operation before the last equality.
// Not a candidate, and can never become one. preDel = false;
equalitiesLength = 0;
lastequality = null; // Is there an insertion operation after the last equality.
} postIns = false;
postIns = postDel = false;
// Is there a deletion operation after the last equality.
// An insertion or deletion. postDel = false;
} else { while (pointer < diffs.length) {
if ( diffs[ pointer ][ 0 ] === DIFF_DELETE ) { // Equality found.
postDel = true; if (diffs[pointer][0] === DIFF_EQUAL) {
} else { if (diffs[pointer][1].length < 4 && (postIns || postDel)) {
postIns = true;
} // Candidate found.
equalities[equalitiesLength++] = pointer;
/* preIns = postIns;
* Five types to be split: preDel = postDel;
* <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del> lastequality = diffs[pointer][1];
* <ins>A</ins>X<ins>C</ins><del>D</del> } else {
* <ins>A</ins><del>B</del>X<ins>C</ins>
* <ins>A</del>X<ins>C</ins><del>D</del> // Not a candidate, and can never become one.
* <ins>A</ins><del>B</del>X<del>C</del> equalitiesLength = 0;
*/ lastequality = null;
if ( lastequality && ( ( preIns && preDel && postIns && postDel ) || }
( ( lastequality.length < 2 ) && postIns = postDel = false;
( preIns + preDel + postIns + postDel ) === 3 ) ) ) {
// An insertion or deletion.
// Duplicate record. } else {
diffs.splice(
equalities[ equalitiesLength - 1 ], if (diffs[pointer][0] === DIFF_DELETE) {
0, postDel = true;
[ DIFF_DELETE, lastequality ] } else {
); postIns = true;
}
// Change second copy to insert.
diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT; /*
equalitiesLength--; // Throw away the equality we just deleted; * Five types to be split:
lastequality = null; * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
if ( preIns && preDel ) { * <ins>A</ins>X<ins>C</ins><del>D</del>
* <ins>A</ins><del>B</del>X<ins>C</ins>
// No changes made which could affect previous entry, keep going. * <ins>A</del>X<ins>C</ins><del>D</del>
postIns = postDel = true; * <ins>A</ins><del>B</del>X<del>C</del>
equalitiesLength = 0; */
} else { if (lastequality && (preIns && preDel && postIns && postDel || lastequality.length < 2 && preIns + preDel + postIns + postDel === 3)) {
equalitiesLength--; // Throw away the previous equality.
pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1; // Duplicate record.
postIns = postDel = false; diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]);
}
changes = true; // Change second copy to insert.
} diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
} equalitiesLength--; // Throw away the equality we just deleted;
pointer++; lastequality = null;
} if (preIns && preDel) {
if ( changes ) { // No changes made which could affect previous entry, keep going.
this.diffCleanupMerge( diffs ); postIns = postDel = true;
} equalitiesLength = 0;
}; } else {
equalitiesLength--; // Throw away the previous equality.
/** pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
* Convert a diff array into a pretty HTML report. postIns = postDel = false;
* @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples. }
* @param {integer} string to be beautified. changes = true;
* @return {string} HTML representation. }
*/ }
DiffMatchPatch.prototype.diffPrettyHtml = function( diffs ) { pointer++;
var op, data, x, }
html = [];
for ( x = 0; x < diffs.length; x++ ) { if (changes) {
op = diffs[ x ][ 0 ]; // Operation (insert, delete, equal) this.diffCleanupMerge(diffs);
data = diffs[ x ][ 1 ]; // Text of change. }
switch ( op ) { };
case DIFF_INSERT:
html[ x ] = "<ins>" + escapeText( data ) + "</ins>"; /**
break; * Convert a diff array into a pretty HTML report.
case DIFF_DELETE: * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
html[ x ] = "<del>" + escapeText( data ) + "</del>"; * @param {integer} string to be beautified.
break; * @return {string} HTML representation.
case DIFF_EQUAL: */
html[ x ] = "<span>" + escapeText( data ) + "</span>"; DiffMatchPatch.prototype.diffPrettyHtml = function (diffs) {
break; var op,
} data,
} x,
return html.join( "" ); html = [];
}; for (x = 0; x < diffs.length; x++) {
op = diffs[x][0]; // Operation (insert, delete, equal)
/** data = diffs[x][1]; // Text of change.
* Determine the common prefix of two strings. switch (op) {
* @param {string} text1 First string. case DIFF_INSERT:
* @param {string} text2 Second string. html[x] = "<ins>" + escapeText(data) + "</ins>";
* @return {number} The number of characters common to the start of each break;
* string. case DIFF_DELETE:
*/ html[x] = "<del>" + escapeText(data) + "</del>";
DiffMatchPatch.prototype.diffCommonPrefix = function( text1, text2 ) { break;
var pointermid, pointermax, pointermin, pointerstart; case DIFF_EQUAL:
html[x] = "<span>" + escapeText(data) + "</span>";
// Quick check for common null cases. break;
if ( !text1 || !text2 || text1.charAt( 0 ) !== text2.charAt( 0 ) ) { }
return 0; }
} return html.join("");
};
// Binary search.
// Performance analysis: https://neil.fraser.name/news/2007/10/09/ /**
pointermin = 0; * Determine the common prefix of two strings.
pointermax = Math.min( text1.length, text2.length ); * @param {string} text1 First string.
pointermid = pointermax; * @param {string} text2 Second string.
pointerstart = 0; * @return {number} The number of characters common to the start of each
while ( pointermin < pointermid ) { * string.
if ( text1.substring( pointerstart, pointermid ) === */
text2.substring( pointerstart, pointermid ) ) { DiffMatchPatch.prototype.diffCommonPrefix = function (text1, text2) {
pointermin = pointermid; var pointermid, pointermax, pointermin, pointerstart;
pointerstart = pointermin;
} else { // Quick check for common null cases.
pointermax = pointermid; if (!text1 || !text2 || text1.charAt(0) !== text2.charAt(0)) {
} return 0;
pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin ); }
}
return pointermid; // Binary search.
}; // Performance analysis: https://neil.fraser.name/news/2007/10/09/
pointermin = 0;
/** pointermax = Math.min(text1.length, text2.length);
* Determine the common suffix of two strings. pointermid = pointermax;
* @param {string} text1 First string. pointerstart = 0;
* @param {string} text2 Second string. while (pointermin < pointermid) {
* @return {number} The number of characters common to the end of each string. if (text1.substring(pointerstart, pointermid) === text2.substring(pointerstart, pointermid)) {
*/ pointermin = pointermid;
DiffMatchPatch.prototype.diffCommonSuffix = function( text1, text2 ) { pointerstart = pointermin;
var pointermid, pointermax, pointermin, pointerend; } else {
pointermax = pointermid;
// Quick check for common null cases. }
if ( !text1 || pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
!text2 || }
text1.charAt( text1.length - 1 ) !== text2.charAt( text2.length - 1 ) ) { return pointermid;
return 0; };
}
/**
// Binary search. * Determine the common suffix of two strings.
// Performance analysis: https://neil.fraser.name/news/2007/10/09/ * @param {string} text1 First string.
pointermin = 0; * @param {string} text2 Second string.
pointermax = Math.min( text1.length, text2.length ); * @return {number} The number of characters common to the end of each string.
pointermid = pointermax; */
pointerend = 0; DiffMatchPatch.prototype.diffCommonSuffix = function (text1, text2) {
while ( pointermin < pointermid ) { var pointermid, pointermax, pointermin, pointerend;
if ( text1.substring( text1.length - pointermid, text1.length - pointerend ) ===
text2.substring( text2.length - pointermid, text2.length - pointerend ) ) { // Quick check for common null cases.
pointermin = pointermid; if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) {
pointerend = pointermin; return 0;
} else { }
pointermax = pointermid;
} // Binary search.
pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin ); // Performance analysis: https://neil.fraser.name/news/2007/10/09/
} pointermin = 0;
return pointermid; pointermax = Math.min(text1.length, text2.length);
}; pointermid = pointermax;
pointerend = 0;
/** while (pointermin < pointermid) {
* Find the differences between two texts. Assumes that the texts do not if (text1.substring(text1.length - pointermid, text1.length - pointerend) === text2.substring(text2.length - pointermid, text2.length - pointerend)) {
* have any common prefix or suffix. pointermin = pointermid;
* @param {string} text1 Old string to be diffed. pointerend = pointermin;
* @param {string} text2 New string to be diffed. } else {
* @param {boolean} checklines Speedup flag. If false, then don't run a pointermax = pointermid;
* line-level diff first to identify the changed areas. }
* If true, then run a faster, slightly less optimal diff. pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
* @param {number} deadline Time when the diff should be complete by. }
* @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples. return pointermid;
* @private };
*/
DiffMatchPatch.prototype.diffCompute = function( text1, text2, checklines, deadline ) { /**
var diffs, longtext, shorttext, i, hm, * Find the differences between two texts. Assumes that the texts do not
text1A, text2A, text1B, text2B, * have any common prefix or suffix.
midCommon, diffsA, diffsB; * @param {string} text1 Old string to be diffed.
* @param {string} text2 New string to be diffed.
if ( !text1 ) { * @param {boolean} checklines Speedup flag. If false, then don't run a
* line-level diff first to identify the changed areas.
// Just add some text (speedup). * If true, then run a faster, slightly less optimal diff.
return [ * @param {number} deadline Time when the diff should be complete by.
[ DIFF_INSERT, text2 ] * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
]; * @private
} */
DiffMatchPatch.prototype.diffCompute = function (text1, text2, checklines, deadline) {
if ( !text2 ) { var diffs, longtext, shorttext, i, hm, text1A, text2A, text1B, text2B, midCommon, diffsA, diffsB;
// Just delete some text (speedup). if (!text1) {
return [
[ DIFF_DELETE, text1 ] // Just add some text (speedup).
]; return [[DIFF_INSERT, text2]];
} }
longtext = text1.length > text2.length ? text1 : text2; if (!text2) {
shorttext = text1.length > text2.length ? text2 : text1;
i = longtext.indexOf( shorttext ); // Just delete some text (speedup).
if ( i !== -1 ) { return [[DIFF_DELETE, text1]];
}
// Shorter text is inside the longer text (speedup).
diffs = [ longtext = text1.length > text2.length ? text1 : text2;
[ DIFF_INSERT, longtext.substring( 0, i ) ], shorttext = text1.length > text2.length ? text2 : text1;
[ DIFF_EQUAL, shorttext ], i = longtext.indexOf(shorttext);
[ DIFF_INSERT, longtext.substring( i + shorttext.length ) ] if (i !== -1) {
];
// Shorter text is inside the longer text (speedup).
// Swap insertions for deletions if diff is reversed. diffs = [[DIFF_INSERT, longtext.substring(0, i)], [DIFF_EQUAL, shorttext], [DIFF_INSERT, longtext.substring(i + shorttext.length)]];
if ( text1.length > text2.length ) {
diffs[ 0 ][ 0 ] = diffs[ 2 ][ 0 ] = DIFF_DELETE; // Swap insertions for deletions if diff is reversed.
} if (text1.length > text2.length) {
return diffs; diffs[0][0] = diffs[2][0] = DIFF_DELETE;
} }
return diffs;
if ( shorttext.length === 1 ) { }
// Single character string. if (shorttext.length === 1) {
// After the previous speedup, the character can't be an equality.
return [ // Single character string.
[ DIFF_DELETE, text1 ], // After the previous speedup, the character can't be an equality.
[ DIFF_INSERT, text2 ] return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
]; }
}
// Check to see if the problem can be split in two.
// Check to see if the problem can be split in two. hm = this.diffHalfMatch(text1, text2);
hm = this.diffHalfMatch( text1, text2 ); if (hm) {
if ( hm ) {
// A half-match was found, sort out the return data.
// A half-match was found, sort out the return data. text1A = hm[0];
text1A = hm[ 0 ]; text1B = hm[1];
text1B = hm[ 1 ]; text2A = hm[2];
text2A = hm[ 2 ]; text2B = hm[3];
text2B = hm[ 3 ]; midCommon = hm[4];
midCommon = hm[ 4 ];
// Send both pairs off for separate processing.
// Send both pairs off for separate processing. diffsA = this.DiffMain(text1A, text2A, checklines, deadline);
diffsA = this.DiffMain( text1A, text2A, checklines, deadline ); diffsB = this.DiffMain(text1B, text2B, checklines, deadline);
diffsB = this.DiffMain( text1B, text2B, checklines, deadline );
// Merge the results.
// Merge the results. return diffsA.concat([[DIFF_EQUAL, midCommon]], diffsB);
return diffsA.concat( [ }
[ DIFF_EQUAL, midCommon ]
], diffsB ); if (checklines && text1.length > 100 && text2.length > 100) {
} return this.diffLineMode(text1, text2, deadline);
}
if ( checklines && text1.length > 100 && text2.length > 100 ) {
return this.diffLineMode( text1, text2, deadline ); return this.diffBisect(text1, text2, deadline);
} };
return this.diffBisect( text1, text2, deadline ); /**
}; * Do the two texts share a substring which is at least half the length of the
* longer text?
/** * This speedup can produce non-minimal diffs.
* Do the two texts share a substring which is at least half the length of the * @param {string} text1 First string.
* longer text? * @param {string} text2 Second string.
* This speedup can produce non-minimal diffs. * @return {Array.<string>} Five element Array, containing the prefix of
* @param {string} text1 First string. * text1, the suffix of text1, the prefix of text2, the suffix of
* @param {string} text2 Second string. * text2 and the common middle. Or null if there was no match.
* @return {Array.<string>} Five element Array, containing the prefix of * @private
* text1, the suffix of text1, the prefix of text2, the suffix of */
* text2 and the common middle. Or null if there was no match. DiffMatchPatch.prototype.diffHalfMatch = function (text1, text2) {
* @private var longtext, shorttext, dmp, text1A, text2B, text2A, text1B, midCommon, hm1, hm2, hm;
*/
DiffMatchPatch.prototype.diffHalfMatch = function( text1, text2 ) { longtext = text1.length > text2.length ? text1 : text2;
var longtext, shorttext, dmp, shorttext = text1.length > text2.length ? text2 : text1;
text1A, text2B, text2A, text1B, midCommon, if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {
hm1, hm2, hm; return null; // Pointless.
}
longtext = text1.length > text2.length ? text1 : text2; dmp = this; // 'this' becomes 'window' in a closure.
shorttext = text1.length > text2.length ? text2 : text1;
if ( longtext.length < 4 || shorttext.length * 2 < longtext.length ) { /**
return null; // Pointless. * Does a substring of shorttext exist within longtext such that the substring
} * is at least half the length of longtext?
dmp = this; // 'this' becomes 'window' in a closure. * Closure, but does not reference any external variables.
* @param {string} longtext Longer string.
/** * @param {string} shorttext Shorter string.
* Does a substring of shorttext exist within longtext such that the substring * @param {number} i Start index of quarter length substring within longtext.
* is at least half the length of longtext? * @return {Array.<string>} Five element Array, containing the prefix of
* Closure, but does not reference any external variables. * longtext, the suffix of longtext, the prefix of shorttext, the suffix
* @param {string} longtext Longer string. * of shorttext and the common middle. Or null if there was no match.
* @param {string} shorttext Shorter string. * @private
* @param {number} i Start index of quarter length substring within longtext. */
* @return {Array.<string>} Five element Array, containing the prefix of function diffHalfMatchI(longtext, shorttext, i) {
* longtext, the suffix of longtext, the prefix of shorttext, the suffix var seed, j, bestCommon, prefixLength, suffixLength, bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB;
* of shorttext and the common middle. Or null if there was no match.
* @private // Start with a 1/4 length substring at position i as a seed.
*/ seed = longtext.substring(i, i + Math.floor(longtext.length / 4));
function diffHalfMatchI( longtext, shorttext, i ) { j = -1;
var seed, j, bestCommon, prefixLength, suffixLength, bestCommon = "";
bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB; while ((j = shorttext.indexOf(seed, j + 1)) !== -1) {
prefixLength = dmp.diffCommonPrefix(longtext.substring(i), shorttext.substring(j));
// Start with a 1/4 length substring at position i as a seed. suffixLength = dmp.diffCommonSuffix(longtext.substring(0, i), shorttext.substring(0, j));
seed = longtext.substring( i, i + Math.floor( longtext.length / 4 ) ); if (bestCommon.length < suffixLength + prefixLength) {
j = -1; bestCommon = shorttext.substring(j - suffixLength, j) + shorttext.substring(j, j + prefixLength);
bestCommon = ""; bestLongtextA = longtext.substring(0, i - suffixLength);
while ( ( j = shorttext.indexOf( seed, j + 1 ) ) !== -1 ) { bestLongtextB = longtext.substring(i + prefixLength);
prefixLength = dmp.diffCommonPrefix( longtext.substring( i ), bestShorttextA = shorttext.substring(0, j - suffixLength);
shorttext.substring( j ) ); bestShorttextB = shorttext.substring(j + prefixLength);
suffixLength = dmp.diffCommonSuffix( longtext.substring( 0, i ), }
shorttext.substring( 0, j ) ); }
if ( bestCommon.length < suffixLength + prefixLength ) { if (bestCommon.length * 2 >= longtext.length) {
bestCommon = shorttext.substring( j - suffixLength, j ) + return [bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB, bestCommon];
shorttext.substring( j, j + prefixLength ); } else {
bestLongtextA = longtext.substring( 0, i - suffixLength ); return null;
bestLongtextB = longtext.substring( i + prefixLength ); }
bestShorttextA = shorttext.substring( 0, j - suffixLength ); }
bestShorttextB = shorttext.substring( j + prefixLength );
} // First check if the second quarter is the seed for a half-match.
} hm1 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 4));
if ( bestCommon.length * 2 >= longtext.length ) {
return [ bestLongtextA, bestLongtextB, // Check again based on the third quarter.
bestShorttextA, bestShorttextB, bestCommon hm2 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 2));
]; if (!hm1 && !hm2) {
} else { return null;
return null; } else if (!hm2) {
} hm = hm1;
} } else if (!hm1) {
hm = hm2;
// First check if the second quarter is the seed for a half-match. } else {
hm1 = diffHalfMatchI( longtext, shorttext,
Math.ceil( longtext.length / 4 ) ); // Both matched. Select the longest.
hm = hm1[4].length > hm2[4].length ? hm1 : hm2;
// Check again based on the third quarter. }
hm2 = diffHalfMatchI( longtext, shorttext,
Math.ceil( longtext.length / 2 ) ); // A half-match was found, sort out the return data.
if ( !hm1 && !hm2 ) { if (text1.length > text2.length) {
return null; text1A = hm[0];
} else if ( !hm2 ) { text1B = hm[1];
hm = hm1; text2A = hm[2];
} else if ( !hm1 ) { text2B = hm[3];
hm = hm2; } else {
} else { text2A = hm[0];
text2B = hm[1];
// Both matched. Select the longest. text1A = hm[2];
hm = hm1[ 4 ].length > hm2[ 4 ].length ? hm1 : hm2; text1B = hm[3];
} }
midCommon = hm[4];
// A half-match was found, sort out the return data. return [text1A, text1B, text2A, text2B, midCommon];
text1A, text1B, text2A, text2B; };
if ( text1.length > text2.length ) {
text1A = hm[ 0 ]; /**
text1B = hm[ 1 ]; * Do a quick line-level diff on both strings, then rediff the parts for
text2A = hm[ 2 ]; * greater accuracy.
text2B = hm[ 3 ]; * This speedup can produce non-minimal diffs.
} else { * @param {string} text1 Old string to be diffed.
text2A = hm[ 0 ]; * @param {string} text2 New string to be diffed.
text2B = hm[ 1 ]; * @param {number} deadline Time when the diff should be complete by.
text1A = hm[ 2 ]; * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
text1B = hm[ 3 ]; * @private
} */
midCommon = hm[ 4 ]; DiffMatchPatch.prototype.diffLineMode = function (text1, text2, deadline) {
return [ text1A, text1B, text2A, text2B, midCommon ]; var a, diffs, linearray, pointer, countInsert, countDelete, textInsert, textDelete, j;
};
// Scan the text on a line-by-line basis first.
/** a = this.diffLinesToChars(text1, text2);
* Do a quick line-level diff on both strings, then rediff the parts for text1 = a.chars1;
* greater accuracy. text2 = a.chars2;
* This speedup can produce non-minimal diffs. linearray = a.lineArray;
* @param {string} text1 Old string to be diffed.
* @param {string} text2 New string to be diffed. diffs = this.DiffMain(text1, text2, false, deadline);
* @param {number} deadline Time when the diff should be complete by.
* @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples. // Convert the diff back to original text.
* @private this.diffCharsToLines(diffs, linearray);
*/
DiffMatchPatch.prototype.diffLineMode = function( text1, text2, deadline ) { // Eliminate freak matches (e.g. blank lines)
var a, diffs, linearray, pointer, countInsert, this.diffCleanupSemantic(diffs);
countDelete, textInsert, textDelete, j;
// Rediff any replacement blocks, this time character-by-character.
// Scan the text on a line-by-line basis first. // Add a dummy entry at the end.
a = this.diffLinesToChars( text1, text2 ); diffs.push([DIFF_EQUAL, ""]);
text1 = a.chars1; pointer = 0;
text2 = a.chars2; countDelete = 0;
linearray = a.lineArray; countInsert = 0;
textDelete = "";
diffs = this.DiffMain( text1, text2, false, deadline ); textInsert = "";
while (pointer < diffs.length) {
// Convert the diff back to original text. switch (diffs[pointer][0]) {
this.diffCharsToLines( diffs, linearray ); case DIFF_INSERT:
countInsert++;
// Eliminate freak matches (e.g. blank lines) textInsert += diffs[pointer][1];
this.diffCleanupSemantic( diffs ); break;
case DIFF_DELETE:
// Rediff any replacement blocks, this time character-by-character. countDelete++;
// Add a dummy entry at the end. textDelete += diffs[pointer][1];
diffs.push( [ DIFF_EQUAL, "" ] ); break;
pointer = 0; case DIFF_EQUAL:
countDelete = 0;
countInsert = 0; // Upon reaching an equality, check for prior redundancies.
textDelete = ""; if (countDelete >= 1 && countInsert >= 1) {
textInsert = "";
while ( pointer < diffs.length ) { // Delete the offending records and add the merged ones.
switch ( diffs[ pointer ][ 0 ] ) { diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert);
case DIFF_INSERT: pointer = pointer - countDelete - countInsert;
countInsert++; a = this.DiffMain(textDelete, textInsert, false, deadline);
textInsert += diffs[ pointer ][ 1 ]; for (j = a.length - 1; j >= 0; j--) {
break; diffs.splice(pointer, 0, a[j]);
case DIFF_DELETE: }
countDelete++; pointer = pointer + a.length;
textDelete += diffs[ pointer ][ 1 ]; }
break; countInsert = 0;
case DIFF_EQUAL: countDelete = 0;
textDelete = "";
// Upon reaching an equality, check for prior redundancies. textInsert = "";
if ( countDelete >= 1 && countInsert >= 1 ) { break;
}
// Delete the offending records and add the merged ones. pointer++;
diffs.splice( pointer - countDelete - countInsert, }
countDelete + countInsert ); diffs.pop(); // Remove the dummy entry at the end.
pointer = pointer - countDelete - countInsert;
a = this.DiffMain( textDelete, textInsert, false, deadline ); return diffs;
for ( j = a.length - 1; j >= 0; j-- ) { };
diffs.splice( pointer, 0, a[ j ] );
} /**
pointer = pointer + a.length; * Find the 'middle snake' of a diff, split the problem in two
} * and return the recursively constructed diff.
countInsert = 0; * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
countDelete = 0; * @param {string} text1 Old string to be diffed.
textDelete = ""; * @param {string} text2 New string to be diffed.
textInsert = ""; * @param {number} deadline Time at which to bail if not yet complete.
break; * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
} * @private
pointer++; */
} DiffMatchPatch.prototype.diffBisect = function (text1, text2, deadline) {
diffs.pop(); // Remove the dummy entry at the end. var text1Length, text2Length, maxD, vOffset, vLength, v1, v2, x, delta, front, k1start, k1end, k2start, k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2;
return diffs; // Cache the text lengths to prevent multiple calls.
}; text1Length = text1.length;
text2Length = text2.length;
/** maxD = Math.ceil((text1Length + text2Length) / 2);
* Find the 'middle snake' of a diff, split the problem in two vOffset = maxD;
* and return the recursively constructed diff. vLength = 2 * maxD;
* See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. v1 = new Array(vLength);
* @param {string} text1 Old string to be diffed. v2 = new Array(vLength);
* @param {string} text2 New string to be diffed.
* @param {number} deadline Time at which to bail if not yet complete. // Setting all elements to -1 is faster in Chrome & Firefox than mixing
* @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples. // integers and undefined.
* @private for (x = 0; x < vLength; x++) {
*/ v1[x] = -1;
DiffMatchPatch.prototype.diffBisect = function( text1, text2, deadline ) { v2[x] = -1;
var text1Length, text2Length, maxD, vOffset, vLength, }
v1, v2, x, delta, front, k1start, k1end, k2start, v1[vOffset + 1] = 0;
k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2; v2[vOffset + 1] = 0;
delta = text1Length - text2Length;
// Cache the text lengths to prevent multiple calls.
text1Length = text1.length; // If the total number of characters is odd, then the front path will collide
text2Length = text2.length; // with the reverse path.
maxD = Math.ceil( ( text1Length + text2Length ) / 2 ); front = delta % 2 !== 0;
vOffset = maxD;
vLength = 2 * maxD; // Offsets for start and end of k loop.
v1 = new Array( vLength ); // Prevents mapping of space beyond the grid.
v2 = new Array( vLength ); k1start = 0;
k1end = 0;
// Setting all elements to -1 is faster in Chrome & Firefox than mixing k2start = 0;
// integers and undefined. k2end = 0;
for ( x = 0; x < vLength; x++ ) { for (d = 0; d < maxD; d++) {
v1[ x ] = -1;
v2[ x ] = -1; // Bail out if deadline is reached.
} if (new Date().getTime() > deadline) {
v1[ vOffset + 1 ] = 0; break;
v2[ vOffset + 1 ] = 0; }
delta = text1Length - text2Length;
// Walk the front path one step.
// If the total number of characters is odd, then the front path will collide for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {
// with the reverse path. k1Offset = vOffset + k1;
front = ( delta % 2 !== 0 ); if (k1 === -d || k1 !== d && v1[k1Offset - 1] < v1[k1Offset + 1]) {
x1 = v1[k1Offset + 1];
// Offsets for start and end of k loop. } else {
// Prevents mapping of space beyond the grid. x1 = v1[k1Offset - 1] + 1;
k1start = 0; }
k1end = 0; y1 = x1 - k1;
k2start = 0; while (x1 < text1Length && y1 < text2Length && text1.charAt(x1) === text2.charAt(y1)) {
k2end = 0; x1++;
for ( d = 0; d < maxD; d++ ) { y1++;
}
// Bail out if deadline is reached. v1[k1Offset] = x1;
if ( ( new Date() ).getTime() > deadline ) { if (x1 > text1Length) {
break;
} // Ran off the right of the graph.
k1end += 2;
// Walk the front path one step. } else if (y1 > text2Length) {
for ( k1 = -d + k1start; k1 <= d - k1end; k1 += 2 ) {
k1Offset = vOffset + k1; // Ran off the bottom of the graph.
if ( k1 === -d || ( k1 !== d && v1[ k1Offset - 1 ] < v1[ k1Offset + 1 ] ) ) { k1start += 2;
x1 = v1[ k1Offset + 1 ]; } else if (front) {
} else { k2Offset = vOffset + delta - k1;
x1 = v1[ k1Offset - 1 ] + 1; if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) {
}
y1 = x1 - k1; // Mirror x2 onto top-left coordinate system.
while ( x1 < text1Length && y1 < text2Length && x2 = text1Length - v2[k2Offset];
text1.charAt( x1 ) === text2.charAt( y1 ) ) { if (x1 >= x2) {
x1++;
y1++; // Overlap detected.
} return this.diffBisectSplit(text1, text2, x1, y1, deadline);
v1[ k1Offset ] = x1; }
if ( x1 > text1Length ) { }
}
// Ran off the right of the graph. }
k1end += 2;
} else if ( y1 > text2Length ) { // Walk the reverse path one step.
for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {
// Ran off the bottom of the graph. k2Offset = vOffset + k2;
k1start += 2; if (k2 === -d || k2 !== d && v2[k2Offset - 1] < v2[k2Offset + 1]) {
} else if ( front ) { x2 = v2[k2Offset + 1];
k2Offset = vOffset + delta - k1; } else {
if ( k2Offset >= 0 && k2Offset < vLength && v2[ k2Offset ] !== -1 ) { x2 = v2[k2Offset - 1] + 1;
}
// Mirror x2 onto top-left coordinate system. y2 = x2 - k2;
x2 = text1Length - v2[ k2Offset ]; while (x2 < text1Length && y2 < text2Length && text1.charAt(text1Length - x2 - 1) === text2.charAt(text2Length - y2 - 1)) {
if ( x1 >= x2 ) { x2++;
y2++;
// Overlap detected. }
return this.diffBisectSplit( text1, text2, x1, y1, deadline ); v2[k2Offset] = x2;
} if (x2 > text1Length) {
}
} // Ran off the left of the graph.
} k2end += 2;
} else if (y2 > text2Length) {
// Walk the reverse path one step.
for ( k2 = -d + k2start; k2 <= d - k2end; k2 += 2 ) { // Ran off the top of the graph.
k2Offset = vOffset + k2; k2start += 2;
if ( k2 === -d || ( k2 !== d && v2[ k2Offset - 1 ] < v2[ k2Offset + 1 ] ) ) { } else if (!front) {
x2 = v2[ k2Offset + 1 ]; k1Offset = vOffset + delta - k2;
} else { if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) {
x2 = v2[ k2Offset - 1 ] + 1; x1 = v1[k1Offset];
} y1 = vOffset + x1 - k1Offset;
y2 = x2 - k2;
while ( x2 < text1Length && y2 < text2Length && // Mirror x2 onto top-left coordinate system.
text1.charAt( text1Length - x2 - 1 ) === x2 = text1Length - x2;
text2.charAt( text2Length - y2 - 1 ) ) { if (x1 >= x2) {
x2++;
y2++; // Overlap detected.
} return this.diffBisectSplit(text1, text2, x1, y1, deadline);
v2[ k2Offset ] = x2; }
if ( x2 > text1Length ) { }
}
// Ran off the left of the graph. }
k2end += 2; }
} else if ( y2 > text2Length ) {
// Diff took too long and hit the deadline or
// Ran off the top of the graph. // number of diffs equals number of characters, no commonality at all.
k2start += 2; return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
} else if ( !front ) { };
k1Offset = vOffset + delta - k2;
if ( k1Offset >= 0 && k1Offset < vLength && v1[ k1Offset ] !== -1 ) { /**
x1 = v1[ k1Offset ]; * Given the location of the 'middle snake', split the diff in two parts
y1 = vOffset + x1 - k1Offset; * and recurse.
* @param {string} text1 Old string to be diffed.
// Mirror x2 onto top-left coordinate system. * @param {string} text2 New string to be diffed.
x2 = text1Length - x2; * @param {number} x Index of split point in text1.
if ( x1 >= x2 ) { * @param {number} y Index of split point in text2.
* @param {number} deadline Time at which to bail if not yet complete.
// Overlap detected. * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
return this.diffBisectSplit( text1, text2, x1, y1, deadline ); * @private
} */
} DiffMatchPatch.prototype.diffBisectSplit = function (text1, text2, x, y, deadline) {
} var text1a, text1b, text2a, text2b, diffs, diffsb;
} text1a = text1.substring(0, x);
} text2a = text2.substring(0, y);
text1b = text1.substring(x);
// Diff took too long and hit the deadline or text2b = text2.substring(y);
// number of diffs equals number of characters, no commonality at all.
return [ // Compute both diffs serially.
[ DIFF_DELETE, text1 ], diffs = this.DiffMain(text1a, text2a, false, deadline);
[ DIFF_INSERT, text2 ] diffsb = this.DiffMain(text1b, text2b, false, deadline);
];
}; return diffs.concat(diffsb);
};
/**
* Given the location of the 'middle snake', split the diff in two parts /**
* and recurse. * Reduce the number of edits by eliminating semantically trivial equalities.
* @param {string} text1 Old string to be diffed. * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
* @param {string} text2 New string to be diffed. */
* @param {number} x Index of split point in text1. DiffMatchPatch.prototype.diffCleanupSemantic = function (diffs) {
* @param {number} y Index of split point in text2. var changes, equalities, equalitiesLength, lastequality, pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2;
* @param {number} deadline Time at which to bail if not yet complete. changes = false;
* @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples. equalities = []; // Stack of indices where equalities are found.
* @private equalitiesLength = 0; // Keeping our own length var is faster in JS.
*/ /** @type {?string} */
DiffMatchPatch.prototype.diffBisectSplit = function( text1, text2, x, y, deadline ) { lastequality = null;
var text1a, text1b, text2a, text2b, diffs, diffsb;
text1a = text1.substring( 0, x ); // Always equal to diffs[equalities[equalitiesLength - 1]][1]
text2a = text2.substring( 0, y ); pointer = 0; // Index of current position.
text1b = text1.substring( x );
text2b = text2.substring( y ); // Number of characters that changed prior to the equality.
lengthInsertions1 = 0;
// Compute both diffs serially. lengthDeletions1 = 0;
diffs = this.DiffMain( text1a, text2a, false, deadline );
diffsb = this.DiffMain( text1b, text2b, false, deadline ); // Number of characters that changed after the equality.
lengthInsertions2 = 0;
return diffs.concat( diffsb ); lengthDeletions2 = 0;
}; while (pointer < diffs.length) {
if (diffs[pointer][0] === DIFF_EQUAL) {
/** // Equality found.
* Reduce the number of edits by eliminating semantically trivial equalities. equalities[equalitiesLength++] = pointer;
* @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples. lengthInsertions1 = lengthInsertions2;
*/ lengthDeletions1 = lengthDeletions2;
DiffMatchPatch.prototype.diffCleanupSemantic = function( diffs ) { lengthInsertions2 = 0;
var changes, equalities, equalitiesLength, lastequality, lengthDeletions2 = 0;
pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, lastequality = diffs[pointer][1];
lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2; } else {
changes = false; // An insertion or deletion.
equalities = []; // Stack of indices where equalities are found. if (diffs[pointer][0] === DIFF_INSERT) {
equalitiesLength = 0; // Keeping our own length var is faster in JS. lengthInsertions2 += diffs[pointer][1].length;
/** @type {?string} */ } else {
lastequality = null; lengthDeletions2 += diffs[pointer][1].length;
}
// Always equal to diffs[equalities[equalitiesLength - 1]][1]
pointer = 0; // Index of current position. // Eliminate an equality that is smaller or equal to the edits on both
// sides of it.
// Number of characters that changed prior to the equality. if (lastequality && lastequality.length <= Math.max(lengthInsertions1, lengthDeletions1) && lastequality.length <= Math.max(lengthInsertions2, lengthDeletions2)) {
lengthInsertions1 = 0;
lengthDeletions1 = 0; // Duplicate record.
diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]);
// Number of characters that changed after the equality.
lengthInsertions2 = 0; // Change second copy to insert.
lengthDeletions2 = 0; diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
while ( pointer < diffs.length ) {
if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) { // Equality found. // Throw away the equality we just deleted.
equalities[ equalitiesLength++ ] = pointer; equalitiesLength--;
lengthInsertions1 = lengthInsertions2;
lengthDeletions1 = lengthDeletions2; // Throw away the previous equality (it needs to be reevaluated).
lengthInsertions2 = 0; equalitiesLength--;
lengthDeletions2 = 0; pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
lastequality = diffs[ pointer ][ 1 ];
} else { // An insertion or deletion. // Reset the counters.
if ( diffs[ pointer ][ 0 ] === DIFF_INSERT ) { lengthInsertions1 = 0;
lengthInsertions2 += diffs[ pointer ][ 1 ].length; lengthDeletions1 = 0;
} else { lengthInsertions2 = 0;
lengthDeletions2 += diffs[ pointer ][ 1 ].length; lengthDeletions2 = 0;
} lastequality = null;
changes = true;
// Eliminate an equality that is smaller or equal to the edits on both }
// sides of it. }
if ( lastequality && ( lastequality.length <= pointer++;
Math.max( lengthInsertions1, lengthDeletions1 ) ) && }
( lastequality.length <= Math.max( lengthInsertions2,
lengthDeletions2 ) ) ) { // Normalize the diff.
if (changes) {
// Duplicate record. this.diffCleanupMerge(diffs);
diffs.splice( }
equalities[ equalitiesLength - 1 ],
0, // Find any overlaps between deletions and insertions.
[ DIFF_DELETE, lastequality ] // e.g: <del>abcxxx</del><ins>xxxdef</ins>
); // -> <del>abc</del>xxx<ins>def</ins>
// e.g: <del>xxxabc</del><ins>defxxx</ins>
// Change second copy to insert. // -> <ins>def</ins>xxx<del>abc</del>
diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT; // Only extract an overlap if it is as big as the edit ahead or behind it.
pointer = 1;
// Throw away the equality we just deleted. while (pointer < diffs.length) {
equalitiesLength--; if (diffs[pointer - 1][0] === DIFF_DELETE && diffs[pointer][0] === DIFF_INSERT) {
deletion = diffs[pointer - 1][1];
// Throw away the previous equality (it needs to be reevaluated). insertion = diffs[pointer][1];
equalitiesLength--; overlapLength1 = this.diffCommonOverlap(deletion, insertion);
pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1; overlapLength2 = this.diffCommonOverlap(insertion, deletion);
if (overlapLength1 >= overlapLength2) {
// Reset the counters. if (overlapLength1 >= deletion.length / 2 || overlapLength1 >= insertion.length / 2) {
lengthInsertions1 = 0;
lengthDeletions1 = 0; // Overlap found. Insert an equality and trim the surrounding edits.
lengthInsertions2 = 0; diffs.splice(pointer, 0, [DIFF_EQUAL, insertion.substring(0, overlapLength1)]);
lengthDeletions2 = 0; diffs[pointer - 1][1] = deletion.substring(0, deletion.length - overlapLength1);
lastequality = null; diffs[pointer + 1][1] = insertion.substring(overlapLength1);
changes = true; pointer++;
} }
} } else {
pointer++; if (overlapLength2 >= deletion.length / 2 || overlapLength2 >= insertion.length / 2) {
}
// Reverse overlap found.
// Normalize the diff. // Insert an equality and swap and trim the surrounding edits.
if ( changes ) { diffs.splice(pointer, 0, [DIFF_EQUAL, deletion.substring(0, overlapLength2)]);
this.diffCleanupMerge( diffs );
} diffs[pointer - 1][0] = DIFF_INSERT;
diffs[pointer - 1][1] = insertion.substring(0, insertion.length - overlapLength2);
// Find any overlaps between deletions and insertions. diffs[pointer + 1][0] = DIFF_DELETE;
// e.g: <del>abcxxx</del><ins>xxxdef</ins> diffs[pointer + 1][1] = deletion.substring(overlapLength2);
// -> <del>abc</del>xxx<ins>def</ins> pointer++;
// e.g: <del>xxxabc</del><ins>defxxx</ins> }
// -> <ins>def</ins>xxx<del>abc</del> }
// Only extract an overlap if it is as big as the edit ahead or behind it. pointer++;
pointer = 1; }
while ( pointer < diffs.length ) { pointer++;
if ( diffs[ pointer - 1 ][ 0 ] === DIFF_DELETE && }
diffs[ pointer ][ 0 ] === DIFF_INSERT ) { };
deletion = diffs[ pointer - 1 ][ 1 ];
insertion = diffs[ pointer ][ 1 ]; /**
overlapLength1 = this.diffCommonOverlap( deletion, insertion ); * Determine if the suffix of one string is the prefix of another.
overlapLength2 = this.diffCommonOverlap( insertion, deletion ); * @param {string} text1 First string.
if ( overlapLength1 >= overlapLength2 ) { * @param {string} text2 Second string.
if ( overlapLength1 >= deletion.length / 2 || * @return {number} The number of characters common to the end of the first
overlapLength1 >= insertion.length / 2 ) { * string and the start of the second string.
* @private
// Overlap found. Insert an equality and trim the surrounding edits. */
diffs.splice( DiffMatchPatch.prototype.diffCommonOverlap = function (text1, text2) {
pointer, var text1Length, text2Length, textLength, best, length, pattern, found;
0,
[ DIFF_EQUAL, insertion.substring( 0, overlapLength1 ) ] // Cache the text lengths to prevent multiple calls.
); text1Length = text1.length;
diffs[ pointer - 1 ][ 1 ] = text2Length = text2.length;
deletion.substring( 0, deletion.length - overlapLength1 );
diffs[ pointer + 1 ][ 1 ] = insertion.substring( overlapLength1 ); // Eliminate the null case.
pointer++; if (text1Length === 0 || text2Length === 0) {
} return 0;
} else { }
if ( overlapLength2 >= deletion.length / 2 ||
overlapLength2 >= insertion.length / 2 ) { // Truncate the longer string.
if (text1Length > text2Length) {
// Reverse overlap found. text1 = text1.substring(text1Length - text2Length);
// Insert an equality and swap and trim the surrounding edits. } else if (text1Length < text2Length) {
diffs.splice( text2 = text2.substring(0, text1Length);
pointer, }
0, textLength = Math.min(text1Length, text2Length);
[ DIFF_EQUAL, deletion.substring( 0, overlapLength2 ) ]
); // Quick check for the worst case.
if (text1 === text2) {
diffs[ pointer - 1 ][ 0 ] = DIFF_INSERT; return textLength;
diffs[ pointer - 1 ][ 1 ] = }
insertion.substring( 0, insertion.length - overlapLength2 );
diffs[ pointer + 1 ][ 0 ] = DIFF_DELETE; // Start by looking for a single character match
diffs[ pointer + 1 ][ 1 ] = // and increase length until no match is found.
deletion.substring( overlapLength2 ); // Performance analysis: https://neil.fraser.name/news/2010/11/04/
pointer++; best = 0;
} length = 1;
} while (true) {
pointer++; pattern = text1.substring(textLength - length);
} found = text2.indexOf(pattern);
pointer++; if (found === -1) {
} return best;
}; }
length += found;
/** if (found === 0 || text1.substring(textLength - length) === text2.substring(0, length)) {
* Determine if the suffix of one string is the prefix of another. best = length;
* @param {string} text1 First string. length++;
* @param {string} text2 Second string. }
* @return {number} The number of characters common to the end of the first }
* string and the start of the second string. };
* @private
*/ /**
DiffMatchPatch.prototype.diffCommonOverlap = function( text1, text2 ) { * Split two texts into an array of strings. Reduce the texts to a string of
var text1Length, text2Length, textLength, * hashes where each Unicode character represents one line.
best, length, pattern, found; * @param {string} text1 First string.
* @param {string} text2 Second string.
// Cache the text lengths to prevent multiple calls. * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}}
text1Length = text1.length; * An object containing the encoded text1, the encoded text2 and
text2Length = text2.length; * the array of unique strings.
* The zeroth element of the array of unique strings is intentionally blank.
// Eliminate the null case. * @private
if ( text1Length === 0 || text2Length === 0 ) { */
return 0; DiffMatchPatch.prototype.diffLinesToChars = function (text1, text2) {
} var lineArray, lineHash, chars1, chars2;
lineArray = []; // E.g. lineArray[4] === 'Hello\n'
// Truncate the longer string. lineHash = {}; // E.g. lineHash['Hello\n'] === 4
if ( text1Length > text2Length ) {
text1 = text1.substring( text1Length - text2Length ); // '\x00' is a valid character, but various debuggers don't like it.
} else if ( text1Length < text2Length ) { // So we'll insert a junk entry to avoid generating a null character.
text2 = text2.substring( 0, text1Length ); lineArray[0] = "";
}
textLength = Math.min( text1Length, text2Length ); /**
* Split a text into an array of strings. Reduce the texts to a string of
// Quick check for the worst case. * hashes where each Unicode character represents one line.
if ( text1 === text2 ) { * Modifies linearray and linehash through being a closure.
return textLength; * @param {string} text String to encode.
} * @return {string} Encoded string.
* @private
// Start by looking for a single character match */
// and increase length until no match is found. function diffLinesToCharsMunge(text) {
// Performance analysis: https://neil.fraser.name/news/2010/11/04/ var chars, lineStart, lineEnd, lineArrayLength, line;
best = 0; chars = "";
length = 1;
while ( true ) { // Walk the text, pulling out a substring for each line.
pattern = text1.substring( textLength - length ); // text.split('\n') would would temporarily double our memory footprint.
found = text2.indexOf( pattern ); // Modifying text would create many large strings to garbage collect.
if ( found === -1 ) { lineStart = 0;
return best; lineEnd = -1;
}
length += found; // Keeping our own length variable is faster than looking it up.
if ( found === 0 || text1.substring( textLength - length ) === lineArrayLength = lineArray.length;
text2.substring( 0, length ) ) { while (lineEnd < text.length - 1) {
best = length; lineEnd = text.indexOf("\n", lineStart);
length++; if (lineEnd === -1) {
} lineEnd = text.length - 1;
} }
}; line = text.substring(lineStart, lineEnd + 1);
lineStart = lineEnd + 1;
/**
* Split two texts into an array of strings. Reduce the texts to a string of if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : lineHash[line] !== undefined) {
* hashes where each Unicode character represents one line. chars += String.fromCharCode(lineHash[line]);
* @param {string} text1 First string. } else {
* @param {string} text2 Second string. chars += String.fromCharCode(lineArrayLength);
* @return {{chars1: string, chars2: string, lineArray: !Array.<string>}} lineHash[line] = lineArrayLength;
* An object containing the encoded text1, the encoded text2 and lineArray[lineArrayLength++] = line;
* the array of unique strings. }
* The zeroth element of the array of unique strings is intentionally blank. }
* @private return chars;
*/ }
DiffMatchPatch.prototype.diffLinesToChars = function( text1, text2 ) {
var lineArray, lineHash, chars1, chars2; chars1 = diffLinesToCharsMunge(text1);
lineArray = []; // E.g. lineArray[4] === 'Hello\n' chars2 = diffLinesToCharsMunge(text2);
lineHash = {}; // E.g. lineHash['Hello\n'] === 4 return {
chars1: chars1,
// '\x00' is a valid character, but various debuggers don't like it. chars2: chars2,
// So we'll insert a junk entry to avoid generating a null character. lineArray: lineArray
lineArray[ 0 ] = ""; };
};
/**
* Split a text into an array of strings. Reduce the texts to a string of /**
* hashes where each Unicode character represents one line. * Rehydrate the text in a diff from a string of line hashes to real lines of
* Modifies linearray and linehash through being a closure. * text.
* @param {string} text String to encode. * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
* @return {string} Encoded string. * @param {!Array.<string>} lineArray Array of unique strings.
* @private * @private
*/ */
function diffLinesToCharsMunge( text ) { DiffMatchPatch.prototype.diffCharsToLines = function (diffs, lineArray) {
var chars, lineStart, lineEnd, lineArrayLength, line; var x, chars, text, y;
chars = ""; for (x = 0; x < diffs.length; x++) {
chars = diffs[x][1];
// Walk the text, pulling out a substring for each line. text = [];
// text.split('\n') would would temporarily double our memory footprint. for (y = 0; y < chars.length; y++) {
// Modifying text would create many large strings to garbage collect. text[y] = lineArray[chars.charCodeAt(y)];
lineStart = 0; }
lineEnd = -1; diffs[x][1] = text.join("");
}
// Keeping our own length variable is faster than looking it up. };
lineArrayLength = lineArray.length;
while ( lineEnd < text.length - 1 ) { /**
lineEnd = text.indexOf( "\n", lineStart ); * Reorder and merge like edit sections. Merge equalities.
if ( lineEnd === -1 ) { * Any edit section can move as long as it doesn't cross an equality.
lineEnd = text.length - 1; * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
} */
line = text.substring( lineStart, lineEnd + 1 ); DiffMatchPatch.prototype.diffCleanupMerge = function (diffs) {
lineStart = lineEnd + 1; var pointer, countDelete, countInsert, textInsert, textDelete, commonlength, changes, diffPointer, position;
diffs.push([DIFF_EQUAL, ""]); // Add a dummy entry at the end.
if ( lineHash.hasOwnProperty ? lineHash.hasOwnProperty( line ) : pointer = 0;
( lineHash[ line ] !== undefined ) ) { countDelete = 0;
chars += String.fromCharCode( lineHash[ line ] ); countInsert = 0;
} else { textDelete = "";
chars += String.fromCharCode( lineArrayLength ); textInsert = "";
lineHash[ line ] = lineArrayLength;
lineArray[ lineArrayLength++ ] = line; while (pointer < diffs.length) {
} switch (diffs[pointer][0]) {
} case DIFF_INSERT:
return chars; countInsert++;
} textInsert += diffs[pointer][1];
pointer++;
chars1 = diffLinesToCharsMunge( text1 ); break;
chars2 = diffLinesToCharsMunge( text2 ); case DIFF_DELETE:
return { countDelete++;
chars1: chars1, textDelete += diffs[pointer][1];
chars2: chars2, pointer++;
lineArray: lineArray break;
}; case DIFF_EQUAL:
};
// Upon reaching an equality, check for prior redundancies.
/** if (countDelete + countInsert > 1) {
* Rehydrate the text in a diff from a string of line hashes to real lines of if (countDelete !== 0 && countInsert !== 0) {
* text.
* @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples. // Factor out any common prefixes.
* @param {!Array.<string>} lineArray Array of unique strings. commonlength = this.diffCommonPrefix(textInsert, textDelete);
* @private if (commonlength !== 0) {
*/ if (pointer - countDelete - countInsert > 0 && diffs[pointer - countDelete - countInsert - 1][0] === DIFF_EQUAL) {
DiffMatchPatch.prototype.diffCharsToLines = function( diffs, lineArray ) { diffs[pointer - countDelete - countInsert - 1][1] += textInsert.substring(0, commonlength);
var x, chars, text, y; } else {
for ( x = 0; x < diffs.length; x++ ) { diffs.splice(0, 0, [DIFF_EQUAL, textInsert.substring(0, commonlength)]);
chars = diffs[ x ][ 1 ]; pointer++;
text = []; }
for ( y = 0; y < chars.length; y++ ) { textInsert = textInsert.substring(commonlength);
text[ y ] = lineArray[ chars.charCodeAt( y ) ]; textDelete = textDelete.substring(commonlength);
} }
diffs[ x ][ 1 ] = text.join( "" );
} // Factor out any common suffixies.
}; commonlength = this.diffCommonSuffix(textInsert, textDelete);
if (commonlength !== 0) {
/** diffs[pointer][1] = textInsert.substring(textInsert.length - commonlength) + diffs[pointer][1];
* Reorder and merge like edit sections. Merge equalities. textInsert = textInsert.substring(0, textInsert.length - commonlength);
* Any edit section can move as long as it doesn't cross an equality. textDelete = textDelete.substring(0, textDelete.length - commonlength);
* @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples. }
*/ }
DiffMatchPatch.prototype.diffCleanupMerge = function( diffs ) {
var pointer, countDelete, countInsert, textInsert, textDelete, // Delete the offending records and add the merged ones.
commonlength, changes, diffPointer, position; if (countDelete === 0) {
diffs.push( [ DIFF_EQUAL, "" ] ); // Add a dummy entry at the end. diffs.splice(pointer - countInsert, countDelete + countInsert, [DIFF_INSERT, textInsert]);
pointer = 0; } else if (countInsert === 0) {
countDelete = 0; diffs.splice(pointer - countDelete, countDelete + countInsert, [DIFF_DELETE, textDelete]);
countInsert = 0; } else {
textDelete = ""; diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert, [DIFF_DELETE, textDelete], [DIFF_INSERT, textInsert]);
textInsert = ""; }
commonlength; pointer = pointer - countDelete - countInsert + (countDelete ? 1 : 0) + (countInsert ? 1 : 0) + 1;
while ( pointer < diffs.length ) { } else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) {
switch ( diffs[ pointer ][ 0 ] ) {
case DIFF_INSERT: // Merge this equality with the previous one.
countInsert++; diffs[pointer - 1][1] += diffs[pointer][1];
textInsert += diffs[ pointer ][ 1 ]; diffs.splice(pointer, 1);
pointer++; } else {
break; pointer++;
case DIFF_DELETE: }
countDelete++; countInsert = 0;
textDelete += diffs[ pointer ][ 1 ]; countDelete = 0;
pointer++; textDelete = "";
break; textInsert = "";
case DIFF_EQUAL: break;
}
// Upon reaching an equality, check for prior redundancies. }
if ( countDelete + countInsert > 1 ) { if (diffs[diffs.length - 1][1] === "") {
if ( countDelete !== 0 && countInsert !== 0 ) { diffs.pop(); // Remove the dummy entry at the end.
}
// Factor out any common prefixes.
commonlength = this.diffCommonPrefix( textInsert, textDelete ); // Second pass: look for single edits surrounded on both sides by equalities
if ( commonlength !== 0 ) { // which can be shifted sideways to eliminate an equality.
if ( ( pointer - countDelete - countInsert ) > 0 && // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
diffs[ pointer - countDelete - countInsert - 1 ][ 0 ] === changes = false;
DIFF_EQUAL ) { pointer = 1;
diffs[ pointer - countDelete - countInsert - 1 ][ 1 ] +=
textInsert.substring( 0, commonlength ); // Intentionally ignore the first and last element (don't need checking).
} else { while (pointer < diffs.length - 1) {
diffs.splice( 0, 0, [ DIFF_EQUAL, if (diffs[pointer - 1][0] === DIFF_EQUAL && diffs[pointer + 1][0] === DIFF_EQUAL) {
textInsert.substring( 0, commonlength )
] ); diffPointer = diffs[pointer][1];
pointer++; position = diffPointer.substring(diffPointer.length - diffs[pointer - 1][1].length);
}
textInsert = textInsert.substring( commonlength ); // This is a single edit surrounded by equalities.
textDelete = textDelete.substring( commonlength ); if (position === diffs[pointer - 1][1]) {
}
// Shift the edit over the previous equality.
// Factor out any common suffixies. diffs[pointer][1] = diffs[pointer - 1][1] + diffs[pointer][1].substring(0, diffs[pointer][1].length - diffs[pointer - 1][1].length);
commonlength = this.diffCommonSuffix( textInsert, textDelete ); diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];
if ( commonlength !== 0 ) { diffs.splice(pointer - 1, 1);
diffs[ pointer ][ 1 ] = textInsert.substring( textInsert.length - changes = true;
commonlength ) + diffs[ pointer ][ 1 ]; } else if (diffPointer.substring(0, diffs[pointer + 1][1].length) === diffs[pointer + 1][1]) {
textInsert = textInsert.substring( 0, textInsert.length -
commonlength ); // Shift the edit over the next equality.
textDelete = textDelete.substring( 0, textDelete.length - diffs[pointer - 1][1] += diffs[pointer + 1][1];
commonlength ); diffs[pointer][1] = diffs[pointer][1].substring(diffs[pointer + 1][1].length) + diffs[pointer + 1][1];
} diffs.splice(pointer + 1, 1);
} changes = true;
}
// Delete the offending records and add the merged ones. }
if ( countDelete === 0 ) { pointer++;
diffs.splice( pointer - countInsert, }
countDelete + countInsert, [ DIFF_INSERT, textInsert ] );
} else if ( countInsert === 0 ) { // If shifts were made, the diff needs reordering and another shift sweep.
diffs.splice( pointer - countDelete, if (changes) {
countDelete + countInsert, [ DIFF_DELETE, textDelete ] ); this.diffCleanupMerge(diffs);
} else { }
diffs.splice( };
pointer - countDelete - countInsert,
countDelete + countInsert, return function (o, n) {
[ DIFF_DELETE, textDelete ], [ DIFF_INSERT, textInsert ] var diff, output, text;
); diff = new DiffMatchPatch();
} output = diff.DiffMain(o, n);
pointer = pointer - countDelete - countInsert + diff.diffCleanupEfficiency(output);
( countDelete ? 1 : 0 ) + ( countInsert ? 1 : 0 ) + 1; text = diff.diffPrettyHtml(output);
} else if ( pointer !== 0 && diffs[ pointer - 1 ][ 0 ] === DIFF_EQUAL ) {
return text;
// Merge this equality with the previous one. };
diffs[ pointer - 1 ][ 1 ] += diffs[ pointer ][ 1 ]; }();
diffs.splice( pointer, 1 );
} else { }((function() { return this; }())));
pointer++;
}
countInsert = 0;
countDelete = 0;
textDelete = "";
textInsert = "";
break;
}
}
if ( diffs[ diffs.length - 1 ][ 1 ] === "" ) {
diffs.pop(); // Remove the dummy entry at the end.
}
// Second pass: look for single edits surrounded on both sides by equalities
// which can be shifted sideways to eliminate an equality.
// e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
changes = false;
pointer = 1;
// Intentionally ignore the first and last element (don't need checking).
while ( pointer < diffs.length - 1 ) {
if ( diffs[ pointer - 1 ][ 0 ] === DIFF_EQUAL &&
diffs[ pointer + 1 ][ 0 ] === DIFF_EQUAL ) {
diffPointer = diffs[ pointer ][ 1 ];
position = diffPointer.substring(
diffPointer.length - diffs[ pointer - 1 ][ 1 ].length
);
// This is a single edit surrounded by equalities.
if ( position === diffs[ pointer - 1 ][ 1 ] ) {
// Shift the edit over the previous equality.
diffs[ pointer ][ 1 ] = diffs[ pointer - 1 ][ 1 ] +
diffs[ pointer ][ 1 ].substring( 0, diffs[ pointer ][ 1 ].length -
diffs[ pointer - 1 ][ 1 ].length );
diffs[ pointer + 1 ][ 1 ] =
diffs[ pointer - 1 ][ 1 ] + diffs[ pointer + 1 ][ 1 ];
diffs.splice( pointer - 1, 1 );
changes = true;
} else if ( diffPointer.substring( 0, diffs[ pointer + 1 ][ 1 ].length ) ===
diffs[ pointer + 1 ][ 1 ] ) {
// Shift the edit over the next equality.
diffs[ pointer - 1 ][ 1 ] += diffs[ pointer + 1 ][ 1 ];
diffs[ pointer ][ 1 ] =
diffs[ pointer ][ 1 ].substring( diffs[ pointer + 1 ][ 1 ].length ) +
diffs[ pointer + 1 ][ 1 ];
diffs.splice( pointer + 1, 1 );
changes = true;
}
}
pointer++;
}
// If shifts were made, the diff needs reordering and another shift sweep.
if ( changes ) {
this.diffCleanupMerge( diffs );
}
};
return function( o, n ) {
var diff, output, text;
diff = new DiffMatchPatch();
output = diff.DiffMain( o, n );
diff.diffCleanupEfficiency( output );
text = diff.diffPrettyHtml( output );
return text;
};
}() );
}() );

View File

@ -18,8 +18,8 @@
], ],
"scripts": { "scripts": {
"change-version": "node grunt/change-version.js", "change-version": "node grunt/change-version.js",
"clean-css": "cleancss --skip-advanced --source-map --output dist/css/bootstrap.min.css dist/css/bootstrap.css && cleancss --skip-advanced --source-map --output dist/css/bootstrap-grid.min.css dist/css/bootstrap-grid.css && cleancss --skip-advanced --source-map --output dist/css/bootstrap-reboot.min.css dist/css/bootstrap-reboot.css", "clean-css": "cleancss --level 1 --source-map --output dist/css/bootstrap.min.css dist/css/bootstrap.css && cleancss --level 1 --source-map --output dist/css/bootstrap-grid.min.css dist/css/bootstrap-grid.css && cleancss --level 1 --source-map --output dist/css/bootstrap-reboot.min.css dist/css/bootstrap-reboot.css",
"clean-css-docs": "cleancss --skip-advanced --source-map --output docs/assets/css/docs.min.css docs/assets/css/docs.min.css", "clean-css-docs": "cleancss --level 1 --source-map --output docs/assets/css/docs.min.css docs/assets/css/docs.min.css",
"eslint": "eslint --ignore-path .eslintignore js && eslint --config js/tests/.eslintrc.json --env node grunt Gruntfile.js && eslint --config js/tests/.eslintrc.json docs/assets/js/src docs/assets/js/ie-emulation-modes-warning.js docs/assets/js/ie10-viewport-bug-workaround.js", "eslint": "eslint --ignore-path .eslintignore js && eslint --config js/tests/.eslintrc.json --env node grunt Gruntfile.js && eslint --config js/tests/.eslintrc.json docs/assets/js/src docs/assets/js/ie-emulation-modes-warning.js docs/assets/js/ie10-viewport-bug-workaround.js",
"htmlhint": "htmlhint --config docs/.htmlhintrc _gh_pages/", "htmlhint": "htmlhint --config docs/.htmlhintrc _gh_pages/",
"htmllint": "htmllint --rc docs/.htmllintrc _gh_pages/**/*.html js/tests/visual/*.html", "htmllint": "htmllint --rc docs/.htmllintrc _gh_pages/**/*.html js/tests/visual/*.html",
@ -50,35 +50,35 @@
"tether": "^1.4.0" "tether": "^1.4.0"
}, },
"devDependencies": { "devDependencies": {
"autoprefixer": "^6.6.1", "autoprefixer": "^6.7.2",
"babel-eslint": "^7.1.1", "babel-eslint": "^7.1.1",
"babel-plugin-transform-es2015-modules-strip": "^0.1.0", "babel-plugin-transform-es2015-modules-strip": "^0.1.0",
"babel-preset-es2015": "^6.18.0", "babel-preset-es2015": "^6.22.0",
"clean-css": "^3.4.23", "clean-css-cli": "^4.0.0",
"eslint": "^3.12.2", "eslint": "^3.15.0",
"grunt": "^1.0.1", "grunt": "^1.0.1",
"grunt-babel": "^6.0.0", "grunt-babel": "^6.0.0",
"grunt-build-control": "^0.7.1", "grunt-build-control": "^0.7.1",
"grunt-contrib-clean": "^1.0.0", "grunt-contrib-clean": "^1.0.0",
"grunt-contrib-compress": "^1.3.0", "grunt-contrib-compress": "^1.4.1",
"grunt-contrib-concat": "^1.0.1", "grunt-contrib-concat": "^1.0.1",
"grunt-contrib-connect": "^1.0.2", "grunt-contrib-connect": "^1.0.2",
"grunt-contrib-copy": "^1.0.0", "grunt-contrib-copy": "^1.0.0",
"grunt-contrib-qunit": "^1.2.0", "grunt-contrib-qunit": "^1.2.0",
"grunt-contrib-watch": "^1.0.0", "grunt-contrib-watch": "^1.0.0",
"grunt-exec": "^1.0.1", "grunt-exec": "^1.0.1",
"grunt-jekyll": "^0.4.4", "grunt-jekyll": "^0.4.6",
"grunt-saucelabs": "^9.0.0", "grunt-saucelabs": "^9.0.0",
"grunt-stamp": "^0.3.0", "grunt-stamp": "^0.3.0",
"htmlhint": "^0.9.13", "htmlhint": "^0.9.13",
"htmllint-cli": "0.0.6", "htmllint-cli": "^0.0.6",
"is-travis": "^1.0.0", "is-travis": "^1.0.0",
"load-grunt-tasks": "^3.5.2", "load-grunt-tasks": "^3.5.2",
"node-sass": "^4.1.1", "node-sass": "^4.5.0",
"postcss-cli": "^2.6.0", "postcss-cli": "^2.6.0",
"postcss-flexbugs-fixes": "^2.1.0", "postcss-flexbugs-fixes": "^2.1.0",
"shelljs": "^0.7.5", "shelljs": "^0.7.6",
"shx": "^0.2.1", "shx": "^0.2.2",
"time-grunt": "^1.4.0", "time-grunt": "^1.4.0",
"uglify-js": "^2.7.5" "uglify-js": "^2.7.5"
}, },