diff --git a/docs/app/e2e/app.scenario.js b/docs/app/e2e/app.scenario.js index 829323bb6115..4bacb00de97a 100644 --- a/docs/app/e2e/app.scenario.js +++ b/docs/app/e2e/app.scenario.js @@ -1,6 +1,6 @@ 'use strict'; -var webdriver = require('protractor/node_modules/selenium-webdriver'); +var webdriver = require('selenium-webdriver'); describe('docs.angularjs.org', function() { diff --git a/docs/content/error/$compile/badrestrict.ngdoc b/docs/content/error/$compile/badrestrict.ngdoc new file mode 100644 index 000000000000..7c4eb41a1984 --- /dev/null +++ b/docs/content/error/$compile/badrestrict.ngdoc @@ -0,0 +1,18 @@ +@ngdoc error +@name $compile:badrestrict +@fullName Invalid Directive Restrict +@description + +This error occurs when the restrict property of a directive is not valid. + +The directive restrict property must be a string including one of more of the following characters: +* E (element) +* A (attribute) +* C (class) +* M (comment) + +For example: +```javascript +restrict: 'E' +restrict: 'EAC' +``` \ No newline at end of file diff --git a/docs/content/error/$compile/multilink.ngdoc b/docs/content/error/$compile/multilink.ngdoc new file mode 100644 index 000000000000..6404ec04f69a --- /dev/null +++ b/docs/content/error/$compile/multilink.ngdoc @@ -0,0 +1,27 @@ +@ngdoc error +@name $compile:multilink +@fullName Linking Element Multiple Times +@description + +This error occurs when a single element is linked more then once. + +For example, if an element is compiled and linked twice without cloning: +``` + var linker = $compile(template); + linker($scope); //=> ok + linker($scope); //=> multilink error +``` + +Linking an element as a clone multiple times is ok: +``` + var linker = $compile(template); + linker($scope, function() { ... }); //=> ok + linker($scope, function() { ... }); //=> ok +``` + +However once an element has been linked it can not be re-linked as a clone: +``` + var linker = $compile(template); + linker($scope); //=> ok + linker($scope, function() { ... }); //=> multilink error +``` \ No newline at end of file diff --git a/docs/content/error/$compile/noident.ngdoc b/docs/content/error/$compile/noident.ngdoc index 428629b5d7be..9770a94585e1 100644 --- a/docs/content/error/$compile/noident.ngdoc +++ b/docs/content/error/$compile/noident.ngdoc @@ -16,7 +16,7 @@ For example, the following directives are valid: directive("okay", function() { return { bindToController: true, - controller: "myCtrl as $ctrl" + controller: "myCtrl as $ctrl", scope: { text: "@text" } diff --git a/docs/content/guide/external-resources.ngdoc b/docs/content/guide/external-resources.ngdoc index 1191a09da3f2..3113dcd76623 100644 --- a/docs/content/guide/external-resources.ngdoc +++ b/docs/content/guide/external-resources.ngdoc @@ -140,7 +140,7 @@ You can find a larger list of Angular external libraries at [ngmodules.org](http [Pluralsite (3 courses)](http://www.pluralsight.com/training/Courses/Find?highlight=true&searchTerm=angularjs), [Tuts+](https://tutsplus.com/course/easier-js-apps-with-angular/), [lynda.com](http://www.lynda.com/AngularJS-tutorials/Up-Running-AngularJS/133318-2.html), - [WintellectNOW (4 lessons)](http://www.wintellectnow.com/Course/Detail/mastering-angularjs) + [WintellectNOW (4 lessons)](http://www.wintellectnow.com/Course/Detail/mastering-angularjs), + [Packt](https://www.packtpub.com/web-development/angularjs-maintaining-web-applications) * **Paid onsite:** [angularbootcamp.com](http://angularbootcamp.com/) - diff --git a/karma-shared.conf.js b/karma-shared.conf.js index ee4c78d00d7b..44acad709f48 100644 --- a/karma-shared.conf.js +++ b/karma-shared.conf.js @@ -170,8 +170,8 @@ module.exports = function(config, specificOptions) { '/someSanitizedUrl', '/{{testUrl}}' ]; - var log4js = require('./node_modules/karma/node_modules/log4js'); - var layouts = require('./node_modules/karma/node_modules/log4js/lib/layouts'); + var log4js = require('log4js'); + var layouts = require('log4js/lib/layouts'); var originalConfigure = log4js.configure; log4js.configure = function(log4jsConfig) { var consoleAppender = log4jsConfig.appenders.shift(); diff --git a/lib/grunt/plugins.js b/lib/grunt/plugins.js index 6a436505a727..14cdecd300ce 100644 --- a/lib/grunt/plugins.js +++ b/lib/grunt/plugins.js @@ -39,7 +39,7 @@ module.exports = function(grunt) { grunt.registerTask('docs', 'create angular docs', function() { - var gruntProc = shelljs.exec('"node_modules/.bin/gulp" --gulpfile docs/gulpfile.js'); + var gruntProc = shelljs.exec('npm run gulp -- --gulpfile docs/gulpfile.js'); if (gruntProc.code !== 0) { throw new Error('doc generation failed'); } diff --git a/lib/grunt/utils.js b/lib/grunt/utils.js index 9837478b7c55..187bb8383b53 100644 --- a/lib/grunt/utils.js +++ b/lib/grunt/utils.js @@ -3,7 +3,7 @@ var fs = require('fs'); var shell = require('shelljs'); var grunt = require('grunt'); -var spawn = require('child_process').spawn; +var spawn = require('cross-spawn'); var CSP_CSS_HEADER = '/* Include this file in your html if you are using the CSP mode. */\n\n'; @@ -15,7 +15,7 @@ module.exports = { var reporters = grunt.option('reporters'); var noColor = grunt.option('no-colors'); var port = grunt.option('port'); - var p = spawn('node', ['node_modules/karma/bin/karma', 'start', config, + var p = spawn('./node_modules/.bin/karma', ['start', config, singleRun ? '--single-run=true' : '', reporters ? '--reporters=' + reporters : '', browsers ? '--browsers=' + browsers : '', @@ -38,7 +38,7 @@ module.exports = { done(); return; } - var p = spawn('node', ['node_modules/protractor/bin/webdriver-manager', 'update']); + var p = spawn('./node_modules/.bin/webdriver-manager', ['update']); p.stdout.pipe(process.stdout); p.stderr.pipe(process.stderr); p.on('exit', function(code) { @@ -54,7 +54,7 @@ module.exports = { var sauceBuild = grunt.option('capabilities.build'); var browser = grunt.option('browser'); var specs = grunt.option('specs'); - var args = ['node_modules/protractor/bin/protractor', config]; + var args = [config]; if (sauceUser) args.push('--sauceUser=' + sauceUser); if (sauceKey) args.push('--sauceKey=' + sauceKey); if (tunnelIdentifier) args.push('--capabilities.tunnel-identifier=' + tunnelIdentifier); @@ -65,7 +65,7 @@ module.exports = { } - var p = spawn('node', args); + var p = spawn('./node_modules/.bin/protractor', args); p.stdout.pipe(process.stdout); p.stderr.pipe(process.stderr); p.on('exit', function(code) { diff --git a/npm-shrinkwrap.clean.json b/npm-shrinkwrap.clean.json index 53c84d8a7264..16f7fc7818c4 100644 --- a/npm-shrinkwrap.clean.json +++ b/npm-shrinkwrap.clean.json @@ -2630,6 +2630,30 @@ } } }, + "cross-spawn": { + "version": "4.0.0", + "dependencies": { + "lru-cache": { + "version": "4.0.1", + "dependencies": { + "pseudomap": { + "version": "1.0.2" + }, + "yallist": { + "version": "2.0.0" + } + } + }, + "which": { + "version": "1.2.10", + "dependencies": { + "isexe": { + "version": "1.1.2" + } + } + } + } + }, "cz-conventional-changelog": { "version": "1.1.4", "dependencies": { @@ -2933,14 +2957,6 @@ }, "async-each": { "version": "0.1.6" - }, - "fsevents": { - "version": "0.3.8", - "dependencies": { - "nan": { - "version": "2.4.0" - } - } } } } @@ -6966,378 +6982,6 @@ "version": "1.0.1" } } - }, - "fsevents": { - "version": "1.0.14", - "dependencies": { - "nan": { - "version": "2.4.0" - }, - "node-pre-gyp": { - "version": "0.6.29" - }, - "abbrev": { - "version": "1.0.9" - }, - "ansi-regex": { - "version": "2.0.0" - }, - "ansi-styles": { - "version": "2.2.1" - }, - "aproba": { - "version": "1.0.4" - }, - "are-we-there-yet": { - "version": "1.1.2" - }, - "asn1": { - "version": "0.2.3" - }, - "assert-plus": { - "version": "0.2.0" - }, - "async": { - "version": "1.5.2" - }, - "aws-sign2": { - "version": "0.6.0" - }, - "aws4": { - "version": "1.4.1" - }, - "balanced-match": { - "version": "0.4.2" - }, - "block-stream": { - "version": "0.0.9" - }, - "boom": { - "version": "2.10.1" - }, - "brace-expansion": { - "version": "1.1.5" - }, - "buffer-shims": { - "version": "1.0.0" - }, - "caseless": { - "version": "0.11.0" - }, - "chalk": { - "version": "1.1.3" - }, - "code-point-at": { - "version": "1.0.0" - }, - "combined-stream": { - "version": "1.0.5" - }, - "commander": { - "version": "2.9.0" - }, - "concat-map": { - "version": "0.0.1" - }, - "console-control-strings": { - "version": "1.1.0" - }, - "core-util-is": { - "version": "1.0.2" - }, - "cryptiles": { - "version": "2.0.5" - }, - "debug": { - "version": "2.2.0" - }, - "deep-extend": { - "version": "0.4.1" - }, - "delayed-stream": { - "version": "1.0.0" - }, - "delegates": { - "version": "1.0.0" - }, - "ecc-jsbn": { - "version": "0.1.1" - }, - "escape-string-regexp": { - "version": "1.0.5" - }, - "extend": { - "version": "3.0.0" - }, - "extsprintf": { - "version": "1.0.2" - }, - "forever-agent": { - "version": "0.6.1" - }, - "form-data": { - "version": "1.0.0-rc4" - }, - "fs.realpath": { - "version": "1.0.0" - }, - "fstream": { - "version": "1.0.10" - }, - "fstream-ignore": { - "version": "1.0.5" - }, - "gauge": { - "version": "2.6.0" - }, - "generate-function": { - "version": "2.0.0" - }, - "generate-object-property": { - "version": "1.2.0" - }, - "glob": { - "version": "7.0.5" - }, - "graceful-fs": { - "version": "4.1.4" - }, - "graceful-readlink": { - "version": "1.0.1" - }, - "har-validator": { - "version": "2.0.6" - }, - "has-ansi": { - "version": "2.0.0" - }, - "has-color": { - "version": "0.1.7" - }, - "has-unicode": { - "version": "2.0.1" - }, - "hawk": { - "version": "3.1.3" - }, - "hoek": { - "version": "2.16.3" - }, - "http-signature": { - "version": "1.1.1" - }, - "inflight": { - "version": "1.0.5" - }, - "inherits": { - "version": "2.0.1" - }, - "ini": { - "version": "1.3.4" - }, - "is-fullwidth-code-point": { - "version": "1.0.0" - }, - "is-my-json-valid": { - "version": "2.13.1" - }, - "is-property": { - "version": "1.0.2" - }, - "is-typedarray": { - "version": "1.0.0" - }, - "isarray": { - "version": "1.0.0" - }, - "isstream": { - "version": "0.1.2" - }, - "jodid25519": { - "version": "1.0.2" - }, - "jsbn": { - "version": "0.1.0" - }, - "json-schema": { - "version": "0.2.2" - }, - "json-stringify-safe": { - "version": "5.0.1" - }, - "jsonpointer": { - "version": "2.0.0" - }, - "jsprim": { - "version": "1.3.0" - }, - "mime-db": { - "version": "1.23.0" - }, - "mime-types": { - "version": "2.1.11" - }, - "minimatch": { - "version": "3.0.2" - }, - "minimist": { - "version": "0.0.8" - }, - "mkdirp": { - "version": "0.5.1" - }, - "ms": { - "version": "0.7.1" - }, - "node-uuid": { - "version": "1.4.7" - }, - "nopt": { - "version": "3.0.6" - }, - "npmlog": { - "version": "3.1.2" - }, - "number-is-nan": { - "version": "1.0.0" - }, - "oauth-sign": { - "version": "0.8.2" - }, - "object-assign": { - "version": "4.1.0" - }, - "once": { - "version": "1.3.3" - }, - "path-is-absolute": { - "version": "1.0.0" - }, - "pinkie": { - "version": "2.0.4" - }, - "pinkie-promise": { - "version": "2.0.1" - }, - "process-nextick-args": { - "version": "1.0.7" - }, - "qs": { - "version": "6.2.0" - }, - "readable-stream": { - "version": "2.1.4" - }, - "request": { - "version": "2.73.0" - }, - "rimraf": { - "version": "2.5.3" - }, - "semver": { - "version": "5.2.0" - }, - "set-blocking": { - "version": "2.0.0" - }, - "signal-exit": { - "version": "3.0.0" - }, - "sntp": { - "version": "1.0.9" - }, - "string-width": { - "version": "1.0.1" - }, - "string_decoder": { - "version": "0.10.31" - }, - "stringstream": { - "version": "0.0.5" - }, - "strip-ansi": { - "version": "3.0.1" - }, - "strip-json-comments": { - "version": "1.0.4" - }, - "supports-color": { - "version": "2.0.0" - }, - "tar": { - "version": "2.2.1" - }, - "tar-pack": { - "version": "3.1.4" - }, - "tough-cookie": { - "version": "2.2.2" - }, - "tunnel-agent": { - "version": "0.4.3" - }, - "tweetnacl": { - "version": "0.13.3" - }, - "uid-number": { - "version": "0.0.6" - }, - "util-deprecate": { - "version": "1.0.2" - }, - "verror": { - "version": "1.3.6" - }, - "wide-align": { - "version": "1.1.0" - }, - "wrappy": { - "version": "1.0.2" - }, - "xtend": { - "version": "4.0.1" - }, - "bl": { - "version": "1.1.2", - "dependencies": { - "readable-stream": { - "version": "2.0.6" - } - } - }, - "dashdash": { - "version": "1.14.0", - "dependencies": { - "assert-plus": { - "version": "1.0.0" - } - } - }, - "getpass": { - "version": "0.1.6", - "dependencies": { - "assert-plus": { - "version": "1.0.0" - } - } - }, - "rc": { - "version": "1.1.6", - "dependencies": { - "minimist": { - "version": "1.2.0" - } - } - }, - "sshpk": { - "version": "1.8.3", - "dependencies": { - "assert-plus": { - "version": "1.0.0" - } - } - } - } } } }, @@ -7490,31 +7134,6 @@ "lodash": { "version": "3.10.1" }, - "log4js": { - "version": "0.6.38", - "dependencies": { - "readable-stream": { - "version": "1.0.34", - "dependencies": { - "core-util-is": { - "version": "1.0.2" - }, - "isarray": { - "version": "0.0.1" - }, - "string_decoder": { - "version": "0.10.31" - }, - "inherits": { - "version": "2.0.1" - } - } - }, - "semver": { - "version": "4.3.6" - } - } - }, "mime": { "version": "1.3.4" }, @@ -8379,6 +7998,31 @@ "lodash": { "version": "2.4.2" }, + "log4js": { + "version": "0.6.38", + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "dependencies": { + "core-util-is": { + "version": "1.0.2" + }, + "isarray": { + "version": "0.0.1" + }, + "string_decoder": { + "version": "0.10.31" + }, + "inherits": { + "version": "2.0.1" + } + } + }, + "semver": { + "version": "4.3.6" + } + } + }, "marked": { "version": "0.3.5" }, @@ -8636,42 +8280,6 @@ } } }, - "selenium-webdriver": { - "version": "2.53.3", - "dependencies": { - "adm-zip": { - "version": "0.4.4" - }, - "rimraf": { - "version": "2.5.4" - }, - "tmp": { - "version": "0.0.24" - }, - "ws": { - "version": "1.1.1", - "dependencies": { - "options": { - "version": "0.0.6" - }, - "ultron": { - "version": "1.0.2" - } - } - }, - "xml2js": { - "version": "0.4.4", - "dependencies": { - "sax": { - "version": "0.6.1" - }, - "xmlbuilder": { - "version": "8.2.2" - } - } - } - } - }, "source-map-support": { "version": "0.4.2", "dependencies": { @@ -9004,6 +8612,90 @@ "sax": { "version": "1.2.1" }, + "selenium-webdriver": { + "version": "2.53.3", + "dependencies": { + "adm-zip": { + "version": "0.4.4" + }, + "rimraf": { + "version": "2.5.4", + "dependencies": { + "glob": { + "version": "7.0.6", + "dependencies": { + "fs.realpath": { + "version": "1.0.0" + }, + "inflight": { + "version": "1.0.5", + "dependencies": { + "wrappy": { + "version": "1.0.2" + } + } + }, + "inherits": { + "version": "2.0.1" + }, + "minimatch": { + "version": "3.0.3", + "dependencies": { + "brace-expansion": { + "version": "1.1.6", + "dependencies": { + "balanced-match": { + "version": "0.4.2" + }, + "concat-map": { + "version": "0.0.1" + } + } + } + } + }, + "once": { + "version": "1.3.3", + "dependencies": { + "wrappy": { + "version": "1.0.2" + } + } + }, + "path-is-absolute": { + "version": "1.0.0" + } + } + } + } + }, + "tmp": { + "version": "0.0.24" + }, + "ws": { + "version": "1.1.1", + "dependencies": { + "options": { + "version": "0.0.6" + }, + "ultron": { + "version": "1.0.2" + } + } + }, + "xml2js": { + "version": "0.4.4", + "dependencies": { + "sax": { + "version": "0.6.1" + }, + "xmlbuilder": { + "version": "8.2.2" + } + } + } + } + }, "semver": { "version": "4.0.3" }, diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index c176d82cb3ec..4023e8f04610 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -3506,90 +3506,90 @@ }, "commitizen": { "version": "2.8.4", - "from": "commitizen@>=2.3.0 <3.0.0", + "from": "https://registry.npmjs.org/commitizen/-/commitizen-2.8.4.tgz", "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-2.8.4.tgz", "dependencies": { "chalk": { "version": "1.1.3", - "from": "chalk@1.1.3", + "from": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "dependencies": { "ansi-styles": { "version": "2.2.1", - "from": "ansi-styles@>=2.2.1 <3.0.0", + "from": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" }, "escape-string-regexp": { "version": "1.0.5", - "from": "escape-string-regexp@>=1.0.2 <2.0.0", + "from": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" }, "has-ansi": { "version": "2.0.0", - "from": "has-ansi@>=2.0.0 <3.0.0", + "from": "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", "dependencies": { "ansi-regex": { "version": "2.0.0", - "from": "ansi-regex@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz" } } }, "strip-ansi": { "version": "3.0.1", - "from": "strip-ansi@>=3.0.0 <4.0.0", + "from": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "dependencies": { "ansi-regex": { "version": "2.0.0", - "from": "ansi-regex@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz" } } }, "supports-color": { "version": "2.0.0", - "from": "supports-color@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" } } }, "cz-conventional-changelog": { "version": "1.1.6", - "from": "cz-conventional-changelog@1.1.6", + "from": "https://registry.npmjs.org/cz-conventional-changelog/-/cz-conventional-changelog-1.1.6.tgz", "resolved": "https://registry.npmjs.org/cz-conventional-changelog/-/cz-conventional-changelog-1.1.6.tgz", "dependencies": { "word-wrap": { "version": "1.1.0", - "from": "word-wrap@>=1.0.3 <2.0.0", + "from": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.1.0.tgz", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.1.0.tgz" } } }, "dedent": { "version": "0.6.0", - "from": "dedent@0.6.0", + "from": "https://registry.npmjs.org/dedent/-/dedent-0.6.0.tgz", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.6.0.tgz" }, "detect-indent": { "version": "4.0.0", - "from": "detect-indent@4.0.0", + "from": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", "dependencies": { "repeating": { "version": "2.0.1", - "from": "repeating@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", "dependencies": { "is-finite": { "version": "1.0.1", - "from": "is-finite@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.1.tgz", "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.1.tgz", "dependencies": { "number-is-nan": { "version": "1.0.0", - "from": "number-is-nan@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" } } @@ -3600,54 +3600,54 @@ }, "find-node-modules": { "version": "1.0.3", - "from": "find-node-modules@1.0.3", + "from": "https://registry.npmjs.org/find-node-modules/-/find-node-modules-1.0.3.tgz", "resolved": "https://registry.npmjs.org/find-node-modules/-/find-node-modules-1.0.3.tgz", "dependencies": { "findup-sync": { "version": "0.2.1", - "from": "findup-sync@>=0.2.1 <0.3.0", + "from": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.2.1.tgz", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.2.1.tgz", "dependencies": { "glob": { "version": "4.3.5", - "from": "glob@>=4.3.0 <4.4.0", + "from": "https://registry.npmjs.org/glob/-/glob-4.3.5.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-4.3.5.tgz", "dependencies": { "inflight": { "version": "1.0.5", - "from": "inflight@>=1.0.4 <2.0.0", + "from": "https://registry.npmjs.org/inflight/-/inflight-1.0.5.tgz", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.5.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "minimatch": { "version": "2.0.10", - "from": "minimatch@>=2.0.1 <3.0.0", + "from": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", "dependencies": { "brace-expansion": { "version": "1.1.6", - "from": "brace-expansion@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", "dependencies": { "balanced-match": { "version": "0.4.2", - "from": "balanced-match@>=0.4.1 <0.5.0", + "from": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" }, "concat-map": { "version": "0.0.1", - "from": "concat-map@0.0.1", + "from": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" } } @@ -3656,12 +3656,12 @@ }, "once": { "version": "1.3.3", - "from": "once@>=1.3.0 <2.0.0", + "from": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } @@ -3672,61 +3672,61 @@ }, "merge": { "version": "1.2.0", - "from": "merge@>=1.2.0 <2.0.0", + "from": "https://registry.npmjs.org/merge/-/merge-1.2.0.tgz", "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.0.tgz" } } }, "find-root": { "version": "1.0.0", - "from": "find-root@1.0.0", + "from": "https://registry.npmjs.org/find-root/-/find-root-1.0.0.tgz", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.0.0.tgz" }, "glob": { "version": "7.0.5", - "from": "glob@7.0.5", + "from": "https://registry.npmjs.org/glob/-/glob-7.0.5.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.5.tgz", "dependencies": { "fs.realpath": { "version": "1.0.0", - "from": "fs.realpath@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" }, "inflight": { "version": "1.0.5", - "from": "inflight@>=1.0.4 <2.0.0", + "from": "https://registry.npmjs.org/inflight/-/inflight-1.0.5.tgz", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.5.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "minimatch": { "version": "3.0.2", - "from": "minimatch@>=3.0.2 <4.0.0", + "from": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.2.tgz", "dependencies": { "brace-expansion": { "version": "1.1.6", - "from": "brace-expansion@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", "dependencies": { "balanced-match": { "version": "0.4.2", - "from": "balanced-match@>=0.4.1 <0.5.0", + "from": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" }, "concat-map": { "version": "0.0.1", - "from": "concat-map@0.0.1", + "from": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" } } @@ -3735,68 +3735,68 @@ }, "once": { "version": "1.3.3", - "from": "once@>=1.3.0 <2.0.0", + "from": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "path-is-absolute": { "version": "1.0.0", - "from": "path-is-absolute@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" } } }, "home-or-tmp": { "version": "2.0.0", - "from": "home-or-tmp@2.0.0", + "from": "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", "dependencies": { "os-homedir": { "version": "1.0.1", - "from": "os-homedir@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.1.tgz", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.1.tgz" }, "os-tmpdir": { "version": "1.0.1", - "from": "os-tmpdir@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.1.tgz", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.1.tgz" } } }, "inquirer": { "version": "1.1.2", - "from": "inquirer@1.1.2", + "from": "https://registry.npmjs.org/inquirer/-/inquirer-1.1.2.tgz", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-1.1.2.tgz", "dependencies": { "ansi-escapes": { "version": "1.4.0", - "from": "ansi-escapes@>=1.1.0 <2.0.0", + "from": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz" }, "cli-cursor": { "version": "1.0.2", - "from": "cli-cursor@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", "dependencies": { "restore-cursor": { "version": "1.0.1", - "from": "restore-cursor@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", "dependencies": { "exit-hook": { "version": "1.1.1", - "from": "exit-hook@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz" }, "onetime": { "version": "1.1.0", - "from": "onetime@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz" } } @@ -3805,67 +3805,67 @@ }, "cli-width": { "version": "2.1.0", - "from": "cli-width@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz" }, "external-editor": { "version": "1.0.3", - "from": "external-editor@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/external-editor/-/external-editor-1.0.3.tgz", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-1.0.3.tgz", "dependencies": { "extend": { "version": "3.0.0", - "from": "extend@>=3.0.0 <4.0.0", + "from": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz" }, "spawn-sync": { "version": "1.0.15", - "from": "spawn-sync@>=1.0.15 <2.0.0", + "from": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz", "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz", "dependencies": { "concat-stream": { "version": "1.5.1", - "from": "concat-stream@>=1.4.7 <2.0.0", + "from": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.1.tgz", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.1.tgz", "dependencies": { "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "typedarray": { "version": "0.0.6", - "from": "typedarray@>=0.0.5 <0.1.0", + "from": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" }, "readable-stream": { "version": "2.0.6", - "from": "readable-stream@>=2.0.0 <2.1.0", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "dependencies": { "core-util-is": { "version": "1.0.2", - "from": "core-util-is@>=1.0.0 <1.1.0", + "from": "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" }, "isarray": { "version": "1.0.0", - "from": "isarray@>=1.0.0 <1.1.0", + "from": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" }, "process-nextick-args": { "version": "1.0.7", - "from": "process-nextick-args@>=1.0.6 <1.1.0", + "from": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", + "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "util-deprecate": { "version": "1.0.2", - "from": "util-deprecate@>=1.0.1 <1.1.0", + "from": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" } } @@ -3874,24 +3874,24 @@ }, "os-shim": { "version": "0.1.3", - "from": "os-shim@>=0.1.2 <0.2.0", + "from": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz", "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz" } } }, "temp": { "version": "0.8.3", - "from": "temp@>=0.8.3 <0.9.0", + "from": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz", "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz", "dependencies": { "os-tmpdir": { "version": "1.0.1", - "from": "os-tmpdir@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.1.tgz", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.1.tgz" }, "rimraf": { "version": "2.2.8", - "from": "rimraf@>=2.2.6 <2.3.0", + "from": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" } } @@ -3900,80 +3900,80 @@ }, "figures": { "version": "1.7.0", - "from": "figures@>=1.3.5 <2.0.0", + "from": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", "dependencies": { "escape-string-regexp": { "version": "1.0.5", - "from": "escape-string-regexp@>=1.0.5 <2.0.0", + "from": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" }, "object-assign": { "version": "4.1.0", - "from": "object-assign@>=4.1.0 <5.0.0", + "from": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz" } } }, "mute-stream": { "version": "0.0.6", - "from": "mute-stream@0.0.6", + "from": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.6.tgz", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.6.tgz" }, "pinkie-promise": { "version": "2.0.1", - "from": "pinkie-promise@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "dependencies": { "pinkie": { "version": "2.0.4", - "from": "pinkie@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" } } }, "run-async": { "version": "2.2.0", - "from": "run-async@>=2.2.0 <3.0.0", + "from": "https://registry.npmjs.org/run-async/-/run-async-2.2.0.tgz", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.2.0.tgz", "dependencies": { "is-promise": { "version": "2.1.0", - "from": "is-promise@>=2.1.0 <3.0.0", + "from": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz" } } }, "rx": { "version": "4.1.0", - "from": "rx@>=4.1.0 <5.0.0", + "from": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz" }, "string-width": { "version": "1.0.1", - "from": "string-width@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/string-width/-/string-width-1.0.1.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.1.tgz", "dependencies": { "code-point-at": { "version": "1.0.0", - "from": "code-point-at@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.0.0.tgz", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.0.0.tgz", "dependencies": { "number-is-nan": { "version": "1.0.0", - "from": "number-is-nan@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" } } }, "is-fullwidth-code-point": { "version": "1.0.0", - "from": "is-fullwidth-code-point@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "dependencies": { "number-is-nan": { "version": "1.0.0", - "from": "number-is-nan@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" } } @@ -3982,46 +3982,46 @@ }, "strip-ansi": { "version": "3.0.1", - "from": "strip-ansi@>=3.0.0 <4.0.0", + "from": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "dependencies": { "ansi-regex": { "version": "2.0.0", - "from": "ansi-regex@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz" } } }, "through": { "version": "2.3.8", - "from": "through@>=2.3.6 <3.0.0", + "from": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" } } }, "lodash": { "version": "4.14.1", - "from": "lodash@4.14.1", + "from": "https://registry.npmjs.org/lodash/-/lodash-4.14.1.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.14.1.tgz" }, "minimist": { "version": "1.2.0", - "from": "minimist@1.2.0", + "from": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" }, "path-exists": { "version": "2.1.0", - "from": "path-exists@2.1.0", + "from": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "dependencies": { "pinkie-promise": { "version": "2.0.1", - "from": "pinkie-promise@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "dependencies": { "pinkie": { "version": "2.0.4", - "from": "pinkie@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" } } @@ -4030,16 +4030,52 @@ }, "shelljs": { "version": "0.5.3", - "from": "shelljs@0.5.3", + "from": "https://registry.npmjs.org/shelljs/-/shelljs-0.5.3.tgz", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.5.3.tgz" }, "strip-json-comments": { "version": "2.0.1", - "from": "strip-json-comments@2.0.1", + "from": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" } } }, + "cross-spawn": { + "version": "4.0.0", + "from": "cross-spawn@latest", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.0.tgz", + "dependencies": { + "lru-cache": { + "version": "4.0.1", + "from": "lru-cache@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.1.tgz", + "dependencies": { + "pseudomap": { + "version": "1.0.2", + "from": "pseudomap@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz" + }, + "yallist": { + "version": "2.0.0", + "from": "yallist@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.0.0.tgz" + } + } + }, + "which": { + "version": "1.2.10", + "from": "which@>=1.2.9 <2.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.2.10.tgz", + "dependencies": { + "isexe": { + "version": "1.1.2", + "from": "isexe@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-1.1.2.tgz" + } + } + } + } + }, "cz-conventional-changelog": { "version": "1.1.4", "from": "https://registry.npmjs.org/cz-conventional-changelog/-/cz-conventional-changelog-1.1.4.tgz", @@ -4145,22 +4181,22 @@ }, "dgeni-packages": { "version": "0.14.0", - "from": "dgeni-packages@>=0.14.0 <0.15.0", + "from": "https://registry.npmjs.org/dgeni-packages/-/dgeni-packages-0.14.0.tgz", "resolved": "https://registry.npmjs.org/dgeni-packages/-/dgeni-packages-0.14.0.tgz", "dependencies": { "catharsis": { "version": "0.8.8", - "from": "catharsis@>=0.8.1 <0.9.0", + "from": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.8.tgz", "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.8.tgz", "dependencies": { "underscore-contrib": { "version": "0.3.0", - "from": "underscore-contrib@>=0.3.0 <0.4.0", + "from": "https://registry.npmjs.org/underscore-contrib/-/underscore-contrib-0.3.0.tgz", "resolved": "https://registry.npmjs.org/underscore-contrib/-/underscore-contrib-0.3.0.tgz", "dependencies": { "underscore": { "version": "1.6.0", - "from": "underscore@1.6.0", + "from": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" } } @@ -4169,185 +4205,185 @@ }, "change-case": { "version": "3.0.0", - "from": "change-case@3.0.0", + "from": "https://registry.npmjs.org/change-case/-/change-case-3.0.0.tgz", "resolved": "https://registry.npmjs.org/change-case/-/change-case-3.0.0.tgz", "dependencies": { "camel-case": { "version": "3.0.0", - "from": "camel-case@>=3.0.0 <4.0.0", + "from": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz" }, "constant-case": { "version": "2.0.0", - "from": "constant-case@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/constant-case/-/constant-case-2.0.0.tgz", "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-2.0.0.tgz" }, "dot-case": { "version": "2.1.0", - "from": "dot-case@>=2.1.0 <3.0.0", + "from": "https://registry.npmjs.org/dot-case/-/dot-case-2.1.0.tgz", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-2.1.0.tgz" }, "header-case": { "version": "1.0.0", - "from": "header-case@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/header-case/-/header-case-1.0.0.tgz", "resolved": "https://registry.npmjs.org/header-case/-/header-case-1.0.0.tgz" }, "is-lower-case": { "version": "1.1.3", - "from": "is-lower-case@>=1.1.0 <2.0.0", + "from": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-1.1.3.tgz", "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-1.1.3.tgz" }, "is-upper-case": { "version": "1.1.2", - "from": "is-upper-case@>=1.1.0 <2.0.0", + "from": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz", "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz" }, "lower-case": { "version": "1.1.3", - "from": "lower-case@>=1.1.1 <2.0.0", + "from": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.3.tgz", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.3.tgz" }, "lower-case-first": { "version": "1.0.2", - "from": "lower-case-first@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-1.0.2.tgz", "resolved": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-1.0.2.tgz" }, "no-case": { "version": "2.3.0", - "from": "no-case@>=2.2.0 <3.0.0", + "from": "https://registry.npmjs.org/no-case/-/no-case-2.3.0.tgz", "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.0.tgz" }, "param-case": { "version": "2.1.0", - "from": "param-case@>=2.1.0 <3.0.0", + "from": "https://registry.npmjs.org/param-case/-/param-case-2.1.0.tgz", "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.0.tgz" }, "pascal-case": { "version": "2.0.0", - "from": "pascal-case@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/pascal-case/-/pascal-case-2.0.0.tgz", "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-2.0.0.tgz" }, "path-case": { "version": "2.1.0", - "from": "path-case@>=2.1.0 <3.0.0", + "from": "https://registry.npmjs.org/path-case/-/path-case-2.1.0.tgz", "resolved": "https://registry.npmjs.org/path-case/-/path-case-2.1.0.tgz" }, "sentence-case": { "version": "2.1.0", - "from": "sentence-case@>=2.1.0 <3.0.0", + "from": "https://registry.npmjs.org/sentence-case/-/sentence-case-2.1.0.tgz", "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-2.1.0.tgz" }, "snake-case": { "version": "2.1.0", - "from": "snake-case@>=2.1.0 <3.0.0", + "from": "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz" }, "swap-case": { "version": "1.1.2", - "from": "swap-case@>=1.1.0 <2.0.0", + "from": "https://registry.npmjs.org/swap-case/-/swap-case-1.1.2.tgz", "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-1.1.2.tgz" }, "title-case": { "version": "2.1.0", - "from": "title-case@>=2.1.0 <3.0.0", + "from": "https://registry.npmjs.org/title-case/-/title-case-2.1.0.tgz", "resolved": "https://registry.npmjs.org/title-case/-/title-case-2.1.0.tgz" }, "upper-case": { "version": "1.1.3", - "from": "upper-case@>=1.1.1 <2.0.0", + "from": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz" }, "upper-case-first": { "version": "1.1.2", - "from": "upper-case-first@>=1.1.0 <2.0.0", + "from": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz", "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz" } } }, "espree": { "version": "2.2.5", - "from": "espree@>=2.2.3 <3.0.0", + "from": "https://registry.npmjs.org/espree/-/espree-2.2.5.tgz", "resolved": "https://registry.npmjs.org/espree/-/espree-2.2.5.tgz" }, "estraverse": { "version": "4.2.0", - "from": "estraverse@>=4.1.0 <5.0.0", + "from": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz" }, "glob": { "version": "7.0.5", - "from": "glob@>=7.0.5 <8.0.0", + "from": "https://registry.npmjs.org/glob/-/glob-7.0.5.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.5.tgz", "dependencies": { "fs.realpath": { "version": "1.0.0", - "from": "fs.realpath@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" }, "inflight": { "version": "1.0.5", - "from": "inflight@>=1.0.4 <2.0.0", + "from": "https://registry.npmjs.org/inflight/-/inflight-1.0.5.tgz", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.5.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "once": { "version": "1.3.3", - "from": "once@>=1.3.0 <2.0.0", + "from": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "path-is-absolute": { "version": "1.0.0", - "from": "path-is-absolute@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" } } }, "htmlparser2": { "version": "3.9.1", - "from": "htmlparser2@>=3.7.3 <4.0.0", + "from": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.1.tgz", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.1.tgz", "dependencies": { "domelementtype": { "version": "1.3.0", - "from": "domelementtype@>=1.3.0 <2.0.0", + "from": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz" }, "domhandler": { "version": "2.3.0", - "from": "domhandler@>=2.3.0 <3.0.0", + "from": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz" }, "domutils": { "version": "1.5.1", - "from": "domutils@>=1.5.1 <2.0.0", + "from": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", "dependencies": { "dom-serializer": { "version": "0.1.0", - "from": "dom-serializer@>=0.0.0 <1.0.0", + "from": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", "dependencies": { "domelementtype": { "version": "1.1.3", - "from": "domelementtype@>=1.1.1 <1.2.0", + "from": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz" } } @@ -4356,47 +4392,47 @@ }, "entities": { "version": "1.1.1", - "from": "entities@>=1.1.1 <2.0.0", + "from": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "readable-stream": { "version": "2.1.4", - "from": "readable-stream@>=2.0.2 <3.0.0", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.4.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.4.tgz", "dependencies": { "buffer-shims": { "version": "1.0.0", - "from": "buffer-shims@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" }, "core-util-is": { "version": "1.0.2", - "from": "core-util-is@>=1.0.0 <1.1.0", + "from": "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" }, "isarray": { "version": "1.0.0", - "from": "isarray@>=1.0.0 <1.1.0", + "from": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" }, "process-nextick-args": { "version": "1.0.7", - "from": "process-nextick-args@>=1.0.6 <1.1.0", + "from": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", + "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "util-deprecate": { "version": "1.0.2", - "from": "util-deprecate@>=1.0.1 <1.1.0", + "from": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" } } @@ -4405,27 +4441,27 @@ }, "lodash": { "version": "4.14.1", - "from": "lodash@>=4.13.1 <5.0.0", + "from": "https://registry.npmjs.org/lodash/-/lodash-4.14.1.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.14.1.tgz" }, "minimatch": { "version": "3.0.2", - "from": "minimatch@>=3.0.2 <4.0.0", + "from": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.2.tgz", "dependencies": { "brace-expansion": { "version": "1.1.6", - "from": "brace-expansion@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", "dependencies": { "balanced-match": { "version": "0.4.2", - "from": "balanced-match@>=0.4.1 <0.5.0", + "from": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" }, "concat-map": { "version": "0.0.1", - "from": "concat-map@0.0.1", + "from": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" } } @@ -4434,81 +4470,81 @@ }, "nunjucks": { "version": "1.3.4", - "from": "nunjucks@>=1.2.0 <2.0.0", + "from": "https://registry.npmjs.org/nunjucks/-/nunjucks-1.3.4.tgz", "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-1.3.4.tgz", "dependencies": { "optimist": { "version": "0.6.1", - "from": "optimist@*", + "from": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", "dependencies": { "wordwrap": { "version": "0.0.3", - "from": "wordwrap@>=0.0.2 <0.1.0", + "from": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz" }, "minimist": { "version": "0.0.10", - "from": "minimist@>=0.0.1 <0.1.0", + "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz" } } }, "chokidar": { "version": "0.12.6", - "from": "chokidar@>=0.12.5 <0.13.0", + "from": "https://registry.npmjs.org/chokidar/-/chokidar-0.12.6.tgz", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-0.12.6.tgz", "dependencies": { "readdirp": { "version": "1.3.0", - "from": "readdirp@>=1.3.0 <1.4.0", + "from": "https://registry.npmjs.org/readdirp/-/readdirp-1.3.0.tgz", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-1.3.0.tgz", "dependencies": { "graceful-fs": { "version": "2.0.3", - "from": "graceful-fs@>=2.0.0 <2.1.0", + "from": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz" }, "minimatch": { "version": "0.2.14", - "from": "minimatch@>=0.2.12 <0.3.0", + "from": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", "dependencies": { "lru-cache": { "version": "2.7.3", - "from": "lru-cache@>=2.0.0 <3.0.0", + "from": "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" }, "sigmund": { "version": "1.0.1", - "from": "sigmund@>=1.0.0 <1.1.0", + "from": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" } } }, "readable-stream": { "version": "1.0.34", - "from": "readable-stream@>=1.0.26-2 <1.1.0", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "dependencies": { "core-util-is": { "version": "1.0.2", - "from": "core-util-is@>=1.0.0 <1.1.0", + "from": "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" }, "isarray": { "version": "0.0.1", - "from": "isarray@0.0.1", + "from": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", + "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } @@ -4517,20 +4553,8 @@ }, "async-each": { "version": "0.1.6", - "from": "async-each@>=0.1.5 <0.2.0", + "from": "https://registry.npmjs.org/async-each/-/async-each-0.1.6.tgz", "resolved": "https://registry.npmjs.org/async-each/-/async-each-0.1.6.tgz" - }, - "fsevents": { - "version": "0.3.8", - "from": "fsevents@>=0.3.1 <0.4.0", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-0.3.8.tgz", - "dependencies": { - "nan": { - "version": "2.4.0", - "from": "nan@>=2.0.2 <3.0.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.4.0.tgz" - } - } } } } @@ -4538,32 +4562,32 @@ }, "q": { "version": "1.4.1", - "from": "q@>=1.4.1 <1.5.0", + "from": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz" }, "semver": { "version": "5.3.0", - "from": "semver@>=5.2.0 <6.0.0", + "from": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz" }, "shelljs": { "version": "0.7.3", - "from": "shelljs@>=0.7.0 <0.8.0", + "from": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.3.tgz", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.3.tgz", "dependencies": { "interpret": { "version": "1.0.1", - "from": "interpret@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/interpret/-/interpret-1.0.1.tgz", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.1.tgz" }, "rechoir": { "version": "0.6.2", - "from": "rechoir@>=0.6.2 <0.7.0", + "from": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", "dependencies": { "resolve": { "version": "1.1.7", - "from": "resolve@>=1.1.6 <2.0.0", + "from": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz" } } @@ -4572,12 +4596,12 @@ }, "spdx-license-list": { "version": "2.1.0", - "from": "spdx-license-list@>=2.1.0 <3.0.0", + "from": "https://registry.npmjs.org/spdx-license-list/-/spdx-license-list-2.1.0.tgz", "resolved": "https://registry.npmjs.org/spdx-license-list/-/spdx-license-list-2.1.0.tgz" }, "typescript": { "version": "1.8.10", - "from": "typescript@>=1.7.5 <2.0.0", + "from": "https://registry.npmjs.org/typescript/-/typescript-1.8.10.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-1.8.10.tgz" } } @@ -6080,103 +6104,103 @@ }, "grunt-eslint": { "version": "19.0.0", - "from": "grunt-eslint@latest", + "from": "https://registry.npmjs.org/grunt-eslint/-/grunt-eslint-19.0.0.tgz", "resolved": "https://registry.npmjs.org/grunt-eslint/-/grunt-eslint-19.0.0.tgz", "dependencies": { "chalk": { "version": "1.1.3", - "from": "chalk@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "dependencies": { "ansi-styles": { "version": "2.2.1", - "from": "ansi-styles@>=2.2.1 <3.0.0", + "from": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" }, "escape-string-regexp": { "version": "1.0.5", - "from": "escape-string-regexp@>=1.0.2 <2.0.0", + "from": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" }, "has-ansi": { "version": "2.0.0", - "from": "has-ansi@>=2.0.0 <3.0.0", + "from": "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", "dependencies": { "ansi-regex": { "version": "2.0.0", - "from": "ansi-regex@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz" } } }, "strip-ansi": { "version": "3.0.1", - "from": "strip-ansi@>=3.0.0 <4.0.0", + "from": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "dependencies": { "ansi-regex": { "version": "2.0.0", - "from": "ansi-regex@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz" } } }, "supports-color": { "version": "2.0.0", - "from": "supports-color@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" } } }, "eslint": { "version": "3.2.2", - "from": "eslint@>=3.0.0 <4.0.0", + "from": "https://registry.npmjs.org/eslint/-/eslint-3.2.2.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.2.2.tgz", "dependencies": { "concat-stream": { "version": "1.5.1", - "from": "concat-stream@>=1.4.6 <2.0.0", + "from": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.1.tgz", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.1.tgz", "dependencies": { "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "typedarray": { "version": "0.0.6", - "from": "typedarray@>=0.0.5 <0.1.0", + "from": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" }, "readable-stream": { "version": "2.0.6", - "from": "readable-stream@>=2.0.0 <2.1.0", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "dependencies": { "core-util-is": { "version": "1.0.2", - "from": "core-util-is@>=1.0.0 <1.1.0", + "from": "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" }, "isarray": { "version": "1.0.0", - "from": "isarray@>=1.0.0 <1.1.0", + "from": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" }, "process-nextick-args": { "version": "1.0.7", - "from": "process-nextick-args@>=1.0.6 <1.1.0", + "from": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", + "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "util-deprecate": { "version": "1.0.2", - "from": "util-deprecate@>=1.0.1 <1.1.0", + "from": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" } } @@ -6185,115 +6209,115 @@ }, "debug": { "version": "2.2.0", - "from": "debug@>=2.1.1 <3.0.0", + "from": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "dependencies": { "ms": { "version": "0.7.1", - "from": "ms@0.7.1", + "from": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" } } }, "doctrine": { "version": "1.2.2", - "from": "doctrine@>=1.2.2 <2.0.0", + "from": "https://registry.npmjs.org/doctrine/-/doctrine-1.2.2.tgz", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.2.2.tgz", "dependencies": { "esutils": { "version": "1.1.6", - "from": "esutils@>=1.1.6 <2.0.0", + "from": "https://registry.npmjs.org/esutils/-/esutils-1.1.6.tgz", "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.1.6.tgz" }, "isarray": { "version": "1.0.0", - "from": "isarray@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" } } }, "escope": { "version": "3.6.0", - "from": "escope@>=3.6.0 <4.0.0", + "from": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", "dependencies": { "es6-map": { "version": "0.1.4", - "from": "es6-map@>=0.1.3 <0.2.0", + "from": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.4.tgz", "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.4.tgz", "dependencies": { "d": { "version": "0.1.1", - "from": "d@>=0.1.1 <0.2.0", + "from": "https://registry.npmjs.org/d/-/d-0.1.1.tgz", "resolved": "https://registry.npmjs.org/d/-/d-0.1.1.tgz" }, "es5-ext": { "version": "0.10.12", - "from": "es5-ext@>=0.10.8 <0.11.0", + "from": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.12.tgz", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.12.tgz" }, "es6-iterator": { "version": "2.0.0", - "from": "es6-iterator@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.0.tgz", "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.0.tgz" }, "es6-set": { "version": "0.1.4", - "from": "es6-set@>=0.1.3 <0.2.0", + "from": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.4.tgz", "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.4.tgz" }, "es6-symbol": { "version": "3.1.0", - "from": "es6-symbol@>=3.1.0 <3.2.0", + "from": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.0.tgz", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.0.tgz" }, "event-emitter": { "version": "0.3.4", - "from": "event-emitter@>=0.3.4 <0.4.0", + "from": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.4.tgz", "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.4.tgz" } } }, "es6-weak-map": { "version": "2.0.1", - "from": "es6-weak-map@>=2.0.1 <3.0.0", + "from": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.1.tgz", "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.1.tgz", "dependencies": { "d": { "version": "0.1.1", - "from": "d@>=0.1.1 <0.2.0", + "from": "https://registry.npmjs.org/d/-/d-0.1.1.tgz", "resolved": "https://registry.npmjs.org/d/-/d-0.1.1.tgz" }, "es5-ext": { "version": "0.10.12", - "from": "es5-ext@>=0.10.8 <0.11.0", + "from": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.12.tgz", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.12.tgz" }, "es6-iterator": { "version": "2.0.0", - "from": "es6-iterator@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.0.tgz", "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.0.tgz" }, "es6-symbol": { "version": "3.1.0", - "from": "es6-symbol@>=3.1.0 <3.2.0", + "from": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.0.tgz", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.0.tgz" } } }, "esrecurse": { "version": "4.1.0", - "from": "esrecurse@>=4.1.0 <5.0.0", + "from": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.1.0.tgz", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.1.0.tgz", "dependencies": { "estraverse": { "version": "4.1.1", - "from": "estraverse@>=4.1.0 <4.2.0", + "from": "https://registry.npmjs.org/estraverse/-/estraverse-4.1.1.tgz", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.1.1.tgz" }, "object-assign": { "version": "4.1.0", - "from": "object-assign@>=4.0.1 <5.0.0", + "from": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz" } } @@ -6302,180 +6326,180 @@ }, "espree": { "version": "3.1.7", - "from": "espree@>=3.1.6 <4.0.0", + "from": "https://registry.npmjs.org/espree/-/espree-3.1.7.tgz", "resolved": "https://registry.npmjs.org/espree/-/espree-3.1.7.tgz", "dependencies": { "acorn": { "version": "3.3.0", - "from": "acorn@>=3.3.0 <4.0.0", + "from": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz" }, "acorn-jsx": { "version": "3.0.1", - "from": "acorn-jsx@>=3.0.0 <4.0.0", + "from": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz" } } }, "estraverse": { "version": "4.2.0", - "from": "estraverse@>=4.2.0 <5.0.0", + "from": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz" }, "esutils": { "version": "2.0.2", - "from": "esutils@>=2.0.2 <3.0.0", + "from": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz" }, "file-entry-cache": { "version": "1.3.1", - "from": "file-entry-cache@>=1.3.1 <2.0.0", + "from": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-1.3.1.tgz", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-1.3.1.tgz", "dependencies": { "flat-cache": { "version": "1.2.1", - "from": "flat-cache@>=1.2.1 <2.0.0", + "from": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.1.tgz", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.1.tgz", "dependencies": { "circular-json": { "version": "0.3.1", - "from": "circular-json@>=0.3.0 <0.4.0", + "from": "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" }, "del": { "version": "2.2.1", - "from": "del@>=2.0.2 <3.0.0", + "from": "https://registry.npmjs.org/del/-/del-2.2.1.tgz", "resolved": "https://registry.npmjs.org/del/-/del-2.2.1.tgz", "dependencies": { "globby": { "version": "5.0.0", - "from": "globby@>=5.0.0 <6.0.0", + "from": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", "dependencies": { "array-union": { "version": "1.0.2", - "from": "array-union@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", "dependencies": { "array-uniq": { "version": "1.0.3", - "from": "array-uniq@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz" } } }, "arrify": { "version": "1.0.1", - "from": "arrify@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz" } } }, "is-path-cwd": { "version": "1.0.0", - "from": "is-path-cwd@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz" }, "is-path-in-cwd": { "version": "1.0.0", - "from": "is-path-in-cwd@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", "dependencies": { "is-path-inside": { "version": "1.0.0", - "from": "is-path-inside@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz" } } }, "pify": { "version": "2.3.0", - "from": "pify@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" }, "pinkie-promise": { "version": "2.0.1", - "from": "pinkie-promise@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "dependencies": { "pinkie": { "version": "2.0.4", - "from": "pinkie@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" } } }, "rimraf": { "version": "2.5.4", - "from": "rimraf@>=2.2.8 <3.0.0", + "from": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz" } } }, "graceful-fs": { "version": "4.1.5", - "from": "graceful-fs@>=4.1.2 <5.0.0", + "from": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.5.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.5.tgz" }, "write": { "version": "0.2.1", - "from": "write@>=0.2.1 <0.3.0", + "from": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz" } } }, "object-assign": { "version": "4.1.0", - "from": "object-assign@>=4.0.1 <5.0.0", + "from": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz" } } }, "glob": { "version": "7.0.5", - "from": "glob@>=7.0.3 <8.0.0", + "from": "https://registry.npmjs.org/glob/-/glob-7.0.5.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.5.tgz", "dependencies": { "fs.realpath": { "version": "1.0.0", - "from": "fs.realpath@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" }, "inflight": { "version": "1.0.5", - "from": "inflight@>=1.0.4 <2.0.0", + "from": "https://registry.npmjs.org/inflight/-/inflight-1.0.5.tgz", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.5.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "minimatch": { "version": "3.0.2", - "from": "minimatch@>=3.0.2 <4.0.0", + "from": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.2.tgz", "dependencies": { "brace-expansion": { "version": "1.1.6", - "from": "brace-expansion@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", "dependencies": { "balanced-match": { "version": "0.4.2", - "from": "balanced-match@>=0.4.1 <0.5.0", + "from": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" }, "concat-map": { "version": "0.0.1", - "from": "concat-map@0.0.1", + "from": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" } } @@ -6484,71 +6508,71 @@ }, "once": { "version": "1.3.3", - "from": "once@>=1.3.0 <2.0.0", + "from": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "path-is-absolute": { "version": "1.0.0", - "from": "path-is-absolute@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" } } }, "globals": { "version": "9.9.0", - "from": "globals@>=9.2.0 <10.0.0", + "from": "https://registry.npmjs.org/globals/-/globals-9.9.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-9.9.0.tgz" }, "ignore": { "version": "3.1.3", - "from": "ignore@>=3.1.2 <4.0.0", + "from": "https://registry.npmjs.org/ignore/-/ignore-3.1.3.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.1.3.tgz" }, "imurmurhash": { "version": "0.1.4", - "from": "imurmurhash@>=0.1.4 <0.2.0", + "from": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" }, "inquirer": { "version": "0.12.0", - "from": "inquirer@>=0.12.0 <0.13.0", + "from": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", "dependencies": { "ansi-escapes": { "version": "1.4.0", - "from": "ansi-escapes@>=1.1.0 <2.0.0", + "from": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz" }, "ansi-regex": { "version": "2.0.0", - "from": "ansi-regex@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz" }, "cli-cursor": { "version": "1.0.2", - "from": "cli-cursor@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", "dependencies": { "restore-cursor": { "version": "1.0.1", - "from": "restore-cursor@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", "dependencies": { "exit-hook": { "version": "1.1.1", - "from": "exit-hook@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz" }, "onetime": { "version": "1.1.0", - "from": "onetime@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz" } } @@ -6557,75 +6581,75 @@ }, "cli-width": { "version": "2.1.0", - "from": "cli-width@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz" }, "figures": { "version": "1.7.0", - "from": "figures@>=1.3.5 <2.0.0", + "from": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", "dependencies": { "escape-string-regexp": { "version": "1.0.5", - "from": "escape-string-regexp@>=1.0.5 <2.0.0", + "from": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" }, "object-assign": { "version": "4.1.0", - "from": "object-assign@>=4.1.0 <5.0.0", + "from": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz" } } }, "readline2": { "version": "1.0.1", - "from": "readline2@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", "dependencies": { "code-point-at": { "version": "1.0.0", - "from": "code-point-at@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.0.0.tgz", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.0.0.tgz", "dependencies": { "number-is-nan": { "version": "1.0.0", - "from": "number-is-nan@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" } } }, "is-fullwidth-code-point": { "version": "1.0.0", - "from": "is-fullwidth-code-point@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "dependencies": { "number-is-nan": { "version": "1.0.0", - "from": "number-is-nan@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" } } }, "mute-stream": { "version": "0.0.5", - "from": "mute-stream@0.0.5", + "from": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz" } } }, "run-async": { "version": "0.1.0", - "from": "run-async@>=0.1.0 <0.2.0", + "from": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", "dependencies": { "once": { "version": "1.3.3", - "from": "once@>=1.3.0 <2.0.0", + "from": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } @@ -6634,34 +6658,34 @@ }, "rx-lite": { "version": "3.1.2", - "from": "rx-lite@>=3.1.2 <4.0.0", + "from": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz" }, "string-width": { "version": "1.0.1", - "from": "string-width@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/string-width/-/string-width-1.0.1.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.1.tgz", "dependencies": { "code-point-at": { "version": "1.0.0", - "from": "code-point-at@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.0.0.tgz", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.0.0.tgz", "dependencies": { "number-is-nan": { "version": "1.0.0", - "from": "number-is-nan@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" } } }, "is-fullwidth-code-point": { "version": "1.0.0", - "from": "is-fullwidth-code-point@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "dependencies": { "number-is-nan": { "version": "1.0.0", - "from": "number-is-nan@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" } } @@ -6670,258 +6694,258 @@ }, "strip-ansi": { "version": "3.0.1", - "from": "strip-ansi@>=3.0.0 <4.0.0", + "from": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" }, "through": { "version": "2.3.8", - "from": "through@>=2.3.6 <3.0.0", + "from": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" } } }, "is-my-json-valid": { "version": "2.13.1", - "from": "is-my-json-valid@>=2.10.0 <3.0.0", + "from": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.13.1.tgz", "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.13.1.tgz", "dependencies": { "generate-function": { "version": "2.0.0", - "from": "generate-function@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz" }, "generate-object-property": { "version": "1.2.0", - "from": "generate-object-property@>=1.1.0 <2.0.0", + "from": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", "dependencies": { "is-property": { "version": "1.0.2", - "from": "is-property@>=1.0.0 <2.0.0", + "from": "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" } } }, "jsonpointer": { "version": "2.0.0", - "from": "jsonpointer@2.0.0", + "from": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-2.0.0.tgz", "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-2.0.0.tgz" }, "xtend": { "version": "4.0.1", - "from": "xtend@>=4.0.0 <5.0.0", + "from": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" } } }, "is-resolvable": { "version": "1.0.0", - "from": "is-resolvable@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", "dependencies": { "tryit": { "version": "1.0.2", - "from": "tryit@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/tryit/-/tryit-1.0.2.tgz", "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.2.tgz" } } }, "js-yaml": { "version": "3.6.1", - "from": "js-yaml@>=3.5.1 <4.0.0", + "from": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.6.1.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.6.1.tgz", "dependencies": { "argparse": { "version": "1.0.7", - "from": "argparse@>=1.0.7 <2.0.0", + "from": "https://registry.npmjs.org/argparse/-/argparse-1.0.7.tgz", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.7.tgz", "dependencies": { "sprintf-js": { "version": "1.0.3", - "from": "sprintf-js@>=1.0.2 <1.1.0", + "from": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" } } }, "esprima": { "version": "2.7.2", - "from": "esprima@>=2.6.0 <3.0.0", + "from": "https://registry.npmjs.org/esprima/-/esprima-2.7.2.tgz", "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.2.tgz" } } }, "json-stable-stringify": { "version": "1.0.1", - "from": "json-stable-stringify@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", "dependencies": { "jsonify": { "version": "0.0.0", - "from": "jsonify@>=0.0.0 <0.1.0", + "from": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" } } }, "levn": { "version": "0.3.0", - "from": "levn@>=0.3.0 <0.4.0", + "from": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "dependencies": { "prelude-ls": { "version": "1.1.2", - "from": "prelude-ls@>=1.1.2 <1.2.0", + "from": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" }, "type-check": { "version": "0.3.2", - "from": "type-check@>=0.3.2 <0.4.0", + "from": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz" } } }, "lodash": { "version": "4.14.1", - "from": "lodash@>=4.0.0 <5.0.0", + "from": "https://registry.npmjs.org/lodash/-/lodash-4.14.1.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.14.1.tgz" }, "mkdirp": { "version": "0.5.1", - "from": "mkdirp@>=0.5.0 <0.6.0", + "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "dependencies": { "minimist": { "version": "0.0.8", - "from": "minimist@0.0.8", + "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" } } }, "optionator": { "version": "0.8.1", - "from": "optionator@>=0.8.1 <0.9.0", + "from": "https://registry.npmjs.org/optionator/-/optionator-0.8.1.tgz", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.1.tgz", "dependencies": { "prelude-ls": { "version": "1.1.2", - "from": "prelude-ls@>=1.1.2 <1.2.0", + "from": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" }, "deep-is": { "version": "0.1.3", - "from": "deep-is@>=0.1.3 <0.2.0", + "from": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz" }, "wordwrap": { "version": "1.0.0", - "from": "wordwrap@>=1.0.0 <1.1.0", + "from": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz" }, "type-check": { "version": "0.3.2", - "from": "type-check@>=0.3.2 <0.4.0", + "from": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz" }, "fast-levenshtein": { "version": "1.1.4", - "from": "fast-levenshtein@>=1.1.0 <2.0.0", + "from": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz" } } }, "path-is-inside": { "version": "1.0.1", - "from": "path-is-inside@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.1.tgz", "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.1.tgz" }, "pluralize": { "version": "1.2.1", - "from": "pluralize@>=1.2.1 <2.0.0", + "from": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz" }, "progress": { "version": "1.1.8", - "from": "progress@>=1.1.8 <2.0.0", + "from": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz" }, "require-uncached": { "version": "1.0.2", - "from": "require-uncached@>=1.0.2 <2.0.0", + "from": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.2.tgz", "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.2.tgz", "dependencies": { "caller-path": { "version": "0.1.0", - "from": "caller-path@>=0.1.0 <0.2.0", + "from": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", "dependencies": { "callsites": { "version": "0.2.0", - "from": "callsites@>=0.2.0 <0.3.0", + "from": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz" } } }, "resolve-from": { "version": "1.0.1", - "from": "resolve-from@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz" } } }, "shelljs": { "version": "0.6.0", - "from": "shelljs@>=0.6.0 <0.7.0", + "from": "https://registry.npmjs.org/shelljs/-/shelljs-0.6.0.tgz", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.6.0.tgz" }, "strip-bom": { "version": "3.0.0", - "from": "strip-bom@>=3.0.0 <4.0.0", + "from": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" }, "strip-json-comments": { "version": "1.0.4", - "from": "strip-json-comments@>=1.0.1 <1.1.0", + "from": "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-1.0.4.tgz" }, "table": { "version": "3.7.8", - "from": "table@>=3.7.8 <4.0.0", + "from": "https://registry.npmjs.org/table/-/table-3.7.8.tgz", "resolved": "https://registry.npmjs.org/table/-/table-3.7.8.tgz", "dependencies": { "bluebird": { "version": "3.4.1", - "from": "bluebird@>=3.1.1 <4.0.0", + "from": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.1.tgz", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.1.tgz" }, "slice-ansi": { "version": "0.0.4", - "from": "slice-ansi@0.0.4", + "from": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz" }, "string-width": { "version": "1.0.1", - "from": "string-width@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/string-width/-/string-width-1.0.1.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.1.tgz", "dependencies": { "code-point-at": { "version": "1.0.0", - "from": "code-point-at@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.0.0.tgz", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.0.0.tgz", "dependencies": { "number-is-nan": { "version": "1.0.0", - "from": "number-is-nan@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" } } }, "is-fullwidth-code-point": { "version": "1.0.0", - "from": "is-fullwidth-code-point@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "dependencies": { "number-is-nan": { "version": "1.0.0", - "from": "number-is-nan@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" } } @@ -6930,41 +6954,41 @@ }, "strip-ansi": { "version": "3.0.1", - "from": "strip-ansi@>=3.0.0 <4.0.0", + "from": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "dependencies": { "ansi-regex": { "version": "2.0.0", - "from": "ansi-regex@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz" } } }, "tv4": { "version": "1.2.7", - "from": "tv4@>=1.2.7 <2.0.0", + "from": "https://registry.npmjs.org/tv4/-/tv4-1.2.7.tgz", "resolved": "https://registry.npmjs.org/tv4/-/tv4-1.2.7.tgz" }, "xregexp": { "version": "3.1.1", - "from": "xregexp@>=3.0.0 <4.0.0", + "from": "https://registry.npmjs.org/xregexp/-/xregexp-3.1.1.tgz", "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-3.1.1.tgz" } } }, "text-table": { "version": "0.2.0", - "from": "text-table@>=0.2.0 <0.3.0", + "from": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" }, "user-home": { "version": "2.0.0", - "from": "user-home@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", "dependencies": { "os-homedir": { "version": "1.0.1", - "from": "os-homedir@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.1.tgz", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.1.tgz" } } @@ -7608,52 +7632,52 @@ }, "gulp-eslint": { "version": "3.0.1", - "from": "gulp-eslint@latest", + "from": "https://registry.npmjs.org/gulp-eslint/-/gulp-eslint-3.0.1.tgz", "resolved": "https://registry.npmjs.org/gulp-eslint/-/gulp-eslint-3.0.1.tgz", "dependencies": { "bufferstreams": { "version": "1.1.1", - "from": "bufferstreams@>=1.1.1 <2.0.0", + "from": "https://registry.npmjs.org/bufferstreams/-/bufferstreams-1.1.1.tgz", "resolved": "https://registry.npmjs.org/bufferstreams/-/bufferstreams-1.1.1.tgz", "dependencies": { "readable-stream": { "version": "2.1.4", - "from": "readable-stream@>=2.0.2 <3.0.0", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.4.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.4.tgz", "dependencies": { "buffer-shims": { "version": "1.0.0", - "from": "buffer-shims@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" }, "core-util-is": { "version": "1.0.2", - "from": "core-util-is@>=1.0.0 <1.1.0", + "from": "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" }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "isarray": { "version": "1.0.0", - "from": "isarray@>=1.0.0 <1.1.0", + "from": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" }, "process-nextick-args": { "version": "1.0.7", - "from": "process-nextick-args@>=1.0.6 <1.1.0", + "from": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", + "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "util-deprecate": { "version": "1.0.2", - "from": "util-deprecate@>=1.0.1 <1.1.0", + "from": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" } } @@ -7662,98 +7686,98 @@ }, "eslint": { "version": "3.2.2", - "from": "eslint@>=3.0.0 <4.0.0", + "from": "https://registry.npmjs.org/eslint/-/eslint-3.2.2.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.2.2.tgz", "dependencies": { "chalk": { "version": "1.1.3", - "from": "chalk@>=1.1.3 <2.0.0", + "from": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "dependencies": { "ansi-styles": { "version": "2.2.1", - "from": "ansi-styles@>=2.2.1 <3.0.0", + "from": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" }, "escape-string-regexp": { "version": "1.0.5", - "from": "escape-string-regexp@>=1.0.2 <2.0.0", + "from": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" }, "has-ansi": { "version": "2.0.0", - "from": "has-ansi@>=2.0.0 <3.0.0", + "from": "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", "dependencies": { "ansi-regex": { "version": "2.0.0", - "from": "ansi-regex@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz" } } }, "strip-ansi": { "version": "3.0.1", - "from": "strip-ansi@>=3.0.0 <4.0.0", + "from": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "dependencies": { "ansi-regex": { "version": "2.0.0", - "from": "ansi-regex@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz" } } }, "supports-color": { "version": "2.0.0", - "from": "supports-color@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" } } }, "concat-stream": { "version": "1.5.1", - "from": "concat-stream@>=1.4.6 <2.0.0", + "from": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.1.tgz", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.1.tgz", "dependencies": { "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "typedarray": { "version": "0.0.6", - "from": "typedarray@>=0.0.5 <0.1.0", + "from": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" }, "readable-stream": { "version": "2.0.6", - "from": "readable-stream@>=2.0.0 <2.1.0", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "dependencies": { "core-util-is": { "version": "1.0.2", - "from": "core-util-is@>=1.0.0 <1.1.0", + "from": "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" }, "isarray": { "version": "1.0.0", - "from": "isarray@>=1.0.0 <1.1.0", + "from": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" }, "process-nextick-args": { "version": "1.0.7", - "from": "process-nextick-args@>=1.0.6 <1.1.0", + "from": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", + "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "util-deprecate": { "version": "1.0.2", - "from": "util-deprecate@>=1.0.1 <1.1.0", + "from": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" } } @@ -7762,115 +7786,115 @@ }, "debug": { "version": "2.2.0", - "from": "debug@>=2.1.1 <3.0.0", + "from": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "dependencies": { "ms": { "version": "0.7.1", - "from": "ms@0.7.1", + "from": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" } } }, "doctrine": { "version": "1.2.2", - "from": "doctrine@>=1.2.2 <2.0.0", + "from": "https://registry.npmjs.org/doctrine/-/doctrine-1.2.2.tgz", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.2.2.tgz", "dependencies": { "esutils": { "version": "1.1.6", - "from": "esutils@>=1.1.6 <2.0.0", + "from": "https://registry.npmjs.org/esutils/-/esutils-1.1.6.tgz", "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.1.6.tgz" }, "isarray": { "version": "1.0.0", - "from": "isarray@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" } } }, "escope": { "version": "3.6.0", - "from": "escope@>=3.6.0 <4.0.0", + "from": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", "dependencies": { "es6-map": { "version": "0.1.4", - "from": "es6-map@>=0.1.3 <0.2.0", + "from": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.4.tgz", "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.4.tgz", "dependencies": { "d": { "version": "0.1.1", - "from": "d@>=0.1.1 <0.2.0", + "from": "https://registry.npmjs.org/d/-/d-0.1.1.tgz", "resolved": "https://registry.npmjs.org/d/-/d-0.1.1.tgz" }, "es5-ext": { "version": "0.10.12", - "from": "es5-ext@>=0.10.8 <0.11.0", + "from": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.12.tgz", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.12.tgz" }, "es6-iterator": { "version": "2.0.0", - "from": "es6-iterator@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.0.tgz", "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.0.tgz" }, "es6-set": { "version": "0.1.4", - "from": "es6-set@>=0.1.3 <0.2.0", + "from": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.4.tgz", "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.4.tgz" }, "es6-symbol": { "version": "3.1.0", - "from": "es6-symbol@>=3.1.0 <3.2.0", + "from": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.0.tgz", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.0.tgz" }, "event-emitter": { "version": "0.3.4", - "from": "event-emitter@>=0.3.4 <0.4.0", + "from": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.4.tgz", "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.4.tgz" } } }, "es6-weak-map": { "version": "2.0.1", - "from": "es6-weak-map@>=2.0.1 <3.0.0", + "from": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.1.tgz", "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.1.tgz", "dependencies": { "d": { "version": "0.1.1", - "from": "d@>=0.1.1 <0.2.0", + "from": "https://registry.npmjs.org/d/-/d-0.1.1.tgz", "resolved": "https://registry.npmjs.org/d/-/d-0.1.1.tgz" }, "es5-ext": { "version": "0.10.12", - "from": "es5-ext@>=0.10.8 <0.11.0", + "from": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.12.tgz", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.12.tgz" }, "es6-iterator": { "version": "2.0.0", - "from": "es6-iterator@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.0.tgz", "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.0.tgz" }, "es6-symbol": { "version": "3.1.0", - "from": "es6-symbol@>=3.1.0 <3.2.0", + "from": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.0.tgz", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.0.tgz" } } }, "esrecurse": { "version": "4.1.0", - "from": "esrecurse@>=4.1.0 <5.0.0", + "from": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.1.0.tgz", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.1.0.tgz", "dependencies": { "estraverse": { "version": "4.1.1", - "from": "estraverse@>=4.1.0 <4.2.0", + "from": "https://registry.npmjs.org/estraverse/-/estraverse-4.1.1.tgz", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.1.1.tgz" }, "object-assign": { "version": "4.1.0", - "from": "object-assign@>=4.0.1 <5.0.0", + "from": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz" } } @@ -7879,180 +7903,180 @@ }, "espree": { "version": "3.1.7", - "from": "espree@>=3.1.6 <4.0.0", + "from": "https://registry.npmjs.org/espree/-/espree-3.1.7.tgz", "resolved": "https://registry.npmjs.org/espree/-/espree-3.1.7.tgz", "dependencies": { "acorn": { "version": "3.3.0", - "from": "acorn@>=3.3.0 <4.0.0", + "from": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz" }, "acorn-jsx": { "version": "3.0.1", - "from": "acorn-jsx@>=3.0.0 <4.0.0", + "from": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz" } } }, "estraverse": { "version": "4.2.0", - "from": "estraverse@>=4.2.0 <5.0.0", + "from": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz" }, "esutils": { "version": "2.0.2", - "from": "esutils@>=2.0.2 <3.0.0", + "from": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz" }, "file-entry-cache": { "version": "1.3.1", - "from": "file-entry-cache@>=1.3.1 <2.0.0", + "from": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-1.3.1.tgz", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-1.3.1.tgz", "dependencies": { "flat-cache": { "version": "1.2.1", - "from": "flat-cache@>=1.2.1 <2.0.0", + "from": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.1.tgz", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.1.tgz", "dependencies": { "circular-json": { "version": "0.3.1", - "from": "circular-json@>=0.3.0 <0.4.0", + "from": "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" }, "del": { "version": "2.2.1", - "from": "del@>=2.0.2 <3.0.0", + "from": "https://registry.npmjs.org/del/-/del-2.2.1.tgz", "resolved": "https://registry.npmjs.org/del/-/del-2.2.1.tgz", "dependencies": { "globby": { "version": "5.0.0", - "from": "globby@>=5.0.0 <6.0.0", + "from": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", "dependencies": { "array-union": { "version": "1.0.2", - "from": "array-union@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", "dependencies": { "array-uniq": { "version": "1.0.3", - "from": "array-uniq@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz" } } }, "arrify": { "version": "1.0.1", - "from": "arrify@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz" } } }, "is-path-cwd": { "version": "1.0.0", - "from": "is-path-cwd@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz" }, "is-path-in-cwd": { "version": "1.0.0", - "from": "is-path-in-cwd@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", "dependencies": { "is-path-inside": { "version": "1.0.0", - "from": "is-path-inside@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz" } } }, "pify": { "version": "2.3.0", - "from": "pify@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" }, "pinkie-promise": { "version": "2.0.1", - "from": "pinkie-promise@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "dependencies": { "pinkie": { "version": "2.0.4", - "from": "pinkie@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" } } }, "rimraf": { "version": "2.5.4", - "from": "rimraf@>=2.2.8 <3.0.0", + "from": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz" } } }, "graceful-fs": { "version": "4.1.5", - "from": "graceful-fs@>=4.1.2 <5.0.0", + "from": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.5.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.5.tgz" }, "write": { "version": "0.2.1", - "from": "write@>=0.2.1 <0.3.0", + "from": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz" } } }, "object-assign": { "version": "4.1.0", - "from": "object-assign@>=4.0.1 <5.0.0", + "from": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz" } } }, "glob": { "version": "7.0.5", - "from": "glob@>=7.0.3 <8.0.0", + "from": "https://registry.npmjs.org/glob/-/glob-7.0.5.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.5.tgz", "dependencies": { "fs.realpath": { "version": "1.0.0", - "from": "fs.realpath@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" }, "inflight": { "version": "1.0.5", - "from": "inflight@>=1.0.4 <2.0.0", + "from": "https://registry.npmjs.org/inflight/-/inflight-1.0.5.tgz", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.5.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "minimatch": { "version": "3.0.2", - "from": "minimatch@>=3.0.2 <4.0.0", + "from": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.2.tgz", "dependencies": { "brace-expansion": { "version": "1.1.6", - "from": "brace-expansion@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", "dependencies": { "balanced-match": { "version": "0.4.2", - "from": "balanced-match@>=0.4.1 <0.5.0", + "from": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" }, "concat-map": { "version": "0.0.1", - "from": "concat-map@0.0.1", + "from": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" } } @@ -8061,71 +8085,71 @@ }, "once": { "version": "1.3.3", - "from": "once@>=1.3.0 <2.0.0", + "from": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "path-is-absolute": { "version": "1.0.0", - "from": "path-is-absolute@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" } } }, "globals": { "version": "9.9.0", - "from": "globals@>=9.2.0 <10.0.0", + "from": "https://registry.npmjs.org/globals/-/globals-9.9.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-9.9.0.tgz" }, "ignore": { "version": "3.1.3", - "from": "ignore@>=3.1.2 <4.0.0", + "from": "https://registry.npmjs.org/ignore/-/ignore-3.1.3.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.1.3.tgz" }, "imurmurhash": { "version": "0.1.4", - "from": "imurmurhash@>=0.1.4 <0.2.0", + "from": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" }, "inquirer": { "version": "0.12.0", - "from": "inquirer@>=0.12.0 <0.13.0", + "from": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", "dependencies": { "ansi-escapes": { "version": "1.4.0", - "from": "ansi-escapes@>=1.1.0 <2.0.0", + "from": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz" }, "ansi-regex": { "version": "2.0.0", - "from": "ansi-regex@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz" }, "cli-cursor": { "version": "1.0.2", - "from": "cli-cursor@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", "dependencies": { "restore-cursor": { "version": "1.0.1", - "from": "restore-cursor@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", "dependencies": { "exit-hook": { "version": "1.1.1", - "from": "exit-hook@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz" }, "onetime": { "version": "1.1.0", - "from": "onetime@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz" } } @@ -8134,75 +8158,75 @@ }, "cli-width": { "version": "2.1.0", - "from": "cli-width@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz" }, "figures": { "version": "1.7.0", - "from": "figures@>=1.3.5 <2.0.0", + "from": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", "dependencies": { "escape-string-regexp": { "version": "1.0.5", - "from": "escape-string-regexp@>=1.0.5 <2.0.0", + "from": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" }, "object-assign": { "version": "4.1.0", - "from": "object-assign@>=4.1.0 <5.0.0", + "from": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz" } } }, "readline2": { "version": "1.0.1", - "from": "readline2@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", "dependencies": { "code-point-at": { "version": "1.0.0", - "from": "code-point-at@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.0.0.tgz", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.0.0.tgz", "dependencies": { "number-is-nan": { "version": "1.0.0", - "from": "number-is-nan@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" } } }, "is-fullwidth-code-point": { "version": "1.0.0", - "from": "is-fullwidth-code-point@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "dependencies": { "number-is-nan": { "version": "1.0.0", - "from": "number-is-nan@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" } } }, "mute-stream": { "version": "0.0.5", - "from": "mute-stream@0.0.5", + "from": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz" } } }, "run-async": { "version": "0.1.0", - "from": "run-async@>=0.1.0 <0.2.0", + "from": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", "dependencies": { "once": { "version": "1.3.3", - "from": "once@>=1.3.0 <2.0.0", + "from": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } @@ -8211,34 +8235,34 @@ }, "rx-lite": { "version": "3.1.2", - "from": "rx-lite@>=3.1.2 <4.0.0", + "from": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz" }, "string-width": { "version": "1.0.1", - "from": "string-width@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/string-width/-/string-width-1.0.1.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.1.tgz", "dependencies": { "code-point-at": { "version": "1.0.0", - "from": "code-point-at@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.0.0.tgz", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.0.0.tgz", "dependencies": { "number-is-nan": { "version": "1.0.0", - "from": "number-is-nan@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" } } }, "is-fullwidth-code-point": { "version": "1.0.0", - "from": "is-fullwidth-code-point@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "dependencies": { "number-is-nan": { "version": "1.0.0", - "from": "number-is-nan@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" } } @@ -8247,258 +8271,258 @@ }, "strip-ansi": { "version": "3.0.1", - "from": "strip-ansi@>=3.0.0 <4.0.0", + "from": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" }, "through": { "version": "2.3.8", - "from": "through@>=2.3.6 <3.0.0", + "from": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" } } }, "is-my-json-valid": { "version": "2.13.1", - "from": "is-my-json-valid@>=2.10.0 <3.0.0", + "from": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.13.1.tgz", "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.13.1.tgz", "dependencies": { "generate-function": { "version": "2.0.0", - "from": "generate-function@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz" }, "generate-object-property": { "version": "1.2.0", - "from": "generate-object-property@>=1.1.0 <2.0.0", + "from": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", "dependencies": { "is-property": { "version": "1.0.2", - "from": "is-property@>=1.0.0 <2.0.0", + "from": "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" } } }, "jsonpointer": { "version": "2.0.0", - "from": "jsonpointer@2.0.0", + "from": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-2.0.0.tgz", "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-2.0.0.tgz" }, "xtend": { "version": "4.0.1", - "from": "xtend@>=4.0.0 <5.0.0", + "from": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" } } }, "is-resolvable": { "version": "1.0.0", - "from": "is-resolvable@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", "dependencies": { "tryit": { "version": "1.0.2", - "from": "tryit@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/tryit/-/tryit-1.0.2.tgz", "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.2.tgz" } } }, "js-yaml": { "version": "3.6.1", - "from": "js-yaml@>=3.5.1 <4.0.0", + "from": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.6.1.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.6.1.tgz", "dependencies": { "argparse": { "version": "1.0.7", - "from": "argparse@>=1.0.7 <2.0.0", + "from": "https://registry.npmjs.org/argparse/-/argparse-1.0.7.tgz", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.7.tgz", "dependencies": { "sprintf-js": { "version": "1.0.3", - "from": "sprintf-js@>=1.0.2 <1.1.0", + "from": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" } } }, "esprima": { "version": "2.7.2", - "from": "esprima@>=2.6.0 <3.0.0", + "from": "https://registry.npmjs.org/esprima/-/esprima-2.7.2.tgz", "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.2.tgz" } } }, "json-stable-stringify": { "version": "1.0.1", - "from": "json-stable-stringify@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", "dependencies": { "jsonify": { "version": "0.0.0", - "from": "jsonify@>=0.0.0 <0.1.0", + "from": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" } } }, "levn": { "version": "0.3.0", - "from": "levn@>=0.3.0 <0.4.0", + "from": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "dependencies": { "prelude-ls": { "version": "1.1.2", - "from": "prelude-ls@>=1.1.2 <1.2.0", + "from": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" }, "type-check": { "version": "0.3.2", - "from": "type-check@>=0.3.2 <0.4.0", + "from": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz" } } }, "lodash": { "version": "4.14.1", - "from": "lodash@>=4.0.0 <5.0.0", + "from": "https://registry.npmjs.org/lodash/-/lodash-4.14.1.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.14.1.tgz" }, "mkdirp": { "version": "0.5.1", - "from": "mkdirp@>=0.5.0 <0.6.0", + "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "dependencies": { "minimist": { "version": "0.0.8", - "from": "minimist@0.0.8", + "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" } } }, "optionator": { "version": "0.8.1", - "from": "optionator@>=0.8.1 <0.9.0", + "from": "https://registry.npmjs.org/optionator/-/optionator-0.8.1.tgz", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.1.tgz", "dependencies": { "prelude-ls": { "version": "1.1.2", - "from": "prelude-ls@>=1.1.2 <1.2.0", + "from": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" }, "deep-is": { "version": "0.1.3", - "from": "deep-is@>=0.1.3 <0.2.0", + "from": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz" }, "wordwrap": { "version": "1.0.0", - "from": "wordwrap@>=1.0.0 <1.1.0", + "from": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz" }, "type-check": { "version": "0.3.2", - "from": "type-check@>=0.3.2 <0.4.0", + "from": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz" }, "fast-levenshtein": { "version": "1.1.4", - "from": "fast-levenshtein@>=1.1.0 <2.0.0", + "from": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz" } } }, "path-is-inside": { "version": "1.0.1", - "from": "path-is-inside@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.1.tgz", "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.1.tgz" }, "pluralize": { "version": "1.2.1", - "from": "pluralize@>=1.2.1 <2.0.0", + "from": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz" }, "progress": { "version": "1.1.8", - "from": "progress@>=1.1.8 <2.0.0", + "from": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz" }, "require-uncached": { "version": "1.0.2", - "from": "require-uncached@>=1.0.2 <2.0.0", + "from": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.2.tgz", "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.2.tgz", "dependencies": { "caller-path": { "version": "0.1.0", - "from": "caller-path@>=0.1.0 <0.2.0", + "from": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", "dependencies": { "callsites": { "version": "0.2.0", - "from": "callsites@>=0.2.0 <0.3.0", + "from": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz" } } }, "resolve-from": { "version": "1.0.1", - "from": "resolve-from@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz" } } }, "shelljs": { "version": "0.6.0", - "from": "shelljs@>=0.6.0 <0.7.0", + "from": "https://registry.npmjs.org/shelljs/-/shelljs-0.6.0.tgz", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.6.0.tgz" }, "strip-bom": { "version": "3.0.0", - "from": "strip-bom@>=3.0.0 <4.0.0", + "from": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" }, "strip-json-comments": { "version": "1.0.4", - "from": "strip-json-comments@>=1.0.1 <1.1.0", + "from": "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-1.0.4.tgz" }, "table": { "version": "3.7.8", - "from": "table@>=3.7.8 <4.0.0", + "from": "https://registry.npmjs.org/table/-/table-3.7.8.tgz", "resolved": "https://registry.npmjs.org/table/-/table-3.7.8.tgz", "dependencies": { "bluebird": { "version": "3.4.1", - "from": "bluebird@>=3.1.1 <4.0.0", + "from": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.1.tgz", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.1.tgz" }, "slice-ansi": { "version": "0.0.4", - "from": "slice-ansi@0.0.4", + "from": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz" }, "string-width": { "version": "1.0.1", - "from": "string-width@>=1.0.1 <2.0.0", + "from": "https://registry.npmjs.org/string-width/-/string-width-1.0.1.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.1.tgz", "dependencies": { "code-point-at": { "version": "1.0.0", - "from": "code-point-at@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.0.0.tgz", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.0.0.tgz", "dependencies": { "number-is-nan": { "version": "1.0.0", - "from": "number-is-nan@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" } } }, "is-fullwidth-code-point": { "version": "1.0.0", - "from": "is-fullwidth-code-point@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "dependencies": { "number-is-nan": { "version": "1.0.0", - "from": "number-is-nan@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" } } @@ -8507,41 +8531,41 @@ }, "strip-ansi": { "version": "3.0.1", - "from": "strip-ansi@>=3.0.0 <4.0.0", + "from": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "dependencies": { "ansi-regex": { "version": "2.0.0", - "from": "ansi-regex@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz" } } }, "tv4": { "version": "1.2.7", - "from": "tv4@>=1.2.7 <2.0.0", + "from": "https://registry.npmjs.org/tv4/-/tv4-1.2.7.tgz", "resolved": "https://registry.npmjs.org/tv4/-/tv4-1.2.7.tgz" }, "xregexp": { "version": "3.1.1", - "from": "xregexp@>=3.0.0 <4.0.0", + "from": "https://registry.npmjs.org/xregexp/-/xregexp-3.1.1.tgz", "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-3.1.1.tgz" } } }, "text-table": { "version": "0.2.0", - "from": "text-table@>=0.2.0 <0.3.0", + "from": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" }, "user-home": { "version": "2.0.0", - "from": "user-home@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", "dependencies": { "os-homedir": { "version": "1.0.1", - "from": "os-homedir@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.1.tgz", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.1.tgz" } } @@ -10698,618 +10722,6 @@ "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz" } } - }, - "fsevents": { - "version": "1.0.14", - "from": "https://registry.npmjs.org/fsevents/-/fsevents-1.0.14.tgz", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.0.14.tgz", - "dependencies": { - "nan": { - "version": "2.4.0", - "from": "https://registry.npmjs.org/nan/-/nan-2.4.0.tgz", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.4.0.tgz" - }, - "node-pre-gyp": { - "version": "0.6.29", - "from": "node-pre-gyp@>=0.6.29 <0.7.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.29.tgz" - }, - "abbrev": { - "version": "1.0.9", - "from": "abbrev@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz" - }, - "ansi-regex": { - "version": "2.0.0", - "from": "ansi-regex@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz" - }, - "ansi-styles": { - "version": "2.2.1", - "from": "ansi-styles@>=2.2.1 <3.0.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" - }, - "aproba": { - "version": "1.0.4", - "from": "aproba@>=1.0.3 <2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.0.4.tgz" - }, - "are-we-there-yet": { - "version": "1.1.2", - "from": "are-we-there-yet@>=1.1.2 <1.2.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz" - }, - "asn1": { - "version": "0.2.3", - "from": "asn1@>=0.2.3 <0.3.0", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz" - }, - "assert-plus": { - "version": "0.2.0", - "from": "assert-plus@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" - }, - "async": { - "version": "1.5.2", - "from": "async@>=1.5.2 <2.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz" - }, - "aws-sign2": { - "version": "0.6.0", - "from": "aws-sign2@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" - }, - "aws4": { - "version": "1.4.1", - "from": "aws4@>=1.2.1 <2.0.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.4.1.tgz" - }, - "balanced-match": { - "version": "0.4.2", - "from": "balanced-match@>=0.4.1 <0.5.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" - }, - "block-stream": { - "version": "0.0.9", - "from": "block-stream@*", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz" - }, - "boom": { - "version": "2.10.1", - "from": "boom@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" - }, - "brace-expansion": { - "version": "1.1.5", - "from": "brace-expansion@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.5.tgz" - }, - "buffer-shims": { - "version": "1.0.0", - "from": "buffer-shims@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" - }, - "caseless": { - "version": "0.11.0", - "from": "caseless@>=0.11.0 <0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz" - }, - "chalk": { - "version": "1.1.3", - "from": "chalk@>=1.1.1 <2.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz" - }, - "code-point-at": { - "version": "1.0.0", - "from": "code-point-at@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.0.0.tgz" - }, - "combined-stream": { - "version": "1.0.5", - "from": "combined-stream@>=1.0.5 <1.1.0", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz" - }, - "commander": { - "version": "2.9.0", - "from": "commander@>=2.9.0 <3.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz" - }, - "concat-map": { - "version": "0.0.1", - "from": "concat-map@0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" - }, - "console-control-strings": { - "version": "1.1.0", - "from": "console-control-strings@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" - }, - "core-util-is": { - "version": "1.0.2", - "from": "core-util-is@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - }, - "cryptiles": { - "version": "2.0.5", - "from": "cryptiles@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" - }, - "debug": { - "version": "2.2.0", - "from": "debug@>=2.2.0 <2.3.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz" - }, - "deep-extend": { - "version": "0.4.1", - "from": "deep-extend@>=0.4.0 <0.5.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz" - }, - "delayed-stream": { - "version": "1.0.0", - "from": "delayed-stream@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" - }, - "delegates": { - "version": "1.0.0", - "from": "delegates@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" - }, - "ecc-jsbn": { - "version": "0.1.1", - "from": "ecc-jsbn@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz" - }, - "escape-string-regexp": { - "version": "1.0.5", - "from": "escape-string-regexp@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" - }, - "extend": { - "version": "3.0.0", - "from": "extend@>=3.0.0 <3.1.0", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz" - }, - "extsprintf": { - "version": "1.0.2", - "from": "extsprintf@1.0.2", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz" - }, - "forever-agent": { - "version": "0.6.1", - "from": "forever-agent@>=0.6.1 <0.7.0", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" - }, - "form-data": { - "version": "1.0.0-rc4", - "from": "form-data@>=1.0.0-rc4 <1.1.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.0-rc4.tgz" - }, - "fs.realpath": { - "version": "1.0.0", - "from": "fs.realpath@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" - }, - "fstream": { - "version": "1.0.10", - "from": "fstream@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.10.tgz" - }, - "fstream-ignore": { - "version": "1.0.5", - "from": "fstream-ignore@>=1.0.5 <1.1.0", - "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz" - }, - "gauge": { - "version": "2.6.0", - "from": "gauge@>=2.6.0 <2.7.0", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.6.0.tgz" - }, - "generate-function": { - "version": "2.0.0", - "from": "generate-function@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz" - }, - "generate-object-property": { - "version": "1.2.0", - "from": "generate-object-property@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz" - }, - "glob": { - "version": "7.0.5", - "from": "glob@>=7.0.5 <8.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.5.tgz" - }, - "graceful-fs": { - "version": "4.1.4", - "from": "graceful-fs@>=4.1.2 <5.0.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.4.tgz" - }, - "graceful-readlink": { - "version": "1.0.1", - "from": "graceful-readlink@>=1.0.0", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz" - }, - "har-validator": { - "version": "2.0.6", - "from": "har-validator@>=2.0.6 <2.1.0", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz" - }, - "has-ansi": { - "version": "2.0.0", - "from": "has-ansi@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz" - }, - "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" - }, - "has-unicode": { - "version": "2.0.1", - "from": "has-unicode@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" - }, - "hawk": { - "version": "3.1.3", - "from": "hawk@>=3.1.3 <3.2.0", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz" - }, - "hoek": { - "version": "2.16.3", - "from": "hoek@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" - }, - "http-signature": { - "version": "1.1.1", - "from": "http-signature@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz" - }, - "inflight": { - "version": "1.0.5", - "from": "inflight@>=1.0.4 <2.0.0", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.5.tgz" - }, - "inherits": { - "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" - }, - "ini": { - "version": "1.3.4", - "from": "ini@>=1.3.0 <1.4.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "from": "is-fullwidth-code-point@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz" - }, - "is-my-json-valid": { - "version": "2.13.1", - "from": "is-my-json-valid@>=2.12.4 <3.0.0", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.13.1.tgz" - }, - "is-property": { - "version": "1.0.2", - "from": "is-property@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz" - }, - "is-typedarray": { - "version": "1.0.0", - "from": "is-typedarray@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" - }, - "isarray": { - "version": "1.0.0", - "from": "isarray@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" - }, - "isstream": { - "version": "0.1.2", - "from": "isstream@>=0.1.2 <0.2.0", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" - }, - "jodid25519": { - "version": "1.0.2", - "from": "jodid25519@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz" - }, - "jsbn": { - "version": "0.1.0", - "from": "jsbn@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.0.tgz" - }, - "json-schema": { - "version": "0.2.2", - "from": "json-schema@0.2.2", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.2.tgz" - }, - "json-stringify-safe": { - "version": "5.0.1", - "from": "json-stringify-safe@>=5.0.1 <5.1.0", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" - }, - "jsonpointer": { - "version": "2.0.0", - "from": "jsonpointer@2.0.0", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-2.0.0.tgz" - }, - "jsprim": { - "version": "1.3.0", - "from": "jsprim@>=1.2.2 <2.0.0", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.3.0.tgz" - }, - "mime-db": { - "version": "1.23.0", - "from": "mime-db@>=1.23.0 <1.24.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.23.0.tgz" - }, - "mime-types": { - "version": "2.1.11", - "from": "mime-types@>=2.1.7 <2.2.0", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.11.tgz" - }, - "minimatch": { - "version": "3.0.2", - "from": "minimatch@>=3.0.2 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.2.tgz" - }, - "minimist": { - "version": "0.0.8", - "from": "minimist@0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" - }, - "mkdirp": { - "version": "0.5.1", - "from": "mkdirp@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" - }, - "ms": { - "version": "0.7.1", - "from": "ms@0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" - }, - "node-uuid": { - "version": "1.4.7", - "from": "node-uuid@>=1.4.7 <1.5.0", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" - }, - "nopt": { - "version": "3.0.6", - "from": "nopt@>=3.0.1 <3.1.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz" - }, - "npmlog": { - "version": "3.1.2", - "from": "npmlog@>=3.1.2 <3.2.0", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-3.1.2.tgz" - }, - "number-is-nan": { - "version": "1.0.0", - "from": "number-is-nan@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" - }, - "oauth-sign": { - "version": "0.8.2", - "from": "oauth-sign@>=0.8.1 <0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz" - }, - "object-assign": { - "version": "4.1.0", - "from": "object-assign@>=4.1.0 <5.0.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz" - }, - "once": { - "version": "1.3.3", - "from": "once@>=1.3.0 <2.0.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz" - }, - "path-is-absolute": { - "version": "1.0.0", - "from": "path-is-absolute@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" - }, - "pinkie": { - "version": "2.0.4", - "from": "pinkie@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" - }, - "pinkie-promise": { - "version": "2.0.1", - "from": "pinkie-promise@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" - }, - "process-nextick-args": { - "version": "1.0.7", - "from": "process-nextick-args@>=1.0.6 <1.1.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" - }, - "qs": { - "version": "6.2.0", - "from": "qs@>=6.2.0 <6.3.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.0.tgz" - }, - "readable-stream": { - "version": "2.1.4", - "from": "readable-stream@>=2.0.0 <3.0.0||>=1.1.13 <2.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.4.tgz" - }, - "request": { - "version": "2.73.0", - "from": "request@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.73.0.tgz" - }, - "rimraf": { - "version": "2.5.3", - "from": "rimraf@>=2.5.0 <2.6.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.3.tgz" - }, - "semver": { - "version": "5.2.0", - "from": "semver@>=5.2.0 <5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.2.0.tgz" - }, - "set-blocking": { - "version": "2.0.0", - "from": "set-blocking@>=2.0.0 <2.1.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" - }, - "signal-exit": { - "version": "3.0.0", - "from": "signal-exit@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.0.tgz" - }, - "sntp": { - "version": "1.0.9", - "from": "sntp@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" - }, - "string-width": { - "version": "1.0.1", - "from": "string-width@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.1.tgz" - }, - "string_decoder": { - "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" - }, - "stringstream": { - "version": "0.0.5", - "from": "stringstream@>=0.0.4 <0.1.0", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz" - }, - "strip-ansi": { - "version": "3.0.1", - "from": "strip-ansi@>=3.0.1 <4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" - }, - "strip-json-comments": { - "version": "1.0.4", - "from": "strip-json-comments@>=1.0.4 <1.1.0", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz" - }, - "supports-color": { - "version": "2.0.0", - "from": "supports-color@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" - }, - "tar": { - "version": "2.2.1", - "from": "tar@>=2.2.0 <2.3.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz" - }, - "tar-pack": { - "version": "3.1.4", - "from": "tar-pack@>=3.1.0 <3.2.0", - "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.1.4.tgz" - }, - "tough-cookie": { - "version": "2.2.2", - "from": "tough-cookie@>=2.2.0 <2.3.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz" - }, - "tunnel-agent": { - "version": "0.4.3", - "from": "tunnel-agent@>=0.4.1 <0.5.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz" - }, - "tweetnacl": { - "version": "0.13.3", - "from": "tweetnacl@>=0.13.0 <0.14.0", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.13.3.tgz" - }, - "uid-number": { - "version": "0.0.6", - "from": "uid-number@>=0.0.6 <0.1.0", - "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz" - }, - "util-deprecate": { - "version": "1.0.2", - "from": "util-deprecate@>=1.0.1 <1.1.0", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - }, - "verror": { - "version": "1.3.6", - "from": "verror@1.3.6", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz" - }, - "wide-align": { - "version": "1.1.0", - "from": "wide-align@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.0.tgz" - }, - "wrappy": { - "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - }, - "xtend": { - "version": "4.0.1", - "from": "xtend@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" - }, - "bl": { - "version": "1.1.2", - "from": "bl@>=1.1.2 <1.2.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.1.2.tgz", - "dependencies": { - "readable-stream": { - "version": "2.0.6", - "from": "readable-stream@>=2.0.5 <2.1.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz" - } - } - }, - "dashdash": { - "version": "1.14.0", - "from": "dashdash@>=1.12.0 <2.0.0", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.0.tgz", - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "from": "assert-plus@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - } - } - }, - "getpass": { - "version": "0.1.6", - "from": "getpass@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz", - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "from": "assert-plus@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - } - } - }, - "rc": { - "version": "1.1.6", - "from": "rc@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.1.6.tgz", - "dependencies": { - "minimist": { - "version": "1.2.0", - "from": "minimist@>=1.2.0 <2.0.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" - } - } - }, - "sshpk": { - "version": "1.8.3", - "from": "sshpk@>=1.7.0 <2.0.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.8.3.tgz", - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "from": "assert-plus@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - } - } - } - } } } }, @@ -11544,45 +10956,6 @@ "from": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz" }, - "log4js": { - "version": "0.6.38", - "from": "https://registry.npmjs.com/log4js/-/log4js-0.6.38.tgz", - "resolved": "https://registry.npmjs.com/log4js/-/log4js-0.6.38.tgz", - "dependencies": { - "readable-stream": { - "version": "1.0.34", - "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "dependencies": { - "core-util-is": { - "version": "1.0.2", - "from": "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" - }, - "isarray": { - "version": "0.0.1", - "from": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - }, - "string_decoder": { - "version": "0.10.31", - "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" - }, - "inherits": { - "version": "2.0.1", - "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" - } - } - }, - "semver": { - "version": "4.3.6", - "from": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", - "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz" - } - } - }, "mime": { "version": "1.3.4", "from": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", @@ -12907,6 +12280,45 @@ "from": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" }, + "log4js": { + "version": "0.6.38", + "from": "log4js@>=0.6.27 <0.7.0", + "resolved": "https://registry.npmjs.com/log4js/-/log4js-0.6.38.tgz", + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "from": "readable-stream@>=1.0.2 <1.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.2", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "semver": { + "version": "4.3.6", + "from": "semver@>=4.3.3 <4.4.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz" + } + } + }, "marked": { "version": "0.3.5", "from": "https://registry.npmjs.org/marked/-/marked-0.3.5.tgz", @@ -13302,62 +12714,6 @@ } } }, - "selenium-webdriver": { - "version": "2.53.3", - "from": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-2.53.3.tgz", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-2.53.3.tgz", - "dependencies": { - "adm-zip": { - "version": "0.4.4", - "from": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.4.tgz", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.4.tgz" - }, - "rimraf": { - "version": "2.5.4", - "from": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz" - }, - "tmp": { - "version": "0.0.24", - "from": "https://registry.npmjs.org/tmp/-/tmp-0.0.24.tgz", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.24.tgz" - }, - "ws": { - "version": "1.1.1", - "from": "https://registry.npmjs.org/ws/-/ws-1.1.1.tgz", - "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.1.tgz", - "dependencies": { - "options": { - "version": "0.0.6", - "from": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", - "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz" - }, - "ultron": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz" - } - } - }, - "xml2js": { - "version": "0.4.4", - "from": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.4.tgz", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.4.tgz", - "dependencies": { - "sax": { - "version": "0.6.1", - "from": "https://registry.npmjs.org/sax/-/sax-0.6.1.tgz", - "resolved": "https://registry.npmjs.org/sax/-/sax-0.6.1.tgz" - }, - "xmlbuilder": { - "version": "8.2.2", - "from": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz" - } - } - } - } - }, "source-map-support": { "version": "0.4.2", "from": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.2.tgz", @@ -13874,6 +13230,134 @@ "from": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz" }, + "selenium-webdriver": { + "version": "2.53.3", + "from": "selenium-webdriver@>=2.53.1 <3.0.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-2.53.3.tgz", + "dependencies": { + "adm-zip": { + "version": "0.4.4", + "from": "adm-zip@0.4.4", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.4.tgz" + }, + "rimraf": { + "version": "2.5.4", + "from": "rimraf@>=2.2.8 <3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", + "dependencies": { + "glob": { + "version": "7.0.6", + "from": "glob@>=7.0.5 <8.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", + "dependencies": { + "fs.realpath": { + "version": "1.0.0", + "from": "fs.realpath@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + }, + "inflight": { + "version": "1.0.5", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.5.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.2", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "3.0.3", + "from": "minimatch@>=3.0.2 <4.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.6", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "from": "balanced-match@>=0.4.1 <0.5.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "once": { + "version": "1.3.3", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.2", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + } + } + }, + "path-is-absolute": { + "version": "1.0.0", + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" + } + } + } + } + }, + "tmp": { + "version": "0.0.24", + "from": "tmp@0.0.24", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.24.tgz" + }, + "ws": { + "version": "1.1.1", + "from": "ws@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.1.tgz", + "dependencies": { + "options": { + "version": "0.0.6", + "from": "options@>=0.0.5", + "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz" + }, + "ultron": { + "version": "1.0.2", + "from": "ultron@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz" + } + } + }, + "xml2js": { + "version": "0.4.4", + "from": "xml2js@0.4.4", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.4.tgz", + "dependencies": { + "sax": { + "version": "0.6.1", + "from": "sax@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-0.6.1.tgz" + }, + "xmlbuilder": { + "version": "8.2.2", + "from": "xmlbuilder@>=1.0.0", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz" + } + } + } + } + }, "semver": { "version": "4.0.3", "from": "https://registry.npmjs.org/semver/-/semver-4.0.3.tgz", @@ -14091,7 +13575,7 @@ }, "shelljs": { "version": "0.3.0", - "from": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", + "from": "shelljs@0.3.0", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz" }, "sorted-object": { diff --git a/package.json b/package.json index 9889ac229eb1..84c2718dca5e 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ "postinstall": "node scripts/npm/copy-npm-shrinkwrap.js", "commit": "git-cz", "test-i18n": "jasmine-node i18n/spec", - "test-i18n-ucd": "jasmine-node i18n/ucd/spec" + "test-i18n-ucd": "jasmine-node i18n/ucd/spec", + "gulp": "gulp" }, "devDependencies": { "angular-benchpress": "0.x.x", @@ -27,6 +28,7 @@ "canonical-path": "0.0.2", "cheerio": "^0.17.0", "commitizen": "^2.3.0", + "cross-spawn": "^4.0.0", "cz-conventional-changelog": "1.1.4", "dgeni": "^0.4.0", "dgeni-packages": "^0.14.0", @@ -65,6 +67,7 @@ "karma-script-launcher": "^1.0.0", "load-grunt-tasks": "^3.5.0", "lodash": "~2.4.1", + "log4js": "^0.6.27", "marked": "~0.3.0", "node-html-encoder": "0.0.2", "promises-aplus-tests": "~2.1.0", @@ -74,6 +77,7 @@ "qq": "^0.3.5", "rewire": "~2.1.0", "sax": "^1.1.1", + "selenium-webdriver": "^2.53.1", "semver": "~4.0.3", "serve-favicon": "^2.3.0", "serve-index": "^1.8.0", diff --git a/src/.eslintrc.json b/src/.eslintrc.json index caa332ff8bf7..a5d959535803 100644 --- a/src/.eslintrc.json +++ b/src/.eslintrc.json @@ -45,6 +45,7 @@ "isObject": false, "isString": false, "isNumber": false, + "isNumberNaN": false, "isDate": false, "isArray": false, "isFunction": false, diff --git a/src/Angular.js b/src/Angular.js index 60463f1e1eb3..ad68d3e128d5 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -40,6 +40,7 @@ isBlankObject, isString, isNumber, + isNumberNaN, isDate, isArray, isFunction, @@ -412,6 +413,11 @@ function toInt(str) { return parseInt(str, 10); } +var isNumberNaN = Number.isNaN || function isNumberNaN(num) { + // eslint-disable-next-line no-self-compare + return num !== num; +}; + function inherit(parent, extra) { return extend(Object.create(parent), extra); @@ -1276,7 +1282,7 @@ function timezoneToOffset(timezone, fallback) { // IE/Edge do not "understand" colon (`:`) in timezone timezone = timezone.replace(ALL_COLONS, ''); var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000; - return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset; + return isNumberNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset; } diff --git a/src/AngularPublic.js b/src/AngularPublic.js index b1b5f332ce8a..3edd34e3abe9 100644 --- a/src/AngularPublic.js +++ b/src/AngularPublic.js @@ -79,6 +79,7 @@ $jsonpCallbacksProvider, $LocationProvider, $LogProvider, + $ModelOptionsProvider, $ParseProvider, $RootScopeProvider, $QProvider, @@ -246,6 +247,7 @@ function publishExternalAPI(angular) { $jsonpCallbacks: $jsonpCallbacksProvider, $location: $LocationProvider, $log: $LogProvider, + $modelOptions: $ModelOptionsProvider, $parse: $ParseProvider, $rootScope: $RootScopeProvider, $q: $QProvider, diff --git a/src/ng/compile.js b/src/ng/compile.js index 47fc44c2e053..02cd0bdbaa94 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -697,7 +697,7 @@ * * When you call a transclusion function you can pass in a **clone attach function**. This function accepts * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded - * content and the `scope` is the newly created transclusion scope, to which the clone is bound. + * content and the `scope` is the newly created transclusion scope, which the clone will be linked to. * *
* **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a transclude function @@ -1071,6 +1071,17 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { return require; } + function getDirectiveRestrict(restrict, name) { + if (restrict && !(isString(restrict) && /[EACM]/.test(restrict))) { + throw $compileMinErr('badrestrict', + 'Restrict property \'{0}\' of directive \'{1}\' is invalid', + restrict, + name); + } + + return restrict || 'EA'; + } + /** * @ngdoc method * @name $compileProvider#directive @@ -1109,7 +1120,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { directive.index = index; directive.name = directive.name || name; directive.require = getDirectiveRequire(directive); - directive.restrict = directive.restrict || 'EA'; + directive.restrict = getDirectiveRestrict(directive.restrict, name); directive.$$moduleName = directiveFactory.$$moduleName; directives.push(directive); } catch (e) { @@ -1358,6 +1369,35 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { return debugInfoEnabled; }; + /** + * @ngdoc method + * @name $compileProvider#preAssignBindingsEnabled + * + * @param {boolean=} enabled update the preAssignBindingsEnabled state if provided, otherwise just return the + * current preAssignBindingsEnabled state + * @returns {*} current value if used as getter or itself (chaining) if used as setter + * + * @kind function + * + * @description + * Call this method to enable/disable whether directive controllers are assigned bindings before + * calling the controller's constructor. + * If enabled (true), the compiler assigns the value of each of the bindings to the + * properties of the controller object before the constructor of this object is called. + * + * If disabled (false), the compiler calls the constructor first before assigning bindings. + * + * The default value is true in Angular 1.5.x but will switch to false in Angular 1.6.x. + */ + var preAssignBindingsEnabled = true; + this.preAssignBindingsEnabled = function(enabled) { + if (isDefined(enabled)) { + preAssignBindingsEnabled = enabled; + return this; + } + return preAssignBindingsEnabled; + }; + var TTL = 10; /** @@ -1817,6 +1857,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { compile.$$addScopeClass($compileNodes); var namespace = null; return function publicLinkFn(scope, cloneConnectFn, options) { + if (!$compileNodes) { + throw $compileMinErr('multilink', 'This element has already been linked.'); + } assertArg(scope, 'scope'); if (previousCompileContext && previousCompileContext.needsNewScope) { @@ -1871,6 +1914,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { if (cloneConnectFn) cloneConnectFn($linkNode, scope); if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn); + + if (!cloneConnectFn) { + $compileNodes = compositeLinkFn = null; + } return $linkNode; }; } @@ -2661,22 +2708,29 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { var controller = elementControllers[name]; var bindings = controllerDirective.$$bindings.bindToController; - if (controller.identifier && bindings) { - controller.bindingInfo = - initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective); - } else { - controller.bindingInfo = {}; - } + if (preAssignBindingsEnabled) { + if (controller.identifier && bindings) { + controller.bindingInfo = + initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective); + } else { + controller.bindingInfo = {}; + } - var controllerResult = controller(); - if (controllerResult !== controller.instance) { - // If the controller constructor has a return value, overwrite the instance - // from setupControllers - controller.instance = controllerResult; - $element.data('$' + controllerDirective.name + 'Controller', controllerResult); - if (controller.bindingInfo.removeWatches) { - controller.bindingInfo.removeWatches(); + var controllerResult = controller(); + if (controllerResult !== controller.instance) { + // If the controller constructor has a return value, overwrite the instance + // from setupControllers + controller.instance = controllerResult; + $element.data('$' + controllerDirective.name + 'Controller', controllerResult); + if (controller.bindingInfo.removeWatches) { + controller.bindingInfo.removeWatches(); + } + controller.bindingInfo = + initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective); } + } else { + controller.instance = controller(); + $element.data('$' + controllerDirective.name + 'Controller', controller.instance); controller.bindingInfo = initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective); } @@ -2905,24 +2959,22 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { if (hasDirectives.hasOwnProperty(name)) { for (var directive, directives = $injector.get(name + Suffix), i = 0, ii = directives.length; i < ii; i++) { - try { - directive = directives[i]; - if ((isUndefined(maxPriority) || maxPriority > directive.priority) && - directive.restrict.indexOf(location) !== -1) { - if (startAttrName) { - directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName}); - } - if (!directive.$$bindings) { - var bindings = directive.$$bindings = - parseDirectiveBindings(directive, directive.name); - if (isObject(bindings.isolateScope)) { - directive.$$isolateBindings = bindings.isolateScope; - } + directive = directives[i]; + if ((isUndefined(maxPriority) || maxPriority > directive.priority) && + directive.restrict.indexOf(location) !== -1) { + if (startAttrName) { + directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName}); + } + if (!directive.$$bindings) { + var bindings = directive.$$bindings = + parseDirectiveBindings(directive, directive.name); + if (isObject(bindings.isolateScope)) { + directive.$$isolateBindings = bindings.isolateScope; } - tDirectives.push(directive); - match = directive; } - } catch (e) { $exceptionHandler(e); } + tDirectives.push(directive); + match = directive; + } } } return match; @@ -3484,7 +3536,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { }); function recordChanges(key, currentValue, previousValue) { - if (isFunction(destination.$onChanges) && currentValue !== previousValue) { + if (isFunction(destination.$onChanges) && currentValue !== previousValue && + // eslint-disable-next-line no-self-compare + (currentValue === currentValue || previousValue === previousValue)) { // If we have not already scheduled the top level onChangesQueue handler then do so now if (!onChangesQueue) { scope.$$postDigest(flushOnChangesQueue); diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index 13990b4c0b68..9b109793b90c 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -1424,7 +1424,7 @@ function createDateInputType(type, regexp, parseDate, format) { return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) { badInputChecker(scope, element, attr, ctrl); baseInputType(scope, element, attr, ctrl, $sniffer, $browser); - var timezone = ctrl && ctrl.$options && ctrl.$options.timezone; + var timezone = ctrl && ctrl.$options.getOption('timezone'); var previousDate; ctrl.$$parserName = type; @@ -1526,7 +1526,7 @@ function parseNumberAttrVal(val) { if (isDefined(val) && !isNumber(val)) { val = parseFloat(val); } - return isNumber(val) && !isNaN(val) ? val : undefined; + return !isNumberNaN(val) ? val : undefined; } function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) { @@ -1650,7 +1650,7 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) { function minChange(val) { minVal = parseNumberAttrVal(val); // ignore changes before model is initialized - if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) { + if (isNumberNaN(ctrl.$modelValue)) { return; } @@ -1671,7 +1671,7 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) { function maxChange(val) { maxVal = parseNumberAttrVal(val); // ignore changes before model is initialized - if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) { + if (isNumberNaN(ctrl.$modelValue)) { return; } @@ -1693,7 +1693,7 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) { function stepChange(val) { stepVal = parseNumberAttrVal(val); // ignore changes before model is initialized - if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) { + if (isNumberNaN(ctrl.$modelValue)) { return; } diff --git a/src/ng/directive/ngCsp.js b/src/ng/directive/ngCsp.js index a2866dc1ea59..f0303ef8a844 100644 --- a/src/ng/directive/ngCsp.js +++ b/src/ng/directive/ngCsp.js @@ -161,7 +161,7 @@ beforeEach(function() { util = require('util'); - webdriver = require('protractor/node_modules/selenium-webdriver'); + webdriver = require('selenium-webdriver'); }); // For now, we only test on Chrome, diff --git a/src/ng/directive/ngModel.js b/src/ng/directive/ngModel.js index 6ef8ba408187..fd0ef15ce5ae 100644 --- a/src/ng/directive/ngModel.js +++ b/src/ng/directive/ngModel.js @@ -5,7 +5,8 @@ PRISTINE_CLASS: true, DIRTY_CLASS: true, UNTOUCHED_CLASS: true, - TOUCHED_CLASS: true + TOUCHED_CLASS: true, + $ModelOptionsProvider: true */ var VALID_CLASS = 'ng-valid', @@ -220,8 +221,8 @@ is set to `true`. The parse error is stored in `ngModel.$error.parse`. * * */ -var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate', - /** @this */ function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) { +var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate', '$modelOptions', + /** @this */ function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate, $modelOptions) { this.$viewValue = Number.NaN; this.$modelValue = Number.NaN; this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity. @@ -241,6 +242,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ this.$pending = undefined; // keep pending keys here this.$name = $interpolate($attr.name || '', false)($scope); this.$$parentForm = nullFormCtrl; + this.$options = $modelOptions; var parsedNgModel = $parse($attr.ngModel), parsedNgModelAssign = parsedNgModel.assign, @@ -250,9 +252,10 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ parserValid, ctrl = this; - this.$$setOptions = function(options) { - ctrl.$options = options; - if (options && options.getterSetter) { + + this.$$initGetterSetters = function() { + + if (ctrl.$options.getOption('getterSetter')) { var invokeModelGetter = $parse($attr.ngModel + '()'), invokeModelSetter = $parse($attr.ngModel + '($$$p)'); @@ -276,6 +279,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ } }; + /** * @ngdoc method * @name ngModel.NgModelController#$render @@ -548,7 +552,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ */ this.$validate = function() { // ignore $validate before model is initialized - if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) { + if (isNumberNaN(ctrl.$modelValue)) { return; } @@ -562,7 +566,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ var prevValid = ctrl.$valid; var prevModelValue = ctrl.$modelValue; - var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid; + var allowInvalid = ctrl.$options.getOption('allowInvalid'); ctrl.$$runValidators(modelValue, viewValue, function(allValid) { // If there was no change in validity, don't update the model @@ -719,12 +723,12 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ } } } - if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) { + if (isNumberNaN(ctrl.$modelValue)) { // ctrl.$modelValue has not been touched yet... ctrl.$modelValue = ngModelGet($scope); } var prevModelValue = ctrl.$modelValue; - var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid; + var allowInvalid = ctrl.$options.getOption('allowInvalid'); ctrl.$$rawModelValue = modelValue; if (allowInvalid) { @@ -815,25 +819,19 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ */ this.$setViewValue = function(value, trigger) { ctrl.$viewValue = value; - if (!ctrl.$options || ctrl.$options.updateOnDefault) { + if (ctrl.$options.getOption('updateOnDefault')) { ctrl.$$debounceViewValueCommit(trigger); } }; this.$$debounceViewValueCommit = function(trigger) { - var debounceDelay = 0, - options = ctrl.$options, - debounce; - - if (options && isDefined(options.debounce)) { - debounce = options.debounce; - if (isNumber(debounce)) { - debounceDelay = debounce; - } else if (isNumber(debounce[trigger])) { - debounceDelay = debounce[trigger]; - } else if (isNumber(debounce['default'])) { - debounceDelay = debounce['default']; - } + var options = ctrl.$options, + debounceDelay = options.getOption('debounce'); + + if (isNumber(debounceDelay[trigger])) { + debounceDelay = debounceDelay[trigger]; + } else if (isNumber(debounceDelay['default'])) { + debounceDelay = debounceDelay['default']; } $timeout.cancel(pendingDebounce); @@ -1096,9 +1094,14 @@ var ngModelDirective = ['$rootScope', function($rootScope) { return { pre: function ngModelPreLink(scope, element, attr, ctrls) { var modelCtrl = ctrls[0], - formCtrl = ctrls[1] || modelCtrl.$$parentForm; + formCtrl = ctrls[1] || modelCtrl.$$parentForm, + optionsCtrl = ctrls[2]; + + if (optionsCtrl) { + modelCtrl.$options = optionsCtrl.$options; + } - modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options); + modelCtrl.$$initGetterSetters(); // notify others, especially parent forms formCtrl.$addControl(modelCtrl); @@ -1115,8 +1118,8 @@ var ngModelDirective = ['$rootScope', function($rootScope) { }, post: function ngModelPostLink(scope, element, attr, ctrls) { var modelCtrl = ctrls[0]; - if (modelCtrl.$options && modelCtrl.$options.updateOn) { - element.on(modelCtrl.$options.updateOn, function(ev) { + if (modelCtrl.$options.getOption('updateOn')) { + element.on(modelCtrl.$options.getOption('updateOn'), function(ev) { modelCtrl.$$debounceViewValueCommit(ev && ev.type); }); } @@ -1137,17 +1140,47 @@ var ngModelDirective = ['$rootScope', function($rootScope) { }]; - -var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/; - /** * @ngdoc directive * @name ngModelOptions * * @description - * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of - * events that will trigger a model update and/or a debouncing delay so that the actual update only - * takes place when a timer expires; this timer will be reset after another change takes place. + * This directive allows you to modify the behaviour of ngModel and input directives within your + * application. You can specify an ngModelOptions directive on any element and the settings affect + * the ngModel and input directives on all descendent elements. + * + * The ngModelOptions settings are found by evaluating the value of the ngModelOptions attribute as + * an Angular expression. This expression should evaluate to an object, whose properties contain + * the settings. + * + * If a setting is not specified as a property on the object for a particular ngModelOptions directive + * then it will inherit that setting from the first ngModelOptions directive found by traversing up the + * DOM tree. If there is no ancestor element containing an ngModelOptions directive then the settings in + * {@link $modelOptions} will be used. + * + * For example given the following fragment of HTML + * + * + * ```html + *
+ *
+ * + *
+ *
+ * ``` + * + * the `input` element will have the following settings + * + * ```js + * { allowInvalid: true, updateOn: 'default' } + * ``` + * + * + * ## Triggering and debouncing model updates + * + * The `updateOn` and `debounce` properties allow you to specify a custom list of events that will + * trigger a model update and/or a debouncing delay so that the actual update only takes place when + * a timer expires; this timer will be reset after another change takes place. * * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might * be different from the value in the actual model. This means that if you update the model you @@ -1163,7 +1196,130 @@ var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/; * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit` * to have access to the updated model. * - * `ngModelOptions` has an effect on the element it's declared on and its descendants. + * The following example shows how to override immediate updates. Changes on the inputs within the + * form will update the model only when the control loses focus (blur event). If `escape` key is + * pressed while the input field is focused, the value is reset to the value in the current model. + * + * + * + *
+ *
+ * Name: + *
+ * + * Other data: + *
+ *
+ *
user.name = 
+ *
+ *
+ * + * angular.module('optionsExample', []) + * .controller('ExampleController', ['$scope', function($scope) { + * $scope.user = { name: 'say', data: '' }; + * + * $scope.cancel = function(e) { + * if (e.keyCode === 27) { + * $scope.userForm.userName.$rollbackViewValue(); + * } + * }; + * }]); + * + * + * var model = element(by.binding('user.name')); + * var input = element(by.model('user.name')); + * var other = element(by.model('user.data')); + * + * it('should allow custom events', function() { + * input.sendKeys(' hello'); + * input.click(); + * expect(model.getText()).toEqual('say'); + * other.click(); + * expect(model.getText()).toEqual('say hello'); + * }); + * + * it('should $rollbackViewValue when model changes', function() { + * input.sendKeys(' hello'); + * expect(input.getAttribute('value')).toEqual('say hello'); + * input.sendKeys(protractor.Key.ESCAPE); + * expect(input.getAttribute('value')).toEqual('say'); + * other.click(); + * expect(model.getText()).toEqual('say'); + * }); + * + *
+ * + * The next example shows how to debounce model changes. Model will be updated only 1 sec after last change. + * If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty. + * + * + * + *
+ *
+ * Name: + * + *
+ *
+ *
user.name = 
+ *
+ *
+ * + * angular.module('optionsExample', []) + * .controller('ExampleController', ['$scope', function($scope) { + * $scope.user = { name: 'say' }; + * }]); + * + *
+ * + * ## Model updates and validation + * + * The default behaviour in `ngModel` is that the model value is set to `null` when the validation + * determines that the value is invalid. By setting the `allowInvalid` property to true, the model + * will still be updated even if the value is invalid. + * + * + * ## Connecting to the scope + * + * By setting the `getterSetter` property to true you are telling ngModel that the `ngModel` expression + * on the scope refers to a "getter/setter" function rather than the value itself. + * + * The following example shows how to bind to getter/setters: + * + * + * + *
+ *
+ * Name: + * + *
+ *
user.name = 
+ *
+ *
+ * + * angular.module('getterSetterExample', []) + * .controller('ExampleController', ['$scope', function($scope) { + * var _name = 'Brian'; + * $scope.user = { + * name: function(newName) { + * return angular.isDefined(newName) ? (_name = newName) : _name; + * } + * }; + * }]); + * + *
+ * + * + * ## Specifying timezones + * + * You can specify the timezone that date/time input directives expect by providing its name in the + * `timezone` property. * * @param {Object} ngModelOptions options to apply to the current model. Valid keys are: * - `updateOn`: string specifying which event should the input be bound to. You can set several @@ -1176,153 +1332,134 @@ var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/; * - `allowInvalid`: boolean value which indicates that the model can be set with values that did * not validate correctly instead of the default behavior of setting the model to undefined. * - `getterSetter`: boolean value which determines whether or not to treat functions bound to - `ngModel` as getters/setters. + * `ngModel` as getters/setters. * - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for * ``, ``, ... . It understands UTC/GMT and the * continental US time zone abbreviations, but for general use, use a time zone offset, for * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian) * If not specified, the timezone of the browser will be used. * - * @example - - The following example shows how to override immediate updates. Changes on the inputs within the - form will update the model only when the control loses focus (blur event). If `escape` key is - pressed while the input field is focused, the value is reset to the value in the current model. - - - -
-
-
-
-
-
user.name = 
-
user.data = 
-
-
- - angular.module('optionsExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.user = { name: 'John', data: '' }; - - $scope.cancel = function(e) { - if (e.keyCode === 27) { - $scope.userForm.userName.$rollbackViewValue(); - } - }; - }]); - - - var model = element(by.binding('user.name')); - var input = element(by.model('user.name')); - var other = element(by.model('user.data')); - - it('should allow custom events', function() { - input.sendKeys(' Doe'); - input.click(); - expect(model.getText()).toEqual('John'); - other.click(); - expect(model.getText()).toEqual('John Doe'); - }); + */ +var ngModelOptionsDirective = ['$modelOptions', function($modelOptions) { + return { + restrict: 'A', + // ngModelOptions needs to run before ngModel and input directives + priority: 10, + require: ['ngModelOptions', '?^^ngModelOptions'], + controller: function NgModelOptionsController() {}, + link: { + pre: function ngModelOptionsPreLinkFn(scope, element, attrs, ctrls) { + var optionsCtrl = ctrls[0]; + var parentOptions = ctrls[1] ? ctrls[1].$options : $modelOptions; + optionsCtrl.$options = parentOptions.createChild(scope.$eval(attrs.ngModelOptions)); + } + } + }; +}]; - it('should $rollbackViewValue when model changes', function() { - input.sendKeys(' Doe'); - expect(input.getAttribute('value')).toEqual('John Doe'); - input.sendKeys(protractor.Key.ESCAPE); - expect(input.getAttribute('value')).toEqual('John'); - other.click(); - expect(model.getText()).toEqual('John'); - }); - -
- This one shows how to debounce model changes. Model will be updated only 1 sec after last change. - If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty. +/** + * @ngdoc provider + * @name $modelOptionsProvider + * @description + * + * Here, you can change the default settings from which {@link ngModelOptions} + * directives inherit. + * + * See the {@link ngModelOptions} directive for a list of the available options. + */ +function $ModelOptionsProvider() { + return { + /** + * @ngdoc property + * @name $modelOptionsProvider#defaultOptions + * @type {Object} + * @description + * The default options to fall back on when there are no more ngModelOption + * directives as ancestors. + * Use this property to specify the defaultOptions for the application as a whole. + * + * The initial default options are: + * + * * `updateOn`: `default` + * * `debounce`: `0` + * * `allowInvalid`: `undefined` + * * `getterSetter`: `undefined` + * * `timezone`: 'undefined' + */ + defaultOptions: { + updateOn: 'default', + debounce: 0 + }, - - -
-
- - -
-
-
user.name = 
-
-
- - angular.module('optionsExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.user = { name: 'Igor' }; - }]); - -
+ /** + * @ngdoc service + * @name $modelOptions + * @type ModelOptions + * @description + * + * This service provides the application wide default {@link ModelOptions} options that + * will be used by {@link ngModel} directives if no {@link ngModelOptions} directive is + * specified. + */ + $get: function() { + return new ModelOptions(this.defaultOptions); + } + }; +} - This one shows how to bind to getter/setters: - - -
-
- -
-
user.name = 
-
-
- - angular.module('getterSetterExample', []) - .controller('ExampleController', ['$scope', function($scope) { - var _name = 'Brian'; - $scope.user = { - name: function(newName) { - // Note that newName can be undefined for two reasons: - // 1. Because it is called as a getter and thus called with no arguments - // 2. Because the property should actually be set to undefined. This happens e.g. if the - // input is invalid - return arguments.length ? (_name = newName) : _name; - } - }; - }]); - -
+/** + * @ngdoc type + * @name ModelOptions + * @description + * A container for the options set by the {@link ngModelOptions} directive + * and the {@link $modelOptions} service. */ -var ngModelOptionsDirective = function() { - return { - restrict: 'A', - controller: ['$scope', '$attrs', function NgModelOptionsController($scope, $attrs) { - var that = this; - this.$options = copy($scope.$eval($attrs.ngModelOptions)); - // Allow adding/overriding bound events - if (isDefined(this.$options.updateOn)) { - this.$options.updateOnDefault = false; - // extract "default" pseudo-event from list of events that can trigger a model update - this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() { - that.$options.updateOnDefault = true; - return ' '; - })); - } else { - this.$options.updateOnDefault = true; - } - }] - }; -}; +var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/; +function ModelOptions(options, parentOptions) { + + // Extend the parent's options with these new ones + var _options = extend({}, parentOptions, options); + + // do extra processing on the options + + // updateOn and updateOnDefault + if (isDefined(_options.updateOn) && _options.updateOn.trim()) { + _options.updateOnDefault = false; + // extract "default" pseudo-event from list of events that can trigger a model update + _options.updateOn = trim(_options.updateOn.replace(DEFAULT_REGEXP, function() { + _options.updateOnDefault = true; + return ' '; + })); + } else if (parentOptions) { + _options.updateOn = parentOptions.updateOn; + _options.updateOnDefault = parentOptions.updateOnDefault; + } else { + _options.updateOnDefault = true; + } + /** + * @ngdoc method + * @name ModelOptions#getOption + * @param {string} name the name of the option to retrieve + * @returns {*} the value of the option + * @description + * Returns the value of the given option + */ + this.getOption = function(name) { return _options[name]; }; + + /** + * @ngdoc method + * @name ModelOptions#createChild + * @param {Object} options a hash of options for the new child that will override the parent's options + * @return {ModelOptions} a new `ModelOptions` object initialized with the given options. + */ + this.createChild = function(options) { + return new ModelOptions(options, _options); + }; +} // helper methods function addSetValidityMethod(context) { diff --git a/src/ng/directive/ngPluralize.js b/src/ng/directive/ngPluralize.js index 8d3a8a8cc48d..f0b7a67500ec 100644 --- a/src/ng/directive/ngPluralize.js +++ b/src/ng/directive/ngPluralize.js @@ -206,7 +206,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) { var count = parseFloat(newVal); - var countIsNaN = isNaN(count); + var countIsNaN = isNumberNaN(count); if (!countIsNaN && !(count in whens)) { // If an explicit number rule such as 1, 2, 3... is defined, just use it. @@ -216,7 +216,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, // If both `count` and `lastCount` are NaN, we don't need to re-register a watch. // In JS `NaN !== NaN`, so we have to explicitly check. - if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) { + if ((count !== lastCount) && !(countIsNaN && isNumberNaN(lastCount))) { watchRemover(); var whenExpFn = whensExpFns[count]; if (isUndefined(whenExpFn)) { diff --git a/src/ng/directive/validators.js b/src/ng/directive/validators.js index 052228299faf..36201f9f287f 100644 --- a/src/ng/directive/validators.js +++ b/src/ng/directive/validators.js @@ -259,7 +259,7 @@ var maxlengthDirective = function() { var maxlength = -1; attr.$observe('maxlength', function(value) { var intVal = toInt(value); - maxlength = isNaN(intVal) ? -1 : intVal; + maxlength = isNumberNaN(intVal) ? -1 : intVal; ctrl.$validate(); }); ctrl.$validators.maxlength = function(modelValue, viewValue) { diff --git a/src/ng/filter/filters.js b/src/ng/filter/filters.js index 8d8f221a56cb..f0ef2f29d3aa 100644 --- a/src/ng/filter/filters.js +++ b/src/ng/filter/filters.js @@ -31,7 +31,7 @@ var ZERO_CHAR = '0';

default currency symbol ($): {{amount | currency}}
- custom currency identifier (USD$): {{amount | currency:"USD$"}} + custom currency identifier (USD$): {{amount | currency:"USD$"}}
no fractions (0): {{amount | currency:"USD$":0}}
diff --git a/src/ng/filter/limitTo.js b/src/ng/filter/limitTo.js index 7c87a594345d..3a7998b73957 100644 --- a/src/ng/filter/limitTo.js +++ b/src/ng/filter/limitTo.js @@ -106,7 +106,7 @@ function limitToFilter() { } else { limit = toInt(limit); } - if (isNaN(limit)) return input; + if (isNumberNaN(limit)) return input; if (isNumber(input)) input = input.toString(); if (!isArrayLike(input)) return input; diff --git a/src/ng/rootScope.js b/src/ng/rootScope.js index afefa45e8058..7731c646dfee 100644 --- a/src/ng/rootScope.js +++ b/src/ng/rootScope.js @@ -806,8 +806,7 @@ function $RootScopeProvider() { if ((value = get(current)) !== (last = watch.last) && !(watch.eq ? equals(value, last) - : (typeof value === 'number' && typeof last === 'number' - && isNaN(value) && isNaN(last)))) { + : (isNumberNaN(value) && isNumberNaN(last)))) { dirty = true; lastDirtyWatch = watch; watch.last = watch.eq ? copy(value, null) : value; diff --git a/src/ngSanitize/sanitize.js b/src/ngSanitize/sanitize.js index a816069bcf48..f5f7490c2f67 100644 --- a/src/ngSanitize/sanitize.js +++ b/src/ngSanitize/sanitize.js @@ -499,27 +499,26 @@ function $SanitizeProvider() { * @param node Root element to process */ function stripCustomNsAttrs(node) { - if (node.nodeType === window.Node.ELEMENT_NODE) { - var attrs = node.attributes; - for (var i = 0, l = attrs.length; i < l; i++) { - var attrNode = attrs[i]; - var attrName = attrNode.name.toLowerCase(); - if (attrName === 'xmlns:ns1' || attrName.lastIndexOf('ns1:', 0) === 0) { - node.removeAttributeNode(attrNode); - i--; - l--; + while (node) { + if (node.nodeType === window.Node.ELEMENT_NODE) { + var attrs = node.attributes; + for (var i = 0, l = attrs.length; i < l; i++) { + var attrNode = attrs[i]; + var attrName = attrNode.name.toLowerCase(); + if (attrName === 'xmlns:ns1' || attrName.lastIndexOf('ns1:', 0) === 0) { + node.removeAttributeNode(attrNode); + i--; + l--; + } } } - } - var nextNode = node.firstChild; - if (nextNode) { - stripCustomNsAttrs(nextNode); - } + var nextNode = node.firstChild; + if (nextNode) { + stripCustomNsAttrs(nextNode); + } - nextNode = node.nextSibling; - if (nextNode) { - stripCustomNsAttrs(nextNode); + node = node.nextSibling; } } } diff --git a/test/.eslintrc.json b/test/.eslintrc.json index 2bf89b2bb07e..ca882335b492 100644 --- a/test/.eslintrc.json +++ b/test/.eslintrc.json @@ -59,6 +59,7 @@ "isObject": false, "isString": false, "isNumber": false, + "isNumberNaN": false, "isDate": false, "isArray": false, "isFunction": false, diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index 4c57925ec5a1..e4a8ddf4e960 100755 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -151,6 +151,60 @@ describe('$compile', function() { describe('configuration', function() { + it('should allow aHrefSanitizationWhitelist to be configured', function() { + module(function($compileProvider) { + expect($compileProvider.aHrefSanitizationWhitelist()).toEqual(/^\s*(https?|ftp|mailto|tel|file):/); // the default + $compileProvider.aHrefSanitizationWhitelist(/other/); + expect($compileProvider.aHrefSanitizationWhitelist()).toEqual(/other/); + }); + inject(); + }); + + it('should allow debugInfoEnabled to be configured', function() { + module(function($compileProvider) { + expect($compileProvider.debugInfoEnabled()).toBe(true); // the default + $compileProvider.debugInfoEnabled(false); + expect($compileProvider.debugInfoEnabled()).toBe(false); + }); + inject(); + }); + + it('should allow preAssignBindingsEnabled to be configured', function() { + module(function($compileProvider) { + expect($compileProvider.preAssignBindingsEnabled()).toBe(true); // the default + $compileProvider.preAssignBindingsEnabled(false); + expect($compileProvider.preAssignBindingsEnabled()).toBe(false); + }); + inject(); + }); + + it('should allow onChangesTtl to be configured', function() { + module(function($compileProvider) { + expect($compileProvider.onChangesTtl()).toBe(10); // the default + $compileProvider.onChangesTtl(2); + expect($compileProvider.onChangesTtl()).toBe(2); + }); + inject(); + }); + + it('should allow commentDirectivesEnabled to be configured', function() { + module(function($compileProvider) { + expect($compileProvider.commentDirectivesEnabled()).toBe(true); // the default + $compileProvider.commentDirectivesEnabled(false); + expect($compileProvider.commentDirectivesEnabled()).toBe(false); + }); + inject(); + }); + + it('should allow cssClassDirectivesEnabled to be configured', function() { + module(function($compileProvider) { + expect($compileProvider.cssClassDirectivesEnabled()).toBe(true); // the default + $compileProvider.cssClassDirectivesEnabled(false); + expect($compileProvider.cssClassDirectivesEnabled()).toBe(false); + }); + inject(); + }); + it('should register a directive', function() { module(function() { directive('div', function(log) { @@ -3631,6 +3685,14 @@ describe('$compile', function() { expect(element.text()).toBe('3'); }); }); + + it('should throw multilink error when linking the same element more then once', function() { + var linker = $compile('
'); + linker($rootScope).remove(); + expect(function() { + linker($rootScope); + }).toThrowMinErr('$compile', 'multilink', 'This element has already been linked.'); + }); }); @@ -3837,6002 +3899,6285 @@ describe('$compile', function() { }); }); - describe('controller lifecycle hooks', function() { - - describe('$onInit', function() { + forEach([true, false], function(preAssignBindingsEnabled) { + describe((preAssignBindingsEnabled ? 'with' : 'without') + ' pre-assigned bindings', function() { + beforeEach(module(function($compileProvider) { + $compileProvider.preAssignBindingsEnabled(preAssignBindingsEnabled); + })); - it('should call `$onInit`, if provided, after all the controllers on the element have been initialized', function() { + describe('controller lifecycle hooks', function() { - function check() { - expect(this.element.controller('d1').id).toEqual(1); - expect(this.element.controller('d2').id).toEqual(2); - } + describe('$onInit', function() { - function Controller1($element) { this.id = 1; this.element = $element; } - Controller1.prototype.$onInit = jasmine.createSpy('$onInit').and.callFake(check); + it('should call `$onInit`, if provided, after all the controllers on the element have been initialized', function() { - function Controller2($element) { this.id = 2; this.element = $element; } - Controller2.prototype.$onInit = jasmine.createSpy('$onInit').and.callFake(check); + function check() { + expect(this.element.controller('d1').id).toEqual(1); + expect(this.element.controller('d2').id).toEqual(2); + } - angular.module('my', []) - .directive('d1', valueFn({ controller: Controller1 })) - .directive('d2', valueFn({ controller: Controller2 })); + function Controller1($element) { this.id = 1; this.element = $element; } + Controller1.prototype.$onInit = jasmine.createSpy('$onInit').and.callFake(check); - module('my'); - inject(function($compile, $rootScope) { - element = $compile('
')($rootScope); - expect(Controller1.prototype.$onInit).toHaveBeenCalledOnce(); - expect(Controller2.prototype.$onInit).toHaveBeenCalledOnce(); - }); - }); + function Controller2($element) { this.id = 2; this.element = $element; } + Controller2.prototype.$onInit = jasmine.createSpy('$onInit').and.callFake(check); - it('should continue to trigger other `$onInit` hooks if one throws an error', function() { - function ThrowingController() { - this.$onInit = function() { - throw new Error('bad hook'); - }; - } - function LoggingController($log) { - this.$onInit = function() { - $log.info('onInit'); - }; - } + angular.module('my', []) + .directive('d1', valueFn({ controller: Controller1 })) + .directive('d2', valueFn({ controller: Controller2 })); - angular.module('my', []) - .component('c1', { - controller: ThrowingController, - bindings: {'prop': '<'} - }) - .component('c2', { - controller: LoggingController, - bindings: {'prop': '<'} - }) - .config(function($exceptionHandlerProvider) { - // We need to test with the exceptionHandler not rethrowing... - $exceptionHandlerProvider.mode('log'); + module('my'); + inject(function($compile, $rootScope) { + element = $compile('
')($rootScope); + expect(Controller1.prototype.$onInit).toHaveBeenCalledOnce(); + expect(Controller2.prototype.$onInit).toHaveBeenCalledOnce(); + }); }); - module('my'); - inject(function($compile, $rootScope, $exceptionHandler, $log) { - - // Setup the directive with bindings that will keep updating the bound value forever - element = $compile('
')($rootScope); + it('should continue to trigger other `$onInit` hooks if one throws an error', function() { + function ThrowingController() { + this.$onInit = function() { + throw new Error('bad hook'); + }; + } + function LoggingController($log) { + this.$onInit = function() { + $log.info('onInit'); + }; + } - // The first component's error should be logged - expect($exceptionHandler.errors.pop()).toEqual(new Error('bad hook')); + angular.module('my', []) + .component('c1', { + controller: ThrowingController, + bindings: {'prop': '<'} + }) + .component('c2', { + controller: LoggingController, + bindings: {'prop': '<'} + }) + .config(function($exceptionHandlerProvider) { + // We need to test with the exceptionHandler not rethrowing... + $exceptionHandlerProvider.mode('log'); + }); - // The second component's hook should still be called - expect($log.info.logs.pop()).toEqual(['onInit']); - }); - }); - }); + module('my'); + inject(function($compile, $rootScope, $exceptionHandler, $log) { + // Setup the directive with bindings that will keep updating the bound value forever + element = $compile('
')($rootScope); - describe('$onDestroy', function() { + // The first component's error should be logged + expect($exceptionHandler.errors.pop()).toEqual(new Error('bad hook')); - it('should call `$onDestroy`, if provided, on the controller when its scope is destroyed', function() { + // The second component's hook should still be called + expect($log.info.logs.pop()).toEqual(['onInit']); + }); + }); + }); - function TestController() { this.count = 0; } - TestController.prototype.$onDestroy = function() { this.count++; }; - angular.module('my', []) - .directive('d1', valueFn({ scope: true, controller: TestController })) - .directive('d2', valueFn({ scope: {}, controller: TestController })) - .directive('d3', valueFn({ controller: TestController })); + describe('$onDestroy', function() { - module('my'); - inject(function($compile, $rootScope) { + it('should call `$onDestroy`, if provided, on the controller when its scope is destroyed', function() { - element = $compile('
')($rootScope); + function TestController() { this.count = 0; } + TestController.prototype.$onDestroy = function() { this.count++; }; - $rootScope.$apply('show = [true, true, true]'); - var d1Controller = element.find('d1').controller('d1'); - var d2Controller = element.find('d2').controller('d2'); - var d3Controller = element.find('d3').controller('d3'); + angular.module('my', []) + .directive('d1', valueFn({ scope: true, controller: TestController })) + .directive('d2', valueFn({ scope: {}, controller: TestController })) + .directive('d3', valueFn({ controller: TestController })); - expect([d1Controller.count, d2Controller.count, d3Controller.count]).toEqual([0,0,0]); - $rootScope.$apply('show = [false, true, true]'); - expect([d1Controller.count, d2Controller.count, d3Controller.count]).toEqual([1,0,0]); - $rootScope.$apply('show = [false, false, true]'); - expect([d1Controller.count, d2Controller.count, d3Controller.count]).toEqual([1,1,0]); - $rootScope.$apply('show = [false, false, false]'); - expect([d1Controller.count, d2Controller.count, d3Controller.count]).toEqual([1,1,1]); - }); - }); + module('my'); + inject(function($compile, $rootScope) { + element = $compile('
')($rootScope); - it('should call `$onDestroy` top-down (the same as `scope.$broadcast`)', function() { - var log = []; - function ParentController() { log.push('parent created'); } - ParentController.prototype.$onDestroy = function() { log.push('parent destroyed'); }; - function ChildController() { log.push('child created'); } - ChildController.prototype.$onDestroy = function() { log.push('child destroyed'); }; - function GrandChildController() { log.push('grand child created'); } - GrandChildController.prototype.$onDestroy = function() { log.push('grand child destroyed'); }; + $rootScope.$apply('show = [true, true, true]'); + var d1Controller = element.find('d1').controller('d1'); + var d2Controller = element.find('d2').controller('d2'); + var d3Controller = element.find('d3').controller('d3'); - angular.module('my', []) - .directive('parent', valueFn({ scope: true, controller: ParentController })) - .directive('child', valueFn({ scope: true, controller: ChildController })) - .directive('grandChild', valueFn({ scope: true, controller: GrandChildController })); + expect([d1Controller.count, d2Controller.count, d3Controller.count]).toEqual([0,0,0]); + $rootScope.$apply('show = [false, true, true]'); + expect([d1Controller.count, d2Controller.count, d3Controller.count]).toEqual([1,0,0]); + $rootScope.$apply('show = [false, false, true]'); + expect([d1Controller.count, d2Controller.count, d3Controller.count]).toEqual([1,1,0]); + $rootScope.$apply('show = [false, false, false]'); + expect([d1Controller.count, d2Controller.count, d3Controller.count]).toEqual([1,1,1]); + }); + }); - module('my'); - inject(function($compile, $rootScope) { - element = $compile('')($rootScope); - $rootScope.$apply('show = true'); - expect(log).toEqual(['parent created', 'child created', 'grand child created']); - log = []; - $rootScope.$apply('show = false'); - expect(log).toEqual(['parent destroyed', 'child destroyed', 'grand child destroyed']); - }); - }); - }); + it('should call `$onDestroy` top-down (the same as `scope.$broadcast`)', function() { + var log = []; + function ParentController() { log.push('parent created'); } + ParentController.prototype.$onDestroy = function() { log.push('parent destroyed'); }; + function ChildController() { log.push('child created'); } + ChildController.prototype.$onDestroy = function() { log.push('child destroyed'); }; + function GrandChildController() { log.push('grand child created'); } + GrandChildController.prototype.$onDestroy = function() { log.push('grand child destroyed'); }; + angular.module('my', []) + .directive('parent', valueFn({ scope: true, controller: ParentController })) + .directive('child', valueFn({ scope: true, controller: ChildController })) + .directive('grandChild', valueFn({ scope: true, controller: GrandChildController })); - describe('$postLink', function() { + module('my'); + inject(function($compile, $rootScope) { - it('should call `$postLink`, if provided, after the element has completed linking (i.e. post-link)', function() { + element = $compile('')($rootScope); + $rootScope.$apply('show = true'); + expect(log).toEqual(['parent created', 'child created', 'grand child created']); + log = []; + $rootScope.$apply('show = false'); + expect(log).toEqual(['parent destroyed', 'child destroyed', 'grand child destroyed']); + }); + }); + }); - var log = []; - function Controller1() { } - Controller1.prototype.$postLink = function() { log.push('d1 view init'); }; + describe('$postLink', function() { - function Controller2() { } - Controller2.prototype.$postLink = function() { log.push('d2 view init'); }; + it('should call `$postLink`, if provided, after the element has completed linking (i.e. post-link)', function() { - angular.module('my', []) - .directive('d1', valueFn({ - controller: Controller1, - link: { pre: function(s, e) { log.push('d1 pre: ' + e.text()); }, post: function(s, e) { log.push('d1 post: ' + e.text()); } }, - template: '' - })) - .directive('d2', valueFn({ - controller: Controller2, - link: { pre: function(s, e) { log.push('d2 pre: ' + e.text()); }, post: function(s, e) { log.push('d2 post: ' + e.text()); } }, - template: 'loaded' - })); + var log = []; - module('my'); - inject(function($compile, $rootScope) { - element = $compile('')($rootScope); - expect(log).toEqual([ - 'd1 pre: loaded', - 'd2 pre: loaded', - 'd2 post: loaded', - 'd2 view init', - 'd1 post: loaded', - 'd1 view init' - ]); - }); - }); - }); + function Controller1() { } + Controller1.prototype.$postLink = function() { log.push('d1 view init'); }; - describe('$doCheck', function() { - it('should call `$doCheck`, if provided, for each digest cycle, after $onChanges and $onInit', function() { - var log = []; + function Controller2() { } + Controller2.prototype.$postLink = function() { log.push('d2 view init'); }; - function TestController() { } - TestController.prototype.$doCheck = function() { log.push('$doCheck'); }; - TestController.prototype.$onChanges = function() { log.push('$onChanges'); }; - TestController.prototype.$onInit = function() { log.push('$onInit'); }; + angular.module('my', []) + .directive('d1', valueFn({ + controller: Controller1, + link: { pre: function(s, e) { log.push('d1 pre: ' + e.text()); }, post: function(s, e) { log.push('d1 post: ' + e.text()); } }, + template: '' + })) + .directive('d2', valueFn({ + controller: Controller2, + link: { pre: function(s, e) { log.push('d2 pre: ' + e.text()); }, post: function(s, e) { log.push('d2 post: ' + e.text()); } }, + template: 'loaded' + })); - angular.module('my', []) - .component('dcc', { - controller: TestController, - bindings: { 'prop1': '<' } + module('my'); + inject(function($compile, $rootScope) { + element = $compile('')($rootScope); + expect(log).toEqual([ + 'd1 pre: loaded', + 'd2 pre: loaded', + 'd2 post: loaded', + 'd2 view init', + 'd1 post: loaded', + 'd1 view init' + ]); + }); }); + }); - module('my'); - inject(function($compile, $rootScope) { - element = $compile('')($rootScope); - expect(log).toEqual([ - '$onChanges', - '$onInit', - '$doCheck' - ]); + describe('$doCheck', function() { + it('should call `$doCheck`, if provided, for each digest cycle, after $onChanges and $onInit', function() { + var log = []; - // Clear log - log = []; + function TestController() { } + TestController.prototype.$doCheck = function() { log.push('$doCheck'); }; + TestController.prototype.$onChanges = function() { log.push('$onChanges'); }; + TestController.prototype.$onInit = function() { log.push('$onInit'); }; - $rootScope.$apply(); - expect(log).toEqual([ - '$doCheck', - '$doCheck' - ]); + angular.module('my', []) + .component('dcc', { + controller: TestController, + bindings: { 'prop1': '<' } + }); - // Clear log - log = []; + module('my'); + inject(function($compile, $rootScope) { + element = $compile('')($rootScope); + expect(log).toEqual([ + '$onChanges', + '$onInit', + '$doCheck' + ]); + + // Clear log + log = []; + + $rootScope.$apply(); + expect(log).toEqual([ + '$doCheck', + '$doCheck' + ]); + + // Clear log + log = []; + + $rootScope.$apply('val = 2'); + expect(log).toEqual([ + '$doCheck', + '$onChanges', + '$doCheck' + ]); + }); + }); - $rootScope.$apply('val = 2'); - expect(log).toEqual([ - '$doCheck', - '$onChanges', - '$doCheck' - ]); - }); - }); + it('should work if $doCheck is provided in the constructor', function() { + var log = []; - it('should work if $doCheck is provided in the constructor', function() { - var log = []; + function TestController() { + this.$doCheck = function() { log.push('$doCheck'); }; + this.$onChanges = function() { log.push('$onChanges'); }; + this.$onInit = function() { log.push('$onInit'); }; + } - function TestController() { - this.$doCheck = function() { log.push('$doCheck'); }; - this.$onChanges = function() { log.push('$onChanges'); }; - this.$onInit = function() { log.push('$onInit'); }; - } + angular.module('my', []) + .component('dcc', { + controller: TestController, + bindings: { 'prop1': '<' } + }); - angular.module('my', []) - .component('dcc', { - controller: TestController, - bindings: { 'prop1': '<' } + module('my'); + inject(function($compile, $rootScope) { + element = $compile('')($rootScope); + expect(log).toEqual([ + '$onChanges', + '$onInit', + '$doCheck' + ]); + + // Clear log + log = []; + + $rootScope.$apply(); + expect(log).toEqual([ + '$doCheck', + '$doCheck' + ]); + + // Clear log + log = []; + + $rootScope.$apply('val = 2'); + expect(log).toEqual([ + '$doCheck', + '$onChanges', + '$doCheck' + ]); + }); }); + }); - module('my'); - inject(function($compile, $rootScope) { - element = $compile('')($rootScope); - expect(log).toEqual([ - '$onChanges', - '$onInit', - '$doCheck' - ]); - - // Clear log - log = []; + describe('$onChanges', function() { - $rootScope.$apply(); - expect(log).toEqual([ - '$doCheck', - '$doCheck' - ]); + it('should call `$onChanges`, if provided, when a one-way (`<`) or interpolation (`@`) bindings are updated', function() { + var log = []; + function TestController() { } + TestController.prototype.$onChanges = function(change) { log.push(change); }; - // Clear log - log = []; + angular.module('my', []) + .component('c1', { + controller: TestController, + bindings: { 'prop1': '<', 'prop2': '<', 'other': '=', 'attr': '@' } + }); - $rootScope.$apply('val = 2'); - expect(log).toEqual([ - '$doCheck', - '$onChanges', - '$doCheck' - ]); - }); - }); - }); + module('my'); + inject(function($compile, $rootScope) { + // Setup a watch to indicate some complicated updated logic + $rootScope.$watch('val', function(val, oldVal) { $rootScope.val2 = val * 2; }); + // Setup the directive with two bindings + element = $compile('')($rootScope); + + expect(log).toEqual([ + { + prop1: jasmine.objectContaining({currentValue: undefined}), + prop2: jasmine.objectContaining({currentValue: undefined}), + attr: jasmine.objectContaining({currentValue: ''}) + } + ]); - describe('$onChanges', function() { + // Clear the initial changes from the log + log = []; - it('should call `$onChanges`, if provided, when a one-way (`<`) or interpolation (`@`) bindings are updated', function() { - var log = []; - function TestController() { } - TestController.prototype.$onChanges = function(change) { log.push(change); }; + // Update val to trigger the onChanges + $rootScope.$apply('val = 42'); - angular.module('my', []) - .component('c1', { - controller: TestController, - bindings: { 'prop1': '<', 'prop2': '<', 'other': '=', 'attr': '@' } + // Now we should have a single changes entry in the log + expect(log).toEqual([ + { + prop1: jasmine.objectContaining({currentValue: 42}), + prop2: jasmine.objectContaining({currentValue: 84}) + } + ]); + + // Clear the log + log = []; + + // Update val to trigger the onChanges + $rootScope.$apply('val = 17'); + // Now we should have a single changes entry in the log + expect(log).toEqual([ + { + prop1: jasmine.objectContaining({previousValue: 42, currentValue: 17}), + prop2: jasmine.objectContaining({previousValue: 84, currentValue: 34}) + } + ]); + + // Clear the log + log = []; + + // Update val3 to trigger the "other" two-way binding + $rootScope.$apply('val3 = 63'); + // onChanges should not have been called + expect(log).toEqual([]); + + // Update val4 to trigger the "attr" interpolation binding + $rootScope.$apply('val4 = 22'); + // onChanges should not have been called + expect(log).toEqual([ + { + attr: jasmine.objectContaining({previousValue: '', currentValue: '22'}) + } + ]); + }); }); - module('my'); - inject(function($compile, $rootScope) { - // Setup a watch to indicate some complicated updated logic - $rootScope.$watch('val', function(val, oldVal) { $rootScope.val2 = val * 2; }); - // Setup the directive with two bindings - element = $compile('')($rootScope); - - expect(log).toEqual([ - { - prop1: jasmine.objectContaining({currentValue: undefined}), - prop2: jasmine.objectContaining({currentValue: undefined}), - attr: jasmine.objectContaining({currentValue: ''}) - } - ]); - - // Clear the initial changes from the log - log = []; - // Update val to trigger the onChanges - $rootScope.$apply('val = 42'); + it('should trigger `$onChanges` even if the inner value already equals the new outer value', function() { + var log = []; + function TestController() { } + TestController.prototype.$onChanges = function(change) { log.push(change); }; - // Now we should have a single changes entry in the log - expect(log).toEqual([ - { - prop1: jasmine.objectContaining({currentValue: 42}), - prop2: jasmine.objectContaining({currentValue: 84}) - } - ]); - - // Clear the log - log = []; - - // Update val to trigger the onChanges - $rootScope.$apply('val = 17'); - // Now we should have a single changes entry in the log - expect(log).toEqual([ - { - prop1: jasmine.objectContaining({previousValue: 42, currentValue: 17}), - prop2: jasmine.objectContaining({previousValue: 84, currentValue: 34}) - } - ]); - - // Clear the log - log = []; - - // Update val3 to trigger the "other" two-way binding - $rootScope.$apply('val3 = 63'); - // onChanges should not have been called - expect(log).toEqual([]); - - // Update val4 to trigger the "attr" interpolation binding - $rootScope.$apply('val4 = 22'); - // onChanges should not have been called - expect(log).toEqual([ - { - attr: jasmine.objectContaining({previousValue: '', currentValue: '22'}) - } - ]); - }); - }); + angular.module('my', []) + .component('c1', { + controller: TestController, + bindings: { 'prop1': '<' } + }); + module('my'); + inject(function($compile, $rootScope) { + element = $compile('')($rootScope); - it('should trigger `$onChanges` even if the inner value already equals the new outer value', function() { - var log = []; - function TestController() { } - TestController.prototype.$onChanges = function(change) { log.push(change); }; + $rootScope.$apply('val = 1'); + expect(log.pop()).toEqual({prop1: jasmine.objectContaining({previousValue: undefined, currentValue: 1})}); - angular.module('my', []) - .component('c1', { - controller: TestController, - bindings: { 'prop1': '<' } + element.isolateScope().$ctrl.prop1 = 2; + $rootScope.$apply('val = 2'); + expect(log.pop()).toEqual({prop1: jasmine.objectContaining({previousValue: 1, currentValue: 2})}); + }); }); - module('my'); - inject(function($compile, $rootScope) { - element = $compile('')($rootScope); - $rootScope.$apply('val = 1'); - expect(log.pop()).toEqual({prop1: jasmine.objectContaining({previousValue: undefined, currentValue: 1})}); + it('should pass the original value as `previousValue` even if there were multiple changes in a single digest', function() { + var log = []; + function TestController() { } + TestController.prototype.$onChanges = function(change) { log.push(change); }; - element.isolateScope().$ctrl.prop1 = 2; - $rootScope.$apply('val = 2'); - expect(log.pop()).toEqual({prop1: jasmine.objectContaining({previousValue: 1, currentValue: 2})}); - }); - }); + angular.module('my', []) + .component('c1', { + controller: TestController, + bindings: { 'prop': '<' } + }); + module('my'); + inject(function($compile, $rootScope) { + element = $compile('')($rootScope); - it('should pass the original value as `previousValue` even if there were multiple changes in a single digest', function() { - var log = []; - function TestController() { } - TestController.prototype.$onChanges = function(change) { log.push(change); }; + // We add this watch after the compilation to ensure that it will run after the binding watchers + // therefore triggering the thing that this test is hoping to enfore + $rootScope.$watch('a', function(val) { $rootScope.b = val * 2; }); - angular.module('my', []) - .component('c1', { - controller: TestController, - bindings: { 'prop': '<' } - }); + expect(log).toEqual([{prop: jasmine.objectContaining({currentValue: undefined})}]); - module('my'); - inject(function($compile, $rootScope) { - element = $compile('')($rootScope); + // Clear the initial values from the log + log = []; - // We add this watch after the compilation to ensure that it will run after the binding watchers - // therefore triggering the thing that this test is hoping to enfore - $rootScope.$watch('a', function(val) { $rootScope.b = val * 2; }); + // Update val to trigger the onChanges + $rootScope.$apply('a = 42'); + // Now the change should have the real previous value (undefined), not the intermediate one (42) + expect(log).toEqual([{prop: jasmine.objectContaining({currentValue: 126})}]); - expect(log).toEqual([{prop: jasmine.objectContaining({currentValue: undefined})}]); + // Clear the log + log = []; - // Clear the initial values from the log - log = []; + // Update val to trigger the onChanges + $rootScope.$apply('a = 7'); + // Now the change should have the real previous value (126), not the intermediate one, (91) + expect(log).toEqual([{prop: jasmine.objectContaining({previousValue: 126, currentValue: 21})}]); + }); + }); - // Update val to trigger the onChanges - $rootScope.$apply('a = 42'); - // Now the change should have the real previous value (undefined), not the intermediate one (42) - expect(log).toEqual([{prop: jasmine.objectContaining({currentValue: 126})}]); - // Clear the log - log = []; + it('should trigger an initial onChanges call for each binding with the `isFirstChange()` returning true', function() { + var log = []; + function TestController() { } + TestController.prototype.$onChanges = function(change) { log.push(change); }; - // Update val to trigger the onChanges - $rootScope.$apply('a = 7'); - // Now the change should have the real previous value (126), not the intermediate one, (91) - expect(log).toEqual([{prop: jasmine.objectContaining({previousValue: 126, currentValue: 21})}]); - }); - }); + angular.module('my', []) + .component('c1', { + controller: TestController, + bindings: { 'prop': '<', attr: '@' } + }); + module('my'); + inject(function($compile, $rootScope) { - it('should trigger an initial onChanges call for each binding with the `isFirstChange()` returning true', function() { - var log = []; - function TestController() { } - TestController.prototype.$onChanges = function(change) { log.push(change); }; + $rootScope.$apply('a = 7'); + element = $compile('')($rootScope); - angular.module('my', []) - .component('c1', { - controller: TestController, - bindings: { 'prop': '<', attr: '@' } + expect(log).toEqual([ + { + prop: jasmine.objectContaining({currentValue: 7}), + attr: jasmine.objectContaining({currentValue: '7'}) + } + ]); + expect(log[0].prop.isFirstChange()).toEqual(true); + expect(log[0].attr.isFirstChange()).toEqual(true); + + log = []; + $rootScope.$apply('a = 9'); + expect(log).toEqual([ + { + prop: jasmine.objectContaining({previousValue: 7, currentValue: 9}), + attr: jasmine.objectContaining({previousValue: '7', currentValue: '9'}) + } + ]); + expect(log[0].prop.isFirstChange()).toEqual(false); + expect(log[0].attr.isFirstChange()).toEqual(false); + }); }); - module('my'); - inject(function($compile, $rootScope) { - - $rootScope.$apply('a = 7'); - element = $compile('')($rootScope); - expect(log).toEqual([ - { - prop: jasmine.objectContaining({currentValue: 7}), - attr: jasmine.objectContaining({currentValue: '7'}) + it('should trigger an initial onChanges call for each binding even if the hook is defined in the constructor', function() { + var log = []; + function TestController() { + this.$onChanges = function(change) { log.push(change); }; } - ]); - expect(log[0].prop.isFirstChange()).toEqual(true); - expect(log[0].attr.isFirstChange()).toEqual(true); - - log = []; - $rootScope.$apply('a = 9'); - expect(log).toEqual([ - { - prop: jasmine.objectContaining({previousValue: 7, currentValue: 9}), - attr: jasmine.objectContaining({previousValue: '7', currentValue: '9'}) - } - ]); - expect(log[0].prop.isFirstChange()).toEqual(false); - expect(log[0].attr.isFirstChange()).toEqual(false); - }); - }); + angular.module('my', []) + .component('c1', { + controller: TestController, + bindings: { 'prop': '<', attr: '@' } + }); - it('should trigger an initial onChanges call for each binding even if the hook is defined in the constructor', function() { - var log = []; - function TestController() { - this.$onChanges = function(change) { log.push(change); }; - } + module('my'); + inject(function($compile, $rootScope) { + $rootScope.$apply('a = 7'); + element = $compile('')($rootScope); - angular.module('my', []) - .component('c1', { - controller: TestController, - bindings: { 'prop': '<', attr: '@' } + expect(log).toEqual([ + { + prop: jasmine.objectContaining({currentValue: 7}), + attr: jasmine.objectContaining({currentValue: '7'}) + } + ]); + expect(log[0].prop.isFirstChange()).toEqual(true); + expect(log[0].attr.isFirstChange()).toEqual(true); + + log = []; + $rootScope.$apply('a = 10'); + expect(log).toEqual([ + { + prop: jasmine.objectContaining({previousValue: 7, currentValue: 10}), + attr: jasmine.objectContaining({previousValue: '7', currentValue: '10'}) + } + ]); + expect(log[0].prop.isFirstChange()).toEqual(false); + expect(log[0].attr.isFirstChange()).toEqual(false); + }); }); - module('my'); - inject(function($compile, $rootScope) { - $rootScope.$apply('a = 7'); - element = $compile('')($rootScope); - expect(log).toEqual([ - { - prop: jasmine.objectContaining({currentValue: 7}), - attr: jasmine.objectContaining({currentValue: '7'}) - } - ]); - expect(log[0].prop.isFirstChange()).toEqual(true); - expect(log[0].attr.isFirstChange()).toEqual(true); - - log = []; - $rootScope.$apply('a = 10'); - expect(log).toEqual([ - { - prop: jasmine.objectContaining({previousValue: 7, currentValue: 10}), - attr: jasmine.objectContaining({previousValue: '7', currentValue: '10'}) - } - ]); - expect(log[0].prop.isFirstChange()).toEqual(false); - expect(log[0].attr.isFirstChange()).toEqual(false); - }); - }); + it('should not call `$onChanges` twice even when the initial value is `NaN`', function() { + var onChangesSpy = jasmine.createSpy('$onChanges'); + module(function($compileProvider) { + $compileProvider.component('test', { + bindings: {prop: '<', attr: '@'}, + controller: function TestController() { + this.$onChanges = onChangesSpy; + } + }); + }); - it('should only trigger one extra digest however many controllers have changes', function() { - var log = []; - function TestController1() { } - TestController1.prototype.$onChanges = function(change) { log.push(['TestController1', change]); }; - function TestController2() { } - TestController2.prototype.$onChanges = function(change) { log.push(['TestController2', change]); }; + inject(function($compile, $rootScope) { + var template = '' + + ''; + $rootScope.a = 'foo'; + $rootScope.b = NaN; - angular.module('my', []) - .component('c1', { - controller: TestController1, - bindings: {'prop': '<'} - }) - .component('c2', { - controller: TestController2, - bindings: {'prop': '<'} - }); + element = $compile(template)($rootScope); + $rootScope.$digest(); - module('my'); - inject(function($compile, $rootScope) { + expect(onChangesSpy).toHaveBeenCalledTimes(2); + expect(onChangesSpy.calls.argsFor(0)[0]).toEqual({ + prop: jasmine.objectContaining({currentValue: 'foo'}), + attr: jasmine.objectContaining({currentValue: 'foo'}) + }); + expect(onChangesSpy.calls.argsFor(1)[0]).toEqual({ + prop: jasmine.objectContaining({currentValue: NaN}), + attr: jasmine.objectContaining({currentValue: 'NaN'}) + }); - // Create a watcher to count the number of digest cycles - var watchCount = 0; - $rootScope.$watch(function() { watchCount++; }); + onChangesSpy.calls.reset(); + $rootScope.$apply('a = "bar"; b = 42'); - // Setup two sibling components with bindings that will change - element = $compile('
')($rootScope); + expect(onChangesSpy).toHaveBeenCalledTimes(2); + expect(onChangesSpy.calls.argsFor(0)[0]).toEqual({ + prop: jasmine.objectContaining({previousValue: 'foo', currentValue: 'bar'}), + attr: jasmine.objectContaining({previousValue: 'foo', currentValue: 'bar'}) + }); + expect(onChangesSpy.calls.argsFor(1)[0]).toEqual({ + prop: jasmine.objectContaining({previousValue: NaN, currentValue: 42}), + attr: jasmine.objectContaining({previousValue: 'NaN', currentValue: '42'}) + }); + }); + }); - // Clear out initial changes - log = []; - // Update val to trigger the onChanges - $rootScope.$apply('val1 = 42; val2 = 17'); + it('should only trigger one extra digest however many controllers have changes', function() { + var log = []; + function TestController1() { } + TestController1.prototype.$onChanges = function(change) { log.push(['TestController1', change]); }; + function TestController2() { } + TestController2.prototype.$onChanges = function(change) { log.push(['TestController2', change]); }; - expect(log).toEqual([ - ['TestController1', {prop: jasmine.objectContaining({currentValue: 42})}], - ['TestController2', {prop: jasmine.objectContaining({currentValue: 17})}] - ]); - // A single apply should only trigger three turns of the digest loop - expect(watchCount).toEqual(3); - }); - }); + angular.module('my', []) + .component('c1', { + controller: TestController1, + bindings: {'prop': '<'} + }) + .component('c2', { + controller: TestController2, + bindings: {'prop': '<'} + }); + module('my'); + inject(function($compile, $rootScope) { - it('should cope with changes occuring inside `$onChanges()` hooks', function() { - var log = []; - function OuterController() { - this.prop1 = 0; - } - OuterController.prototype.$onChanges = function(change) { - log.push(['OuterController', change]); - // Make a change to the inner component - this.b = this.prop1 * 2; - }; + // Create a watcher to count the number of digest cycles + var watchCount = 0; + $rootScope.$watch(function() { watchCount++; }); - function InnerController() { } - InnerController.prototype.$onChanges = function(change) { log.push(['InnerController', change]); }; + // Setup two sibling components with bindings that will change + element = $compile('
')($rootScope); - angular.module('my', []) - .component('outer', { - controller: OuterController, - bindings: {'prop1': '<'}, - template: '' - }) - .component('inner', { - controller: InnerController, - bindings: {'prop2': '<'} + // Clear out initial changes + log = []; + + // Update val to trigger the onChanges + $rootScope.$apply('val1 = 42; val2 = 17'); + + expect(log).toEqual([ + ['TestController1', {prop: jasmine.objectContaining({currentValue: 42})}], + ['TestController2', {prop: jasmine.objectContaining({currentValue: 17})}] + ]); + // A single apply should only trigger three turns of the digest loop + expect(watchCount).toEqual(3); + }); }); - module('my'); - inject(function($compile, $rootScope) { - // Setup the directive with two bindings - element = $compile('')($rootScope); + it('should cope with changes occuring inside `$onChanges()` hooks', function() { + var log = []; + function OuterController() {} + OuterController.prototype.$onChanges = function(change) { + log.push(['OuterController', change]); + // Make a change to the inner component + this.b = this.prop1 * 2; + }; - // Clear out initial changes - log = []; + function InnerController() { } + InnerController.prototype.$onChanges = function(change) { log.push(['InnerController', change]); }; - // Update val to trigger the onChanges - $rootScope.$apply('a = 42'); + angular.module('my', []) + .component('outer', { + controller: OuterController, + bindings: {'prop1': '<'}, + template: '' + }) + .component('inner', { + controller: InnerController, + bindings: {'prop2': '<'} + }); - expect(log).toEqual([ - ['OuterController', {prop1: jasmine.objectContaining({currentValue: 42})}], - ['InnerController', {prop2: jasmine.objectContaining({currentValue: 84})}] - ]); - }); - }); + module('my'); + inject(function($compile, $rootScope) { + // Setup the directive with two bindings + element = $compile('')($rootScope); - it('should throw an error if `$onChanges()` hooks are not stable', function() { - function TestController() {} - TestController.prototype.$onChanges = function(change) { - this.onChange(); - }; + // Clear out initial changes + log = []; + + // Update val to trigger the onChanges + $rootScope.$apply('a = 42'); - angular.module('my', []) - .component('c1', { - controller: TestController, - bindings: {'prop': '<', onChange: '&'} + expect(log).toEqual([ + ['OuterController', {prop1: jasmine.objectContaining({previousValue: undefined, currentValue: 42})}], + ['InnerController', {prop2: jasmine.objectContaining({previousValue: NaN, currentValue: 84})}] + ]); + }); }); - module('my'); - inject(function($compile, $rootScope) { - // Setup the directive with bindings that will keep updating the bound value forever - element = $compile('')($rootScope); + it('should throw an error if `$onChanges()` hooks are not stable', function() { + function TestController() {} + TestController.prototype.$onChanges = function(change) { + this.onChange(); + }; - // Update val to trigger the unstable onChanges, which will result in an error - expect(function() { - $rootScope.$apply('a = 42'); - }).toThrowMinErr('$compile', 'infchng'); + angular.module('my', []) + .component('c1', { + controller: TestController, + bindings: {'prop': '<', onChange: '&'} + }); - dealoc(element); - element = $compile('')($rootScope); - $rootScope.$apply('b = 24'); - $rootScope.$apply('b = 48'); - }); - }); + module('my'); + inject(function($compile, $rootScope) { + // Setup the directive with bindings that will keep updating the bound value forever + element = $compile('')($rootScope); - it('should log an error if `$onChanges()` hooks are not stable', function() { - function TestController() {} - TestController.prototype.$onChanges = function(change) { - this.onChange(); - }; + // Update val to trigger the unstable onChanges, which will result in an error + expect(function() { + $rootScope.$apply('a = 42'); + }).toThrowMinErr('$compile', 'infchng'); - angular.module('my', []) - .component('c1', { - controller: TestController, - bindings: {'prop': '<', onChange: '&'} - }) - .config(function($exceptionHandlerProvider) { - // We need to test with the exceptionHandler not rethrowing... - $exceptionHandlerProvider.mode('log'); + dealoc(element); + element = $compile('')($rootScope); + $rootScope.$apply('b = 24'); + $rootScope.$apply('b = 48'); + }); }); - module('my'); - inject(function($compile, $rootScope, $exceptionHandler) { - // Setup the directive with bindings that will keep updating the bound value forever - element = $compile('')($rootScope); + it('should log an error if `$onChanges()` hooks are not stable', function() { + function TestController() {} + TestController.prototype.$onChanges = function(change) { + this.onChange(); + }; - // Update val to trigger the unstable onChanges, which will result in an error - $rootScope.$apply('a = 42'); - expect($exceptionHandler.errors.length).toEqual(1); - expect($exceptionHandler.errors[0].toString()).toContain('[$compile:infchng] 10 $onChanges() iterations reached.'); - }); - }); + angular.module('my', []) + .component('c1', { + controller: TestController, + bindings: {'prop': '<', onChange: '&'} + }) + .config(function($exceptionHandlerProvider) { + // We need to test with the exceptionHandler not rethrowing... + $exceptionHandlerProvider.mode('log'); + }); + module('my'); + inject(function($compile, $rootScope, $exceptionHandler) { - it('should continue to trigger other `$onChanges` hooks if one throws an error', function() { - function ThrowingController() { - this.$onChanges = function(change) { - throw new Error('bad hook'); - }; - } - function LoggingController($log) { - this.$onChanges = function(change) { - $log.info('onChange'); - }; - } + // Setup the directive with bindings that will keep updating the bound value forever + element = $compile('')($rootScope); - angular.module('my', []) - .component('c1', { - controller: ThrowingController, - bindings: {'prop': '<'} - }) - .component('c2', { - controller: LoggingController, - bindings: {'prop': '<'} - }) - .config(function($exceptionHandlerProvider) { - // We need to test with the exceptionHandler not rethrowing... - $exceptionHandlerProvider.mode('log'); + // Update val to trigger the unstable onChanges, which will result in an error + $rootScope.$apply('a = 42'); + expect($exceptionHandler.errors.length).toEqual(1); + expect($exceptionHandler.errors[0].toString()).toContain('[$compile:infchng] 10 $onChanges() iterations reached.'); + }); }); - module('my'); - inject(function($compile, $rootScope, $exceptionHandler, $log) { - // Setup the directive with bindings that will keep updating the bound value forever - element = $compile('
')($rootScope); + it('should continue to trigger other `$onChanges` hooks if one throws an error', function() { + function ThrowingController() { + this.$onChanges = function(change) { + throw new Error('bad hook'); + }; + } + function LoggingController($log) { + this.$onChanges = function(change) { + $log.info('onChange'); + }; + } - // The first component's error should be logged - expect($exceptionHandler.errors.pop()).toEqual(new Error('bad hook')); + angular.module('my', []) + .component('c1', { + controller: ThrowingController, + bindings: {'prop': '<'} + }) + .component('c2', { + controller: LoggingController, + bindings: {'prop': '<'} + }) + .config(function($exceptionHandlerProvider) { + // We need to test with the exceptionHandler not rethrowing... + $exceptionHandlerProvider.mode('log'); + }); - // The second component's changes should still be called - expect($log.info.logs.pop()).toEqual(['onChange']); + module('my'); + inject(function($compile, $rootScope, $exceptionHandler, $log) { - $rootScope.$apply('a = 42'); + // Setup the directive with bindings that will keep updating the bound value forever + element = $compile('
')($rootScope); - // The first component's error should be logged - var errors = $exceptionHandler.errors.pop(); - expect(errors[0]).toEqual(new Error('bad hook')); + // The first component's error should be logged + expect($exceptionHandler.errors.pop()).toEqual(new Error('bad hook')); - // The second component's changes should still be called - expect($log.info.logs.pop()).toEqual(['onChange']); - }); - }); + // The second component's changes should still be called + expect($log.info.logs.pop()).toEqual(['onChange']); + $rootScope.$apply('a = 42'); - it('should collect up all `$onChanges` errors into one throw', function() { - function ThrowingController() { - this.$onChanges = function(change) { - throw new Error('bad hook: ' + this.prop); - }; - } + // The first component's error should be logged + var errors = $exceptionHandler.errors.pop(); + expect(errors[0]).toEqual(new Error('bad hook')); - angular.module('my', []) - .component('c1', { - controller: ThrowingController, - bindings: {'prop': '<'} - }) - .config(function($exceptionHandlerProvider) { - // We need to test with the exceptionHandler not rethrowing... - $exceptionHandlerProvider.mode('log'); + // The second component's changes should still be called + expect($log.info.logs.pop()).toEqual(['onChange']); + }); }); - module('my'); - inject(function($compile, $rootScope, $exceptionHandler, $log) { - // Setup the directive with bindings that will keep updating the bound value forever - element = $compile('
')($rootScope); + it('should collect up all `$onChanges` errors into one throw', function() { + function ThrowingController() { + this.$onChanges = function(change) { + throw new Error('bad hook: ' + this.prop); + }; + } - // Both component's errors should be logged - expect($exceptionHandler.errors.pop()).toEqual(new Error('bad hook: NaN')); - expect($exceptionHandler.errors.pop()).toEqual(new Error('bad hook: undefined')); + angular.module('my', []) + .component('c1', { + controller: ThrowingController, + bindings: {'prop': '<'} + }) + .config(function($exceptionHandlerProvider) { + // We need to test with the exceptionHandler not rethrowing... + $exceptionHandlerProvider.mode('log'); + }); - $rootScope.$apply('a = 42'); + module('my'); + inject(function($compile, $rootScope, $exceptionHandler, $log) { - // Both component's error should be logged - var errors = $exceptionHandler.errors.pop(); - expect(errors.pop()).toEqual(new Error('bad hook: 84')); - expect(errors.pop()).toEqual(new Error('bad hook: 42')); - }); - }); - }); - }); + // Setup the directive with bindings that will keep updating the bound value forever + element = $compile('
')($rootScope); + // Both component's errors should be logged + expect($exceptionHandler.errors.pop()).toEqual(new Error('bad hook: NaN')); + expect($exceptionHandler.errors.pop()).toEqual(new Error('bad hook: undefined')); - describe('isolated locals', function() { - var componentScope, regularScope; + $rootScope.$apply('a = 42'); - beforeEach(module(function() { - directive('myComponent', function() { - return { - scope: { - attr: '@', - attrAlias: '@attr', - ref: '=', - refAlias: '= ref', - reference: '=', - optref: '=?', - optrefAlias: '=? optref', - optreference: '=?', - colref: '=*', - colrefAlias: '=* colref', - owRef: '<', - owRefAlias: '< owRef', - owOptref: '
'); - $rootScope.$apply(function() { - $rootScope.value = 'from-parent'; - }); - expect(element.find('input').val()).toBe('from-parent'); - expect(componentScope).not.toBe(regularScope); - expect(componentScope.$parent).toBe(regularScope); - })); + describe('isolated locals', function() { + var componentScope, regularScope; + beforeEach(module(function() { + directive('myComponent', function() { + return { + scope: { + attr: '@', + attrAlias: '@attr', + ref: '=', + refAlias: '= ref', + reference: '=', + optref: '=?', + optrefAlias: '=? optref', + optreference: '=?', + colref: '=*', + colrefAlias: '=* colref', + owRef: '<', + owRefAlias: '< owRef', + owOptref: ''); - $rootScope.$apply(function() { - $rootScope.value = 'from-parent'; - }); + it('should give other directives the parent scope', inject(function($rootScope) { + compile('
'); + $rootScope.$apply(function() { + $rootScope.value = 'from-parent'; + }); + expect(element.find('input').val()).toBe('from-parent'); + expect(componentScope).not.toBe(regularScope); + expect(componentScope.$parent).toBe(regularScope); + })); - expect(element.html()).toBe('value: from-parent'); - }); - }); + it('should not give the isolate scope to other directive template', function() { + module(function() { + directive('otherTplDir', function() { + return { + template: 'value: {{value}}' + }; + }); + }); - it('should not give the isolate scope to other directive template (with templateUrl)', function() { - module(function() { - directive('otherTplDir', function() { - return { - templateUrl: 'other.html' - }; - }); - }); + inject(function($rootScope) { + compile('
'); - inject(function($rootScope, $templateCache) { - $templateCache.put('other.html', 'value: {{value}}'); - compile('
'); + $rootScope.$apply(function() { + $rootScope.value = 'from-parent'; + }); - $rootScope.$apply(function() { - $rootScope.value = 'from-parent'; + expect(element.html()).toBe('value: from-parent'); + }); }); - expect(element.html()).toBe('value: from-parent'); - }); - }); + it('should not give the isolate scope to other directive template (with templateUrl)', function() { + module(function() { + directive('otherTplDir', function() { + return { + templateUrl: 'other.html' + }; + }); + }); - it('should not give the isolate scope to regular child elements', function() { - inject(function($rootScope) { - compile('
value: {{value}}
'); + inject(function($rootScope, $templateCache) { + $templateCache.put('other.html', 'value: {{value}}'); + compile('
'); - $rootScope.$apply(function() { - $rootScope.value = 'from-parent'; + $rootScope.$apply(function() { + $rootScope.value = 'from-parent'; + }); + + expect(element.html()).toBe('value: from-parent'); + }); }); - expect(element.html()).toBe('value: from-parent'); - }); - }); + it('should not give the isolate scope to regular child elements', function() { + inject(function($rootScope) { + compile('
value: {{value}}
'); - it('should update parent scope when "="-bound NaN changes', inject(function($compile, $rootScope) { - $rootScope.num = NaN; - compile('
'); - var isolateScope = element.isolateScope(); - expect(isolateScope.reference).toBeNaN(); + $rootScope.$apply(function() { + $rootScope.value = 'from-parent'; + }); - isolateScope.$apply(function(scope) { scope.reference = 64; }); - expect($rootScope.num).toBe(64); - })); + expect(element.html()).toBe('value: from-parent'); + }); + }); - it('should update isolate scope when "="-bound NaN changes', inject(function($compile, $rootScope) { - $rootScope.num = NaN; - compile('
'); - var isolateScope = element.isolateScope(); - expect(isolateScope.reference).toBeNaN(); + it('should update parent scope when "="-bound NaN changes', inject(function($compile, $rootScope) { + $rootScope.num = NaN; + compile('
'); + var isolateScope = element.isolateScope(); + expect(isolateScope.reference).toBeNaN(); - $rootScope.$apply(function(scope) { scope.num = 64; }); - expect(isolateScope.reference).toBe(64); - })); + isolateScope.$apply(function(scope) { scope.reference = 64; }); + expect($rootScope.num).toBe(64); + })); - it('should be able to bind attribute names which are present in Object.prototype', function() { - module(function() { - directive('inProtoAttr', valueFn({ - scope: { - 'constructor': '@', - 'toString': '&', + it('should update isolate scope when "="-bound NaN changes', inject(function($compile, $rootScope) { + $rootScope.num = NaN; + compile('
'); + var isolateScope = element.isolateScope(); + expect(isolateScope.reference).toBeNaN(); - // Spidermonkey extension, may be obsolete in the future - 'watch': '=' - } + $rootScope.$apply(function(scope) { scope.num = 64; }); + expect(isolateScope.reference).toBe(64); })); - }); - inject(function($rootScope) { - expect(function() { - compile('
'); - }).not.toThrow(); - var isolateScope = element.isolateScope(); - - expect(typeof isolateScope.constructor).toBe('string'); - expect(isArray(isolateScope.watch)).toBe(true); - expect(typeof isolateScope.toString).toBe('function'); - expect($rootScope.value).toBeUndefined(); - isolateScope.toString(); - expect($rootScope.value).toBe(true); - }); - }); - it('should be able to interpolate attribute names which are present in Object.prototype', function() { - var attrs; - module(function() { - directive('attrExposer', valueFn({ - link: function($scope, $element, $attrs) { - attrs = $attrs; - } - })); - }); - inject(function($compile, $rootScope) { - $compile('
')($rootScope); - $rootScope.$apply(); - expect(attrs.toString).toBe('2'); - }); - }); + it('should be able to bind attribute names which are present in Object.prototype', function() { + module(function() { + directive('inProtoAttr', valueFn({ + scope: { + 'constructor': '@', + 'toString': '&', - it('should not initialize scope value if optional expression binding is not passed', inject(function($compile) { - compile('
'); - var isolateScope = element.isolateScope(); - expect(isolateScope.optExpr).toBeUndefined(); - })); + // Spidermonkey extension, may be obsolete in the future + 'watch': '=' + } + })); + }); + inject(function($rootScope) { + expect(function() { + compile('
'); + }).not.toThrow(); + var isolateScope = element.isolateScope(); + expect(typeof isolateScope.constructor).toBe('string'); + expect(isArray(isolateScope.watch)).toBe(true); + expect(typeof isolateScope.toString).toBe('function'); + expect($rootScope.value).toBeUndefined(); + isolateScope.toString(); + expect($rootScope.value).toBe(true); + }); + }); - it('should not initialize scope value if optional expression binding with Object.prototype name is not passed', inject(function($compile) { - compile('
'); - var isolateScope = element.isolateScope(); - expect(isolateScope.constructor).toBe($rootScope.constructor); - })); + it('should be able to interpolate attribute names which are present in Object.prototype', function() { + var attrs; + module(function() { + directive('attrExposer', valueFn({ + link: function($scope, $element, $attrs) { + attrs = $attrs; + } + })); + }); + inject(function($compile, $rootScope) { + $compile('
')($rootScope); + $rootScope.$apply(); + expect(attrs.toString).toBe('2'); + }); + }); - it('should initialize scope value if optional expression binding is passed', inject(function($compile) { - compile('
'); - var isolateScope = element.isolateScope(); - expect(typeof isolateScope.optExpr).toBe('function'); - expect(isolateScope.optExpr()).toBe('did!'); - expect($rootScope.value).toBe('did!'); - })); + it('should not initialize scope value if optional expression binding is not passed', inject(function($compile) { + compile('
'); + var isolateScope = element.isolateScope(); + expect(isolateScope.optExpr).toBeUndefined(); + })); - it('should initialize scope value if optional expression binding with Object.prototype name is passed', inject(function($compile) { - compile('
'); - var isolateScope = element.isolateScope(); - expect(typeof isolateScope.constructor).toBe('function'); - expect(isolateScope.constructor()).toBe('did!'); - expect($rootScope.value).toBe('did!'); - })); + it('should not initialize scope value if optional expression binding with Object.prototype name is not passed', inject(function($compile) { + compile('
'); + var isolateScope = element.isolateScope(); + expect(isolateScope.constructor).toBe($rootScope.constructor); + })); - it('should not overwrite @-bound property each digest when not present', function() { - module(function($compileProvider) { - $compileProvider.directive('testDir', valueFn({ - scope: {prop: '@'}, - controller: function($scope) { - $scope.prop = $scope.prop || 'default'; - this.getProp = function() { - return $scope.prop; - }; - }, - controllerAs: 'ctrl', - template: '

' + it('should initialize scope value if optional expression binding is passed', inject(function($compile) { + compile('
'); + var isolateScope = element.isolateScope(); + expect(typeof isolateScope.optExpr).toBe('function'); + expect(isolateScope.optExpr()).toBe('did!'); + expect($rootScope.value).toBe('did!'); })); - }); - inject(function($compile, $rootScope) { - element = $compile('
')($rootScope); - var scope = element.isolateScope(); - expect(scope.ctrl.getProp()).toBe('default'); - - $rootScope.$digest(); - expect(scope.ctrl.getProp()).toBe('default'); - }); - }); - it('should ignore optional "="-bound property if value is the emptry string', function() { - module(function($compileProvider) { - $compileProvider.directive('testDir', valueFn({ - scope: {prop: '=?'}, - controller: function($scope) { - $scope.prop = $scope.prop || 'default'; - this.getProp = function() { - return $scope.prop; - }; - }, - controllerAs: 'ctrl', - template: '

' + it('should initialize scope value if optional expression binding with Object.prototype name is passed', inject(function($compile) { + compile('
'); + var isolateScope = element.isolateScope(); + expect(typeof isolateScope.constructor).toBe('function'); + expect(isolateScope.constructor()).toBe('did!'); + expect($rootScope.value).toBe('did!'); })); - }); - inject(function($compile, $rootScope) { - element = $compile('
')($rootScope); - var scope = element.isolateScope(); - expect(scope.ctrl.getProp()).toBe('default'); - $rootScope.$digest(); - expect(scope.ctrl.getProp()).toBe('default'); - scope.prop = 'foop'; - $rootScope.$digest(); - expect(scope.ctrl.getProp()).toBe('foop'); - }); - }); - describe('bind-once', function() { - - function countWatches(scope) { - var result = 0; - while (scope !== null) { - result += (scope.$$watchers && scope.$$watchers.length) || 0; - result += countWatches(scope.$$childHead); - scope = scope.$$nextSibling; - } - return result; - } + it('should not overwrite @-bound property each digest when not present', function() { + module(function($compileProvider) { + $compileProvider.directive('testDir', valueFn({ + scope: {prop: '@'}, + controller: function($scope) { + $scope.prop = $scope.prop || 'default'; + this.getProp = function() { + return $scope.prop; + }; + }, + controllerAs: 'ctrl', + template: '

' + })); + }); + inject(function($compile, $rootScope) { + element = $compile('
')($rootScope); + var scope = element.isolateScope(); + expect(scope.ctrl.getProp()).toBe('default'); - it('should be possible to one-time bind a parameter on a component with a template', function() { - module(function() { - directive('otherTplDir', function() { - return { - scope: {param1: '=', param2: '='}, - template: '1:{{param1}};2:{{param2}};3:{{::param1}};4:{{::param2}}' - }; + $rootScope.$digest(); + expect(scope.ctrl.getProp()).toBe('default'); }); }); - inject(function($rootScope) { - compile('
'); - expect(countWatches($rootScope)).toEqual(6); // 4 -> template watch group, 2 -> '=' - $rootScope.$digest(); - expect(element.html()).toBe('1:;2:;3:;4:'); - expect(countWatches($rootScope)).toEqual(6); - $rootScope.foo = 'foo'; - $rootScope.$digest(); - expect(element.html()).toBe('1:foo;2:;3:foo;4:'); - expect(countWatches($rootScope)).toEqual(4); + it('should ignore optional "="-bound property if value is the emptry string', function() { + module(function($compileProvider) { + $compileProvider.directive('testDir', valueFn({ + scope: {prop: '=?'}, + controller: function($scope) { + $scope.prop = $scope.prop || 'default'; + this.getProp = function() { + return $scope.prop; + }; + }, + controllerAs: 'ctrl', + template: '

' + })); + }); + inject(function($compile, $rootScope) { + element = $compile('
')($rootScope); + var scope = element.isolateScope(); + expect(scope.ctrl.getProp()).toBe('default'); + $rootScope.$digest(); + expect(scope.ctrl.getProp()).toBe('default'); + scope.prop = 'foop'; + $rootScope.$digest(); + expect(scope.ctrl.getProp()).toBe('foop'); + }); + }); - $rootScope.foo = 'baz'; - $rootScope.bar = 'bar'; - $rootScope.$digest(); - expect(element.html()).toBe('1:foo;2:bar;3:foo;4:bar'); - expect(countWatches($rootScope)).toEqual(3); - $rootScope.bar = 'baz'; - $rootScope.$digest(); - expect(element.html()).toBe('1:foo;2:baz;3:foo;4:bar'); - }); - }); + describe('bind-once', function() { - it('should be possible to one-time bind a parameter on a component with a template', function() { - module(function() { - directive('otherTplDir', function() { - return { - scope: {param1: '@', param2: '@'}, - template: '1:{{param1}};2:{{param2}};3:{{::param1}};4:{{::param2}}' - }; - }); - }); + function countWatches(scope) { + var result = 0; + while (scope !== null) { + result += (scope.$$watchers && scope.$$watchers.length) || 0; + result += countWatches(scope.$$childHead); + scope = scope.$$nextSibling; + } + return result; + } - inject(function($rootScope) { - compile('
'); - expect(countWatches($rootScope)).toEqual(6); // 4 -> template watch group, 2 -> {{ }} - $rootScope.$digest(); - expect(element.html()).toBe('1:;2:;3:;4:'); - expect(countWatches($rootScope)).toEqual(4); // (- 2) -> bind-once in template + it('should be possible to one-time bind a parameter on a component with a template', function() { + module(function() { + directive('otherTplDir', function() { + return { + scope: {param1: '=', param2: '='}, + template: '1:{{param1}};2:{{param2}};3:{{::param1}};4:{{::param2}}' + }; + }); + }); - $rootScope.foo = 'foo'; - $rootScope.$digest(); - expect(element.html()).toBe('1:foo;2:;3:;4:'); - expect(countWatches($rootScope)).toEqual(3); + inject(function($rootScope) { + compile('
'); + expect(countWatches($rootScope)).toEqual(6); // 4 -> template watch group, 2 -> '=' + $rootScope.$digest(); + expect(element.html()).toBe('1:;2:;3:;4:'); + expect(countWatches($rootScope)).toEqual(6); - $rootScope.foo = 'baz'; - $rootScope.bar = 'bar'; - $rootScope.$digest(); - expect(element.html()).toBe('1:foo;2:bar;3:;4:'); - expect(countWatches($rootScope)).toEqual(3); + $rootScope.foo = 'foo'; + $rootScope.$digest(); + expect(element.html()).toBe('1:foo;2:;3:foo;4:'); + expect(countWatches($rootScope)).toEqual(4); - $rootScope.bar = 'baz'; - $rootScope.$digest(); - expect(element.html()).toBe('1:foo;2:baz;3:;4:'); - }); - }); + $rootScope.foo = 'baz'; + $rootScope.bar = 'bar'; + $rootScope.$digest(); + expect(element.html()).toBe('1:foo;2:bar;3:foo;4:bar'); + expect(countWatches($rootScope)).toEqual(3); - it('should be possible to one-time bind a parameter on a component with a template', function() { - module(function() { - directive('otherTplDir', function() { - return { - scope: {param1: '=', param2: '='}, - templateUrl: 'other.html' - }; + $rootScope.bar = 'baz'; + $rootScope.$digest(); + expect(element.html()).toBe('1:foo;2:baz;3:foo;4:bar'); + }); }); - }); - inject(function($rootScope, $templateCache) { - $templateCache.put('other.html', '1:{{param1}};2:{{param2}};3:{{::param1}};4:{{::param2}}'); - compile('
'); - $rootScope.$digest(); - expect(element.html()).toBe('1:;2:;3:;4:'); - expect(countWatches($rootScope)).toEqual(6); // 4 -> template watch group, 2 -> '=' + it('should be possible to one-time bind a parameter on a component with a template', function() { + module(function() { + directive('otherTplDir', function() { + return { + scope: {param1: '@', param2: '@'}, + template: '1:{{param1}};2:{{param2}};3:{{::param1}};4:{{::param2}}' + }; + }); + }); - $rootScope.foo = 'foo'; - $rootScope.$digest(); - expect(element.html()).toBe('1:foo;2:;3:foo;4:'); - expect(countWatches($rootScope)).toEqual(4); + inject(function($rootScope) { + compile('
'); + expect(countWatches($rootScope)).toEqual(6); // 4 -> template watch group, 2 -> {{ }} + $rootScope.$digest(); + expect(element.html()).toBe('1:;2:;3:;4:'); + expect(countWatches($rootScope)).toEqual(4); // (- 2) -> bind-once in template - $rootScope.foo = 'baz'; - $rootScope.bar = 'bar'; - $rootScope.$digest(); - expect(element.html()).toBe('1:foo;2:bar;3:foo;4:bar'); - expect(countWatches($rootScope)).toEqual(3); + $rootScope.foo = 'foo'; + $rootScope.$digest(); + expect(element.html()).toBe('1:foo;2:;3:;4:'); + expect(countWatches($rootScope)).toEqual(3); - $rootScope.bar = 'baz'; - $rootScope.$digest(); - expect(element.html()).toBe('1:foo;2:baz;3:foo;4:bar'); - }); - }); + $rootScope.foo = 'baz'; + $rootScope.bar = 'bar'; + $rootScope.$digest(); + expect(element.html()).toBe('1:foo;2:bar;3:;4:'); + expect(countWatches($rootScope)).toEqual(3); - it('should be possible to one-time bind a parameter on a component with a template', function() { - module(function() { - directive('otherTplDir', function() { - return { - scope: {param1: '@', param2: '@'}, - templateUrl: 'other.html' - }; + $rootScope.bar = 'baz'; + $rootScope.$digest(); + expect(element.html()).toBe('1:foo;2:baz;3:;4:'); + }); }); - }); - inject(function($rootScope, $templateCache) { - $templateCache.put('other.html', '1:{{param1}};2:{{param2}};3:{{::param1}};4:{{::param2}}'); - compile('
'); - $rootScope.$digest(); - expect(element.html()).toBe('1:;2:;3:;4:'); - expect(countWatches($rootScope)).toEqual(4); // (4 - 2) -> template watch group, 2 -> {{ }} + it('should be possible to one-time bind a parameter on a component with a template', function() { + module(function() { + directive('otherTplDir', function() { + return { + scope: {param1: '=', param2: '='}, + templateUrl: 'other.html' + }; + }); + }); - $rootScope.foo = 'foo'; - $rootScope.$digest(); - expect(element.html()).toBe('1:foo;2:;3:;4:'); - expect(countWatches($rootScope)).toEqual(3); + inject(function($rootScope, $templateCache) { + $templateCache.put('other.html', '1:{{param1}};2:{{param2}};3:{{::param1}};4:{{::param2}}'); + compile('
'); + $rootScope.$digest(); + expect(element.html()).toBe('1:;2:;3:;4:'); + expect(countWatches($rootScope)).toEqual(6); // 4 -> template watch group, 2 -> '=' - $rootScope.foo = 'baz'; - $rootScope.bar = 'bar'; - $rootScope.$digest(); - expect(element.html()).toBe('1:foo;2:bar;3:;4:'); - expect(countWatches($rootScope)).toEqual(3); + $rootScope.foo = 'foo'; + $rootScope.$digest(); + expect(element.html()).toBe('1:foo;2:;3:foo;4:'); + expect(countWatches($rootScope)).toEqual(4); - $rootScope.bar = 'baz'; - $rootScope.$digest(); - expect(element.html()).toBe('1:foo;2:baz;3:;4:'); - }); - }); + $rootScope.foo = 'baz'; + $rootScope.bar = 'bar'; + $rootScope.$digest(); + expect(element.html()).toBe('1:foo;2:bar;3:foo;4:bar'); + expect(countWatches($rootScope)).toEqual(3); - it('should continue with a digets cycle when there is a two-way binding from the child to the parent', function() { - module(function() { - directive('hello', function() { - return { - restrict: 'E', - scope: { greeting: '=' }, - template: '', - link: function(scope) { - scope.setGreeting = function() { scope.greeting = 'Hello!'; }; - } - }; + $rootScope.bar = 'baz'; + $rootScope.$digest(); + expect(element.html()).toBe('1:foo;2:baz;3:foo;4:bar'); + }); }); - }); - inject(function($rootScope) { - compile('
' + - '

{{greeting}}

' + - '
' + - '
'); - $rootScope.$digest(); - browserTrigger(element.find('button'), 'click'); - expect(element.find('p').text()).toBe('Hello!'); - }); - }); + it('should be possible to one-time bind a parameter on a component with a template', function() { + module(function() { + directive('otherTplDir', function() { + return { + scope: {param1: '@', param2: '@'}, + templateUrl: 'other.html' + }; + }); + }); - }); + inject(function($rootScope, $templateCache) { + $templateCache.put('other.html', '1:{{param1}};2:{{param2}};3:{{::param1}};4:{{::param2}}'); + compile('
'); + $rootScope.$digest(); + expect(element.html()).toBe('1:;2:;3:;4:'); + expect(countWatches($rootScope)).toEqual(4); // (4 - 2) -> template watch group, 2 -> {{ }} + $rootScope.foo = 'foo'; + $rootScope.$digest(); + expect(element.html()).toBe('1:foo;2:;3:;4:'); + expect(countWatches($rootScope)).toEqual(3); - describe('attribute', function() { - it('should copy simple attribute', inject(function() { - compile('
'); + $rootScope.foo = 'baz'; + $rootScope.bar = 'bar'; + $rootScope.$digest(); + expect(element.html()).toBe('1:foo;2:bar;3:;4:'); + expect(countWatches($rootScope)).toEqual(3); - expect(componentScope.attr).toEqual('some text'); - expect(componentScope.attrAlias).toEqual('some text'); - expect(componentScope.attrAlias).toEqual(componentScope.attr); - })); + $rootScope.bar = 'baz'; + $rootScope.$digest(); + expect(element.html()).toBe('1:foo;2:baz;3:;4:'); + }); + }); - it('should copy an attribute with spaces', inject(function() { - compile('
'); + it('should continue with a digets cycle when there is a two-way binding from the child to the parent', function() { + module(function() { + directive('hello', function() { + return { + restrict: 'E', + scope: { greeting: '=' }, + template: '', + link: function(scope) { + scope.setGreeting = function() { scope.greeting = 'Hello!'; }; + } + }; + }); + }); - expect(componentScope.attr).toEqual(' some text '); - expect(componentScope.attrAlias).toEqual(' some text '); - expect(componentScope.attrAlias).toEqual(componentScope.attr); - })); + inject(function($rootScope) { + compile('
' + + '

{{greeting}}

' + + '
' + + '
'); + $rootScope.$digest(); + browserTrigger(element.find('button'), 'click'); + expect(element.find('p').text()).toBe('Hello!'); + }); + }); - it('should set up the interpolation before it reaches the link function', inject(function() { - $rootScope.name = 'misko'; - compile('
'); - expect(componentScope.attr).toEqual('hello misko'); - expect(componentScope.attrAlias).toEqual('hello misko'); - })); + }); - it('should update when interpolated attribute updates', inject(function() { - compile('
'); - $rootScope.name = 'igor'; - $rootScope.$apply(); + describe('attribute', function() { + it('should copy simple attribute', inject(function() { + compile('
'); - expect(componentScope.attr).toEqual('hello igor'); - expect(componentScope.attrAlias).toEqual('hello igor'); - })); - }); + expect(componentScope.attr).toEqual('some text'); + expect(componentScope.attrAlias).toEqual('some text'); + expect(componentScope.attrAlias).toEqual(componentScope.attr); + })); + it('should copy an attribute with spaces', inject(function() { + compile('
'); - describe('object reference', function() { - it('should update local when origin changes', inject(function() { - compile('
'); - expect(componentScope.ref).toBeUndefined(); - expect(componentScope.refAlias).toBe(componentScope.ref); - - $rootScope.name = 'misko'; - $rootScope.$apply(); - - expect($rootScope.name).toBe('misko'); - expect(componentScope.ref).toBe('misko'); - expect(componentScope.refAlias).toBe('misko'); + expect(componentScope.attr).toEqual(' some text '); + expect(componentScope.attrAlias).toEqual(' some text '); + expect(componentScope.attrAlias).toEqual(componentScope.attr); + })); - $rootScope.name = {}; - $rootScope.$apply(); - expect(componentScope.ref).toBe($rootScope.name); - expect(componentScope.refAlias).toBe($rootScope.name); - })); + it('should set up the interpolation before it reaches the link function', inject(function() { + $rootScope.name = 'misko'; + compile('
'); + expect(componentScope.attr).toEqual('hello misko'); + expect(componentScope.attrAlias).toEqual('hello misko'); + })); + it('should update when interpolated attribute updates', inject(function() { + compile('
'); - it('should update local when both change', inject(function() { - compile('
'); - $rootScope.name = {mark:123}; - componentScope.ref = 'misko'; + $rootScope.name = 'igor'; + $rootScope.$apply(); - $rootScope.$apply(); - expect($rootScope.name).toEqual({mark:123}); - expect(componentScope.ref).toBe($rootScope.name); - expect(componentScope.refAlias).toBe($rootScope.name); + expect(componentScope.attr).toEqual('hello igor'); + expect(componentScope.attrAlias).toEqual('hello igor'); + })); + }); - $rootScope.name = 'igor'; - componentScope.ref = {}; - $rootScope.$apply(); - expect($rootScope.name).toEqual('igor'); - expect(componentScope.ref).toBe($rootScope.name); - expect(componentScope.refAlias).toBe($rootScope.name); - })); - it('should not break if local and origin both change to the same value', inject(function() { - $rootScope.name = 'aaa'; + describe('object reference', function() { + it('should update local when origin changes', inject(function() { + compile('
'); + expect(componentScope.ref).toBeUndefined(); + expect(componentScope.refAlias).toBe(componentScope.ref); - compile('
'); + $rootScope.name = 'misko'; + $rootScope.$apply(); - //change both sides to the same item within the same digest cycle - componentScope.ref = 'same'; - $rootScope.name = 'same'; - $rootScope.$apply(); + expect($rootScope.name).toBe('misko'); + expect(componentScope.ref).toBe('misko'); + expect(componentScope.refAlias).toBe('misko'); - //change origin back to its previous value - $rootScope.name = 'aaa'; - $rootScope.$apply(); + $rootScope.name = {}; + $rootScope.$apply(); + expect(componentScope.ref).toBe($rootScope.name); + expect(componentScope.refAlias).toBe($rootScope.name); + })); - expect($rootScope.name).toBe('aaa'); - expect(componentScope.ref).toBe('aaa'); - })); - it('should complain on non assignable changes', inject(function() { - compile('
'); - $rootScope.name = 'world'; - $rootScope.$apply(); - expect(componentScope.ref).toBe('hello world'); + it('should update local when both change', inject(function() { + compile('
'); + $rootScope.name = {mark:123}; + componentScope.ref = 'misko'; - componentScope.ref = 'ignore me'; - expect(function() { $rootScope.$apply(); }). - toThrowMinErr('$compile', 'nonassign', 'Expression \'\'hello \' + name\' in attribute \'ref\' used with directive \'myComponent\' is non-assignable!'); - expect(componentScope.ref).toBe('hello world'); - // reset since the exception was rethrown which prevented phase clearing - $rootScope.$$phase = null; + $rootScope.$apply(); + expect($rootScope.name).toEqual({mark:123}); + expect(componentScope.ref).toBe($rootScope.name); + expect(componentScope.refAlias).toBe($rootScope.name); - $rootScope.name = 'misko'; - $rootScope.$apply(); - expect(componentScope.ref).toBe('hello misko'); - })); + $rootScope.name = 'igor'; + componentScope.ref = {}; + $rootScope.$apply(); + expect($rootScope.name).toEqual('igor'); + expect(componentScope.ref).toBe($rootScope.name); + expect(componentScope.refAlias).toBe($rootScope.name); + })); - it('should complain if assigning to undefined', inject(function() { - compile('
'); - $rootScope.$apply(); - expect(componentScope.ref).toBeUndefined(); + it('should not break if local and origin both change to the same value', inject(function() { + $rootScope.name = 'aaa'; - componentScope.ref = 'ignore me'; - expect(function() { $rootScope.$apply(); }). - toThrowMinErr('$compile', 'nonassign', 'Expression \'undefined\' in attribute \'ref\' used with directive \'myComponent\' is non-assignable!'); - expect(componentScope.ref).toBeUndefined(); + compile('
'); - $rootScope.$$phase = null; // reset since the exception was rethrown which prevented phase clearing - $rootScope.$apply(); - expect(componentScope.ref).toBeUndefined(); - })); + //change both sides to the same item within the same digest cycle + componentScope.ref = 'same'; + $rootScope.name = 'same'; + $rootScope.$apply(); - // regression - it('should stabilize model', inject(function() { - compile('
'); + //change origin back to its previous value + $rootScope.name = 'aaa'; + $rootScope.$apply(); - var lastRefValueInParent; - $rootScope.$watch('name', function(ref) { - lastRefValueInParent = ref; - }); + expect($rootScope.name).toBe('aaa'); + expect(componentScope.ref).toBe('aaa'); + })); - $rootScope.name = 'aaa'; - $rootScope.$apply(); + it('should complain on non assignable changes', inject(function() { + compile('
'); + $rootScope.name = 'world'; + $rootScope.$apply(); + expect(componentScope.ref).toBe('hello world'); - componentScope.reference = 'new'; - $rootScope.$apply(); + componentScope.ref = 'ignore me'; + expect(function() { $rootScope.$apply(); }). + toThrowMinErr('$compile', 'nonassign', 'Expression \'\'hello \' + name\' in attribute \'ref\' used with directive \'myComponent\' is non-assignable!'); + expect(componentScope.ref).toBe('hello world'); + // reset since the exception was rethrown which prevented phase clearing + $rootScope.$$phase = null; - expect(lastRefValueInParent).toBe('new'); - })); + $rootScope.name = 'misko'; + $rootScope.$apply(); + expect(componentScope.ref).toBe('hello misko'); + })); - describe('literal objects', function() { - it('should copy parent changes', inject(function() { - compile('
'); + it('should complain if assigning to undefined', inject(function() { + compile('
'); + $rootScope.$apply(); + expect(componentScope.ref).toBeUndefined(); - $rootScope.name = 'a'; - $rootScope.$apply(); - expect(componentScope.reference).toEqual({name: 'a'}); + componentScope.ref = 'ignore me'; + expect(function() { $rootScope.$apply(); }). + toThrowMinErr('$compile', 'nonassign', 'Expression \'undefined\' in attribute \'ref\' used with directive \'myComponent\' is non-assignable!'); + expect(componentScope.ref).toBeUndefined(); - $rootScope.name = 'b'; - $rootScope.$apply(); - expect(componentScope.reference).toEqual({name: 'b'}); - })); + $rootScope.$$phase = null; // reset since the exception was rethrown which prevented phase clearing + $rootScope.$apply(); + expect(componentScope.ref).toBeUndefined(); + })); - it('should not change the component when parent does not change', inject(function() { - compile('
'); + // regression + it('should stabilize model', inject(function() { + compile('
'); - $rootScope.name = 'a'; - $rootScope.$apply(); - var lastComponentValue = componentScope.reference; - $rootScope.$apply(); - expect(componentScope.reference).toBe(lastComponentValue); - })); + var lastRefValueInParent; + $rootScope.$watch('name', function(ref) { + lastRefValueInParent = ref; + }); - it('should complain when the component changes', inject(function() { - compile('
'); + $rootScope.name = 'aaa'; + $rootScope.$apply(); - $rootScope.name = 'a'; - $rootScope.$apply(); - componentScope.reference = {name: 'b'}; - expect(function() { + componentScope.reference = 'new'; $rootScope.$apply(); - }).toThrowMinErr('$compile', 'nonassign', 'Expression \'{name: name}\' in attribute \'reference\' used with directive \'myComponent\' is non-assignable!'); - })); + expect(lastRefValueInParent).toBe('new'); + })); - it('should work for primitive literals', inject(function() { - test('1', 1); - test('null', null); - test('undefined', undefined); - test('\'someString\'', 'someString'); - test('true', true); + describe('literal objects', function() { + it('should copy parent changes', inject(function() { + compile('
'); - function test(literalString, literalValue) { - compile('
'); + $rootScope.name = 'a'; + $rootScope.$apply(); + expect(componentScope.reference).toEqual({name: 'a'}); - $rootScope.$apply(); - expect(componentScope.reference).toBe(literalValue); - dealoc(element); - } - })); + $rootScope.name = 'b'; + $rootScope.$apply(); + expect(componentScope.reference).toEqual({name: 'b'}); + })); - }); + it('should not change the component when parent does not change', inject(function() { + compile('
'); - }); + $rootScope.name = 'a'; + $rootScope.$apply(); + var lastComponentValue = componentScope.reference; + $rootScope.$apply(); + expect(componentScope.reference).toBe(lastComponentValue); + })); + it('should complain when the component changes', inject(function() { + compile('
'); - describe('optional object reference', function() { - it('should update local when origin changes', inject(function() { - compile('
'); - expect(componentScope.optRef).toBeUndefined(); - expect(componentScope.optRefAlias).toBe(componentScope.optRef); + $rootScope.name = 'a'; + $rootScope.$apply(); + componentScope.reference = {name: 'b'}; + expect(function() { + $rootScope.$apply(); + }).toThrowMinErr('$compile', 'nonassign', 'Expression \'{name: name}\' in attribute \'reference\' used with directive \'myComponent\' is non-assignable!'); - $rootScope.name = 'misko'; - $rootScope.$apply(); - expect(componentScope.optref).toBe($rootScope.name); - expect(componentScope.optrefAlias).toBe($rootScope.name); + })); - $rootScope.name = {}; - $rootScope.$apply(); - expect(componentScope.optref).toBe($rootScope.name); - expect(componentScope.optrefAlias).toBe($rootScope.name); - })); + it('should work for primitive literals', inject(function() { + test('1', 1); + test('null', null); + test('undefined', undefined); + test('\'someString\'', 'someString'); + test('true', true); - it('should not throw exception when reference does not exist', inject(function() { - compile('
'); + function test(literalString, literalValue) { + compile('
'); - expect(componentScope.optref).toBeUndefined(); - expect(componentScope.optrefAlias).toBeUndefined(); - expect(componentScope.optreference).toBeUndefined(); - })); - }); + $rootScope.$apply(); + expect(componentScope.reference).toBe(literalValue); + dealoc(element); + } + })); + }); - describe('collection object reference', function() { - it('should update isolate scope when origin scope changes', inject(function() { - $rootScope.collection = [{ - name: 'Gabriel', - value: 18 - }, { - name: 'Tony', - value: 91 - }]; - $rootScope.query = ''; - $rootScope.$apply(); + }); - compile('
'); - expect(componentScope.colref).toEqual($rootScope.collection); - expect(componentScope.colrefAlias).toEqual(componentScope.colref); + describe('optional object reference', function() { + it('should update local when origin changes', inject(function() { + compile('
'); + expect(componentScope.optRef).toBeUndefined(); + expect(componentScope.optRefAlias).toBe(componentScope.optRef); - $rootScope.query = 'Gab'; - $rootScope.$apply(); + $rootScope.name = 'misko'; + $rootScope.$apply(); + expect(componentScope.optref).toBe($rootScope.name); + expect(componentScope.optrefAlias).toBe($rootScope.name); - expect(componentScope.colref).toEqual([$rootScope.collection[0]]); - expect(componentScope.colrefAlias).toEqual([$rootScope.collection[0]]); - })); + $rootScope.name = {}; + $rootScope.$apply(); + expect(componentScope.optref).toBe($rootScope.name); + expect(componentScope.optrefAlias).toBe($rootScope.name); + })); - it('should update origin scope when isolate scope changes', inject(function() { - $rootScope.collection = [{ - name: 'Gabriel', - value: 18 - }, { - name: 'Tony', - value: 91 - }]; + it('should not throw exception when reference does not exist', inject(function() { + compile('
'); - compile('
'); + expect(componentScope.optref).toBeUndefined(); + expect(componentScope.optrefAlias).toBeUndefined(); + expect(componentScope.optreference).toBeUndefined(); + })); + }); - var newItem = { - name: 'Pablo', - value: 10 - }; - componentScope.colref.push(newItem); - componentScope.$apply(); - expect($rootScope.collection[2]).toEqual(newItem); - })); - }); + describe('collection object reference', function() { + it('should update isolate scope when origin scope changes', inject(function() { + $rootScope.collection = [{ + name: 'Gabriel', + value: 18 + }, { + name: 'Tony', + value: 91 + }]; + $rootScope.query = ''; + $rootScope.$apply(); + compile('
'); - describe('one-way binding', function() { - it('should update isolate when the identity of origin changes', inject(function() { - compile('
'); + expect(componentScope.colref).toEqual($rootScope.collection); + expect(componentScope.colrefAlias).toEqual(componentScope.colref); - expect(componentScope.owRef).toBeUndefined(); - expect(componentScope.owRefAlias).toBe(componentScope.owRef); + $rootScope.query = 'Gab'; + $rootScope.$apply(); - $rootScope.obj = {value: 'initial'}; - $rootScope.$apply(); + expect(componentScope.colref).toEqual([$rootScope.collection[0]]); + expect(componentScope.colrefAlias).toEqual([$rootScope.collection[0]]); + })); - expect($rootScope.obj).toEqual({value: 'initial'}); - expect(componentScope.owRef).toEqual({value: 'initial'}); - expect(componentScope.owRefAlias).toBe(componentScope.owRef); + it('should update origin scope when isolate scope changes', inject(function() { + $rootScope.collection = [{ + name: 'Gabriel', + value: 18 + }, { + name: 'Tony', + value: 91 + }]; - // This changes in both scopes because of reference - $rootScope.obj.value = 'origin1'; - $rootScope.$apply(); - expect(componentScope.owRef.value).toBe('origin1'); - expect(componentScope.owRefAlias.value).toBe('origin1'); + compile('
'); - componentScope.owRef = {value: 'isolate1'}; - componentScope.$apply(); - expect($rootScope.obj.value).toBe('origin1'); + var newItem = { + name: 'Pablo', + value: 10 + }; + componentScope.colref.push(newItem); + componentScope.$apply(); - // Change does not propagate because object identity hasn't changed - $rootScope.obj.value = 'origin2'; - $rootScope.$apply(); - expect(componentScope.owRef.value).toBe('isolate1'); - expect(componentScope.owRefAlias.value).toBe('origin2'); + expect($rootScope.collection[2]).toEqual(newItem); + })); + }); - // Change does propagate because object identity changes - $rootScope.obj = {value: 'origin3'}; - $rootScope.$apply(); - expect(componentScope.owRef.value).toBe('origin3'); - expect(componentScope.owRef).toBe($rootScope.obj); - expect(componentScope.owRefAlias).toBe($rootScope.obj); - })); - it('should update isolate when both change', inject(function() { - compile('
'); + describe('one-way binding', function() { + it('should update isolate when the identity of origin changes', inject(function() { + compile('
'); - $rootScope.name = {mark:123}; - componentScope.owRef = 'misko'; + expect(componentScope.owRef).toBeUndefined(); + expect(componentScope.owRefAlias).toBe(componentScope.owRef); - $rootScope.$apply(); - expect($rootScope.name).toEqual({mark:123}); - expect(componentScope.owRef).toBe($rootScope.name); - expect(componentScope.owRefAlias).toBe($rootScope.name); + $rootScope.obj = {value: 'initial'}; + $rootScope.$apply(); - $rootScope.name = 'igor'; - componentScope.owRef = {}; - $rootScope.$apply(); - expect($rootScope.name).toEqual('igor'); - expect(componentScope.owRef).toBe($rootScope.name); - expect(componentScope.owRefAlias).toBe($rootScope.name); - })); + expect($rootScope.obj).toEqual({value: 'initial'}); + expect(componentScope.owRef).toEqual({value: 'initial'}); + expect(componentScope.owRefAlias).toBe(componentScope.owRef); - describe('initialization', function() { - var component, log; + // This changes in both scopes because of reference + $rootScope.obj.value = 'origin1'; + $rootScope.$apply(); + expect(componentScope.owRef.value).toBe('origin1'); + expect(componentScope.owRefAlias.value).toBe('origin1'); - beforeEach(function() { - log = []; - angular.module('owComponentTest', []) - .component('owComponent', { - bindings: { input: '<' }, - controller: function() { - component = this; - this.input = 'constructor'; - log.push('constructor'); + componentScope.owRef = {value: 'isolate1'}; + componentScope.$apply(); + expect($rootScope.obj.value).toBe('origin1'); - this.$onInit = function() { - this.input = '$onInit'; - log.push('$onInit'); - }; + // Change does not propagate because object identity hasn't changed + $rootScope.obj.value = 'origin2'; + $rootScope.$apply(); + expect(componentScope.owRef.value).toBe('isolate1'); + expect(componentScope.owRefAlias.value).toBe('origin2'); - this.$onChanges = function(changes) { - if (changes.input) { - log.push(['$onChanges', changes.input]); - } - }; - } - }); - }); + // Change does propagate because object identity changes + $rootScope.obj = {value: 'origin3'}; + $rootScope.$apply(); + expect(componentScope.owRef.value).toBe('origin3'); + expect(componentScope.owRef).toBe($rootScope.obj); + expect(componentScope.owRefAlias).toBe($rootScope.obj); + })); - it('should not update isolate again after $onInit if outer has not changed', function() { - module('owComponentTest'); - inject(function() { - $rootScope.name = 'outer'; - compile(''); + it('should update isolate when both change', inject(function() { + compile('
'); - expect($rootScope.name).toEqual('outer'); - expect(component.input).toEqual('$onInit'); + $rootScope.name = {mark:123}; + componentScope.owRef = 'misko'; + + $rootScope.$apply(); + expect($rootScope.name).toEqual({mark:123}); + expect(componentScope.owRef).toBe($rootScope.name); + expect(componentScope.owRefAlias).toBe($rootScope.name); + $rootScope.name = 'igor'; + componentScope.owRef = {}; $rootScope.$apply(); + expect($rootScope.name).toEqual('igor'); + expect(componentScope.owRef).toBe($rootScope.name); + expect(componentScope.owRefAlias).toBe($rootScope.name); + })); - expect($rootScope.name).toEqual('outer'); - expect(component.input).toEqual('$onInit'); + describe('initialization', function() { + var component, log; + + beforeEach(function() { + log = []; + angular.module('owComponentTest', []) + .component('owComponent', { + bindings: { input: '<' }, + controller: function() { + component = this; + this.input = 'constructor'; + log.push('constructor'); + + this.$onInit = function() { + this.input = '$onInit'; + log.push('$onInit'); + }; - expect(log).toEqual([ - 'constructor', - ['$onChanges', jasmine.objectContaining({ currentValue: 'outer' })], - '$onInit' - ]); - }); - }); + this.$onChanges = function(changes) { + if (changes.input) { + log.push(['$onChanges', changes.input]); + } + }; + } + }); + }); - it('should update isolate again after $onInit if outer has changed (before initial watchAction call)', function() { - module('owComponentTest'); - inject(function() { - $rootScope.name = 'outer1'; - compile(''); + it('should not update isolate again after $onInit if outer has not changed', function() { + module('owComponentTest'); + inject(function() { + $rootScope.name = 'outer'; + compile(''); - expect(component.input).toEqual('$onInit'); - $rootScope.$apply('name = "outer2"'); + expect($rootScope.name).toEqual('outer'); + expect(component.input).toEqual('$onInit'); - expect($rootScope.name).toEqual('outer2'); - expect(component.input).toEqual('outer2'); - expect(log).toEqual([ - 'constructor', - ['$onChanges', jasmine.objectContaining({ currentValue: 'outer1' })], - '$onInit', - ['$onChanges', jasmine.objectContaining({ currentValue: 'outer2', previousValue: 'outer1' })] - ]); - }); - }); + $rootScope.$apply(); - it('should update isolate again after $onInit if outer has changed (before initial watchAction call)', function() { - angular.module('owComponentTest') - .directive('changeInput', function() { - return function(scope, elem, attrs) { - scope.name = 'outer2'; - }; + expect($rootScope.name).toEqual('outer'); + expect(component.input).toEqual('$onInit'); + + expect(log).toEqual([ + 'constructor', + ['$onChanges', jasmine.objectContaining({ currentValue: 'outer' })], + '$onInit' + ]); + }); }); - module('owComponentTest'); - inject(function() { - $rootScope.name = 'outer1'; - compile(''); - expect(component.input).toEqual('$onInit'); - $rootScope.$digest(); + it('should update isolate again after $onInit if outer has changed (before initial watchAction call)', function() { + module('owComponentTest'); + inject(function() { + $rootScope.name = 'outer1'; + compile(''); + + expect(component.input).toEqual('$onInit'); + $rootScope.$apply('name = "outer2"'); + + expect($rootScope.name).toEqual('outer2'); + expect(component.input).toEqual('outer2'); + expect(log).toEqual([ + 'constructor', + ['$onChanges', jasmine.objectContaining({ currentValue: 'outer1' })], + '$onInit', + ['$onChanges', jasmine.objectContaining({ currentValue: 'outer2', previousValue: 'outer1' })] + ]); + }); + }); - expect($rootScope.name).toEqual('outer2'); - expect(component.input).toEqual('outer2'); - expect(log).toEqual([ - 'constructor', - ['$onChanges', jasmine.objectContaining({ currentValue: 'outer1' })], - '$onInit', - ['$onChanges', jasmine.objectContaining({ currentValue: 'outer2', previousValue: 'outer1' })] - ]); + it('should update isolate again after $onInit if outer has changed (before initial watchAction call)', function() { + angular.module('owComponentTest') + .directive('changeInput', function() { + return function(scope, elem, attrs) { + scope.name = 'outer2'; + }; + }); + module('owComponentTest'); + inject(function() { + $rootScope.name = 'outer1'; + compile(''); + + expect(component.input).toEqual('$onInit'); + $rootScope.$digest(); + + expect($rootScope.name).toEqual('outer2'); + expect(component.input).toEqual('outer2'); + expect(log).toEqual([ + 'constructor', + ['$onChanges', jasmine.objectContaining({ currentValue: 'outer1' })], + '$onInit', + ['$onChanges', jasmine.objectContaining({ currentValue: 'outer2', previousValue: 'outer1' })] + ]); + }); + }); }); - }); - }); - it('should not break when isolate and origin both change to the same value', inject(function() { - $rootScope.name = 'aaa'; - compile('
'); + it('should not break when isolate and origin both change to the same value', inject(function() { + $rootScope.name = 'aaa'; + compile('
'); - //change both sides to the same item within the same digest cycle - componentScope.owRef = 'same'; - $rootScope.name = 'same'; - $rootScope.$apply(); + //change both sides to the same item within the same digest cycle + componentScope.owRef = 'same'; + $rootScope.name = 'same'; + $rootScope.$apply(); - //change origin back to its previous value - $rootScope.name = 'aaa'; - $rootScope.$apply(); + //change origin back to its previous value + $rootScope.name = 'aaa'; + $rootScope.$apply(); - expect($rootScope.name).toBe('aaa'); - expect(componentScope.owRef).toBe('aaa'); - })); + expect($rootScope.name).toBe('aaa'); + expect(componentScope.owRef).toBe('aaa'); + })); - it('should not update origin when identity of isolate changes', inject(function() { - $rootScope.name = {mark:123}; - compile('
'); + it('should not update origin when identity of isolate changes', inject(function() { + $rootScope.name = {mark:123}; + compile('
'); - expect($rootScope.name).toEqual({mark:123}); - expect(componentScope.owRef).toBe($rootScope.name); - expect(componentScope.owRefAlias).toBe($rootScope.name); + expect($rootScope.name).toEqual({mark:123}); + expect(componentScope.owRef).toBe($rootScope.name); + expect(componentScope.owRefAlias).toBe($rootScope.name); - componentScope.owRef = 'martin'; - $rootScope.$apply(); - expect($rootScope.name).toEqual({mark: 123}); - expect(componentScope.owRef).toBe('martin'); - expect(componentScope.owRefAlias).toEqual({mark: 123}); - })); + componentScope.owRef = 'martin'; + $rootScope.$apply(); + expect($rootScope.name).toEqual({mark: 123}); + expect(componentScope.owRef).toBe('martin'); + expect(componentScope.owRefAlias).toEqual({mark: 123}); + })); - it('should update origin when property of isolate object reference changes', inject(function() { - $rootScope.obj = {mark:123}; - compile('
'); + it('should update origin when property of isolate object reference changes', inject(function() { + $rootScope.obj = {mark:123}; + compile('
'); - expect($rootScope.obj).toEqual({mark:123}); - expect(componentScope.owRef).toBe($rootScope.obj); + expect($rootScope.obj).toEqual({mark:123}); + expect(componentScope.owRef).toBe($rootScope.obj); - componentScope.owRef.mark = 789; - $rootScope.$apply(); - expect($rootScope.obj).toEqual({mark: 789}); - expect(componentScope.owRef).toBe($rootScope.obj); - })); + componentScope.owRef.mark = 789; + $rootScope.$apply(); + expect($rootScope.obj).toEqual({mark: 789}); + expect(componentScope.owRef).toBe($rootScope.obj); + })); - it('should not throw on non assignable expressions in the parent', inject(function() { - compile('
'); + it('should not throw on non assignable expressions in the parent', inject(function() { + compile('
'); - $rootScope.name = 'world'; - $rootScope.$apply(); - expect(componentScope.owRef).toBe('hello world'); + $rootScope.name = 'world'; + $rootScope.$apply(); + expect(componentScope.owRef).toBe('hello world'); - componentScope.owRef = 'ignore me'; - expect(componentScope.owRef).toBe('ignore me'); - expect($rootScope.name).toBe('world'); + componentScope.owRef = 'ignore me'; + expect(componentScope.owRef).toBe('ignore me'); + expect($rootScope.name).toBe('world'); - $rootScope.name = 'misko'; - $rootScope.$apply(); - expect(componentScope.owRef).toBe('hello misko'); - })); + $rootScope.name = 'misko'; + $rootScope.$apply(); + expect(componentScope.owRef).toBe('hello misko'); + })); - it('should not throw when assigning to undefined', inject(function() { - compile('
'); + it('should not throw when assigning to undefined', inject(function() { + compile('
'); - expect(componentScope.owRef).toBeUndefined(); + expect(componentScope.owRef).toBeUndefined(); - componentScope.owRef = 'ignore me'; - expect(componentScope.owRef).toBe('ignore me'); + componentScope.owRef = 'ignore me'; + expect(componentScope.owRef).toBe('ignore me'); - $rootScope.$apply(); - expect(componentScope.owRef).toBe('ignore me'); - })); + $rootScope.$apply(); + expect(componentScope.owRef).toBe('ignore me'); + })); - it('should update isolate scope when "<"-bound NaN changes', inject(function() { - $rootScope.num = NaN; - compile('
'); + it('should update isolate scope when "<"-bound NaN changes', inject(function() { + $rootScope.num = NaN; + compile('
'); - var isolateScope = element.isolateScope(); - expect(isolateScope.owRef).toBeNaN(); + var isolateScope = element.isolateScope(); + expect(isolateScope.owRef).toBeNaN(); - $rootScope.num = 64; - $rootScope.$apply(); - expect(isolateScope.owRef).toBe(64); - })); + $rootScope.num = 64; + $rootScope.$apply(); + expect(isolateScope.owRef).toBe(64); + })); - describe('literal objects', function() { - it('should copy parent changes', inject(function() { - compile('
'); + describe('literal objects', function() { + it('should copy parent changes', inject(function() { + compile('
'); - $rootScope.name = 'a'; - $rootScope.$apply(); - expect(componentScope.owRef).toEqual({name: 'a'}); + $rootScope.name = 'a'; + $rootScope.$apply(); + expect(componentScope.owRef).toEqual({name: 'a'}); - $rootScope.name = 'b'; - $rootScope.$apply(); - expect(componentScope.owRef).toEqual({name: 'b'}); - })); + $rootScope.name = 'b'; + $rootScope.$apply(); + expect(componentScope.owRef).toEqual({name: 'b'}); + })); - it('should not change the isolated scope when origin does not change', inject(function() { - compile('
'); + it('should not change the isolated scope when origin does not change', inject(function() { + compile('
'); - $rootScope.name = 'a'; - $rootScope.$apply(); - var lastComponentValue = componentScope.owRef; - $rootScope.$apply(); - expect(componentScope.owRef).toBe(lastComponentValue); - })); + $rootScope.name = 'a'; + $rootScope.$apply(); + var lastComponentValue = componentScope.owRef; + $rootScope.$apply(); + expect(componentScope.owRef).toBe(lastComponentValue); + })); - it('should deep-watch array literals', inject(function() { - $rootScope.name = 'georgios'; - $rootScope.obj = {name: 'pete'}; - compile('
'); + it('should deep-watch array literals', inject(function() { + $rootScope.name = 'georgios'; + $rootScope.obj = {name: 'pete'}; + compile('
'); - expect(componentScope.owRef).toEqual([{name: 'georgios'}, {name: 'pete'}]); + expect(componentScope.owRef).toEqual([{name: 'georgios'}, {name: 'pete'}]); - $rootScope.name = 'lucas'; - $rootScope.obj = {name: 'martin'}; - $rootScope.$apply(); - expect(componentScope.owRef).toEqual([{name: 'lucas'}, {name: 'martin'}]); - })); + $rootScope.name = 'lucas'; + $rootScope.obj = {name: 'martin'}; + $rootScope.$apply(); + expect(componentScope.owRef).toEqual([{name: 'lucas'}, {name: 'martin'}]); + })); - it('should deep-watch object literals', inject(function() { - $rootScope.name = 'georgios'; - $rootScope.obj = {name: 'pete'}; - compile('
'); + it('should deep-watch object literals', inject(function() { + $rootScope.name = 'georgios'; + $rootScope.obj = {name: 'pete'}; + compile('
'); - expect(componentScope.owRef).toEqual({name: 'georgios', item: {name: 'pete'}}); + expect(componentScope.owRef).toEqual({name: 'georgios', item: {name: 'pete'}}); - $rootScope.name = 'lucas'; - $rootScope.obj = {name: 'martin'}; - $rootScope.$apply(); - expect(componentScope.owRef).toEqual({name: 'lucas', item: {name: 'martin'}}); - })); + $rootScope.name = 'lucas'; + $rootScope.obj = {name: 'martin'}; + $rootScope.$apply(); + expect(componentScope.owRef).toEqual({name: 'lucas', item: {name: 'martin'}}); + })); - it('should not complain when the isolated scope changes', inject(function() { - compile('
'); + it('should not complain when the isolated scope changes', inject(function() { + compile('
'); - $rootScope.name = 'a'; - $rootScope.$apply(); - componentScope.owRef = {name: 'b'}; - componentScope.$apply(); + $rootScope.name = 'a'; + $rootScope.$apply(); + componentScope.owRef = {name: 'b'}; + componentScope.$apply(); - expect(componentScope.owRef).toEqual({name: 'b'}); - expect($rootScope.name).toBe('a'); + expect(componentScope.owRef).toEqual({name: 'b'}); + expect($rootScope.name).toBe('a'); - $rootScope.name = 'c'; - $rootScope.$apply(); - expect(componentScope.owRef).toEqual({name: 'c'}); - })); + $rootScope.name = 'c'; + $rootScope.$apply(); + expect(componentScope.owRef).toEqual({name: 'c'}); + })); - it('should work for primitive literals', inject(function() { - test('1', 1); - test('null', null); - test('undefined', undefined); - test('\'someString\'', 'someString'); - test('true', true); + it('should work for primitive literals', inject(function() { + test('1', 1); + test('null', null); + test('undefined', undefined); + test('\'someString\'', 'someString'); + test('true', true); - function test(literalString, literalValue) { - compile('
'); + function test(literalString, literalValue) { + compile('
'); - expect(componentScope.owRef).toBe(literalValue); - dealoc(element); - } - })); + expect(componentScope.owRef).toBe(literalValue); + dealoc(element); + } + })); - describe('optional one-way binding', function() { - it('should update local when origin changes', inject(function() { - compile('
'); + describe('optional one-way binding', function() { + it('should update local when origin changes', inject(function() { + compile('
'); - expect(componentScope.owOptref).toBeUndefined(); - expect(componentScope.owOptrefAlias).toBe(componentScope.owOptref); + expect(componentScope.owOptref).toBeUndefined(); + expect(componentScope.owOptrefAlias).toBe(componentScope.owOptref); - $rootScope.name = 'misko'; - $rootScope.$apply(); - expect(componentScope.owOptref).toBe($rootScope.name); - expect(componentScope.owOptrefAlias).toBe($rootScope.name); + $rootScope.name = 'misko'; + $rootScope.$apply(); + expect(componentScope.owOptref).toBe($rootScope.name); + expect(componentScope.owOptrefAlias).toBe($rootScope.name); - $rootScope.name = {}; - $rootScope.$apply(); - expect(componentScope.owOptref).toBe($rootScope.name); - expect(componentScope.owOptrefAlias).toBe($rootScope.name); - })); + $rootScope.name = {}; + $rootScope.$apply(); + expect(componentScope.owOptref).toBe($rootScope.name); + expect(componentScope.owOptrefAlias).toBe($rootScope.name); + })); - it('should not throw exception when reference does not exist', inject(function() { - compile('
'); + it('should not throw exception when reference does not exist', inject(function() { + compile('
'); - expect(componentScope.owOptref).toBeUndefined(); - expect(componentScope.owOptrefAlias).toBeUndefined(); - })); + expect(componentScope.owOptref).toBeUndefined(); + expect(componentScope.owOptrefAlias).toBeUndefined(); + })); + }); + }); }); - }); - }); - describe('executable expression', function() { - it('should allow expression execution with locals', inject(function() { - compile('
'); - $rootScope.count = 2; + describe('executable expression', function() { + it('should allow expression execution with locals', inject(function() { + compile('
'); + $rootScope.count = 2; - expect(typeof componentScope.expr).toBe('function'); - expect(typeof componentScope.exprAlias).toBe('function'); + expect(typeof componentScope.expr).toBe('function'); + expect(typeof componentScope.exprAlias).toBe('function'); - expect(componentScope.expr({offset: 1})).toEqual(3); - expect($rootScope.count).toEqual(3); - - expect(componentScope.exprAlias({offset: 10})).toEqual(13); - expect($rootScope.count).toEqual(13); - })); - }); - - it('should throw on unknown definition', inject(function() { - expect(function() { - compile('
'); - }).toThrowMinErr('$compile', 'iscp', 'Invalid isolate scope definition for directive \'badDeclaration\'. Definition: {... attr: \'xxx\' ...}'); - })); - - it('should expose a $$isolateBindings property onto the scope', inject(function() { - compile('
'); - - expect(typeof componentScope.$$isolateBindings).toBe('object'); - - expect(componentScope.$$isolateBindings.attr.mode).toBe('@'); - expect(componentScope.$$isolateBindings.attr.attrName).toBe('attr'); - expect(componentScope.$$isolateBindings.attrAlias.attrName).toBe('attr'); - expect(componentScope.$$isolateBindings.ref.mode).toBe('='); - expect(componentScope.$$isolateBindings.ref.attrName).toBe('ref'); - expect(componentScope.$$isolateBindings.refAlias.attrName).toBe('ref'); - expect(componentScope.$$isolateBindings.reference.mode).toBe('='); - expect(componentScope.$$isolateBindings.reference.attrName).toBe('reference'); - expect(componentScope.$$isolateBindings.owRef.mode).toBe('<'); - expect(componentScope.$$isolateBindings.owRef.attrName).toBe('owRef'); - expect(componentScope.$$isolateBindings.owRefAlias.attrName).toBe('owRef'); - expect(componentScope.$$isolateBindings.expr.mode).toBe('&'); - expect(componentScope.$$isolateBindings.expr.attrName).toBe('expr'); - expect(componentScope.$$isolateBindings.exprAlias.attrName).toBe('expr'); - - var firstComponentScope = componentScope, - first$$isolateBindings = componentScope.$$isolateBindings; - - dealoc(element); - compile('
'); - expect(componentScope).not.toBe(firstComponentScope); - expect(componentScope.$$isolateBindings).toBe(first$$isolateBindings); - })); + expect(componentScope.expr({offset: 1})).toEqual(3); + expect($rootScope.count).toEqual(3); + expect(componentScope.exprAlias({offset: 10})).toEqual(13); + expect($rootScope.count).toEqual(13); + })); + }); - it('should expose isolate scope variables on controller with controllerAs when bindToController is true (template)', function() { - var controllerCalled = false; - module(function($compileProvider) { - $compileProvider.directive('fooDir', valueFn({ - template: '

isolate

', - scope: { - 'data': '=dirData', - 'oneway': ''); + }).toThrowMinErr('$compile', 'iscp', 'Invalid isolate scope definition for directive \'badDeclaration\'. Definition: {... attr: \'xxx\' ...}'); })); - }); - inject(function($compile, $rootScope) { - $rootScope.fn = valueFn('called!'); - $rootScope.whom = 'world'; - $rootScope.remoteData = { - 'foo': 'bar', - 'baz': 'biz' - }; - element = $compile('
')($rootScope); - expect(controllerCalled).toBe(true); - }); - }); - - it('should eventually expose isolate scope variables on ES6 class controller with controllerAs when bindToController is true', function() { - if (!/chrome/i.test(window.navigator.userAgent)) return; - var controllerCalled = false; - // eslint-disable-next-line no-eval - var Controller = eval( - 'class Foo {\n' + - ' constructor($scope) {}\n' + - ' $onInit() { this.check(); }\n' + - ' check() {\n' + - ' expect(this.data).toEqualData({\n' + - ' \'foo\': \'bar\',\n' + - ' \'baz\': \'biz\'\n' + - ' });\n' + - ' expect(this.oneway).toEqualData({\n' + - ' \'foo\': \'bar\',\n' + - ' \'baz\': \'biz\'\n' + - ' });\n' + - ' expect(this.str).toBe(\'Hello, world!\');\n' + - ' expect(this.fn()).toBe(\'called!\');\n' + - ' controllerCalled = true;\n' + - ' }\n' + - '}'); - spyOn(Controller.prototype, '$onInit').and.callThrough(); + it('should expose a $$isolateBindings property onto the scope', inject(function() { + compile('
'); + + expect(typeof componentScope.$$isolateBindings).toBe('object'); + + expect(componentScope.$$isolateBindings.attr.mode).toBe('@'); + expect(componentScope.$$isolateBindings.attr.attrName).toBe('attr'); + expect(componentScope.$$isolateBindings.attrAlias.attrName).toBe('attr'); + expect(componentScope.$$isolateBindings.ref.mode).toBe('='); + expect(componentScope.$$isolateBindings.ref.attrName).toBe('ref'); + expect(componentScope.$$isolateBindings.refAlias.attrName).toBe('ref'); + expect(componentScope.$$isolateBindings.reference.mode).toBe('='); + expect(componentScope.$$isolateBindings.reference.attrName).toBe('reference'); + expect(componentScope.$$isolateBindings.owRef.mode).toBe('<'); + expect(componentScope.$$isolateBindings.owRef.attrName).toBe('owRef'); + expect(componentScope.$$isolateBindings.owRefAlias.attrName).toBe('owRef'); + expect(componentScope.$$isolateBindings.expr.mode).toBe('&'); + expect(componentScope.$$isolateBindings.expr.attrName).toBe('expr'); + expect(componentScope.$$isolateBindings.exprAlias.attrName).toBe('expr'); + + var firstComponentScope = componentScope, + first$$isolateBindings = componentScope.$$isolateBindings; - module(function($compileProvider) { - $compileProvider.directive('fooDir', valueFn({ - template: '

isolate

', - scope: { - 'data': '=dirData', - 'oneway': ''); + expect(componentScope).not.toBe(firstComponentScope); + expect(componentScope.$$isolateBindings).toBe(first$$isolateBindings); })); - }); - inject(function($compile, $rootScope) { - $rootScope.fn = valueFn('called!'); - $rootScope.whom = 'world'; - $rootScope.remoteData = { - 'foo': 'bar', - 'baz': 'biz' - }; - element = $compile('
')($rootScope); - expect(Controller.prototype.$onInit).toHaveBeenCalled(); - expect(controllerCalled).toBe(true); - }); - }); - - it('should update @-bindings on controller when bindToController and attribute change observed', function() { - module(function($compileProvider) { - $compileProvider.directive('atBinding', valueFn({ - template: '

{{At.text}}

', - scope: { - text: '@atBinding' - }, - controller: function($scope) {}, - bindToController: true, - controllerAs: 'At' - })); - }); - inject(function($compile, $rootScope) { - element = $compile('
')($rootScope); - var p = element.find('p'); - $rootScope.$digest(); - expect(p.text()).toBe('Test: '); - - $rootScope.text = 'Kittens'; - $rootScope.$digest(); - expect(p.text()).toBe('Test: Kittens'); - }); - }); - - - it('should expose isolate scope variables on controller with controllerAs when bindToController is true (templateUrl)', function() { - var controllerCalled = false; - module(function($compileProvider) { - $compileProvider.directive('fooDir', valueFn({ - templateUrl: 'test.html', - scope: { - 'data': '=dirData', - 'oneway': 'isolate

', + scope: { + 'data': '=dirData', + 'oneway': 'isolate

'); - $rootScope.fn = valueFn('called!'); - $rootScope.whom = 'world'; - $rootScope.remoteData = { - 'foo': 'bar', - 'baz': 'biz' - }; - element = $compile('
')($rootScope); - $rootScope.$digest(); - expect(controllerCalled).toBe(true); - }); - }); - - - it('should throw noctrl when missing controller', function() { - module(function($compileProvider) { - $compileProvider.directive('noCtrl', valueFn({ - templateUrl: 'test.html', - scope: { - 'data': '=dirData', - 'oneway': '')($rootScope); - }).toThrowMinErr('$compile', 'noctrl', - 'Cannot bind to controller without directive \'noCtrl\'s controller.'); - }); - }); - - - it('should throw noident when missing controllerAs directive property', function() { - module(function($compileProvider) { - $compileProvider.directive('noIdent', valueFn({ - templateUrl: 'test.html', - scope: { - 'data': '=dirData', - 'oneway': '')($rootScope); - }).toThrowMinErr('$compile', 'noident', - 'Cannot bind to controller without identifier for directive \'noIdent\'.'); - }); - }); - - - it('should throw noident when missing controller identifier', function() { - module(function($compileProvider, $controllerProvider) { - $controllerProvider.register('myCtrl', function() {}); - $compileProvider.directive('noIdent', valueFn({ - templateUrl: 'test.html', - scope: { - 'data': '=dirData', - 'oneway': '')($rootScope); - }).toThrowMinErr('$compile', 'noident', - 'Cannot bind to controller without identifier for directive \'noIdent\'.'); - }); - }); - - - it('should bind to controller via object notation (isolate scope)', function() { - var controllerCalled = false; - module(function($compileProvider, $controllerProvider) { - $controllerProvider.register('myCtrl', function() { - expect(this.data).toEqualData({ - 'foo': 'bar', - 'baz': 'biz' - }); - expect(this.oneway).toEqualData({ - 'foo': 'bar', - 'baz': 'biz' - }); - expect(this.str).toBe('Hello, world!'); - expect(this.fn()).toBe('called!'); - controllerCalled = true; - }); - $compileProvider.directive('fooDir', valueFn({ - templateUrl: 'test.html', - bindToController: { - 'data': '=dirData', - 'oneway': 'isolate

'); - $rootScope.fn = valueFn('called!'); - $rootScope.whom = 'world'; - $rootScope.remoteData = { - 'foo': 'bar', - 'baz': 'biz' - }; - element = $compile('
')($rootScope); - $rootScope.$digest(); - expect(controllerCalled).toBe(true); - }); - }); - - - it('should bind to controller via object notation (new scope)', function() { - var controllerCalled = false; - module(function($compileProvider, $controllerProvider) { - $controllerProvider.register('myCtrl', function() { - expect(this.data).toEqualData({ - 'foo': 'bar', - 'baz': 'biz' - }); - expect(this.data).toEqualData({ - 'foo': 'bar', - 'baz': 'biz' - }); - expect(this.str).toBe('Hello, world!'); - expect(this.fn()).toBe('called!'); - controllerCalled = true; - }); - $compileProvider.directive('fooDir', valueFn({ - templateUrl: 'test.html', - bindToController: { - 'data': '=dirData', - 'oneway': 'isolate

'); - $rootScope.fn = valueFn('called!'); - $rootScope.whom = 'world'; - $rootScope.remoteData = { - 'foo': 'bar', - 'baz': 'biz' - }; - element = $compile('
')($rootScope); - $rootScope.$digest(); - expect(controllerCalled).toBe(true); - }); - }); - - - it('should bind to multiple directives controllers via object notation (no scope)', function() { - var controller1Called = false; - var controller2Called = false; - module(function($compileProvider, $controllerProvider) { - $compileProvider.directive('foo', valueFn({ - bindToController: { - 'data': '=fooData', - 'oneway': ' ' + - '
')($rootScope); - $rootScope.$digest(); - expect(controller1Called).toBe(true); - expect(controller2Called).toBe(true); - }); - }); - - - it('should bind to multiple directives controllers via object notation (new iso scope)', function() { - var controller1Called = false; - var controller2Called = false; - module(function($compileProvider, $controllerProvider) { - $compileProvider.directive('foo', valueFn({ - bindToController: { - 'data': '=fooData', - 'oneway': ' ' + - '
')($rootScope); - $rootScope.$digest(); - expect(controller1Called).toBe(true); - expect(controller2Called).toBe(true); - }); - }); - - - it('should bind to multiple directives controllers via object notation (new scope)', function() { - var controller1Called = false; - var controller2Called = false; - module(function($compileProvider, $controllerProvider) { - $compileProvider.directive('foo', valueFn({ - bindToController: { - 'data': '=fooData', - 'oneway': ' ' + - '
')($rootScope); - $rootScope.$digest(); - expect(controller1Called).toBe(true); - expect(controller2Called).toBe(true); - }); - }); - - - it('should evaluate against the correct scope, when using `bindToController` (new scope)', - function() { - module(function($compileProvider, $controllerProvider) { - $controllerProvider.register({ - 'ParentCtrl': function() { - this.value1 = 'parent1'; - this.value2 = 'parent2'; - this.value3 = function() { return 'parent3'; }; - this.value4 = 'parent4'; - }, - 'ChildCtrl': function() { - this.value1 = 'child1'; - this.value2 = 'child2'; - this.value3 = function() { return 'child3'; }; - this.value4 = 'child4'; - } + }; + element = $compile('
')($rootScope); + expect(controllerCalled).toBe(true); }); - - $compileProvider.directive('child', valueFn({ - scope: true, - controller: 'ChildCtrl as ctrl', - bindToController: { - fromParent1: '@', - fromParent2: '=', - fromParent3: '&', - fromParent4: '<' - }, - template: '' - })); }); - inject(function($compile, $rootScope) { - element = $compile( - '
' + - '' + - '' + - '
')($rootScope); - $rootScope.$digest(); - var parentCtrl = element.controller('ngController'); - var childCtrl = element.find('child').controller('child'); - - expect(childCtrl.fromParent1).toBe(parentCtrl.value1); - expect(childCtrl.fromParent1).not.toBe(childCtrl.value1); - expect(childCtrl.fromParent2).toBe(parentCtrl.value2); - expect(childCtrl.fromParent2).not.toBe(childCtrl.value2); - expect(childCtrl.fromParent3()()).toBe(parentCtrl.value3()); - expect(childCtrl.fromParent3()()).not.toBe(childCtrl.value3()); - expect(childCtrl.fromParent4).toBe(parentCtrl.value4); - expect(childCtrl.fromParent4).not.toBe(childCtrl.value4); - - childCtrl.fromParent2 = 'modified'; - $rootScope.$digest(); - - expect(parentCtrl.value2).toBe('modified'); - expect(childCtrl.value2).toBe('child2'); - }); - } - ); - - - it('should evaluate against the correct scope, when using `bindToController` (new iso scope)', - function() { - module(function($compileProvider, $controllerProvider) { - $controllerProvider.register({ - 'ParentCtrl': function() { - this.value1 = 'parent1'; - this.value2 = 'parent2'; - this.value3 = function() { return 'parent3'; }; - this.value4 = 'parent4'; - }, - 'ChildCtrl': function() { - this.value1 = 'child1'; - this.value2 = 'child2'; - this.value3 = function() { return 'child3'; }; - this.value4 = 'child4'; - } + it('should not pre-assign bound properties to the controller if `preAssignBindingsEnabled` is disabled', function() { + var controllerCalled = false, onInitCalled = false; + module(function($compileProvider) { + $compileProvider.preAssignBindingsEnabled(false); + $compileProvider.directive('fooDir', valueFn({ + template: '

isolate

', + scope: { + 'data': '=dirData', + 'oneway': '
')($rootScope); + expect(controllerCalled).toBe(true); + expect(onInitCalled).toBe(true); }); - - $compileProvider.directive('child', valueFn({ - scope: {}, - controller: 'ChildCtrl as ctrl', - bindToController: { - fromParent1: '@', - fromParent2: '=', - fromParent3: '&', - fromParent4: '<' - }, - template: '' - })); - }); - - inject(function($compile, $rootScope) { - element = $compile( - '
' + - '' + - '' + - '
')($rootScope); - $rootScope.$digest(); - - var parentCtrl = element.controller('ngController'); - var childCtrl = element.find('child').controller('child'); - - expect(childCtrl.fromParent1).toBe(parentCtrl.value1); - expect(childCtrl.fromParent1).not.toBe(childCtrl.value1); - expect(childCtrl.fromParent2).toBe(parentCtrl.value2); - expect(childCtrl.fromParent2).not.toBe(childCtrl.value2); - expect(childCtrl.fromParent3()()).toBe(parentCtrl.value3()); - expect(childCtrl.fromParent3()()).not.toBe(childCtrl.value3()); - expect(childCtrl.fromParent4).toBe(parentCtrl.value4); - expect(childCtrl.fromParent4).not.toBe(childCtrl.value4); - - childCtrl.fromParent2 = 'modified'; - $rootScope.$digest(); - - expect(parentCtrl.value2).toBe('modified'); - expect(childCtrl.value2).toBe('child2'); - }); - } - ); - - - it('should put controller in scope when controller identifier present but not using controllerAs', function() { - var controllerCalled = false; - var myCtrl; - module(function($compileProvider, $controllerProvider) { - $controllerProvider.register('myCtrl', function() { - controllerCalled = true; - myCtrl = this; }); - $compileProvider.directive('fooDir', valueFn({ - templateUrl: 'test.html', - bindToController: {}, - scope: true, - controller: 'myCtrl as theCtrl' - })); - }); - inject(function($compile, $rootScope, $templateCache) { - $templateCache.put('test.html', '

isolate

'); - element = $compile('
')($rootScope); - $rootScope.$digest(); - expect(controllerCalled).toBe(true); - var childScope = element.children().scope(); - expect(childScope).not.toBe($rootScope); - expect(childScope.theCtrl).toBe(myCtrl); - }); - }); + it('should pre-assign bound properties to the controller if `preAssignBindingsEnabled` is enabled', function() { + var controllerCalled = false, onInitCalled = false; + module(function($compileProvider) { + $compileProvider.preAssignBindingsEnabled(true); + $compileProvider.directive('fooDir', valueFn({ + template: '

isolate

', + scope: { + 'data': '=dirData', + 'oneway': '
')($rootScope); + expect(controllerCalled).toBe(true); + expect(onInitCalled).toBe(true); + }); + }); + + it('should eventually expose isolate scope variables on ES6 class controller with controllerAs when bindToController is true', function() { + if (!/chrome/i.test(window.navigator.userAgent)) return; + var controllerCalled = false; + // eslint-disable-next-line no-eval + var Controller = eval( + 'class Foo {\n' + + ' constructor($scope) {}\n' + + ' $onInit() { this.check(); }\n' + + ' check() {\n' + + ' expect(this.data).toEqualData({\n' + + ' \'foo\': \'bar\',\n' + + ' \'baz\': \'biz\'\n' + + ' });\n' + + ' expect(this.oneway).toEqualData({\n' + + ' \'foo\': \'bar\',\n' + + ' \'baz\': \'biz\'\n' + + ' });\n' + + ' expect(this.str).toBe(\'Hello, world!\');\n' + + ' expect(this.fn()).toBe(\'called!\');\n' + + ' controllerCalled = true;\n' + + ' }\n' + + '}'); + spyOn(Controller.prototype, '$onInit').and.callThrough(); - it('should re-install controllerAs and bindings for returned value from controller (new scope)', function() { - var controllerCalled = false; - var myCtrl; - - function MyCtrl() { - } - MyCtrl.prototype.test = function() { - expect(this.data).toEqualData({ - 'foo': 'bar', - 'baz': 'biz' - }); - expect(this.oneway).toEqualData({ - 'foo': 'bar', - 'baz': 'biz' + module(function($compileProvider) { + $compileProvider.directive('fooDir', valueFn({ + template: '

isolate

', + scope: { + 'data': '=dirData', + 'oneway': '
')($rootScope); + expect(Controller.prototype.$onInit).toHaveBeenCalled(); + expect(controllerCalled).toBe(true); + }); }); - expect(this.str).toBe('Hello, world!'); - expect(this.fn()).toBe('called!'); - }; - module(function($compileProvider, $controllerProvider) { - $controllerProvider.register('myCtrl', function() { - controllerCalled = true; - myCtrl = this; - return new MyCtrl(); - }); - $compileProvider.directive('fooDir', valueFn({ - templateUrl: 'test.html', - bindToController: { - 'data': '=dirData', - 'oneway': 'isolate

'); - $rootScope.fn = valueFn('called!'); - $rootScope.whom = 'world'; - $rootScope.remoteData = { - 'foo': 'bar', - 'baz': 'biz' - }; - element = $compile('
')($rootScope); - $rootScope.$digest(); - expect(controllerCalled).toBe(true); - var childScope = element.children().scope(); - expect(childScope).not.toBe($rootScope); - expect(childScope.theCtrl).not.toBe(myCtrl); - expect(childScope.theCtrl.constructor).toBe(MyCtrl); - childScope.theCtrl.test(); - }); - }); + it('should update @-bindings on controller when bindToController and attribute change observed', function() { + module(function($compileProvider) { + $compileProvider.directive('atBinding', valueFn({ + template: '

{{At.text}}

', + scope: { + text: '@atBinding' + }, + controller: function($scope) {}, + bindToController: true, + controllerAs: 'At' + })); + }); - it('should re-install controllerAs and bindings for returned value from controller (isolate scope)', function() { - var controllerCalled = false; - var myCtrl; + inject(function($compile, $rootScope) { + element = $compile('
')($rootScope); + var p = element.find('p'); + $rootScope.$digest(); + expect(p.text()).toBe('Test: '); - function MyCtrl() { - } - MyCtrl.prototype.test = function() { - expect(this.data).toEqualData({ - 'foo': 'bar', - 'baz': 'biz' - }); - expect(this.oneway).toEqualData({ - 'foo': 'bar', - 'baz': 'biz' + $rootScope.text = 'Kittens'; + $rootScope.$digest(); + expect(p.text()).toBe('Test: Kittens'); + }); }); - expect(this.str).toBe('Hello, world!'); - expect(this.fn()).toBe('called!'); - }; - module(function($compileProvider, $controllerProvider) { - $controllerProvider.register('myCtrl', function() { - controllerCalled = true; - myCtrl = this; - return new MyCtrl(); - }); - $compileProvider.directive('fooDir', valueFn({ - templateUrl: 'test.html', - bindToController: true, - scope: { - 'data': '=dirData', - 'oneway': 'isolate

'); - $rootScope.fn = valueFn('called!'); - $rootScope.whom = 'world'; - $rootScope.remoteData = { - 'foo': 'bar', - 'baz': 'biz' - }; - element = $compile('
')($rootScope); - $rootScope.$digest(); - expect(controllerCalled).toBe(true); - var childScope = element.children().scope(); - expect(childScope).not.toBe($rootScope); - expect(childScope.theCtrl).not.toBe(myCtrl); - expect(childScope.theCtrl.constructor).toBe(MyCtrl); - childScope.theCtrl.test(); - }); - }); - - describe('should not overwrite @-bound property each digest when not present', function() { - it('when creating new scope', function() { - module(function($compileProvider) { - $compileProvider.directive('testDir', valueFn({ - scope: true, - bindToController: { - prop: '@' - }, - controller: function() { - var self = this; - this.prop = this.prop || 'default'; - this.getProp = function() { - return self.prop; - }; - }, - controllerAs: 'ctrl', - template: '

' - })); - }); - inject(function($compile, $rootScope) { - element = $compile('
')($rootScope); - var scope = element.scope(); - expect(scope.ctrl.getProp()).toBe('default'); - $rootScope.$digest(); - expect(scope.ctrl.getProp()).toBe('default'); + it('should expose isolate scope variables on controller with controllerAs when bindToController is true (templateUrl)', function() { + var controllerCalled = false; + module(function($compileProvider) { + $compileProvider.directive('fooDir', valueFn({ + templateUrl: 'test.html', + scope: { + 'data': '=dirData', + 'oneway': 'isolate

'); + $rootScope.fn = valueFn('called!'); + $rootScope.whom = 'world'; + $rootScope.remoteData = { + 'foo': 'bar', + 'baz': 'biz' + }; + element = $compile('
')($rootScope); + $rootScope.$digest(); + expect(controllerCalled).toBe(true); + }); }); - }); - it('when creating isolate scope', function() { - module(function($compileProvider) { - $compileProvider.directive('testDir', valueFn({ - scope: {}, - bindToController: { - prop: '@' - }, - controller: function() { - var self = this; - this.prop = this.prop || 'default'; - this.getProp = function() { - return self.prop; - }; - }, - controllerAs: 'ctrl', - template: '

' - })); - }); - inject(function($compile, $rootScope) { - element = $compile('
')($rootScope); - var scope = element.isolateScope(); - expect(scope.ctrl.getProp()).toBe('default'); - $rootScope.$digest(); - expect(scope.ctrl.getProp()).toBe('default'); + it('should throw noctrl when missing controller', function() { + module(function($compileProvider) { + $compileProvider.directive('noCtrl', valueFn({ + templateUrl: 'test.html', + scope: { + 'data': '=dirData', + 'oneway': '')($rootScope); + }).toThrowMinErr('$compile', 'noctrl', + 'Cannot bind to controller without directive \'noCtrl\'s controller.'); + }); }); - }); - }); - }); - - - describe('controller', function() { - it('should get required controller', function() { - module(function() { - directive('main', function(log) { - return { - priority: 2, - controller: function() { - this.name = 'main'; - }, - link: function(scope, element, attrs, controller) { - log(controller.name); - } - }; - }); - directive('dep', function(log) { - return { - priority: 1, - require: 'main', - link: function(scope, element, attrs, controller) { - log('dep:' + controller.name); - } - }; - }); - directive('other', function(log) { - return { - link: function(scope, element, attrs, controller) { - log(!!controller); // should be false - } - }; - }); - }); - inject(function(log, $compile, $rootScope) { - element = $compile('
')($rootScope); - expect(log).toEqual('false; dep:main; main'); - }); - }); + it('should throw badrestrict on first compilation when restrict is invalid', function() { + module(function($compileProvider, $exceptionHandlerProvider) { + $compileProvider.directive('invalidRestrictBadString', valueFn({restrict: '"'})); + $compileProvider.directive('invalidRestrictTrue', valueFn({restrict: true})); + $compileProvider.directive('invalidRestrictObject', valueFn({restrict: {}})); + $compileProvider.directive('invalidRestrictNumber', valueFn({restrict: 42})); - it('should respect explicit return value from controller', function() { - var expectedController; - module(function() { - directive('logControllerProp', function(log) { - return { - controller: function($scope) { - this.foo = 'baz'; // value should not be used. - expectedController = {foo: 'bar'}; - return expectedController; - }, - link: function(scope, element, attrs, controller) { - expect(expectedController).toBeDefined(); - expect(controller).toBe(expectedController); - expect(controller.foo).toBe('bar'); - log('done'); - } - }; - }); - }); - inject(function(log, $compile, $rootScope) { - element = $compile('')($rootScope); - expect(log).toEqual('done'); - expect(element.data('$logControllerPropController')).toBe(expectedController); - }); - }); + // We need to test with the exceptionHandler not rethrowing... + $exceptionHandlerProvider.mode('log'); + }); + inject(function($exceptionHandler, $compile, $rootScope) { + $compile('
')($rootScope); + expect($exceptionHandler.errors.length).toBe(1); + expect($exceptionHandler.errors[0]).toMatch(/\$compile.*badrestrict.*'true'/); - it('should get explicit return value of required parent controller', function() { - var expectedController; - module(function() { - directive('nested', function(log) { - return { - require: '^^?nested', - controller: function() { - if (!expectedController) expectedController = {foo: 'bar'}; - return expectedController; - }, - link: function(scope, element, attrs, controller) { - if (element.parent().length) { - expect(expectedController).toBeDefined(); - expect(controller).toBe(expectedController); - expect(controller.foo).toBe('bar'); - log('done'); - } - } - }; - }); - }); - inject(function(log, $compile, $rootScope) { - element = $compile('
')($rootScope); - expect(log).toEqual('done'); - expect(element.data('$nestedController')).toBe(expectedController); - }); - }); + $compile('
')($rootScope); + $compile('
')($rootScope); + expect($exceptionHandler.errors.length).toBe(2); + expect($exceptionHandler.errors[1]).toMatch(/\$compile.*badrestrict.*'"'/); + $compile('
')($rootScope); + expect($exceptionHandler.errors.length).toBe(3); + expect($exceptionHandler.errors[2]).toMatch(/\$compile.*badrestrict.*'{}'/); - it('should respect explicit controller return value when using controllerAs', function() { - module(function() { - directive('main', function() { - return { - templateUrl: 'main.html', - scope: {}, - controller: function() { - this.name = 'lucas'; - return {name: 'george'}; - }, - controllerAs: 'mainCtrl' - }; + $compile('
')($rootScope); + expect($exceptionHandler.errors.length).toBe(4); + expect($exceptionHandler.errors[3]).toMatch(/\$compile.*badrestrict.*'42'/); + }); }); - }); - inject(function($templateCache, $compile, $rootScope) { - $templateCache.put('main.html', 'template:{{mainCtrl.name}}'); - element = $compile('
')($rootScope); - $rootScope.$apply(); - expect(element.text()).toBe('template:george'); - }); - }); - it('transcluded children should receive explicit return value of parent controller', function() { - var expectedController; - module(function() { - directive('nester', valueFn({ - transclude: true, - controller: function($transclude) { - this.foo = 'baz'; - expectedController = {transclude:$transclude, foo: 'bar'}; - return expectedController; - }, - link: function(scope, el, attr, ctrl) { - ctrl.transclude(cloneAttach); - function cloneAttach(clone) { - el.append(clone); - } - } - })); - directive('nested', function(log) { - return { - require: '^^nester', - link: function(scope, element, attrs, controller) { - expect(controller).toBeDefined(); - expect(controller).toBe(expectedController); - log('done'); - } - }; + it('should throw noident when missing controllerAs directive property', function() { + module(function($compileProvider) { + $compileProvider.directive('noIdent', valueFn({ + templateUrl: 'test.html', + scope: { + 'data': '=dirData', + 'oneway': '')($rootScope); + }).toThrowMinErr('$compile', 'noident', + 'Cannot bind to controller without identifier for directive \'noIdent\'.'); + }); }); - }); - inject(function(log, $compile) { - element = $compile('
')($rootScope); - $rootScope.$apply(); - expect(log.toString()).toBe('done'); - expect(element.data('$nesterController')).toBe(expectedController); - }); - }); - it('explicit controller return values are ignored if they are primitives', function() { - module(function() { - directive('logControllerProp', function(log) { - return { - controller: function($scope) { - this.foo = 'baz'; // value *will* be used. - return 'bar'; - }, - link: function(scope, element, attrs, controller) { - log(controller.foo); - } - }; + it('should throw noident when missing controller identifier', function() { + module(function($compileProvider, $controllerProvider) { + $controllerProvider.register('myCtrl', function() {}); + $compileProvider.directive('noIdent', valueFn({ + templateUrl: 'test.html', + scope: { + 'data': '=dirData', + 'oneway': '')($rootScope); + }).toThrowMinErr('$compile', 'noident', + 'Cannot bind to controller without identifier for directive \'noIdent\'.'); + }); }); - }); - inject(function(log, $compile, $rootScope) { - element = $compile('')($rootScope); - expect(log).toEqual('baz'); - expect(element.data('$logControllerPropController').foo).toEqual('baz'); - }); - }); - - it('should correctly assign controller return values for multiple directives', function() { - var directiveController, otherDirectiveController; - module(function() { - directive('myDirective', function(log) { - return { - scope: true, - controller: function($scope) { - directiveController = { - foo: 'bar' + it('should bind to controller via object notation (isolate scope)', function() { + var controllerCalled = false; + module(function($compileProvider, $controllerProvider) { + $controllerProvider.register('myCtrl', function() { + this.check = function() { + expect(this.data).toEqualData({ + 'foo': 'bar', + 'baz': 'biz' + }); + expect(this.oneway).toEqualData({ + 'foo': 'bar', + 'baz': 'biz' + }); + expect(this.str).toBe('Hello, world!'); + expect(this.fn()).toBe('called!'); }; - return directiveController; - } - }; + controllerCalled = true; + if (preAssignBindingsEnabled) { + this.check(); + } else { + this.$onInit = this.check; + } + }); + $compileProvider.directive('fooDir', valueFn({ + templateUrl: 'test.html', + bindToController: { + 'data': '=dirData', + 'oneway': 'isolate

'); + $rootScope.fn = valueFn('called!'); + $rootScope.whom = 'world'; + $rootScope.remoteData = { + 'foo': 'bar', + 'baz': 'biz' + }; + element = $compile('
')($rootScope); + $rootScope.$digest(); + expect(controllerCalled).toBe(true); + }); }); - directive('myOtherDirective', function(log) { - return { - controller: function($scope) { - otherDirectiveController = { - baz: 'luh' + + it('should bind to controller via object notation (new scope)', function() { + var controllerCalled = false; + module(function($compileProvider, $controllerProvider) { + $controllerProvider.register('myCtrl', function() { + this.check = function() { + expect(this.data).toEqualData({ + 'foo': 'bar', + 'baz': 'biz' + }); + expect(this.data).toEqualData({ + 'foo': 'bar', + 'baz': 'biz' + }); + expect(this.str).toBe('Hello, world!'); + expect(this.fn()).toBe('called!'); }; - return otherDirectiveController; - } - }; + controllerCalled = true; + if (preAssignBindingsEnabled) { + this.check(); + } else { + this.$onInit = this.check; + } + }); + $compileProvider.directive('fooDir', valueFn({ + templateUrl: 'test.html', + bindToController: { + 'data': '=dirData', + 'oneway': 'isolate

'); + $rootScope.fn = valueFn('called!'); + $rootScope.whom = 'world'; + $rootScope.remoteData = { + 'foo': 'bar', + 'baz': 'biz' + }; + element = $compile('
')($rootScope); + $rootScope.$digest(); + expect(controllerCalled).toBe(true); + }); }); - }); - - inject(function(log, $compile, $rootScope) { - element = $compile('')($rootScope); - expect(element.data('$myDirectiveController')).toBe(directiveController); - expect(element.data('$myOtherDirectiveController')).toBe(otherDirectiveController); - }); - }); - - it('should get required parent controller', function() { - module(function() { - directive('nested', function(log) { - return { - require: '^^?nested', - controller: function($scope) {}, - link: function(scope, element, attrs, controller) { - log(!!controller); - } - }; + it('should bind to multiple directives controllers via object notation (no scope)', function() { + var controller1Called = false; + var controller2Called = false; + module(function($compileProvider, $controllerProvider) { + $compileProvider.directive('foo', valueFn({ + bindToController: { + 'data': '=fooData', + 'oneway': ' ' + + '
')($rootScope); + $rootScope.$digest(); + expect(controller1Called).toBe(true); + expect(controller2Called).toBe(true); + }); }); - }); - inject(function(log, $compile, $rootScope) { - element = $compile('
')($rootScope); - expect(log).toEqual('true; false'); - }); - }); - it('should get required parent controller when the question mark precedes the ^^', function() { - module(function() { - directive('nested', function(log) { - return { - require: '?^^nested', - controller: function($scope) {}, - link: function(scope, element, attrs, controller) { - log(!!controller); - } - }; + it('should bind to multiple directives controllers via object notation (new iso scope)', function() { + var controller1Called = false; + var controller2Called = false; + module(function($compileProvider, $controllerProvider) { + $compileProvider.directive('foo', valueFn({ + bindToController: { + 'data': '=fooData', + 'oneway': ' ' + + '
')($rootScope); + $rootScope.$digest(); + expect(controller1Called).toBe(true); + expect(controller2Called).toBe(true); + }); }); - }); - inject(function(log, $compile, $rootScope) { - element = $compile('
')($rootScope); - expect(log).toEqual('true; false'); - }); - }); - it('should throw if required parent is not found', function() { - module(function() { - directive('nested', function() { - return { - require: '^^nested', - controller: function($scope) {}, - link: function(scope, element, attrs, controller) {} - }; + it('should bind to multiple directives controllers via object notation (new scope)', function() { + var controller1Called = false; + var controller2Called = false; + module(function($compileProvider, $controllerProvider) { + $compileProvider.directive('foo', valueFn({ + bindToController: { + 'data': '=fooData', + 'oneway': ' ' + + '
')($rootScope); + $rootScope.$digest(); + expect(controller1Called).toBe(true); + expect(controller2Called).toBe(true); + }); }); - }); - inject(function($compile, $rootScope) { - expect(function() { - element = $compile('
')($rootScope); - }).toThrowMinErr('$compile', 'ctreq', 'Controller \'nested\', required by directive \'nested\', can\'t be found!'); - }); - }); - - it('should get required controller via linkingFn (template)', function() { - module(function() { - directive('dirA', function() { - return { - controller: function() { - this.name = 'dirA'; - } - }; - }); - directive('dirB', function(log) { - return { - require: 'dirA', - template: '

dirB

', - link: function(scope, element, attrs, dirAController) { - log('dirAController.name: ' + dirAController.name); - } - }; - }); - }); - inject(function(log, $compile, $rootScope) { - element = $compile('
')($rootScope); - expect(log).toEqual('dirAController.name: dirA'); - }); - }); + it('should evaluate against the correct scope, when using `bindToController` (new scope)', + function() { + module(function($compileProvider, $controllerProvider) { + $controllerProvider.register({ + 'ParentCtrl': function() { + this.value1 = 'parent1'; + this.value2 = 'parent2'; + this.value3 = function() { return 'parent3'; }; + this.value4 = 'parent4'; + }, + 'ChildCtrl': function() { + this.value1 = 'child1'; + this.value2 = 'child2'; + this.value3 = function() { return 'child3'; }; + this.value4 = 'child4'; + } + }); - it('should get required controller via linkingFn (templateUrl)', function() { - module(function() { - directive('dirA', function() { - return { - controller: function() { - this.name = 'dirA'; - } - }; - }); - directive('dirB', function(log) { - return { - require: 'dirA', - templateUrl: 'dirB.html', - link: function(scope, element, attrs, dirAController) { - log('dirAController.name: ' + dirAController.name); - } - }; - }); - }); - inject(function(log, $compile, $rootScope, $templateCache) { - $templateCache.put('dirB.html', '

dirB

'); - element = $compile('
')($rootScope); - $rootScope.$digest(); - expect(log).toEqual('dirAController.name: dirA'); - }); - }); + $compileProvider.directive('child', valueFn({ + scope: true, + controller: 'ChildCtrl as ctrl', + bindToController: { + fromParent1: '@', + fromParent2: '=', + fromParent3: '&', + fromParent4: '<' + }, + template: '' + })); + }); - it('should bind the required controllers to the directive controller, if provided as an object and bindToController is truthy', function() { - var parentController, siblingController; + inject(function($compile, $rootScope) { + element = $compile( + '
' + + '' + + '' + + '
')($rootScope); + $rootScope.$digest(); - function ParentController() { this.name = 'Parent'; } - function SiblingController() { this.name = 'Sibling'; } - function MeController() { this.name = 'Me'; } - MeController.prototype.$onInit = function() { - parentController = this.container; - siblingController = this.friend; - }; - spyOn(MeController.prototype, '$onInit').and.callThrough(); + var parentCtrl = element.controller('ngController'); + var childCtrl = element.find('child').controller('child'); - angular.module('my', []) - .directive('me', function() { - return { - restrict: 'E', - scope: {}, - require: { container: '^parent', friend: 'sibling' }, - bindToController: true, - controller: MeController, - controllerAs: '$ctrl' - }; - }) - .directive('parent', function() { - return { - restrict: 'E', - scope: {}, - controller: ParentController - }; - }) - .directive('sibling', function() { - return { - controller: SiblingController - }; - }); + expect(childCtrl.fromParent1).toBe(parentCtrl.value1); + expect(childCtrl.fromParent1).not.toBe(childCtrl.value1); + expect(childCtrl.fromParent2).toBe(parentCtrl.value2); + expect(childCtrl.fromParent2).not.toBe(childCtrl.value2); + expect(childCtrl.fromParent3()()).toBe(parentCtrl.value3()); + expect(childCtrl.fromParent3()()).not.toBe(childCtrl.value3()); + expect(childCtrl.fromParent4).toBe(parentCtrl.value4); + expect(childCtrl.fromParent4).not.toBe(childCtrl.value4); - module('my'); - inject(function($compile, $rootScope, meDirective) { - element = $compile('')($rootScope); - expect(MeController.prototype.$onInit).toHaveBeenCalled(); - expect(parentController).toEqual(jasmine.any(ParentController)); - expect(siblingController).toEqual(jasmine.any(SiblingController)); - }); - }); + childCtrl.fromParent2 = 'modified'; + $rootScope.$digest(); - it('should use the key if the name of a required controller is omitted', function() { - function ParentController() { this.name = 'Parent'; } - function ParentOptController() { this.name = 'ParentOpt'; } - function ParentOrSiblingController() { this.name = 'ParentOrSibling'; } - function ParentOrSiblingOptController() { this.name = 'ParentOrSiblingOpt'; } - function SiblingController() { this.name = 'Sibling'; } - function SiblingOptController() { this.name = 'SiblingOpt'; } - - angular.module('my', []) - .component('me', { - require: { - parent: '^^', - parentOpt: '?^^', - parentOrSibling1: '^', - parentOrSiblingOpt1: '?^', - parentOrSibling2: '^', - parentOrSiblingOpt2: '?^', - sibling: '', - siblingOpt: '?' + expect(parentCtrl.value2).toBe('modified'); + expect(childCtrl.value2).toBe('child2'); + }); } - }) - .directive('parent', function() { - return {controller: ParentController}; - }) - .directive('parentOpt', function() { - return {controller: ParentOptController}; - }) - .directive('parentOrSibling1', function() { - return {controller: ParentOrSiblingController}; - }) - .directive('parentOrSiblingOpt1', function() { - return {controller: ParentOrSiblingOptController}; - }) - .directive('parentOrSibling2', function() { - return {controller: ParentOrSiblingController}; - }) - .directive('parentOrSiblingOpt2', function() { - return {controller: ParentOrSiblingOptController}; - }) - .directive('sibling', function() { - return {controller: SiblingController}; - }) - .directive('siblingOpt', function() { - return {controller: SiblingOptController}; - }); + ); - module('my'); - inject(function($compile, $rootScope) { - var template = - '
' + - // With optional - '' + - '' + - '' + - // Without optional - '' + - '' + - '' + - '
'; - element = $compile(template)($rootScope); - - var ctrl1 = element.find('me').eq(0).controller('me'); - expect(ctrl1.parent).toEqual(jasmine.any(ParentController)); - expect(ctrl1.parentOpt).toEqual(jasmine.any(ParentOptController)); - expect(ctrl1.parentOrSibling1).toEqual(jasmine.any(ParentOrSiblingController)); - expect(ctrl1.parentOrSiblingOpt1).toEqual(jasmine.any(ParentOrSiblingOptController)); - expect(ctrl1.parentOrSibling2).toEqual(jasmine.any(ParentOrSiblingController)); - expect(ctrl1.parentOrSiblingOpt2).toEqual(jasmine.any(ParentOrSiblingOptController)); - expect(ctrl1.sibling).toEqual(jasmine.any(SiblingController)); - expect(ctrl1.siblingOpt).toEqual(jasmine.any(SiblingOptController)); - - var ctrl2 = element.find('me').eq(1).controller('me'); - expect(ctrl2.parent).toEqual(jasmine.any(ParentController)); - expect(ctrl2.parentOpt).toBe(null); - expect(ctrl2.parentOrSibling1).toEqual(jasmine.any(ParentOrSiblingController)); - expect(ctrl2.parentOrSiblingOpt1).toBe(null); - expect(ctrl2.parentOrSibling2).toEqual(jasmine.any(ParentOrSiblingController)); - expect(ctrl2.parentOrSiblingOpt2).toBe(null); - expect(ctrl2.sibling).toEqual(jasmine.any(SiblingController)); - expect(ctrl2.siblingOpt).toBe(null); - }); - }); + it('should evaluate against the correct scope, when using `bindToController` (new iso scope)', + function() { + module(function($compileProvider, $controllerProvider) { + $controllerProvider.register({ + 'ParentCtrl': function() { + this.value1 = 'parent1'; + this.value2 = 'parent2'; + this.value3 = function() { return 'parent3'; }; + this.value4 = 'parent4'; + }, + 'ChildCtrl': function() { + this.value1 = 'child1'; + this.value2 = 'child2'; + this.value3 = function() { return 'child3'; }; + this.value4 = 'child4'; + } + }); - it('should not bind required controllers if bindToController is falsy', function() { - var parentController, siblingController; + $compileProvider.directive('child', valueFn({ + scope: {}, + controller: 'ChildCtrl as ctrl', + bindToController: { + fromParent1: '@', + fromParent2: '=', + fromParent3: '&', + fromParent4: '<' + }, + template: '' + })); + }); - function ParentController() { this.name = 'Parent'; } - function SiblingController() { this.name = 'Sibling'; } - function MeController() { this.name = 'Me'; } - MeController.prototype.$onInit = function() { - parentController = this.container; - siblingController = this.friend; - }; - spyOn(MeController.prototype, '$onInit').and.callThrough(); + inject(function($compile, $rootScope) { + element = $compile( + '
' + + '' + + '' + + '
')($rootScope); + $rootScope.$digest(); - angular.module('my', []) - .directive('me', function() { - return { - restrict: 'E', - scope: {}, - require: { container: '^parent', friend: 'sibling' }, - controller: MeController - }; - }) - .directive('parent', function() { - return { - restrict: 'E', - scope: {}, - controller: ParentController - }; - }) - .directive('sibling', function() { - return { - controller: SiblingController - }; - }); + var parentCtrl = element.controller('ngController'); + var childCtrl = element.find('child').controller('child'); - module('my'); - inject(function($compile, $rootScope, meDirective) { - element = $compile('')($rootScope); - expect(MeController.prototype.$onInit).toHaveBeenCalled(); - expect(parentController).toBeUndefined(); - expect(siblingController).toBeUndefined(); - }); - }); + expect(childCtrl.fromParent1).toBe(parentCtrl.value1); + expect(childCtrl.fromParent1).not.toBe(childCtrl.value1); + expect(childCtrl.fromParent2).toBe(parentCtrl.value2); + expect(childCtrl.fromParent2).not.toBe(childCtrl.value2); + expect(childCtrl.fromParent3()()).toBe(parentCtrl.value3()); + expect(childCtrl.fromParent3()()).not.toBe(childCtrl.value3()); + expect(childCtrl.fromParent4).toBe(parentCtrl.value4); + expect(childCtrl.fromParent4).not.toBe(childCtrl.value4); - it('should bind required controllers to controller that has an explicit constructor return value', function() { - var parentController, siblingController, meController; + childCtrl.fromParent2 = 'modified'; + $rootScope.$digest(); - function ParentController() { this.name = 'Parent'; } - function SiblingController() { this.name = 'Sibling'; } - function MeController() { - meController = { - name: 'Me', - $onInit: function() { - parentController = this.container; - siblingController = this.friend; + expect(parentCtrl.value2).toBe('modified'); + expect(childCtrl.value2).toBe('child2'); + }); } - }; - spyOn(meController, '$onInit').and.callThrough(); - return meController; - } - - angular.module('my', []) - .directive('me', function() { - return { - restrict: 'E', - scope: {}, - require: { container: '^parent', friend: 'sibling' }, - bindToController: true, - controller: MeController, - controllerAs: '$ctrl' - }; - }) - .directive('parent', function() { - return { - restrict: 'E', - scope: {}, - controller: ParentController - }; - }) - .directive('sibling', function() { - return { - controller: SiblingController - }; - }); + ); - module('my'); - inject(function($compile, $rootScope, meDirective) { - element = $compile('')($rootScope); - expect(meController.$onInit).toHaveBeenCalled(); - expect(parentController).toEqual(jasmine.any(ParentController)); - expect(siblingController).toEqual(jasmine.any(SiblingController)); - }); - }); + it('should put controller in scope when controller identifier present but not using controllerAs', function() { + var controllerCalled = false; + var myCtrl; + module(function($compileProvider, $controllerProvider) { + $controllerProvider.register('myCtrl', function() { + controllerCalled = true; + myCtrl = this; + }); + $compileProvider.directive('fooDir', valueFn({ + templateUrl: 'test.html', + bindToController: {}, + scope: true, + controller: 'myCtrl as theCtrl' + })); + }); + inject(function($compile, $rootScope, $templateCache) { + $templateCache.put('test.html', '

isolate

'); + element = $compile('
')($rootScope); + $rootScope.$digest(); + expect(controllerCalled).toBe(true); + var childScope = element.children().scope(); + expect(childScope).not.toBe($rootScope); + expect(childScope.theCtrl).toBe(myCtrl); + }); + }); - it('should bind required controllers to controllers that return an explicit constructor return value', function() { - var parentController, containerController, siblingController, friendController, meController; - function MeController() { - this.name = 'Me'; - this.$onInit = function() { - containerController = this.container; - friendController = this.friend; - }; - } - function ParentController() { - parentController = { name: 'Parent' }; - return parentController; - } - function SiblingController() { - siblingController = { name: 'Sibling' }; - return siblingController; - } + it('should re-install controllerAs and bindings for returned value from controller (new scope)', function() { + var controllerCalled = false; + var myCtrl; - angular.module('my', []) - .directive('me', function() { - return { - priority: 1, // make sure it is run before sibling to test this case correctly - restrict: 'E', - scope: {}, - require: { container: '^parent', friend: 'sibling' }, - bindToController: true, - controller: MeController, - controllerAs: '$ctrl' - }; - }) - .directive('parent', function() { - return { - restrict: 'E', - scope: {}, - controller: ParentController - }; - }) - .directive('sibling', function() { - return { - controller: SiblingController + function MyCtrl() { + } + MyCtrl.prototype.test = function() { + expect(this.data).toEqualData({ + 'foo': 'bar', + 'baz': 'biz' + }); + expect(this.oneway).toEqualData({ + 'foo': 'bar', + 'baz': 'biz' + }); + expect(this.str).toBe('Hello, world!'); + expect(this.fn()).toBe('called!'); }; + + module(function($compileProvider, $controllerProvider) { + $controllerProvider.register('myCtrl', function() { + controllerCalled = true; + myCtrl = this; + return new MyCtrl(); + }); + $compileProvider.directive('fooDir', valueFn({ + templateUrl: 'test.html', + bindToController: { + 'data': '=dirData', + 'oneway': 'isolate

'); + $rootScope.fn = valueFn('called!'); + $rootScope.whom = 'world'; + $rootScope.remoteData = { + 'foo': 'bar', + 'baz': 'biz' + }; + element = $compile('
')($rootScope); + $rootScope.$digest(); + expect(controllerCalled).toBe(true); + var childScope = element.children().scope(); + expect(childScope).not.toBe($rootScope); + expect(childScope.theCtrl).not.toBe(myCtrl); + expect(childScope.theCtrl.constructor).toBe(MyCtrl); + childScope.theCtrl.test(); + }); }); - module('my'); - inject(function($compile, $rootScope, meDirective) { - element = $compile('')($rootScope); - expect(containerController).toEqual(parentController); - expect(friendController).toEqual(siblingController); - }); - }); - it('should require controller of an isolate directive from a non-isolate directive on the ' + - 'same element', function() { - var IsolateController = function() {}; - var isolateDirControllerInNonIsolateDirective; + it('should re-install controllerAs and bindings for returned value from controller (isolate scope)', function() { + var controllerCalled = false; + var myCtrl; - module(function() { - directive('isolate', function() { - return { - scope: {}, - controller: IsolateController - }; - }); - directive('nonIsolate', function() { - return { - require: 'isolate', - link: function(_, __, ___, isolateDirController) { - isolateDirControllerInNonIsolateDirective = isolateDirController; - } + function MyCtrl() { + } + MyCtrl.prototype.test = function() { + expect(this.data).toEqualData({ + 'foo': 'bar', + 'baz': 'biz' + }); + expect(this.oneway).toEqualData({ + 'foo': 'bar', + 'baz': 'biz' + }); + expect(this.str).toBe('Hello, world!'); + expect(this.fn()).toBe('called!'); }; + + module(function($compileProvider, $controllerProvider) { + $controllerProvider.register('myCtrl', function() { + controllerCalled = true; + myCtrl = this; + return new MyCtrl(); + }); + $compileProvider.directive('fooDir', valueFn({ + templateUrl: 'test.html', + bindToController: true, + scope: { + 'data': '=dirData', + 'oneway': 'isolate

'); + $rootScope.fn = valueFn('called!'); + $rootScope.whom = 'world'; + $rootScope.remoteData = { + 'foo': 'bar', + 'baz': 'biz' + }; + element = $compile('
')($rootScope); + $rootScope.$digest(); + expect(controllerCalled).toBe(true); + var childScope = element.children().scope(); + expect(childScope).not.toBe($rootScope); + expect(childScope.theCtrl).not.toBe(myCtrl); + expect(childScope.theCtrl.constructor).toBe(MyCtrl); + childScope.theCtrl.test(); + }); }); - }); - inject(function($compile, $rootScope) { - element = $compile('
')($rootScope); + describe('should not overwrite @-bound property each digest when not present', function() { + it('when creating new scope', function() { + module(function($compileProvider) { + $compileProvider.directive('testDir', valueFn({ + scope: true, + bindToController: { + prop: '@' + }, + controller: function() { + var self = this; + this.initProp = function() { + this.prop = this.prop || 'default'; + }; + if (preAssignBindingsEnabled) { + this.initProp(); + } else { + this.$onInit = this.initProp; + } + this.getProp = function() { + return self.prop; + }; + }, + controllerAs: 'ctrl', + template: '

' + })); + }); + inject(function($compile, $rootScope) { + element = $compile('
')($rootScope); + var scope = element.scope(); + expect(scope.ctrl.getProp()).toBe('default'); - expect(isolateDirControllerInNonIsolateDirective).toBeDefined(); - expect(isolateDirControllerInNonIsolateDirective instanceof IsolateController).toBe(true); - }); - }); + $rootScope.$digest(); + expect(scope.ctrl.getProp()).toBe('default'); + }); + }); + it('when creating isolate scope', function() { + module(function($compileProvider) { + $compileProvider.directive('testDir', valueFn({ + scope: {}, + bindToController: { + prop: '@' + }, + controller: function() { + var self = this; + this.initProp = function() { + this.prop = this.prop || 'default'; + }; + this.getProp = function() { + return self.prop; + }; + if (preAssignBindingsEnabled) { + this.initProp(); + } else { + this.$onInit = this.initProp; + } + }, + controllerAs: 'ctrl', + template: '

' + })); + }); + inject(function($compile, $rootScope) { + element = $compile('
')($rootScope); + var scope = element.isolateScope(); + expect(scope.ctrl.getProp()).toBe('default'); - it('should give the isolate scope to the controller of another replaced directives in the template', function() { - module(function() { - directive('testDirective', function() { - return { - replace: true, - restrict: 'E', - scope: {}, - template: '' - }; + $rootScope.$digest(); + expect(scope.ctrl.getProp()).toBe('default'); + }); + }); }); - }); - - inject(function($rootScope) { - compile('
'); - element = element.children().eq(0); - expect(element[0].checked).toBe(false); - element.isolateScope().model = true; - $rootScope.$digest(); - expect(element[0].checked).toBe(true); }); - }); + describe('require', function() { - it('should share isolate scope with replaced directives (template)', function() { - var normalScope; - var isolateScope; - - module(function() { - directive('isolate', function() { - return { - replace: true, - scope: {}, - template: '{{name}}', - link: function(s) { - isolateScope = s; - } - }; - }); - directive('nonIsolate', function() { - return { - link: function(s) { - normalScope = s; - } - }; + it('should get required controller', function() { + module(function() { + directive('main', function(log) { + return { + priority: 2, + controller: function() { + this.name = 'main'; + }, + link: function(scope, element, attrs, controller) { + log(controller.name); + } + }; + }); + directive('dep', function(log) { + return { + priority: 1, + require: 'main', + link: function(scope, element, attrs, controller) { + log('dep:' + controller.name); + } + }; + }); + directive('other', function(log) { + return { + link: function(scope, element, attrs, controller) { + log(!!controller); // should be false + } + }; + }); + }); + inject(function(log, $compile, $rootScope) { + element = $compile('
')($rootScope); + expect(log).toEqual('false; dep:main; main'); + }); }); - }); - - inject(function($compile, $rootScope) { - element = $compile('
')($rootScope); - expect(normalScope).toBe($rootScope); - expect(normalScope.name).toEqual(undefined); - expect(isolateScope.name).toEqual('WORKS'); - $rootScope.$digest(); - expect(element.text()).toEqual('WORKS'); - }); - }); + it('should respect explicit return value from controller', function() { + var expectedController; + module(function() { + directive('logControllerProp', function(log) { + return { + controller: function($scope) { + this.foo = 'baz'; // value should not be used. + expectedController = {foo: 'bar'}; + return expectedController; + }, + link: function(scope, element, attrs, controller) { + expect(expectedController).toBeDefined(); + expect(controller).toBe(expectedController); + expect(controller.foo).toBe('bar'); + log('done'); + } + }; + }); + }); + inject(function(log, $compile, $rootScope) { + element = $compile('')($rootScope); + expect(log).toEqual('done'); + expect(element.data('$logControllerPropController')).toBe(expectedController); + }); + }); - it('should share isolate scope with replaced directives (templateUrl)', function() { - var normalScope; - var isolateScope; - module(function() { - directive('isolate', function() { - return { - replace: true, - scope: {}, - templateUrl: 'main.html', - link: function(s) { - isolateScope = s; - } - }; - }); - directive('nonIsolate', function() { - return { - link: function(s) { - normalScope = s; - } - }; + it('should get explicit return value of required parent controller', function() { + var expectedController; + module(function() { + directive('nested', function(log) { + return { + require: '^^?nested', + controller: function() { + if (!expectedController) expectedController = {foo: 'bar'}; + return expectedController; + }, + link: function(scope, element, attrs, controller) { + if (element.parent().length) { + expect(expectedController).toBeDefined(); + expect(controller).toBe(expectedController); + expect(controller.foo).toBe('bar'); + log('done'); + } + } + }; + }); + }); + inject(function(log, $compile, $rootScope) { + element = $compile('
')($rootScope); + expect(log).toEqual('done'); + expect(element.data('$nestedController')).toBe(expectedController); + }); }); - }); - inject(function($compile, $rootScope, $templateCache) { - $templateCache.put('main.html', '{{name}}'); - element = $compile('
')($rootScope); - $rootScope.$apply(); - - expect(normalScope).toBe($rootScope); - expect(normalScope.name).toEqual(undefined); - expect(isolateScope.name).toEqual('WORKS'); - expect(element.text()).toEqual('WORKS'); - }); - }); + it('should respect explicit controller return value when using controllerAs', function() { + module(function() { + directive('main', function() { + return { + templateUrl: 'main.html', + scope: {}, + controller: function() { + this.name = 'lucas'; + return {name: 'george'}; + }, + controllerAs: 'mainCtrl' + }; + }); + }); + inject(function($templateCache, $compile, $rootScope) { + $templateCache.put('main.html', 'template:{{mainCtrl.name}}'); + element = $compile('
')($rootScope); + $rootScope.$apply(); + expect(element.text()).toBe('template:george'); + }); + }); - it('should not get confused about where to use isolate scope when a replaced directive is used multiple times', - function() { - module(function() { - directive('isolate', function() { - return { - replace: true, - scope: {}, - template: '' - }; - }); - directive('scopeTester', function(log) { - return { - link: function($scope, $element) { - log($element.attr('scope-tester') + '=' + ($scope.$root === $scope ? 'non-isolate' : 'isolate')); - } - }; + it('transcluded children should receive explicit return value of parent controller', function() { + var expectedController; + module(function() { + directive('nester', valueFn({ + transclude: true, + controller: function($transclude) { + this.foo = 'baz'; + expectedController = {transclude:$transclude, foo: 'bar'}; + return expectedController; + }, + link: function(scope, el, attr, ctrl) { + ctrl.transclude(cloneAttach); + function cloneAttach(clone) { + el.append(clone); + } + } + })); + directive('nested', function(log) { + return { + require: '^^nester', + link: function(scope, element, attrs, controller) { + expect(controller).toBeDefined(); + expect(controller).toBe(expectedController); + log('done'); + } + }; + }); + }); + inject(function(log, $compile) { + element = $compile('
')($rootScope); + $rootScope.$apply(); + expect(log.toString()).toBe('done'); + expect(element.data('$nesterController')).toBe(expectedController); + }); }); - }); - - inject(function($compile, $rootScope, log) { - element = $compile('
' + - '
' + - '' + - '
')($rootScope); - $rootScope.$digest(); - expect(log).toEqual('inside=isolate; ' + - 'outside replaced=non-isolate; ' + // outside - 'outside replaced=isolate; ' + // replaced - 'sibling=non-isolate'); - }); - }); + it('explicit controller return values are ignored if they are primitives', function() { + module(function() { + directive('logControllerProp', function(log) { + return { + controller: function($scope) { + this.foo = 'baz'; // value *will* be used. + return 'bar'; + }, + link: function(scope, element, attrs, controller) { + log(controller.foo); + } + }; + }); + }); + inject(function(log, $compile, $rootScope) { + element = $compile('')($rootScope); + expect(log).toEqual('baz'); + expect(element.data('$logControllerPropController').foo).toEqual('baz'); + }); + }); - it('should require controller of a non-isolate directive from an isolate directive on the ' + - 'same element', function() { - var NonIsolateController = function() {}; - var nonIsolateDirControllerInIsolateDirective; - module(function() { - directive('isolate', function() { - return { - scope: {}, - require: 'nonIsolate', - link: function(_, __, ___, nonIsolateDirController) { - nonIsolateDirControllerInIsolateDirective = nonIsolateDirController; - } - }; - }); - directive('nonIsolate', function() { - return { - controller: NonIsolateController - }; - }); - }); + it('should correctly assign controller return values for multiple directives', function() { + var directiveController, otherDirectiveController; + module(function() { - inject(function($compile, $rootScope) { - element = $compile('
')($rootScope); + directive('myDirective', function(log) { + return { + scope: true, + controller: function($scope) { + directiveController = { + foo: 'bar' + }; + return directiveController; + } + }; + }); - expect(nonIsolateDirControllerInIsolateDirective).toBeDefined(); - expect(nonIsolateDirControllerInIsolateDirective instanceof NonIsolateController).toBe(true); - }); - }); + directive('myOtherDirective', function(log) { + return { + controller: function($scope) { + otherDirectiveController = { + baz: 'luh' + }; + return otherDirectiveController; + } + }; + }); + }); - it('should support controllerAs', function() { - module(function() { - directive('main', function() { - return { - templateUrl: 'main.html', - transclude: true, - scope: {}, - controller: function() { - this.name = 'lucas'; - }, - controllerAs: 'mainCtrl' - }; + inject(function(log, $compile, $rootScope) { + element = $compile('')($rootScope); + expect(element.data('$myDirectiveController')).toBe(directiveController); + expect(element.data('$myOtherDirectiveController')).toBe(otherDirectiveController); + }); }); - }); - inject(function($templateCache, $compile, $rootScope) { - $templateCache.put('main.html', 'template:{{mainCtrl.name}}
'); - element = $compile('
transclude:{{mainCtrl.name}}
')($rootScope); - $rootScope.$apply(); - expect(element.text()).toBe('template:lucas transclude:'); - }); - }); - it('should support controller alias', function() { - module(function($controllerProvider) { - $controllerProvider.register('MainCtrl', function() { - this.name = 'lucas'; - }); - directive('main', function() { - return { - templateUrl: 'main.html', - scope: {}, - controller: 'MainCtrl as mainCtrl' - }; + it('should get required parent controller', function() { + module(function() { + directive('nested', function(log) { + return { + require: '^^?nested', + controller: function($scope) {}, + link: function(scope, element, attrs, controller) { + log(!!controller); + } + }; + }); + }); + inject(function(log, $compile, $rootScope) { + element = $compile('
')($rootScope); + expect(log).toEqual('true; false'); + }); }); - }); - inject(function($templateCache, $compile, $rootScope) { - $templateCache.put('main.html', '{{mainCtrl.name}}'); - element = $compile('
')($rootScope); - $rootScope.$apply(); - expect(element.text()).toBe('lucas'); - }); - }); - - it('should require controller on parent element',function() { - module(function() { - directive('main', function(log) { - return { - controller: function() { - this.name = 'main'; - } - }; - }); - directive('dep', function(log) { - return { - require: '^main', - link: function(scope, element, attrs, controller) { - log('dep:' + controller.name); - } - }; + it('should get required parent controller when the question mark precedes the ^^', function() { + module(function() { + directive('nested', function(log) { + return { + require: '?^^nested', + controller: function($scope) {}, + link: function(scope, element, attrs, controller) { + log(!!controller); + } + }; + }); + }); + inject(function(log, $compile, $rootScope) { + element = $compile('
')($rootScope); + expect(log).toEqual('true; false'); + }); }); - }); - inject(function(log, $compile, $rootScope) { - element = $compile('
')($rootScope); - expect(log).toEqual('dep:main'); - }); - }); - it('should throw an error if required controller can\'t be found',function() { - module(function() { - directive('dep', function(log) { - return { - require: '^main', - link: function(scope, element, attrs, controller) { - log('dep:' + controller.name); - } - }; + it('should throw if required parent is not found', function() { + module(function() { + directive('nested', function() { + return { + require: '^^nested', + controller: function($scope) {}, + link: function(scope, element, attrs, controller) {} + }; + }); + }); + inject(function($compile, $rootScope) { + expect(function() { + element = $compile('
')($rootScope); + }).toThrowMinErr('$compile', 'ctreq', 'Controller \'nested\', required by directive \'nested\', can\'t be found!'); + }); }); - }); - inject(function(log, $compile, $rootScope) { - expect(function() { - $compile('
')($rootScope); - }).toThrowMinErr('$compile', 'ctreq', 'Controller \'main\', required by directive \'dep\', can\'t be found!'); - }); - }); - it('should pass null if required controller can\'t be found and is optional',function() { - module(function() { - directive('dep', function(log) { - return { - require: '?^main', - link: function(scope, element, attrs, controller) { - log('dep:' + controller); - } - }; + it('should get required controller via linkingFn (template)', function() { + module(function() { + directive('dirA', function() { + return { + controller: function() { + this.name = 'dirA'; + } + }; + }); + directive('dirB', function(log) { + return { + require: 'dirA', + template: '

dirB

', + link: function(scope, element, attrs, dirAController) { + log('dirAController.name: ' + dirAController.name); + } + }; + }); + }); + inject(function(log, $compile, $rootScope) { + element = $compile('
')($rootScope); + expect(log).toEqual('dirAController.name: dirA'); + }); }); - }); - inject(function(log, $compile, $rootScope) { - $compile('
')($rootScope); - expect(log).toEqual('dep:null'); - }); - }); - it('should pass null if required controller can\'t be found and is optional with the question mark on the right',function() { - module(function() { - directive('dep', function(log) { - return { - require: '^?main', - link: function(scope, element, attrs, controller) { - log('dep:' + controller); - } - }; + it('should get required controller via linkingFn (templateUrl)', function() { + module(function() { + directive('dirA', function() { + return { + controller: function() { + this.name = 'dirA'; + } + }; + }); + directive('dirB', function(log) { + return { + require: 'dirA', + templateUrl: 'dirB.html', + link: function(scope, element, attrs, dirAController) { + log('dirAController.name: ' + dirAController.name); + } + }; + }); + }); + inject(function(log, $compile, $rootScope, $templateCache) { + $templateCache.put('dirB.html', '

dirB

'); + element = $compile('
')($rootScope); + $rootScope.$digest(); + expect(log).toEqual('dirAController.name: dirA'); + }); }); - }); - inject(function(log, $compile, $rootScope) { - $compile('
')($rootScope); - expect(log).toEqual('dep:null'); - }); - }); + it('should bind the required controllers to the directive controller, if provided as an object and bindToController is truthy', function() { + var parentController, siblingController; - it('should have optional controller on current element', function() { - module(function() { - directive('dep', function(log) { - return { - require: '?main', - link: function(scope, element, attrs, controller) { - log('dep:' + !!controller); - } + function ParentController() { this.name = 'Parent'; } + function SiblingController() { this.name = 'Sibling'; } + function MeController() { this.name = 'Me'; } + MeController.prototype.$onInit = function() { + parentController = this.container; + siblingController = this.friend; }; - }); - }); - inject(function(log, $compile, $rootScope) { - element = $compile('
')($rootScope); - expect(log).toEqual('dep:false'); - }); - }); + spyOn(MeController.prototype, '$onInit').and.callThrough(); + + angular.module('my', []) + .directive('me', function() { + return { + restrict: 'E', + scope: {}, + require: { container: '^parent', friend: 'sibling' }, + bindToController: true, + controller: MeController, + controllerAs: '$ctrl' + }; + }) + .directive('parent', function() { + return { + restrict: 'E', + scope: {}, + controller: ParentController + }; + }) + .directive('sibling', function() { + return { + controller: SiblingController + }; + }); + module('my'); + inject(function($compile, $rootScope, meDirective) { + element = $compile('')($rootScope); + expect(MeController.prototype.$onInit).toHaveBeenCalled(); + expect(parentController).toEqual(jasmine.any(ParentController)); + expect(siblingController).toEqual(jasmine.any(SiblingController)); + }); + }); + + it('should use the key if the name of a required controller is omitted', function() { + function ParentController() { this.name = 'Parent'; } + function ParentOptController() { this.name = 'ParentOpt'; } + function ParentOrSiblingController() { this.name = 'ParentOrSibling'; } + function ParentOrSiblingOptController() { this.name = 'ParentOrSiblingOpt'; } + function SiblingController() { this.name = 'Sibling'; } + function SiblingOptController() { this.name = 'SiblingOpt'; } + + angular.module('my', []) + .component('me', { + require: { + parent: '^^', + parentOpt: '?^^', + parentOrSibling1: '^', + parentOrSiblingOpt1: '?^', + parentOrSibling2: '^', + parentOrSiblingOpt2: '?^', + sibling: '', + siblingOpt: '?' + } + }) + .directive('parent', function() { + return {controller: ParentController}; + }) + .directive('parentOpt', function() { + return {controller: ParentOptController}; + }) + .directive('parentOrSibling1', function() { + return {controller: ParentOrSiblingController}; + }) + .directive('parentOrSiblingOpt1', function() { + return {controller: ParentOrSiblingOptController}; + }) + .directive('parentOrSibling2', function() { + return {controller: ParentOrSiblingController}; + }) + .directive('parentOrSiblingOpt2', function() { + return {controller: ParentOrSiblingOptController}; + }) + .directive('sibling', function() { + return {controller: SiblingController}; + }) + .directive('siblingOpt', function() { + return {controller: SiblingOptController}; + }); - it('should support multiple controllers', function() { - module(function() { - directive('c1', valueFn({ - controller: function() { this.name = 'c1'; } - })); - directive('c2', valueFn({ - controller: function() { this.name = 'c2'; } - })); - directive('dep', function(log) { - return { - require: ['^c1', '^c2'], - link: function(scope, element, attrs, controller) { - log('dep:' + controller[0].name + '-' + controller[1].name); - } + module('my'); + inject(function($compile, $rootScope) { + var template = + '
' + + // With optional + '' + + '' + + '' + + // Without optional + '' + + '' + + '' + + '
'; + element = $compile(template)($rootScope); + + var ctrl1 = element.find('me').eq(0).controller('me'); + expect(ctrl1.parent).toEqual(jasmine.any(ParentController)); + expect(ctrl1.parentOpt).toEqual(jasmine.any(ParentOptController)); + expect(ctrl1.parentOrSibling1).toEqual(jasmine.any(ParentOrSiblingController)); + expect(ctrl1.parentOrSiblingOpt1).toEqual(jasmine.any(ParentOrSiblingOptController)); + expect(ctrl1.parentOrSibling2).toEqual(jasmine.any(ParentOrSiblingController)); + expect(ctrl1.parentOrSiblingOpt2).toEqual(jasmine.any(ParentOrSiblingOptController)); + expect(ctrl1.sibling).toEqual(jasmine.any(SiblingController)); + expect(ctrl1.siblingOpt).toEqual(jasmine.any(SiblingOptController)); + + var ctrl2 = element.find('me').eq(1).controller('me'); + expect(ctrl2.parent).toEqual(jasmine.any(ParentController)); + expect(ctrl2.parentOpt).toBe(null); + expect(ctrl2.parentOrSibling1).toEqual(jasmine.any(ParentOrSiblingController)); + expect(ctrl2.parentOrSiblingOpt1).toBe(null); + expect(ctrl2.parentOrSibling2).toEqual(jasmine.any(ParentOrSiblingController)); + expect(ctrl2.parentOrSiblingOpt2).toBe(null); + expect(ctrl2.sibling).toEqual(jasmine.any(SiblingController)); + expect(ctrl2.siblingOpt).toBe(null); + }); + }); + + + it('should not bind required controllers if bindToController is falsy', function() { + var parentController, siblingController; + + function ParentController() { this.name = 'Parent'; } + function SiblingController() { this.name = 'Sibling'; } + function MeController() { this.name = 'Me'; } + MeController.prototype.$onInit = function() { + parentController = this.container; + siblingController = this.friend; }; - }); - }); - inject(function(log, $compile, $rootScope) { - element = $compile('
')($rootScope); - expect(log).toEqual('dep:c1-c2'); - }); - }); + spyOn(MeController.prototype, '$onInit').and.callThrough(); - it('should support multiple controllers as an object hash', function() { - module(function() { - directive('c1', valueFn({ - controller: function() { this.name = 'c1'; } - })); - directive('c2', valueFn({ - controller: function() { this.name = 'c2'; } - })); - directive('dep', function(log) { - return { - require: { myC1: '^c1', myC2: '^c2' }, - link: function(scope, element, attrs, controllers) { - log('dep:' + controllers.myC1.name + '-' + controllers.myC2.name); - } - }; + angular.module('my', []) + .directive('me', function() { + return { + restrict: 'E', + scope: {}, + require: { container: '^parent', friend: 'sibling' }, + controller: MeController + }; + }) + .directive('parent', function() { + return { + restrict: 'E', + scope: {}, + controller: ParentController + }; + }) + .directive('sibling', function() { + return { + controller: SiblingController + }; + }); + + module('my'); + inject(function($compile, $rootScope, meDirective) { + element = $compile('')($rootScope); + expect(MeController.prototype.$onInit).toHaveBeenCalled(); + expect(parentController).toBeUndefined(); + expect(siblingController).toBeUndefined(); + }); }); - }); - inject(function(log, $compile, $rootScope) { - element = $compile('
')($rootScope); - expect(log).toEqual('dep:c1-c2'); - }); - }); - it('should support omitting the name of the required controller if it is the same as the key', - function() { - module(function() { - directive('myC1', valueFn({ - controller: function() { this.name = 'c1'; } - })); - directive('myC2', valueFn({ - controller: function() { this.name = 'c2'; } - })); - directive('dep', function(log) { - return { - require: { myC1: '^', myC2: '^' }, - link: function(scope, element, attrs, controllers) { - log('dep:' + controllers.myC1.name + '-' + controllers.myC2.name); + it('should bind required controllers to controller that has an explicit constructor return value', function() { + var parentController, siblingController, meController; + + function ParentController() { this.name = 'Parent'; } + function SiblingController() { this.name = 'Sibling'; } + function MeController() { + meController = { + name: 'Me', + $onInit: function() { + parentController = this.container; + siblingController = this.friend; } }; + spyOn(meController, '$onInit').and.callThrough(); + return meController; + } + + angular.module('my', []) + .directive('me', function() { + return { + restrict: 'E', + scope: {}, + require: { container: '^parent', friend: 'sibling' }, + bindToController: true, + controller: MeController, + controllerAs: '$ctrl' + }; + }) + .directive('parent', function() { + return { + restrict: 'E', + scope: {}, + controller: ParentController + }; + }) + .directive('sibling', function() { + return { + controller: SiblingController + }; + }); + + module('my'); + inject(function($compile, $rootScope, meDirective) { + element = $compile('')($rootScope); + expect(meController.$onInit).toHaveBeenCalled(); + expect(parentController).toEqual(jasmine.any(ParentController)); + expect(siblingController).toEqual(jasmine.any(SiblingController)); }); }); - inject(function(log, $compile, $rootScope) { - element = $compile('
')($rootScope); - expect(log).toEqual('dep:c1-c2'); - }); - } - ); - it('should instantiate the controller just once when template/templateUrl', function() { - var syncCtrlSpy = jasmine.createSpy('sync controller'), - asyncCtrlSpy = jasmine.createSpy('async controller'); - module(function() { - directive('myDirectiveSync', valueFn({ - template: '
Hello!
', - controller: syncCtrlSpy - })); - directive('myDirectiveAsync', valueFn({ - templateUrl: 'myDirectiveAsync.html', - controller: asyncCtrlSpy, - compile: function() { - return function() { + it('should bind required controllers to controllers that return an explicit constructor return value', function() { + var parentController, containerController, siblingController, friendController, meController; + + function MeController() { + this.name = 'Me'; + this.$onInit = function() { + containerController = this.container; + friendController = this.friend; }; } - })); - }); + function ParentController() { + parentController = { name: 'Parent' }; + return parentController; + } + function SiblingController() { + siblingController = { name: 'Sibling' }; + return siblingController; + } - inject(function($templateCache, $compile, $rootScope) { - expect(syncCtrlSpy).not.toHaveBeenCalled(); - expect(asyncCtrlSpy).not.toHaveBeenCalled(); + angular.module('my', []) + .directive('me', function() { + return { + priority: 1, // make sure it is run before sibling to test this case correctly + restrict: 'E', + scope: {}, + require: { container: '^parent', friend: 'sibling' }, + bindToController: true, + controller: MeController, + controllerAs: '$ctrl' + }; + }) + .directive('parent', function() { + return { + restrict: 'E', + scope: {}, + controller: ParentController + }; + }) + .directive('sibling', function() { + return { + controller: SiblingController + }; + }); - $templateCache.put('myDirectiveAsync.html', '
Hello!
'); - element = $compile('
' + - '' + - '' + - '
')($rootScope); - expect(syncCtrlSpy).not.toHaveBeenCalled(); - expect(asyncCtrlSpy).not.toHaveBeenCalled(); + module('my'); + inject(function($compile, $rootScope, meDirective) { + element = $compile('')($rootScope); + expect(containerController).toEqual(parentController); + expect(friendController).toEqual(siblingController); + }); + }); - $rootScope.$apply(); + it('should require controller of an isolate directive from a non-isolate directive on the ' + + 'same element', function() { + var IsolateController = function() {}; + var isolateDirControllerInNonIsolateDirective; - //expect(syncCtrlSpy).toHaveBeenCalledOnce(); - expect(asyncCtrlSpy).toHaveBeenCalledOnce(); - }); - }); + module(function() { + directive('isolate', function() { + return { + scope: {}, + controller: IsolateController + }; + }); + directive('nonIsolate', function() { + return { + require: 'isolate', + link: function(_, __, ___, isolateDirController) { + isolateDirControllerInNonIsolateDirective = isolateDirController; + } + }; + }); + }); + inject(function($compile, $rootScope) { + element = $compile('
')($rootScope); + expect(isolateDirControllerInNonIsolateDirective).toBeDefined(); + expect(isolateDirControllerInNonIsolateDirective instanceof IsolateController).toBe(true); + }); + }); - it('should instantiate controllers in the parent->child order when transluction, templateUrl and replacement ' + - 'are in the mix', function() { - // When a child controller is in the transclusion that replaces the parent element that has a directive with - // a controller, we should ensure that we first instantiate the parent and only then stuff that comes from the - // transclusion. - // - // The transclusion moves the child controller onto the same element as parent controller so both controllers are - // on the same level. - module(function() { - directive('parentDirective', function() { - return { - transclude: true, - replace: true, - templateUrl: 'parentDirective.html', - controller: function(log) { log('parentController'); } - }; + it('should give the isolate scope to the controller of another replaced directives in the template', function() { + module(function() { + directive('testDirective', function() { + return { + replace: true, + restrict: 'E', + scope: {}, + template: '' + }; + }); + }); + + inject(function($rootScope) { + compile('
'); + + element = element.children().eq(0); + expect(element[0].checked).toBe(false); + element.isolateScope().model = true; + $rootScope.$digest(); + expect(element[0].checked).toBe(true); + }); }); - directive('childDirective', function() { - return { - require: '^parentDirective', - templateUrl: 'childDirective.html', - controller: function(log) { log('childController'); } - }; + + + it('should share isolate scope with replaced directives (template)', function() { + var normalScope; + var isolateScope; + + module(function() { + directive('isolate', function() { + return { + replace: true, + scope: {}, + template: '{{name}}', + link: function(s) { + isolateScope = s; + } + }; + }); + directive('nonIsolate', function() { + return { + link: function(s) { + normalScope = s; + } + }; + }); + }); + + inject(function($compile, $rootScope) { + element = $compile('
')($rootScope); + + expect(normalScope).toBe($rootScope); + expect(normalScope.name).toEqual(undefined); + expect(isolateScope.name).toEqual('WORKS'); + $rootScope.$digest(); + expect(element.text()).toEqual('WORKS'); + }); }); - }); - inject(function($templateCache, log, $compile, $rootScope) { - $templateCache.put('parentDirective.html', '
parentTemplateText;
'); - $templateCache.put('childDirective.html', 'childTemplateText;'); - element = $compile('
childContentText;
')($rootScope); - $rootScope.$apply(); - expect(log).toEqual('parentController; childController'); - expect(element.text()).toBe('childTemplateText;childContentText;'); - }); - }); + it('should share isolate scope with replaced directives (templateUrl)', function() { + var normalScope; + var isolateScope; + module(function() { + directive('isolate', function() { + return { + replace: true, + scope: {}, + templateUrl: 'main.html', + link: function(s) { + isolateScope = s; + } + }; + }); + directive('nonIsolate', function() { + return { + link: function(s) { + normalScope = s; + } + }; + }); + }); - it('should instantiate the controller after the isolate scope bindings are initialized (with template)', function() { - module(function() { - var Ctrl = function($scope, log) { - log('myFoo=' + $scope.myFoo); - }; + inject(function($compile, $rootScope, $templateCache) { + $templateCache.put('main.html', '{{name}}'); + element = $compile('
')($rootScope); + $rootScope.$apply(); - directive('myDirective', function() { - return { - scope: { - myFoo: '=' - }, - template: '

Hello

', - controller: Ctrl - }; + expect(normalScope).toBe($rootScope); + expect(normalScope.name).toEqual(undefined); + expect(isolateScope.name).toEqual('WORKS'); + expect(element.text()).toEqual('WORKS'); + }); }); - }); - inject(function($templateCache, $compile, $rootScope, log) { - $rootScope.foo = 'bar'; - element = $compile('
')($rootScope); - $rootScope.$apply(); - expect(log).toEqual('myFoo=bar'); - }); - }); + it('should not get confused about where to use isolate scope when a replaced directive is used multiple times', + function() { + module(function() { + directive('isolate', function() { + return { + replace: true, + scope: {}, + template: '' + }; + }); + directive('scopeTester', function(log) { + return { + link: function($scope, $element) { + log($element.attr('scope-tester') + '=' + ($scope.$root === $scope ? 'non-isolate' : 'isolate')); + } + }; + }); + }); - it('should instantiate the controller after the isolate scope bindings are initialized (with templateUrl)', function() { - module(function() { - var Ctrl = function($scope, log) { - log('myFoo=' + $scope.myFoo); - }; + inject(function($compile, $rootScope, log) { + element = $compile('
' + + '
' + + '' + + '
')($rootScope); - directive('myDirective', function() { - return { - scope: { - myFoo: '=' - }, - templateUrl: 'hello.html', - controller: Ctrl - }; + $rootScope.$digest(); + expect(log).toEqual('inside=isolate; ' + + 'outside replaced=non-isolate; ' + // outside + 'outside replaced=isolate; ' + // replaced + 'sibling=non-isolate'); + }); }); - }); - inject(function($templateCache, $compile, $rootScope, log) { - $templateCache.put('hello.html', '

Hello

'); - $rootScope.foo = 'bar'; - element = $compile('
')($rootScope); - $rootScope.$apply(); - expect(log).toEqual('myFoo=bar'); - }); - }); + it('should require controller of a non-isolate directive from an isolate directive on the ' + + 'same element', function() { + var NonIsolateController = function() {}; + var nonIsolateDirControllerInIsolateDirective; + module(function() { + directive('isolate', function() { + return { + scope: {}, + require: 'nonIsolate', + link: function(_, __, ___, nonIsolateDirController) { + nonIsolateDirControllerInIsolateDirective = nonIsolateDirController; + } + }; + }); + directive('nonIsolate', function() { + return { + controller: NonIsolateController + }; + }); + }); - it('should instantiate controllers in the parent->child->baby order when nested transluction, templateUrl and ' + - 'replacement are in the mix', function() { - // similar to the test above, except that we have one more layer of nesting and nested transclusion + inject(function($compile, $rootScope) { + element = $compile('
')($rootScope); - module(function() { - directive('parentDirective', function() { - return { - transclude: true, - replace: true, - templateUrl: 'parentDirective.html', - controller: function(log) { log('parentController'); } - }; - }); - directive('childDirective', function() { - return { - require: '^parentDirective', - transclude: true, - replace: true, - templateUrl: 'childDirective.html', - controller: function(log) { log('childController'); } - }; + expect(nonIsolateDirControllerInIsolateDirective).toBeDefined(); + expect(nonIsolateDirControllerInIsolateDirective instanceof NonIsolateController).toBe(true); + }); }); - directive('babyDirective', function() { - return { - require: '^childDirective', - templateUrl: 'babyDirective.html', - controller: function(log) { log('babyController'); } - }; + + + it('should support controllerAs', function() { + module(function() { + directive('main', function() { + return { + templateUrl: 'main.html', + transclude: true, + scope: {}, + controller: function() { + this.name = 'lucas'; + }, + controllerAs: 'mainCtrl' + }; + }); + }); + inject(function($templateCache, $compile, $rootScope) { + $templateCache.put('main.html', 'template:{{mainCtrl.name}}
'); + element = $compile('
transclude:{{mainCtrl.name}}
')($rootScope); + $rootScope.$apply(); + expect(element.text()).toBe('template:lucas transclude:'); + }); }); - }); - inject(function($templateCache, log, $compile, $rootScope) { - $templateCache.put('parentDirective.html', '
parentTemplateText;
'); - $templateCache.put('childDirective.html', 'childTemplateText;'); - $templateCache.put('babyDirective.html', 'babyTemplateText;'); - element = $compile('
' + - '
' + - 'childContentText;' + - '
babyContent;
' + - '
' + - '
')($rootScope); - $rootScope.$apply(); - expect(log).toEqual('parentController; childController; babyController'); - expect(element.text()).toBe('childContentText;babyTemplateText;'); - }); - }); + it('should support controller alias', function() { + module(function($controllerProvider) { + $controllerProvider.register('MainCtrl', function() { + this.name = 'lucas'; + }); + directive('main', function() { + return { + templateUrl: 'main.html', + scope: {}, + controller: 'MainCtrl as mainCtrl' + }; + }); + }); + inject(function($templateCache, $compile, $rootScope) { + $templateCache.put('main.html', '{{mainCtrl.name}}'); + element = $compile('
')($rootScope); + $rootScope.$apply(); + expect(element.text()).toBe('lucas'); + }); + }); - it('should allow controller usage in pre-link directive functions with templateUrl', function() { - module(function() { - var Ctrl = function(log) { - log('instance'); - }; - directive('myDirective', function() { - return { - scope: true, - templateUrl: 'hello.html', - controller: Ctrl, - compile: function() { + it('should require controller on parent element',function() { + module(function() { + directive('main', function(log) { return { - pre: function(scope, template, attr, ctrl) {}, - post: function() {} + controller: function() { + this.name = 'main'; + } }; - } - }; + }); + directive('dep', function(log) { + return { + require: '^main', + link: function(scope, element, attrs, controller) { + log('dep:' + controller.name); + } + }; + }); + }); + inject(function(log, $compile, $rootScope) { + element = $compile('
')($rootScope); + expect(log).toEqual('dep:main'); + }); }); - }); - inject(function($templateCache, $compile, $rootScope, log) { - $templateCache.put('hello.html', '

Hello

'); - element = $compile('
')($rootScope); - $rootScope.$apply(); + it('should throw an error if required controller can\'t be found',function() { + module(function() { + directive('dep', function(log) { + return { + require: '^main', + link: function(scope, element, attrs, controller) { + log('dep:' + controller.name); + } + }; + }); + }); + inject(function(log, $compile, $rootScope) { + expect(function() { + $compile('
')($rootScope); + }).toThrowMinErr('$compile', 'ctreq', 'Controller \'main\', required by directive \'dep\', can\'t be found!'); + }); + }); - expect(log).toEqual('instance'); - expect(element.text()).toBe('Hello'); - }); - }); + it('should pass null if required controller can\'t be found and is optional',function() { + module(function() { + directive('dep', function(log) { + return { + require: '?^main', + link: function(scope, element, attrs, controller) { + log('dep:' + controller); + } + }; + }); + }); + inject(function(log, $compile, $rootScope) { + $compile('
')($rootScope); + expect(log).toEqual('dep:null'); + }); + }); - it('should allow controller usage in pre-link directive functions with a template', function() { - module(function() { - var Ctrl = function(log) { - log('instance'); - }; - directive('myDirective', function() { - return { - scope: true, - template: '

Hello

', - controller: Ctrl, - compile: function() { + it('should pass null if required controller can\'t be found and is optional with the question mark on the right',function() { + module(function() { + directive('dep', function(log) { return { - pre: function(scope, template, attr, ctrl) {}, - post: function() {} + require: '^?main', + link: function(scope, element, attrs, controller) { + log('dep:' + controller); + } }; - } - }; + }); + }); + inject(function(log, $compile, $rootScope) { + $compile('
')($rootScope); + expect(log).toEqual('dep:null'); + }); }); - }); - inject(function($templateCache, $compile, $rootScope, log) { - element = $compile('
')($rootScope); - $rootScope.$apply(); - expect(log).toEqual('instance'); - expect(element.text()).toBe('Hello'); - }); - }); + it('should have optional controller on current element', function() { + module(function() { + directive('dep', function(log) { + return { + require: '?main', + link: function(scope, element, attrs, controller) { + log('dep:' + !!controller); + } + }; + }); + }); + inject(function(log, $compile, $rootScope) { + element = $compile('
')($rootScope); + expect(log).toEqual('dep:false'); + }); + }); - it('should throw ctreq with correct directive name, regardless of order', function() { - module(function($compileProvider) { - $compileProvider.directive('aDir', valueFn({ - restrict: 'E', - require: 'ngModel', - link: noop - })); - }); - inject(function($compile, $rootScope) { - expect(function() { - // a-dir will cause a ctreq error to be thrown. Previously, the error would reference - // the last directive in the chain (which in this case would be ngClick), based on - // priority and alphabetical ordering. This test verifies that the ordering does not - // affect which directive is referenced in the minErr message. - element = $compile('')($rootScope); - }).toThrowMinErr('$compile', 'ctreq', - 'Controller \'ngModel\', required by directive \'aDir\', can\'t be found!'); - }); - }); - }); + it('should support multiple controllers', function() { + module(function() { + directive('c1', valueFn({ + controller: function() { this.name = 'c1'; } + })); + directive('c2', valueFn({ + controller: function() { this.name = 'c2'; } + })); + directive('dep', function(log) { + return { + require: ['^c1', '^c2'], + link: function(scope, element, attrs, controller) { + log('dep:' + controller[0].name + '-' + controller[1].name); + } + }; + }); + }); + inject(function(log, $compile, $rootScope) { + element = $compile('
')($rootScope); + expect(log).toEqual('dep:c1-c2'); + }); + }); + it('should support multiple controllers as an object hash', function() { + module(function() { + directive('c1', valueFn({ + controller: function() { this.name = 'c1'; } + })); + directive('c2', valueFn({ + controller: function() { this.name = 'c2'; } + })); + directive('dep', function(log) { + return { + require: { myC1: '^c1', myC2: '^c2' }, + link: function(scope, element, attrs, controllers) { + log('dep:' + controllers.myC1.name + '-' + controllers.myC2.name); + } + }; + }); + }); + inject(function(log, $compile, $rootScope) { + element = $compile('
')($rootScope); + expect(log).toEqual('dep:c1-c2'); + }); + }); - describe('transclude', function() { + it('should support omitting the name of the required controller if it is the same as the key', + function() { + module(function() { + directive('myC1', valueFn({ + controller: function() { this.name = 'c1'; } + })); + directive('myC2', valueFn({ + controller: function() { this.name = 'c2'; } + })); + directive('dep', function(log) { + return { + require: { myC1: '^', myC2: '^' }, + link: function(scope, element, attrs, controllers) { + log('dep:' + controllers.myC1.name + '-' + controllers.myC2.name); + } + }; + }); + }); + inject(function(log, $compile, $rootScope) { + element = $compile('
')($rootScope); + expect(log).toEqual('dep:c1-c2'); + }); + } + ); - describe('content transclusion', function() { + it('should instantiate the controller just once when template/templateUrl', function() { + var syncCtrlSpy = jasmine.createSpy('sync controller'), + asyncCtrlSpy = jasmine.createSpy('async controller'); - it('should support transclude directive', function() { - module(function() { - directive('trans', function() { - return { - transclude: 'content', - replace: true, - scope: {}, - link: function(scope) { - scope.x = 'iso'; - }, - template: '
  • W:{{x}}-{{$parent.$id}}-{{$id}};
' - }; + module(function() { + directive('myDirectiveSync', valueFn({ + template: '
Hello!
', + controller: syncCtrlSpy + })); + directive('myDirectiveAsync', valueFn({ + templateUrl: 'myDirectiveAsync.html', + controller: asyncCtrlSpy, + compile: function() { + return function() { + }; + } + })); }); - }); - inject(function(log, $rootScope, $compile) { - element = $compile('
T:{{x}}-{{$parent.$id}}-{{$id}};
')($rootScope); - $rootScope.x = 'root'; - $rootScope.$apply(); - expect(element.text()).toEqual('W:iso-1-2;T:root-2-3;'); - expect(jqLite(jqLite(element.find('li')[1]).contents()[0]).text()).toEqual('T:root-2-3'); - expect(jqLite(element.find('span')[0]).text()).toEqual(';'); - }); - }); + inject(function($templateCache, $compile, $rootScope) { + expect(syncCtrlSpy).not.toHaveBeenCalled(); + expect(asyncCtrlSpy).not.toHaveBeenCalled(); - it('should transclude transcluded content', function() { - module(function() { - directive('book', valueFn({ - transclude: 'content', - template: '
book-
(
)
' - })); - directive('chapter', valueFn({ - transclude: 'content', - templateUrl: 'chapter.html' - })); - directive('section', valueFn({ - transclude: 'content', - template: '
section-!
!
' - })); - return function($httpBackend) { - $httpBackend. - expect('GET', 'chapter.html'). - respond('
chapter-
[
]
'); - }; - }); - inject(function(log, $rootScope, $compile, $httpBackend) { - element = $compile('
paragraph
')($rootScope); - $rootScope.$apply(); + $templateCache.put('myDirectiveAsync.html', '
Hello!
'); + element = $compile('
' + + '' + + '' + + '
')($rootScope); + expect(syncCtrlSpy).not.toHaveBeenCalled(); + expect(asyncCtrlSpy).not.toHaveBeenCalled(); - expect(element.text()).toEqual('book-'); + $rootScope.$apply(); - $httpBackend.flush(); - $rootScope.$apply(); - expect(element.text()).toEqual('book-chapter-section-![(paragraph)]!'); + //expect(syncCtrlSpy).toHaveBeenCalledOnce(); + expect(asyncCtrlSpy).toHaveBeenCalledOnce(); + }); }); - }); - it('should not merge text elements from transcluded content', function() { - module(function() { - directive('foo', valueFn({ - transclude: 'content', - template: '
This is before {{before}}.
', - link: function(scope, element, attr, ctrls, $transclude) { - var futureParent = element.children().eq(0); - $transclude(function(clone) { - futureParent.append(clone); - }, futureParent); - }, - scope: true - })); - }); - inject(function($rootScope, $compile) { - element = $compile('
This is after {{after}}
')($rootScope); - $rootScope.before = 'BEFORE'; - $rootScope.after = 'AFTER'; - $rootScope.$apply(); - expect(element.text()).toEqual('This is before BEFORE. This is after AFTER'); - $rootScope.before = 'Not-Before'; - $rootScope.after = 'AfTeR'; - $rootScope.$$childHead.before = 'BeFoRe'; - $rootScope.$$childHead.after = 'Not-After'; - $rootScope.$apply(); - expect(element.text()).toEqual('This is before BeFoRe. This is after AfTeR'); - }); - }); + it('should instantiate controllers in the parent->child order when transluction, templateUrl and replacement ' + + 'are in the mix', function() { + // When a child controller is in the transclusion that replaces the parent element that has a directive with + // a controller, we should ensure that we first instantiate the parent and only then stuff that comes from the + // transclusion. + // + // The transclusion moves the child controller onto the same element as parent controller so both controllers are + // on the same level. + + module(function() { + directive('parentDirective', function() { + return { + transclude: true, + replace: true, + templateUrl: 'parentDirective.html', + controller: function(log) { log('parentController'); } + }; + }); + directive('childDirective', function() { + return { + require: '^parentDirective', + templateUrl: 'childDirective.html', + controller: function(log) { log('childController'); } + }; + }); + }); + inject(function($templateCache, log, $compile, $rootScope) { + $templateCache.put('parentDirective.html', '
parentTemplateText;
'); + $templateCache.put('childDirective.html', 'childTemplateText;'); - it('should only allow one content transclusion per element', function() { - module(function() { - directive('first', valueFn({ - transclude: true - })); - directive('second', valueFn({ - transclude: true - })); - }); - inject(function($compile) { - expect(function() { - $compile('
'); - }).toThrowMinErr('$compile', 'multidir', /Multiple directives \[first, second\] asking for transclusion on:
childContentText;
')($rootScope); + $rootScope.$apply(); + expect(log).toEqual('parentController; childController'); + expect(element.text()).toBe('childTemplateText;childContentText;'); + }); }); - }); - //see issue https://github.com/angular/angular.js/issues/12936 - it('should use the proper scope when it is on the root element of a replaced directive template', function() { - module(function() { - directive('isolate', valueFn({ - scope: {}, - replace: true, - template: '
{{x}}
', - link: function(scope, element, attr, ctrl) { - scope.x = 'iso'; - } - })); - directive('trans', valueFn({ - transclude: 'content', - link: function(scope, element, attr, ctrl, $transclude) { - $transclude(function(clone) { - element.append(clone); - }); - } - })); - }); - inject(function($rootScope, $compile) { - element = $compile('')($rootScope); - $rootScope.x = 'root'; - $rootScope.$apply(); - expect(element.text()).toEqual('iso'); - }); - }); + it('should instantiate the controller after the isolate scope bindings are initialized (with template)', function() { + module(function() { + var Ctrl = function($scope, log) { + log('myFoo=' + $scope.myFoo); + }; + + directive('myDirective', function() { + return { + scope: { + myFoo: '=' + }, + template: '

Hello

', + controller: Ctrl + }; + }); + }); - //see issue https://github.com/angular/angular.js/issues/12936 - it('should use the proper scope when it is on the root element of a replaced directive template with child scope', function() { - module(function() { - directive('child', valueFn({ - scope: true, - replace: true, - template: '
{{x}}
', - link: function(scope, element, attr, ctrl) { - scope.x = 'child'; - } - })); - directive('trans', valueFn({ - transclude: 'content', - link: function(scope, element, attr, ctrl, $transclude) { - $transclude(function(clone) { - element.append(clone); - }); - } - })); - }); - inject(function($rootScope, $compile) { - element = $compile('')($rootScope); - $rootScope.x = 'root'; - $rootScope.$apply(); - expect(element.text()).toEqual('child'); + inject(function($templateCache, $compile, $rootScope, log) { + $rootScope.foo = 'bar'; + + element = $compile('
')($rootScope); + $rootScope.$apply(); + expect(log).toEqual('myFoo=bar'); + }); }); - }); - it('should not leak if two "element" transclusions are on the same element (with debug info)', function() { - if (jQuery) { - // jQuery 2.x doesn't expose the cache storage. - return; - } + it('should instantiate the controller after the isolate scope bindings are initialized (with templateUrl)', function() { + module(function() { + var Ctrl = function($scope, log) { + log('myFoo=' + $scope.myFoo); + }; + directive('myDirective', function() { + return { + scope: { + myFoo: '=' + }, + templateUrl: 'hello.html', + controller: Ctrl + }; + }); + }); - module(function($compileProvider) { - $compileProvider.debugInfoEnabled(true); - }); + inject(function($templateCache, $compile, $rootScope, log) { + $templateCache.put('hello.html', '

Hello

'); + $rootScope.foo = 'bar'; - inject(function($compile, $rootScope) { - var cacheSize = jqLiteCacheSize(); + element = $compile('
')($rootScope); + $rootScope.$apply(); + expect(log).toEqual('myFoo=bar'); + }); + }); - element = $compile('
{{x}}
')($rootScope); - expect(jqLiteCacheSize()).toEqual(cacheSize + 1); - $rootScope.$apply('xs = [0,1]'); - expect(jqLiteCacheSize()).toEqual(cacheSize + 2); + it('should instantiate controllers in the parent->child->baby order when nested transluction, templateUrl and ' + + 'replacement are in the mix', function() { + // similar to the test above, except that we have one more layer of nesting and nested transclusion - $rootScope.$apply('xs = [0]'); - expect(jqLiteCacheSize()).toEqual(cacheSize + 1); + module(function() { + directive('parentDirective', function() { + return { + transclude: true, + replace: true, + templateUrl: 'parentDirective.html', + controller: function(log) { log('parentController'); } + }; + }); + directive('childDirective', function() { + return { + require: '^parentDirective', + transclude: true, + replace: true, + templateUrl: 'childDirective.html', + controller: function(log) { log('childController'); } + }; + }); + directive('babyDirective', function() { + return { + require: '^childDirective', + templateUrl: 'babyDirective.html', + controller: function(log) { log('babyController'); } + }; + }); + }); - $rootScope.$apply('xs = []'); - expect(jqLiteCacheSize()).toEqual(cacheSize + 1); + inject(function($templateCache, log, $compile, $rootScope) { + $templateCache.put('parentDirective.html', '
parentTemplateText;
'); + $templateCache.put('childDirective.html', 'childTemplateText;'); + $templateCache.put('babyDirective.html', 'babyTemplateText;'); - element.remove(); - expect(jqLiteCacheSize()).toEqual(cacheSize + 0); + element = $compile('
' + + '
' + + 'childContentText;' + + '
babyContent;
' + + '
' + + '
')($rootScope); + $rootScope.$apply(); + expect(log).toEqual('parentController; childController; babyController'); + expect(element.text()).toBe('childContentText;babyTemplateText;'); + }); }); - }); - it('should not leak if two "element" transclusions are on the same element (without debug info)', function() { - if (jQuery) { - // jQuery 2.x doesn't expose the cache storage. - return; - } + it('should allow controller usage in pre-link directive functions with templateUrl', function() { + module(function() { + var Ctrl = function(log) { + log('instance'); + }; + + directive('myDirective', function() { + return { + scope: true, + templateUrl: 'hello.html', + controller: Ctrl, + compile: function() { + return { + pre: function(scope, template, attr, ctrl) {}, + post: function() {} + }; + } + }; + }); + }); + inject(function($templateCache, $compile, $rootScope, log) { + $templateCache.put('hello.html', '

Hello

'); - module(function($compileProvider) { - $compileProvider.debugInfoEnabled(false); + element = $compile('
')($rootScope); + $rootScope.$apply(); + + expect(log).toEqual('instance'); + expect(element.text()).toBe('Hello'); + }); }); - inject(function($compile, $rootScope) { - var cacheSize = jqLiteCacheSize(); - element = $compile('
{{x}}
')($rootScope); - expect(jqLiteCacheSize()).toEqual(cacheSize); + it('should allow controller usage in pre-link directive functions with a template', function() { + module(function() { + var Ctrl = function(log) { + log('instance'); + }; + + directive('myDirective', function() { + return { + scope: true, + template: '

Hello

', + controller: Ctrl, + compile: function() { + return { + pre: function(scope, template, attr, ctrl) {}, + post: function() {} + }; + } + }; + }); + }); - $rootScope.$apply('xs = [0,1]'); - expect(jqLiteCacheSize()).toEqual(cacheSize); + inject(function($templateCache, $compile, $rootScope, log) { + element = $compile('
')($rootScope); + $rootScope.$apply(); - $rootScope.$apply('xs = [0]'); - expect(jqLiteCacheSize()).toEqual(cacheSize); + expect(log).toEqual('instance'); + expect(element.text()).toBe('Hello'); + }); + }); - $rootScope.$apply('xs = []'); - expect(jqLiteCacheSize()).toEqual(cacheSize); - element.remove(); - expect(jqLiteCacheSize()).toEqual(cacheSize); + it('should throw ctreq with correct directive name, regardless of order', function() { + module(function($compileProvider) { + $compileProvider.directive('aDir', valueFn({ + restrict: 'E', + require: 'ngModel', + link: noop + })); + }); + inject(function($compile, $rootScope) { + expect(function() { + // a-dir will cause a ctreq error to be thrown. Previously, the error would reference + // the last directive in the chain (which in this case would be ngClick), based on + // priority and alphabetical ordering. This test verifies that the ordering does not + // affect which directive is referenced in the minErr message. + element = $compile('')($rootScope); + }).toThrowMinErr('$compile', 'ctreq', + 'Controller \'ngModel\', required by directive \'aDir\', can\'t be found!'); + }); }); }); - it('should not leak if two "element" transclusions are on the same element (with debug info)', function() { - if (jQuery) { - // jQuery 2.x doesn't expose the cache storage. - return; - } + describe('transclude', function() { - module(function($compileProvider) { - $compileProvider.debugInfoEnabled(true); - }); + describe('content transclusion', function() { - inject(function($compile, $rootScope) { - var cacheSize = jqLiteCacheSize(); - element = $compile('
{{x}}
')($rootScope); + it('should support transclude directive', function() { + module(function() { + directive('trans', function() { + return { + transclude: 'content', + replace: true, + scope: {}, + link: function(scope) { + scope.x = 'iso'; + }, + template: '
  • W:{{x}}-{{$parent.$id}}-{{$id}};
' + }; + }); + }); + inject(function(log, $rootScope, $compile) { + element = $compile('
T:{{x}}-{{$parent.$id}}-{{$id}};
')($rootScope); + $rootScope.x = 'root'; + $rootScope.$apply(); + expect(element.text()).toEqual('W:iso-1-2;T:root-2-3;'); + expect(jqLite(jqLite(element.find('li')[1]).contents()[0]).text()).toEqual('T:root-2-3'); + expect(jqLite(element.find('span')[0]).text()).toEqual(';'); + }); + }); + + + it('should transclude transcluded content', function() { + module(function() { + directive('book', valueFn({ + transclude: 'content', + template: '
book-
(
)
' + })); + directive('chapter', valueFn({ + transclude: 'content', + templateUrl: 'chapter.html' + })); + directive('section', valueFn({ + transclude: 'content', + template: '
section-!
!
' + })); + return function($httpBackend) { + $httpBackend. + expect('GET', 'chapter.html'). + respond('
chapter-
[
]
'); + }; + }); + inject(function(log, $rootScope, $compile, $httpBackend) { + element = $compile('
paragraph
')($rootScope); + $rootScope.$apply(); - $rootScope.$apply('xs = [0,1]'); - // At this point we have a bunch of comment placeholders but no real transcluded elements - // So the cache only contains the root element's data - expect(jqLiteCacheSize()).toEqual(cacheSize + 1); + expect(element.text()).toEqual('book-'); - $rootScope.$apply('val = true'); - // Now we have two concrete transcluded elements plus some comments so two more cache items - expect(jqLiteCacheSize()).toEqual(cacheSize + 3); + $httpBackend.flush(); + $rootScope.$apply(); + expect(element.text()).toEqual('book-chapter-section-![(paragraph)]!'); + }); + }); - $rootScope.$apply('val = false'); - // Once again we only have comments so no transcluded elements and the cache is back to just - // the root element - expect(jqLiteCacheSize()).toEqual(cacheSize + 1); - element.remove(); - // Now we've even removed the root element along with its cache - expect(jqLiteCacheSize()).toEqual(cacheSize + 0); - }); - }); + it('should not merge text elements from transcluded content', function() { + module(function() { + directive('foo', valueFn({ + transclude: 'content', + template: '
This is before {{before}}.
', + link: function(scope, element, attr, ctrls, $transclude) { + var futureParent = element.children().eq(0); + $transclude(function(clone) { + futureParent.append(clone); + }, futureParent); + }, + scope: true + })); + }); + inject(function($rootScope, $compile) { + element = $compile('
This is after {{after}}
')($rootScope); + $rootScope.before = 'BEFORE'; + $rootScope.after = 'AFTER'; + $rootScope.$apply(); + expect(element.text()).toEqual('This is before BEFORE. This is after AFTER'); + + $rootScope.before = 'Not-Before'; + $rootScope.after = 'AfTeR'; + $rootScope.$$childHead.before = 'BeFoRe'; + $rootScope.$$childHead.after = 'Not-After'; + $rootScope.$apply(); + expect(element.text()).toEqual('This is before BeFoRe. This is after AfTeR'); + }); + }); - it('should not leak when continuing the compilation of elements on a scope that was destroyed', function() { - if (jQuery) { - // jQuery 2.x doesn't expose the cache storage. - return; - } - var linkFn = jasmine.createSpy('linkFn'); + it('should only allow one content transclusion per element', function() { + module(function() { + directive('first', valueFn({ + transclude: true + })); + directive('second', valueFn({ + transclude: true + })); + }); + inject(function($compile) { + expect(function() { + $compile('
'); + }).toThrowMinErr('$compile', 'multidir', /Multiple directives \[first, second\] asking for transclusion on:
{{x}}
', + link: function(scope, element, attr, ctrl) { + scope.x = 'iso'; + } + })); + directive('trans', valueFn({ + transclude: 'content', + link: function(scope, element, attr, ctrl, $transclude) { + $transclude(function(clone) { + element.append(clone); + }); + } + })); + }); + inject(function($rootScope, $compile) { + element = $compile('')($rootScope); + $rootScope.x = 'root'; + $rootScope.$apply(); + expect(element.text()).toEqual('iso'); }); }); - $compileProvider.directive('isolateRed', function() { - return { - restrict: 'A', - scope: {}, - template: '
' - }; + + + //see issue https://github.com/angular/angular.js/issues/12936 + it('should use the proper scope when it is on the root element of a replaced directive template with child scope', function() { + module(function() { + directive('child', valueFn({ + scope: true, + replace: true, + template: '
{{x}}
', + link: function(scope, element, attr, ctrl) { + scope.x = 'child'; + } + })); + directive('trans', valueFn({ + transclude: 'content', + link: function(scope, element, attr, ctrl, $transclude) { + $transclude(function(clone) { + element.append(clone); + }); + } + })); + }); + inject(function($rootScope, $compile) { + element = $compile('')($rootScope); + $rootScope.x = 'root'; + $rootScope.$apply(); + expect(element.text()).toEqual('child'); + }); }); - $compileProvider.directive('red', function() { - return { - restrict: 'A', - templateUrl: 'red.html', - scope: {}, - link: linkFn - }; + + it('should throw if a transcluded node is transcluded again', function() { + module(function() { + directive('trans', valueFn({ + transclude: true, + link: function(scope, element, attr, ctrl, $transclude) { + $transclude(); + $transclude(); + } + })); + }); + inject(function($rootScope, $compile) { + expect(function() { + $compile('')($rootScope); + }).toThrowMinErr('$compile', 'multilink', 'This element has already been linked.'); + }); }); - }); - inject(function($compile, $rootScope, $httpBackend, $timeout, $templateCache) { - var cacheSize = jqLiteCacheSize(); - $httpBackend.whenGET('red.html').respond('

red.html

'); - var template = $compile( - '
' + - '
' + - '
' + - '
' + - '
' + - '
' + - '
'); - element = template($rootScope); - $rootScope.$digest(); - $timeout.flush(); - $httpBackend.flush(); - expect(linkFn).not.toHaveBeenCalled(); - expect(jqLiteCacheSize()).toEqual(cacheSize + 2); + it('should not leak if two "element" transclusions are on the same element (with debug info)', function() { + if (jQuery) { + // jQuery 2.x doesn't expose the cache storage. + return; + } - $templateCache.removeAll(); - var destroyedScope = $rootScope.$new(); - destroyedScope.$destroy(); - var clone = template(destroyedScope); - $rootScope.$digest(); - $timeout.flush(); - expect(linkFn).not.toHaveBeenCalled(); - }); - }); - if (jQuery) { - describe('cleaning up after a replaced element', function() { - var $compile, xs; - beforeEach(inject(function(_$compile_) { - $compile = _$compile_; - xs = [0, 1]; - })); + module(function($compileProvider) { + $compileProvider.debugInfoEnabled(true); + }); - function testCleanup() { - var privateData, firstRepeatedElem; + inject(function($compile, $rootScope) { + var cacheSize = jqLiteCacheSize(); - element = $compile('
{{x}}
')($rootScope); + element = $compile('
{{x}}
')($rootScope); + expect(jqLiteCacheSize()).toEqual(cacheSize + 1); - $rootScope.$apply('xs = [' + xs + ']'); - firstRepeatedElem = element.children('.ng-scope').eq(0); + $rootScope.$apply('xs = [0,1]'); + expect(jqLiteCacheSize()).toEqual(cacheSize + 2); - expect(firstRepeatedElem.data('$scope')).toBeDefined(); - privateData = jQuery._data(firstRepeatedElem[0]); - expect(privateData.events).toBeDefined(); - expect(privateData.events.click).toBeDefined(); - expect(privateData.events.click[0]).toBeDefined(); + $rootScope.$apply('xs = [0]'); + expect(jqLiteCacheSize()).toEqual(cacheSize + 1); - //Ensure the angular $destroy event is still sent - var destroyCount = 0; - element.find('div').on('$destroy', function() { destroyCount++; }); + $rootScope.$apply('xs = []'); + expect(jqLiteCacheSize()).toEqual(cacheSize + 1); - $rootScope.$apply('xs = null'); + element.remove(); + expect(jqLiteCacheSize()).toEqual(cacheSize + 0); + }); + }); - expect(destroyCount).toBe(2); - expect(firstRepeatedElem.data('$scope')).not.toBeDefined(); - privateData = jQuery._data(firstRepeatedElem[0]); - expect(privateData && privateData.events).not.toBeDefined(); - } - it('should work without external libraries (except jQuery)', testCleanup); - - it('should work with another library patching jQuery.cleanData after Angular', function() { - var cleanedCount = 0; - var currentCleanData = jQuery.cleanData; - jQuery.cleanData = function(elems) { - cleanedCount += elems.length; - // Don't return the output and explicitly pass only the first parameter - // so that we're sure we're not relying on either of them. jQuery UI patch - // behaves in this way. - currentCleanData(elems); - }; + it('should not leak if two "element" transclusions are on the same element (without debug info)', function() { + if (jQuery) { + // jQuery 2.x doesn't expose the cache storage. + return; + } - testCleanup(); - // The ng-repeat template is removed/cleaned (the +1) - // and each clone of the ng-repeat template is also removed (xs.length) - expect(cleanedCount).toBe(xs.length + 1); + module(function($compileProvider) { + $compileProvider.debugInfoEnabled(false); + }); - // Restore the previous jQuery.cleanData. - jQuery.cleanData = currentCleanData; - }); - }); - } + inject(function($compile, $rootScope) { + var cacheSize = jqLiteCacheSize(); + element = $compile('
{{x}}
')($rootScope); + expect(jqLiteCacheSize()).toEqual(cacheSize); - it('should add a $$transcluded property onto the transcluded scope', function() { - module(function() { - directive('trans', function() { - return { - transclude: true, - replace: true, - scope: true, - template: '
I:{{$$transcluded}}
' - }; - }); - }); - inject(function($rootScope, $compile) { - element = $compile('
T:{{$$transcluded}}
')($rootScope); - $rootScope.$apply(); - expect(jqLite(element.find('span')[0]).text()).toEqual('I:'); - expect(jqLite(element.find('span')[1]).text()).toEqual('T:true'); - }); - }); + $rootScope.$apply('xs = [0,1]'); + expect(jqLiteCacheSize()).toEqual(cacheSize); + $rootScope.$apply('xs = [0]'); + expect(jqLiteCacheSize()).toEqual(cacheSize); - it('should clear contents of the ng-translude element before appending transcluded content' + - ' if transcluded content exists', function() { - module(function() { - directive('trans', function() { - return { - transclude: true, - template: '
old stuff!
' - }; - }); - }); - inject(function($rootScope, $compile) { - element = $compile('
unicorn!
')($rootScope); - $rootScope.$apply(); - expect(sortedHtml(element.html())).toEqual('
unicorn!
'); - }); - }); + $rootScope.$apply('xs = []'); + expect(jqLiteCacheSize()).toEqual(cacheSize); - it('should NOT clear contents of the ng-translude element before appending transcluded content' + - ' if transcluded content does NOT exist', function() { - module(function() { - directive('trans', function() { - return { - transclude: true, - template: '
old stuff!
' - }; + element.remove(); + expect(jqLiteCacheSize()).toEqual(cacheSize); + }); }); - }); - inject(function(log, $rootScope, $compile) { - element = $compile('
')($rootScope); - $rootScope.$apply(); - expect(sortedHtml(element.html())).toEqual('
old stuff!
'); - }); - }); - it('should clear the fallback content from the element during compile and before linking', function() { - module(function() { - directive('trans', function() { - return { - transclude: true, - template: '
fallback content
' - }; - }); - }); - inject(function(log, $rootScope, $compile) { - element = jqLite('
'); - var linkfn = $compile(element); - expect(element.html()).toEqual('
'); - linkfn($rootScope); - $rootScope.$apply(); - expect(sortedHtml(element.html())).toEqual('
fallback content
'); - }); - }); + it('should not leak if two "element" transclusions are on the same element (with debug info)', function() { + if (jQuery) { + // jQuery 2.x doesn't expose the cache storage. + return; + } + module(function($compileProvider) { + $compileProvider.debugInfoEnabled(true); + }); - it('should allow cloning of the fallback via ngRepeat', function() { - module(function() { - directive('trans', function() { - return { - transclude: true, - template: '
{{i}}
' - }; + inject(function($compile, $rootScope) { + var cacheSize = jqLiteCacheSize(); + element = $compile('
{{x}}
')($rootScope); + + $rootScope.$apply('xs = [0,1]'); + // At this point we have a bunch of comment placeholders but no real transcluded elements + // So the cache only contains the root element's data + expect(jqLiteCacheSize()).toEqual(cacheSize + 1); + + $rootScope.$apply('val = true'); + // Now we have two concrete transcluded elements plus some comments so two more cache items + expect(jqLiteCacheSize()).toEqual(cacheSize + 3); + + $rootScope.$apply('val = false'); + // Once again we only have comments so no transcluded elements and the cache is back to just + // the root element + expect(jqLiteCacheSize()).toEqual(cacheSize + 1); + + element.remove(); + // Now we've even removed the root element along with its cache + expect(jqLiteCacheSize()).toEqual(cacheSize + 0); + }); }); - }); - inject(function(log, $rootScope, $compile) { - element = $compile('
')($rootScope); - $rootScope.$apply(); - expect(element.text()).toEqual('012'); - }); - }); + it('should not leak when continuing the compilation of elements on a scope that was destroyed', function() { + if (jQuery) { + // jQuery 2.x doesn't expose the cache storage. + return; + } + + var linkFn = jasmine.createSpy('linkFn'); + + module(function($controllerProvider, $compileProvider) { + $controllerProvider.register('Leak', function($scope, $timeout) { + $scope.code = 'red'; + $timeout(function() { + $scope.code = 'blue'; + }); + }); + $compileProvider.directive('isolateRed', function() { + return { + restrict: 'A', + scope: {}, + template: '
' + }; + }); + $compileProvider.directive('red', function() { + return { + restrict: 'A', + templateUrl: 'red.html', + scope: {}, + link: linkFn + }; + }); + }); - it('should not link the fallback content if transcluded content is provided', function() { - var linkSpy = jasmine.createSpy('postlink'); + inject(function($compile, $rootScope, $httpBackend, $timeout, $templateCache) { + var cacheSize = jqLiteCacheSize(); + $httpBackend.whenGET('red.html').respond('

red.html

'); + var template = $compile( + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
'); + element = template($rootScope, noop); + $rootScope.$digest(); + $timeout.flush(); + $httpBackend.flush(); + expect(linkFn).not.toHaveBeenCalled(); + expect(jqLiteCacheSize()).toEqual(cacheSize + 2); - module(function() { - directive('inner', function() { - return { - restrict: 'E', - template: 'old stuff! ', - link: linkSpy - }; + $templateCache.removeAll(); + var destroyedScope = $rootScope.$new(); + destroyedScope.$destroy(); + var clone = template(destroyedScope, noop); + $rootScope.$digest(); + $timeout.flush(); + expect(linkFn).not.toHaveBeenCalled(); + clone.remove(); + }); }); - directive('trans', function() { - return { - transclude: true, - template: '
' - }; - }); - }); - inject(function($rootScope, $compile) { - element = $compile('
unicorn!
')($rootScope); - $rootScope.$apply(); - expect(sortedHtml(element.html())).toEqual('
unicorn!
'); - expect(linkSpy).not.toHaveBeenCalled(); - }); - }); + if (jQuery) { + describe('cleaning up after a replaced element', function() { + var $compile, xs; + beforeEach(inject(function(_$compile_) { + $compile = _$compile_; + xs = [0, 1]; + })); - it('should compile and link the fallback content if no transcluded content is provided', function() { - var linkSpy = jasmine.createSpy('postlink'); + function testCleanup() { + var privateData, firstRepeatedElem; - module(function() { - directive('inner', function() { - return { - restrict: 'E', - template: 'old stuff! ', - link: linkSpy - }; - }); + element = $compile('
{{x}}
')($rootScope); - directive('trans', function() { - return { - transclude: true, - template: '
' - }; - }); - }); - inject(function(log, $rootScope, $compile) { - element = $compile('
')($rootScope); - $rootScope.$apply(); - expect(sortedHtml(element.html())).toEqual('
old stuff!
'); - expect(linkSpy).toHaveBeenCalled(); - }); - }); + $rootScope.$apply('xs = [' + xs + ']'); + firstRepeatedElem = element.children('.ng-scope').eq(0); - it('should compile and link the fallback content if an optional transclusion slot is not provided', function() { - var linkSpy = jasmine.createSpy('postlink'); + expect(firstRepeatedElem.data('$scope')).toBeDefined(); + privateData = jQuery._data(firstRepeatedElem[0]); + expect(privateData.events).toBeDefined(); + expect(privateData.events.click).toBeDefined(); + expect(privateData.events.click[0]).toBeDefined(); - module(function() { - directive('inner', function() { - return { - restrict: 'E', - template: 'old stuff! ', - link: linkSpy - }; - }); + //Ensure the angular $destroy event is still sent + var destroyCount = 0; + element.find('div').on('$destroy', function() { destroyCount++; }); - directive('trans', function() { - return { - transclude: { optionalSlot: '?optional'}, - template: '
' - }; - }); - }); - inject(function(log, $rootScope, $compile) { - element = $compile('
')($rootScope); - $rootScope.$apply(); - expect(sortedHtml(element.html())).toEqual('
old stuff!
'); - expect(linkSpy).toHaveBeenCalled(); - }); - }); + $rootScope.$apply('xs = null'); - it('should cope if there is neither transcluded content nor fallback content', function() { - module(function() { - directive('trans', function() { - return { - transclude: true, - template: '
' - }; - }); - }); - inject(function($rootScope, $compile) { - element = $compile('
')($rootScope); - $rootScope.$apply(); - expect(sortedHtml(element.html())).toEqual('
'); - }); - }); + expect(destroyCount).toBe(2); + expect(firstRepeatedElem.data('$scope')).not.toBeDefined(); + privateData = jQuery._data(firstRepeatedElem[0]); + expect(privateData && privateData.events).not.toBeDefined(); + } - it('should throw on an ng-transclude element inside no transclusion directive', function() { - inject(function($rootScope, $compile) { - // we need to do this because different browsers print empty attributes differently - try { - $compile('
')($rootScope); - } catch (e) { - expect(e.message).toMatch(new RegExp( - '^\\[ngTransclude:orphan\\] ' + - 'Illegal use of ngTransclude directive in the template! ' + - 'No parent directive that requires a transclusion found\\. ' + - 'Element:
' + - '
' + - '
this one should get replaced with content
' + - '
' + - '
', - transclude: true - })); + it('should add a $$transcluded property onto the transcluded scope', function() { + module(function() { + directive('trans', function() { + return { + transclude: true, + replace: true, + scope: true, + template: '
I:{{$$transcluded}}
' + }; + }); + }); + inject(function($rootScope, $compile) { + element = $compile('
T:{{$$transcluded}}
')($rootScope); + $rootScope.$apply(); + expect(jqLite(element.find('span')[0]).text()).toEqual('I:'); + expect(jqLite(element.find('span')[1]).text()).toEqual('T:true'); + }); + }); - $compileProvider.directive('noTransBar', valueFn({ - template: '
' + - // This ng-transclude is invalid. It should throw an error. - '
' + - '
', - transclude: false - })); - }); + it('should clear contents of the ng-translude element before appending transcluded content' + + ' if transcluded content exists', function() { + module(function() { + directive('trans', function() { + return { + transclude: true, + template: '
old stuff!
' + }; + }); + }); + inject(function($rootScope, $compile) { + element = $compile('
unicorn!
')($rootScope); + $rootScope.$apply(); + expect(sortedHtml(element.html())).toEqual('
unicorn!
'); + }); + }); - inject(function($compile, $rootScope) { - expect(function() { - $compile('
content
')($rootScope); - }).toThrowMinErr('ngTransclude', 'orphan', - 'Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found. Element:
'); - }); - }); + it('should NOT clear contents of the ng-translude element before appending transcluded content' + + ' if transcluded content does NOT exist', function() { + module(function() { + directive('trans', function() { + return { + transclude: true, + template: '
old stuff!
' + }; + }); + }); + inject(function(log, $rootScope, $compile) { + element = $compile('
')($rootScope); + $rootScope.$apply(); + expect(sortedHtml(element.html())).toEqual('
old stuff!
'); + }); + }); - it('should not pass transclusion into a templateUrl directive', function() { + it('should clear the fallback content from the element during compile and before linking', function() { + module(function() { + directive('trans', function() { + return { + transclude: true, + template: '
fallback content
' + }; + }); + }); + inject(function(log, $rootScope, $compile) { + element = jqLite('
'); + var linkfn = $compile(element); + expect(element.html()).toEqual('
'); + linkfn($rootScope); + $rootScope.$apply(); + expect(sortedHtml(element.html())).toEqual('
fallback content
'); + }); + }); - module(function($compileProvider) { - $compileProvider.directive('transFoo', valueFn({ - template: '
' + - '
' + - '
this one should get replaced with content
' + - '
' + - '
', - transclude: true + it('should allow cloning of the fallback via ngRepeat', function() { + module(function() { + directive('trans', function() { + return { + transclude: true, + template: '
{{i}}
' + }; + }); + }); + inject(function(log, $rootScope, $compile) { + element = $compile('
')($rootScope); + $rootScope.$apply(); + expect(element.text()).toEqual('012'); + }); + }); - })); - $compileProvider.directive('noTransBar', valueFn({ - templateUrl: 'noTransBar.html', - transclude: false + it('should not link the fallback content if transcluded content is provided', function() { + var linkSpy = jasmine.createSpy('postlink'); - })); - }); + module(function() { + directive('inner', function() { + return { + restrict: 'E', + template: 'old stuff! ', + link: linkSpy + }; + }); - inject(function($compile, $rootScope, $templateCache) { - $templateCache.put('noTransBar.html', - '
' + - // This ng-transclude is invalid. It should throw an error. - '
' + - '
'); + directive('trans', function() { + return { + transclude: true, + template: '
' + }; + }); + }); + inject(function($rootScope, $compile) { + element = $compile('
unicorn!
')($rootScope); + $rootScope.$apply(); + expect(sortedHtml(element.html())).toEqual('
unicorn!
'); + expect(linkSpy).not.toHaveBeenCalled(); + }); + }); - expect(function() { - element = $compile('
content
')($rootScope); - $rootScope.$apply(); - }).toThrowMinErr('ngTransclude', 'orphan', - 'Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found. Element:
'); - }); - }); + it('should compile and link the fallback content if no transcluded content is provided', function() { + var linkSpy = jasmine.createSpy('postlink'); + module(function() { + directive('inner', function() { + return { + restrict: 'E', + template: 'old stuff! ', + link: linkSpy + }; + }); - it('should expose transcludeFn in compile fn even for templateUrl', function() { - module(function() { - directive('transInCompile', valueFn({ - transclude: true, - // template: '
whatever
', - templateUrl: 'foo.html', - compile: function(_, __, transclude) { - return function(scope, element) { - transclude(scope, function(clone, scope) { - element.html(''); - element.append(clone); - }); - }; - } - })); - }); + directive('trans', function() { + return { + transclude: true, + template: '
' + }; + }); + }); + inject(function(log, $rootScope, $compile) { + element = $compile('
')($rootScope); + $rootScope.$apply(); + expect(sortedHtml(element.html())).toEqual('
old stuff!
'); + expect(linkSpy).toHaveBeenCalled(); + }); + }); - inject(function($compile, $rootScope, $templateCache) { - $templateCache.put('foo.html', '
whatever
'); + it('should compile and link the fallback content if an optional transclusion slot is not provided', function() { + var linkSpy = jasmine.createSpy('postlink'); - compile('
transcluded content
'); - $rootScope.$apply(); + module(function() { + directive('inner', function() { + return { + restrict: 'E', + template: 'old stuff! ', + link: linkSpy + }; + }); - expect(trim(element.text())).toBe('transcluded content'); - }); - }); + directive('trans', function() { + return { + transclude: { optionalSlot: '?optional'}, + template: '
' + }; + }); + }); + inject(function(log, $rootScope, $compile) { + element = $compile('
')($rootScope); + $rootScope.$apply(); + expect(sortedHtml(element.html())).toEqual('
old stuff!
'); + expect(linkSpy).toHaveBeenCalled(); + }); + }); + it('should cope if there is neither transcluded content nor fallback content', function() { + module(function() { + directive('trans', function() { + return { + transclude: true, + template: '
' + }; + }); + }); + inject(function($rootScope, $compile) { + element = $compile('
')($rootScope); + $rootScope.$apply(); + expect(sortedHtml(element.html())).toEqual('
'); + }); + }); - it('should make the result of a transclusion available to the parent directive in post-linking phase' + - '(template)', function() { - module(function() { - directive('trans', function(log) { - return { - transclude: true, - template: '
', - link: { - pre: function($scope, $element) { - log('pre(' + $element.text() + ')'); - }, - post: function($scope, $element) { - log('post(' + $element.text() + ')'); - } + it('should throw on an ng-transclude element inside no transclusion directive', function() { + inject(function($rootScope, $compile) { + // we need to do this because different browsers print empty attributes differently + try { + $compile('
')($rootScope); + } catch (e) { + expect(e.message).toMatch(new RegExp( + '^\\[ngTransclude:orphan\\] ' + + 'Illegal use of ngTransclude directive in the template! ' + + 'No parent directive that requires a transclusion found\\. ' + + 'Element:
unicorn!
')($rootScope); - $rootScope.$apply(); - expect(log).toEqual('pre(); post(unicorn!)'); - }); - }); - it('should make the result of a transclusion available to the parent directive in post-linking phase' + - '(templateUrl)', function() { - // when compiling an async directive the transclusion is always processed before the directive - // this is different compared to sync directive. delaying the transclusion makes little sense. + it('should not pass transclusion into a template directive when the directive didn\'t request transclusion', function() { - module(function() { - directive('trans', function(log) { - return { - transclude: true, - templateUrl: 'trans.html', - link: { - pre: function($scope, $element) { - log('pre(' + $element.text() + ')'); - }, - post: function($scope, $element) { - log('post(' + $element.text() + ')'); - } - } - }; - }); - }); - inject(function(log, $rootScope, $compile, $templateCache) { - $templateCache.put('trans.html', '
'); + module(function($compileProvider) { - element = $compile('
unicorn!
')($rootScope); - $rootScope.$apply(); - expect(log).toEqual('pre(); post(unicorn!)'); - }); - }); + $compileProvider.directive('transFoo', valueFn({ + template: '
' + + '
' + + '
this one should get replaced with content
' + + '
' + + '
', + transclude: true + })); - it('should make the result of a transclusion available to the parent *replace* directive in post-linking phase' + - '(template)', function() { - module(function() { - directive('replacedTrans', function(log) { - return { - transclude: true, - replace: true, - template: '
', - link: { - pre: function($scope, $element) { - log('pre(' + $element.text() + ')'); - }, - post: function($scope, $element) { - log('post(' + $element.text() + ')'); - } - } - }; - }); - }); - inject(function(log, $rootScope, $compile) { - element = $compile('
unicorn!
')($rootScope); - $rootScope.$apply(); - expect(log).toEqual('pre(); post(unicorn!)'); - }); - }); + $compileProvider.directive('noTransBar', valueFn({ + template: '
' + + // This ng-transclude is invalid. It should throw an error. + '
' + + '
', + transclude: false + })); + }); - it('should make the result of a transclusion available to the parent *replace* directive in post-linking phase' + - ' (templateUrl)', function() { - module(function() { - directive('replacedTrans', function(log) { - return { - transclude: true, - replace: true, - templateUrl: 'trans.html', - link: { - pre: function($scope, $element) { - log('pre(' + $element.text() + ')'); - }, - post: function($scope, $element) { - log('post(' + $element.text() + ')'); - } - } - }; + inject(function($compile, $rootScope) { + expect(function() { + $compile('
content
')($rootScope); + }).toThrowMinErr('ngTransclude', 'orphan', + 'Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found. Element:
'); + }); }); - }); - inject(function(log, $rootScope, $compile, $templateCache) { - $templateCache.put('trans.html', '
'); - element = $compile('
unicorn!
')($rootScope); - $rootScope.$apply(); - expect(log).toEqual('pre(); post(unicorn!)'); - }); - }); - it('should copy the directive controller to all clones', function() { - var transcludeCtrl, cloneCount = 2; - module(function() { - directive('transclude', valueFn({ - transclude: 'content', - controller: function($transclude) { - transcludeCtrl = this; - }, - link: function(scope, el, attr, ctrl, $transclude) { - var i; - for (i = 0; i < cloneCount; i++) { - $transclude(cloneAttach); - } + it('should not pass transclusion into a templateUrl directive', function() { - function cloneAttach(clone) { - el.append(clone); - } - } - })); - }); - inject(function($compile) { - element = $compile('
')($rootScope); - var children = element.children(), i; - expect(transcludeCtrl).toBeDefined(); + module(function($compileProvider) { - expect(element.data('$transcludeController')).toBe(transcludeCtrl); - for (i = 0; i < cloneCount; i++) { - expect(children.eq(i).data('$transcludeController')).toBeUndefined(); - } - }); - }); + $compileProvider.directive('transFoo', valueFn({ + template: '
' + + '
' + + '
this one should get replaced with content
' + + '
' + + '
', + transclude: true - it('should provide the $transclude controller local as 5th argument to the pre and post-link function', function() { - var ctrlTransclude, preLinkTransclude, postLinkTransclude; - module(function() { - directive('transclude', valueFn({ - transclude: 'content', - controller: function($transclude) { - ctrlTransclude = $transclude; - }, - compile: function() { - return { - pre: function(scope, el, attr, ctrl, $transclude) { - preLinkTransclude = $transclude; - }, - post: function(scope, el, attr, ctrl, $transclude) { - postLinkTransclude = $transclude; - } - }; - } - })); - }); - inject(function($compile) { - element = $compile('
')($rootScope); - expect(ctrlTransclude).toBeDefined(); - expect(ctrlTransclude).toBe(preLinkTransclude); - expect(ctrlTransclude).toBe(postLinkTransclude); - }); - }); + })); + + $compileProvider.directive('noTransBar', valueFn({ + templateUrl: 'noTransBar.html', + transclude: false + + })); + }); + + inject(function($compile, $rootScope, $templateCache) { + $templateCache.put('noTransBar.html', + '
' + + // This ng-transclude is invalid. It should throw an error. + '
' + + '
'); + + expect(function() { + element = $compile('
content
')($rootScope); + $rootScope.$apply(); + }).toThrowMinErr('ngTransclude', 'orphan', + 'Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found. Element:
'); + }); + }); + + + it('should expose transcludeFn in compile fn even for templateUrl', function() { + module(function() { + directive('transInCompile', valueFn({ + transclude: true, + // template: '
whatever
', + templateUrl: 'foo.html', + compile: function(_, __, transclude) { + return function(scope, element) { + transclude(scope, function(clone, scope) { + element.html(''); + element.append(clone); + }); + }; + } + })); + }); - it('should allow an optional scope argument in $transclude', function() { - var capturedChildCtrl; - module(function() { - directive('transclude', valueFn({ - transclude: 'content', - link: function(scope, element, attr, ctrl, $transclude) { - $transclude(scope, function(clone) { - element.append(clone); + inject(function($compile, $rootScope, $templateCache) { + $templateCache.put('foo.html', '
whatever
'); + + compile('
transcluded content
'); + $rootScope.$apply(); + + expect(trim(element.text())).toBe('transcluded content'); + }); + }); + + + it('should make the result of a transclusion available to the parent directive in post-linking phase' + + '(template)', function() { + module(function() { + directive('trans', function(log) { + return { + transclude: true, + template: '
', + link: { + pre: function($scope, $element) { + log('pre(' + $element.text() + ')'); + }, + post: function($scope, $element) { + log('post(' + $element.text() + ')'); + } + } + }; }); - } - })); - }); - inject(function($compile) { - element = $compile('
{{$id}}
')($rootScope); - $rootScope.$apply(); - expect(element.text()).toBe('' + $rootScope.$id); - }); + }); + inject(function(log, $rootScope, $compile) { + element = $compile('
unicorn!
')($rootScope); + $rootScope.$apply(); + expect(log).toEqual('pre(); post(unicorn!)'); + }); + }); - }); - it('should expose the directive controller to transcluded children', function() { - var capturedChildCtrl; - module(function() { - directive('transclude', valueFn({ - transclude: 'content', - controller: function() { - }, - link: function(scope, element, attr, ctrl, $transclude) { - $transclude(function(clone) { - element.append(clone); + it('should make the result of a transclusion available to the parent directive in post-linking phase' + + '(templateUrl)', function() { + // when compiling an async directive the transclusion is always processed before the directive + // this is different compared to sync directive. delaying the transclusion makes little sense. + + module(function() { + directive('trans', function(log) { + return { + transclude: true, + templateUrl: 'trans.html', + link: { + pre: function($scope, $element) { + log('pre(' + $element.text() + ')'); + }, + post: function($scope, $element) { + log('post(' + $element.text() + ')'); + } + } + }; }); - } - })); - directive('child', valueFn({ - require: '^transclude', - link: function(scope, element, attr, ctrl) { - capturedChildCtrl = ctrl; - } - })); - }); - inject(function($compile) { - element = $compile('
')($rootScope); - expect(capturedChildCtrl).toBeTruthy(); - }); - }); + }); + inject(function(log, $rootScope, $compile, $templateCache) { + $templateCache.put('trans.html', '
'); + element = $compile('
unicorn!
')($rootScope); + $rootScope.$apply(); + expect(log).toEqual('pre(); post(unicorn!)'); + }); + }); - // See issue https://github.com/angular/angular.js/issues/14924 - it('should not process top-level transcluded text nodes merged into their sibling', - function() { - module(function() { - directive('transclude', valueFn({ - template: '', - transclude: true, - scope: {} - })); + + it('should make the result of a transclusion available to the parent *replace* directive in post-linking phase' + + '(template)', function() { + module(function() { + directive('replacedTrans', function(log) { + return { + transclude: true, + replace: true, + template: '
', + link: { + pre: function($scope, $element) { + log('pre(' + $element.text() + ')'); + }, + post: function($scope, $element) { + log('post(' + $element.text() + ')'); + } + } + }; + }); + }); + inject(function(log, $rootScope, $compile) { + element = $compile('
unicorn!
')($rootScope); + $rootScope.$apply(); + expect(log).toEqual('pre(); post(unicorn!)'); + }); }); - inject(function($compile) { - element = jqLite('
'); - element[0].appendChild(document.createTextNode('1{{ value }}')); - element[0].appendChild(document.createTextNode('2{{ value }}')); - element[0].appendChild(document.createTextNode('3{{ value }}')); - var initialWatcherCount = $rootScope.$countWatchers(); - $compile(element)($rootScope); - $rootScope.$apply('value = 0'); - var newWatcherCount = $rootScope.$countWatchers() - initialWatcherCount; + it('should make the result of a transclusion available to the parent *replace* directive in post-linking phase' + + ' (templateUrl)', function() { + module(function() { + directive('replacedTrans', function(log) { + return { + transclude: true, + replace: true, + templateUrl: 'trans.html', + link: { + pre: function($scope, $element) { + log('pre(' + $element.text() + ')'); + }, + post: function($scope, $element) { + log('post(' + $element.text() + ')'); + } + } + }; + }); + }); + inject(function(log, $rootScope, $compile, $templateCache) { + $templateCache.put('trans.html', '
'); - expect(element.text()).toBe('102030'); - expect(newWatcherCount).toBe(3); + element = $compile('
unicorn!
')($rootScope); + $rootScope.$apply(); + expect(log).toEqual('pre(); post(unicorn!)'); + }); }); - } - ); + it('should copy the directive controller to all clones', function() { + var transcludeCtrl, cloneCount = 2; + module(function() { + directive('transclude', valueFn({ + transclude: 'content', + controller: function($transclude) { + transcludeCtrl = this; + }, + link: function(scope, el, attr, ctrl, $transclude) { + var i; + for (i = 0; i < cloneCount; i++) { + $transclude(cloneAttach); + } - // see issue https://github.com/angular/angular.js/issues/9413 - describe('passing a parent bound transclude function to the link ' + - 'function returned from `$compile`', function() { + function cloneAttach(clone) { + el.append(clone); + } + } + })); + }); + inject(function($compile) { + element = $compile('
')($rootScope); + var children = element.children(), i; + expect(transcludeCtrl).toBeDefined(); - beforeEach(module(function() { - directive('lazyCompile', function($compile) { - return { - compile: function(tElement, tAttrs) { - var content = tElement.contents(); - tElement.empty(); - return function(scope, element, attrs, ctrls, transcludeFn) { - element.append(content); - $compile(content)(scope, undefined, { - parentBoundTranscludeFn: transcludeFn - }); - }; + expect(element.data('$transcludeController')).toBe(transcludeCtrl); + for (i = 0; i < cloneCount; i++) { + expect(children.eq(i).data('$transcludeController')).toBeUndefined(); } - }; + }); }); - directive('toggle', valueFn({ - scope: {t: '=toggle'}, - transclude: true, - template: '
' - })); - })); - it('should preserve the bound scope', function() { + it('should provide the $transclude controller local as 5th argument to the pre and post-link function', function() { + var ctrlTransclude, preLinkTransclude, postLinkTransclude; + module(function() { + directive('transclude', valueFn({ + transclude: 'content', + controller: function($transclude) { + ctrlTransclude = $transclude; + }, + compile: function() { + return { + pre: function(scope, el, attr, ctrl, $transclude) { + preLinkTransclude = $transclude; + }, + post: function(scope, el, attr, ctrl, $transclude) { + postLinkTransclude = $transclude; + } + }; + } + })); + }); + inject(function($compile) { + element = $compile('
')($rootScope); + expect(ctrlTransclude).toBeDefined(); + expect(ctrlTransclude).toBe(preLinkTransclude); + expect(ctrlTransclude).toBe(postLinkTransclude); + }); + }); - inject(function($compile, $rootScope) { - element = $compile( - '
' + - '
' + - '
' + - 'SuccessError' + - '
' + - '
')($rootScope); + it('should allow an optional scope argument in $transclude', function() { + var capturedChildCtrl; + module(function() { + directive('transclude', valueFn({ + transclude: 'content', + link: function(scope, element, attr, ctrl, $transclude) { + $transclude(scope, function(clone) { + element.append(clone); + }); + } + })); + }); + inject(function($compile) { + element = $compile('
{{$id}}
')($rootScope); + $rootScope.$apply(); + expect(element.text()).toBe('' + $rootScope.$id); + }); - $rootScope.$apply('t = false'); - expect($rootScope.$countChildScopes()).toBe(1); - expect(element.text()).toBe(''); + }); - $rootScope.$apply('t = true'); - expect($rootScope.$countChildScopes()).toBe(4); - expect(element.text()).toBe('Success'); + it('should expose the directive controller to transcluded children', function() { + var capturedChildCtrl; + module(function() { + directive('transclude', valueFn({ + transclude: 'content', + controller: function() { + }, + link: function(scope, element, attr, ctrl, $transclude) { + $transclude(function(clone) { + element.append(clone); + }); + } + })); + directive('child', valueFn({ + require: '^transclude', + link: function(scope, element, attr, ctrl) { + capturedChildCtrl = ctrl; + } + })); + }); + inject(function($compile) { + element = $compile('
')($rootScope); + expect(capturedChildCtrl).toBeTruthy(); + }); + }); - $rootScope.$apply('t = false'); - expect($rootScope.$countChildScopes()).toBe(1); - expect(element.text()).toBe(''); - $rootScope.$apply('t = true'); - expect($rootScope.$countChildScopes()).toBe(4); - expect(element.text()).toBe('Success'); - }); - }); + // See issue https://github.com/angular/angular.js/issues/14924 + it('should not process top-level transcluded text nodes merged into their sibling', + function() { + module(function() { + directive('transclude', valueFn({ + template: '', + transclude: true, + scope: {} + })); + }); + inject(function($compile) { + element = jqLite('
'); + element[0].appendChild(document.createTextNode('1{{ value }}')); + element[0].appendChild(document.createTextNode('2{{ value }}')); + element[0].appendChild(document.createTextNode('3{{ value }}')); - it('should preserve the bound scope when using recursive transclusion', function() { + var initialWatcherCount = $rootScope.$countWatchers(); + $compile(element)($rootScope); + $rootScope.$apply('value = 0'); + var newWatcherCount = $rootScope.$countWatchers() - initialWatcherCount; - directive('recursiveTransclude', valueFn({ - transclude: true, - template: '
' - })); + expect(element.text()).toBe('102030'); + expect(newWatcherCount).toBe(3); + }); + } + ); - inject(function($compile, $rootScope) { - element = $compile( - '
' + - '
' + - '
' + - '
' + - 'SuccessError' + - '
' + - '
' + - '
')($rootScope); - $rootScope.$apply('t = false'); - expect($rootScope.$countChildScopes()).toBe(1); - expect(element.text()).toBe(''); + // see issue https://github.com/angular/angular.js/issues/9413 + describe('passing a parent bound transclude function to the link ' + + 'function returned from `$compile`', function() { - $rootScope.$apply('t = true'); - expect($rootScope.$countChildScopes()).toBe(4); - expect(element.text()).toBe('Success'); + beforeEach(module(function() { + directive('lazyCompile', function($compile) { + return { + compile: function(tElement, tAttrs) { + var content = tElement.contents(); + tElement.empty(); + return function(scope, element, attrs, ctrls, transcludeFn) { + element.append(content); + $compile(content)(scope, undefined, { + parentBoundTranscludeFn: transcludeFn + }); + }; + } + }; + }); + directive('toggle', valueFn({ + scope: {t: '=toggle'}, + transclude: true, + template: '
' + })); + })); - $rootScope.$apply('t = false'); - expect($rootScope.$countChildScopes()).toBe(1); - expect(element.text()).toBe(''); + it('should preserve the bound scope', function() { - $rootScope.$apply('t = true'); - expect($rootScope.$countChildScopes()).toBe(4); - expect(element.text()).toBe('Success'); - }); - }); - }); + inject(function($compile, $rootScope) { + element = $compile( + '
' + + '
' + + '
' + + 'SuccessError' + + '
' + + '
')($rootScope); + + $rootScope.$apply('t = false'); + expect($rootScope.$countChildScopes()).toBe(1); + expect(element.text()).toBe(''); + + $rootScope.$apply('t = true'); + expect($rootScope.$countChildScopes()).toBe(4); + expect(element.text()).toBe('Success'); + + $rootScope.$apply('t = false'); + expect($rootScope.$countChildScopes()).toBe(1); + expect(element.text()).toBe(''); + + $rootScope.$apply('t = true'); + expect($rootScope.$countChildScopes()).toBe(4); + expect(element.text()).toBe('Success'); + }); + }); - // see issue https://github.com/angular/angular.js/issues/9095 - describe('removing a transcluded element', function() { + it('should preserve the bound scope when using recursive transclusion', function() { - beforeEach(module(function() { - directive('toggle', function() { - return { - transclude: true, - template: '
' - }; + directive('recursiveTransclude', valueFn({ + transclude: true, + template: '
' + })); + + inject(function($compile, $rootScope) { + element = $compile( + '
' + + '
' + + '
' + + '
' + + 'SuccessError' + + '
' + + '
' + + '
')($rootScope); + + $rootScope.$apply('t = false'); + expect($rootScope.$countChildScopes()).toBe(1); + expect(element.text()).toBe(''); + + $rootScope.$apply('t = true'); + expect($rootScope.$countChildScopes()).toBe(4); + expect(element.text()).toBe('Success'); + + $rootScope.$apply('t = false'); + expect($rootScope.$countChildScopes()).toBe(1); + expect(element.text()).toBe(''); + + $rootScope.$apply('t = true'); + expect($rootScope.$countChildScopes()).toBe(4); + expect(element.text()).toBe('Success'); + }); + }); }); - })); - it('should not leak the transclude scope when the transcluded content is an element transclusion directive', - inject(function($compile, $rootScope) { + // see issue https://github.com/angular/angular.js/issues/9095 + describe('removing a transcluded element', function() { - element = $compile( - '
' + - '
{{ msg }}
' + - '
' - )($rootScope); - - $rootScope.$apply('t = true'); - expect(element.text()).toContain('msg-1'); - // Expected scopes: $rootScope, ngIf, transclusion, ngRepeat - expect($rootScope.$countChildScopes()).toBe(3); - - $rootScope.$apply('t = false'); - expect(element.text()).not.toContain('msg-1'); - // Expected scopes: $rootScope - expect($rootScope.$countChildScopes()).toBe(0); - - $rootScope.$apply('t = true'); - expect(element.text()).toContain('msg-1'); - // Expected scopes: $rootScope, ngIf, transclusion, ngRepeat - expect($rootScope.$countChildScopes()).toBe(3); - - $rootScope.$apply('t = false'); - expect(element.text()).not.toContain('msg-1'); - // Expected scopes: $rootScope - expect($rootScope.$countChildScopes()).toBe(0); - })); + beforeEach(module(function() { + directive('toggle', function() { + return { + transclude: true, + template: '
' + }; + }); + })); - it('should not leak the transclude scope when the transcluded content is an multi-element transclusion directive', - inject(function($compile, $rootScope) { + it('should not leak the transclude scope when the transcluded content is an element transclusion directive', + inject(function($compile, $rootScope) { - element = $compile( - '
' + - '
{{ msg }}
' + - '
{{ msg }}
' + - '
' - )($rootScope); - - $rootScope.$apply('t = true'); - expect(element.text()).toContain('msg-1msg-1'); - // Expected scopes: $rootScope, ngIf, transclusion, ngRepeat - expect($rootScope.$countChildScopes()).toBe(3); - - $rootScope.$apply('t = false'); - expect(element.text()).not.toContain('msg-1msg-1'); - // Expected scopes: $rootScope - expect($rootScope.$countChildScopes()).toBe(0); - - $rootScope.$apply('t = true'); - expect(element.text()).toContain('msg-1msg-1'); - // Expected scopes: $rootScope, ngIf, transclusion, ngRepeat - expect($rootScope.$countChildScopes()).toBe(3); - - $rootScope.$apply('t = false'); - expect(element.text()).not.toContain('msg-1msg-1'); - // Expected scopes: $rootScope - expect($rootScope.$countChildScopes()).toBe(0); - })); + element = $compile( + '
' + + '
{{ msg }}
' + + '
' + )($rootScope); + $rootScope.$apply('t = true'); + expect(element.text()).toContain('msg-1'); + // Expected scopes: $rootScope, ngIf, transclusion, ngRepeat + expect($rootScope.$countChildScopes()).toBe(3); + + $rootScope.$apply('t = false'); + expect(element.text()).not.toContain('msg-1'); + // Expected scopes: $rootScope + expect($rootScope.$countChildScopes()).toBe(0); + + $rootScope.$apply('t = true'); + expect(element.text()).toContain('msg-1'); + // Expected scopes: $rootScope, ngIf, transclusion, ngRepeat + expect($rootScope.$countChildScopes()).toBe(3); + + $rootScope.$apply('t = false'); + expect(element.text()).not.toContain('msg-1'); + // Expected scopes: $rootScope + expect($rootScope.$countChildScopes()).toBe(0); + })); - it('should not leak the transclude scope if the transcluded contains only comments', - inject(function($compile, $rootScope) { - element = $compile( - '
' + - '' + - '
' - )($rootScope); - - $rootScope.$apply('t = true'); - expect(element.html()).toContain('some comment'); - // Expected scopes: $rootScope, ngIf, transclusion - expect($rootScope.$countChildScopes()).toBe(2); - - $rootScope.$apply('t = false'); - expect(element.html()).not.toContain('some comment'); - // Expected scopes: $rootScope - expect($rootScope.$countChildScopes()).toBe(0); - - $rootScope.$apply('t = true'); - expect(element.html()).toContain('some comment'); - // Expected scopes: $rootScope, ngIf, transclusion - expect($rootScope.$countChildScopes()).toBe(2); - - $rootScope.$apply('t = false'); - expect(element.html()).not.toContain('some comment'); - // Expected scopes: $rootScope - expect($rootScope.$countChildScopes()).toBe(0); - })); + it('should not leak the transclude scope when the transcluded content is an multi-element transclusion directive', + inject(function($compile, $rootScope) { - it('should not leak the transclude scope if the transcluded contains only text nodes', - inject(function($compile, $rootScope) { + element = $compile( + '
' + + '
{{ msg }}
' + + '
{{ msg }}
' + + '
' + )($rootScope); - element = $compile( - '
' + - 'some text' + - '
' - )($rootScope); - - $rootScope.$apply('t = true'); - expect(element.html()).toContain('some text'); - // Expected scopes: $rootScope, ngIf, transclusion - expect($rootScope.$countChildScopes()).toBe(2); - - $rootScope.$apply('t = false'); - expect(element.html()).not.toContain('some text'); - // Expected scopes: $rootScope - expect($rootScope.$countChildScopes()).toBe(0); - - $rootScope.$apply('t = true'); - expect(element.html()).toContain('some text'); - // Expected scopes: $rootScope, ngIf, transclusion - expect($rootScope.$countChildScopes()).toBe(2); - - $rootScope.$apply('t = false'); - expect(element.html()).not.toContain('some text'); - // Expected scopes: $rootScope - expect($rootScope.$countChildScopes()).toBe(0); - })); + $rootScope.$apply('t = true'); + expect(element.text()).toContain('msg-1msg-1'); + // Expected scopes: $rootScope, ngIf, transclusion, ngRepeat + expect($rootScope.$countChildScopes()).toBe(3); + + $rootScope.$apply('t = false'); + expect(element.text()).not.toContain('msg-1msg-1'); + // Expected scopes: $rootScope + expect($rootScope.$countChildScopes()).toBe(0); + + $rootScope.$apply('t = true'); + expect(element.text()).toContain('msg-1msg-1'); + // Expected scopes: $rootScope, ngIf, transclusion, ngRepeat + expect($rootScope.$countChildScopes()).toBe(3); + + $rootScope.$apply('t = false'); + expect(element.text()).not.toContain('msg-1msg-1'); + // Expected scopes: $rootScope + expect($rootScope.$countChildScopes()).toBe(0); + })); - it('should mark as destroyed all sub scopes of the scope being destroyed', - inject(function($compile, $rootScope) { - element = $compile( - '
' + - '
{{ msg }}
' + - '
' - )($rootScope); + it('should not leak the transclude scope if the transcluded contains only comments', + inject(function($compile, $rootScope) { - $rootScope.$apply('t = true'); - var childScopes = getChildScopes($rootScope); + element = $compile( + '
' + + '' + + '
' + )($rootScope); - $rootScope.$apply('t = false'); - for (var i = 0; i < childScopes.length; ++i) { - expect(childScopes[i].$$destroyed).toBe(true); - } - })); - }); + $rootScope.$apply('t = true'); + expect(element.html()).toContain('some comment'); + // Expected scopes: $rootScope, ngIf, transclusion + expect($rootScope.$countChildScopes()).toBe(2); + + $rootScope.$apply('t = false'); + expect(element.html()).not.toContain('some comment'); + // Expected scopes: $rootScope + expect($rootScope.$countChildScopes()).toBe(0); + + $rootScope.$apply('t = true'); + expect(element.html()).toContain('some comment'); + // Expected scopes: $rootScope, ngIf, transclusion + expect($rootScope.$countChildScopes()).toBe(2); + + $rootScope.$apply('t = false'); + expect(element.html()).not.toContain('some comment'); + // Expected scopes: $rootScope + expect($rootScope.$countChildScopes()).toBe(0); + })); + it('should not leak the transclude scope if the transcluded contains only text nodes', + inject(function($compile, $rootScope) { - describe('nested transcludes', function() { + element = $compile( + '
' + + 'some text' + + '
' + )($rootScope); - beforeEach(module(function($compileProvider) { + $rootScope.$apply('t = true'); + expect(element.html()).toContain('some text'); + // Expected scopes: $rootScope, ngIf, transclusion + expect($rootScope.$countChildScopes()).toBe(2); + + $rootScope.$apply('t = false'); + expect(element.html()).not.toContain('some text'); + // Expected scopes: $rootScope + expect($rootScope.$countChildScopes()).toBe(0); + + $rootScope.$apply('t = true'); + expect(element.html()).toContain('some text'); + // Expected scopes: $rootScope, ngIf, transclusion + expect($rootScope.$countChildScopes()).toBe(2); + + $rootScope.$apply('t = false'); + expect(element.html()).not.toContain('some text'); + // Expected scopes: $rootScope + expect($rootScope.$countChildScopes()).toBe(0); + })); - $compileProvider.directive('noop', valueFn({})); + it('should mark as destroyed all sub scopes of the scope being destroyed', + inject(function($compile, $rootScope) { - $compileProvider.directive('sync', valueFn({ - template: '
', - transclude: true - })); + element = $compile( + '
' + + '
{{ msg }}
' + + '
' + )($rootScope); - $compileProvider.directive('async', valueFn({ - templateUrl: 'async', - transclude: true - })); + $rootScope.$apply('t = true'); + var childScopes = getChildScopes($rootScope); - $compileProvider.directive('syncSync', valueFn({ - template: '
', - transclude: true - })); + $rootScope.$apply('t = false'); + for (var i = 0; i < childScopes.length; ++i) { + expect(childScopes[i].$$destroyed).toBe(true); + } + })); + }); - $compileProvider.directive('syncAsync', valueFn({ - template: '
', - transclude: true - })); - $compileProvider.directive('asyncSync', valueFn({ - templateUrl: 'asyncSync', - transclude: true - })); + describe('nested transcludes', function() { - $compileProvider.directive('asyncAsync', valueFn({ - templateUrl: 'asyncAsync', - transclude: true - })); + beforeEach(module(function($compileProvider) { - })); + $compileProvider.directive('noop', valueFn({})); - beforeEach(inject(function($templateCache) { - $templateCache.put('async', '
'); - $templateCache.put('asyncSync', '
'); - $templateCache.put('asyncAsync', '
'); - })); + $compileProvider.directive('sync', valueFn({ + template: '
', + transclude: true + })); + $compileProvider.directive('async', valueFn({ + templateUrl: 'async', + transclude: true + })); - it('should allow nested transclude directives with sync template containing sync template', inject(function($compile, $rootScope) { - element = $compile('
transcluded content
')($rootScope); - $rootScope.$digest(); - expect(element.text()).toEqual('transcluded content'); - })); + $compileProvider.directive('syncSync', valueFn({ + template: '
', + transclude: true + })); - it('should allow nested transclude directives with sync template containing async template', inject(function($compile, $rootScope) { - element = $compile('
transcluded content
')($rootScope); - $rootScope.$digest(); - expect(element.text()).toEqual('transcluded content'); - })); + $compileProvider.directive('syncAsync', valueFn({ + template: '
', + transclude: true + })); - it('should allow nested transclude directives with async template containing sync template', inject(function($compile, $rootScope) { - element = $compile('
transcluded content
')($rootScope); - $rootScope.$digest(); - expect(element.text()).toEqual('transcluded content'); - })); + $compileProvider.directive('asyncSync', valueFn({ + templateUrl: 'asyncSync', + transclude: true + })); - it('should allow nested transclude directives with async template containing asynch template', inject(function($compile, $rootScope) { - element = $compile('
transcluded content
')($rootScope); - $rootScope.$digest(); - expect(element.text()).toEqual('transcluded content'); - })); + $compileProvider.directive('asyncAsync', valueFn({ + templateUrl: 'asyncAsync', + transclude: true + })); + })); - it('should not leak memory with nested transclusion', function() { - inject(function($compile, $rootScope) { - var size, initialSize = jqLiteCacheSize(); + beforeEach(inject(function($templateCache) { + $templateCache.put('async', '
'); + $templateCache.put('asyncSync', '
'); + $templateCache.put('asyncAsync', '
'); + })); - element = jqLite('
  • {{n}} => EvenOdd
'); - $compile(element)($rootScope.$new()); - $rootScope.nums = [0,1,2]; - $rootScope.$apply(); - size = jqLiteCacheSize(); + it('should allow nested transclude directives with sync template containing sync template', inject(function($compile, $rootScope) { + element = $compile('
transcluded content
')($rootScope); + $rootScope.$digest(); + expect(element.text()).toEqual('transcluded content'); + })); - $rootScope.nums = [3,4,5]; - $rootScope.$apply(); - expect(jqLiteCacheSize()).toEqual(size); + it('should allow nested transclude directives with sync template containing async template', inject(function($compile, $rootScope) { + element = $compile('
transcluded content
')($rootScope); + $rootScope.$digest(); + expect(element.text()).toEqual('transcluded content'); + })); - element.remove(); - expect(jqLiteCacheSize()).toEqual(initialSize); - }); - }); - }); + it('should allow nested transclude directives with async template containing sync template', inject(function($compile, $rootScope) { + element = $compile('
transcluded content
')($rootScope); + $rootScope.$digest(); + expect(element.text()).toEqual('transcluded content'); + })); + it('should allow nested transclude directives with async template containing asynch template', inject(function($compile, $rootScope) { + element = $compile('
transcluded content
')($rootScope); + $rootScope.$digest(); + expect(element.text()).toEqual('transcluded content'); + })); - describe('nested isolated scope transcludes', function() { - beforeEach(module(function($compileProvider) { - $compileProvider.directive('trans', valueFn({ - restrict: 'E', - template: '
', - transclude: true - })); + it('should not leak memory with nested transclusion', function() { + inject(function($compile, $rootScope) { + var size, initialSize = jqLiteCacheSize(); - $compileProvider.directive('transAsync', valueFn({ - restrict: 'E', - templateUrl: 'transAsync', - transclude: true - })); + element = jqLite('
  • {{n}} => EvenOdd
'); + $compile(element)($rootScope.$new()); - $compileProvider.directive('iso', valueFn({ - restrict: 'E', - transclude: true, - template: '', - scope: {} - })); - $compileProvider.directive('isoAsync1', valueFn({ - restrict: 'E', - transclude: true, - template: '', - scope: {} - })); - $compileProvider.directive('isoAsync2', valueFn({ - restrict: 'E', - transclude: true, - templateUrl: 'isoAsync', - scope: {} - })); - })); + $rootScope.nums = [0,1,2]; + $rootScope.$apply(); + size = jqLiteCacheSize(); - beforeEach(inject(function($templateCache) { - $templateCache.put('transAsync', '
'); - $templateCache.put('isoAsync', ''); - })); + $rootScope.nums = [3,4,5]; + $rootScope.$apply(); + expect(jqLiteCacheSize()).toEqual(size); + + element.remove(); + expect(jqLiteCacheSize()).toEqual(initialSize); + }); + }); + }); - it('should pass the outer scope to the transclude on the isolated template sync-sync', inject(function($compile, $rootScope) { + describe('nested isolated scope transcludes', function() { + beforeEach(module(function($compileProvider) { - $rootScope.val = 'transcluded content'; - element = $compile('')($rootScope); - $rootScope.$digest(); - expect(element.text()).toEqual('transcluded content'); - })); + $compileProvider.directive('trans', valueFn({ + restrict: 'E', + template: '
', + transclude: true + })); - it('should pass the outer scope to the transclude on the isolated template async-sync', inject(function($compile, $rootScope) { + $compileProvider.directive('transAsync', valueFn({ + restrict: 'E', + templateUrl: 'transAsync', + transclude: true + })); - $rootScope.val = 'transcluded content'; - element = $compile('')($rootScope); - $rootScope.$digest(); - expect(element.text()).toEqual('transcluded content'); - })); + $compileProvider.directive('iso', valueFn({ + restrict: 'E', + transclude: true, + template: '', + scope: {} + })); + $compileProvider.directive('isoAsync1', valueFn({ + restrict: 'E', + transclude: true, + template: '', + scope: {} + })); + $compileProvider.directive('isoAsync2', valueFn({ + restrict: 'E', + transclude: true, + templateUrl: 'isoAsync', + scope: {} + })); + })); - it('should pass the outer scope to the transclude on the isolated template async-async', inject(function($compile, $rootScope) { + beforeEach(inject(function($templateCache) { + $templateCache.put('transAsync', '
'); + $templateCache.put('isoAsync', ''); + })); - $rootScope.val = 'transcluded content'; - element = $compile('')($rootScope); - $rootScope.$digest(); - expect(element.text()).toEqual('transcluded content'); - })); - }); + it('should pass the outer scope to the transclude on the isolated template sync-sync', inject(function($compile, $rootScope) { - describe('multiple siblings receiving transclusion', function() { + $rootScope.val = 'transcluded content'; + element = $compile('')($rootScope); + $rootScope.$digest(); + expect(element.text()).toEqual('transcluded content'); + })); - it('should only receive transclude from parent', function() { + it('should pass the outer scope to the transclude on the isolated template async-sync', inject(function($compile, $rootScope) { - module(function($compileProvider) { + $rootScope.val = 'transcluded content'; + element = $compile('')($rootScope); + $rootScope.$digest(); + expect(element.text()).toEqual('transcluded content'); + })); - $compileProvider.directive('myExample', valueFn({ - scope: {}, - link: function link(scope, element, attrs) { - var foo = element[0].querySelector('.foo'); - scope.children = angular.element(foo).children().length; - }, - template: '
' + - '
myExample {{children}}!
' + - '
has children
' + - '
' + - '
', - transclude: true + it('should pass the outer scope to the transclude on the isolated template async-async', inject(function($compile, $rootScope) { + $rootScope.val = 'transcluded content'; + element = $compile('')($rootScope); + $rootScope.$digest(); + expect(element.text()).toEqual('transcluded content'); })); }); - inject(function($compile, $rootScope) { - var element = $compile('
')($rootScope); - $rootScope.$digest(); - expect(element.text()).toEqual('myExample 0!'); - dealoc(element); + describe('multiple siblings receiving transclusion', function() { - element = $compile('

')($rootScope); - $rootScope.$digest(); - expect(element.text()).toEqual('myExample 1!has children'); - dealoc(element); + it('should only receive transclude from parent', function() { + + module(function($compileProvider) { + + $compileProvider.directive('myExample', valueFn({ + scope: {}, + link: function link(scope, element, attrs) { + var foo = element[0].querySelector('.foo'); + scope.children = angular.element(foo).children().length; + }, + template: '
' + + '
myExample {{children}}!
' + + '
has children
' + + '
' + + '
', + transclude: true + + })); + + }); + + inject(function($compile, $rootScope) { + var element = $compile('
')($rootScope); + $rootScope.$digest(); + expect(element.text()).toEqual('myExample 0!'); + dealoc(element); + + element = $compile('

')($rootScope); + $rootScope.$digest(); + expect(element.text()).toEqual('myExample 1!has children'); + dealoc(element); + }); + }); }); }); - }); - }); - describe('element transclusion', function() { + describe('element transclusion', function() { - it('should support basic element transclusion', function() { - module(function() { - directive('trans', function(log) { - return { - transclude: 'element', - priority: 2, - controller: function($transclude) { this.$transclude = $transclude; }, - compile: function(element, attrs, template) { - log('compile: ' + angular.mock.dump(element)); - return function(scope, element, attrs, ctrl) { - log('link'); - var cursor = element; - template(scope.$new(), function(clone) {cursor.after(cursor = clone);}); - ctrl.$transclude(function(clone) {cursor.after(clone);}); + it('should support basic element transclusion', function() { + module(function() { + directive('trans', function(log) { + return { + transclude: 'element', + priority: 2, + controller: function($transclude) { this.$transclude = $transclude; }, + compile: function(element, attrs, template) { + log('compile: ' + angular.mock.dump(element)); + return function(scope, element, attrs, ctrl) { + log('link'); + var cursor = element; + template(scope.$new(), function(clone) {cursor.after(cursor = clone);}); + ctrl.$transclude(function(clone) {cursor.after(clone);}); + }; + } }; - } - }; + }); + }); + inject(function(log, $rootScope, $compile) { + element = $compile('
{{$parent.$id}}-{{$id}};
')($rootScope); + $rootScope.$apply(); + expect(log).toEqual('compile: ; link; LOG; LOG; HIGH'); + expect(element.text()).toEqual('1-2;1-3;'); + }); }); - }); - inject(function(log, $rootScope, $compile) { - element = $compile('
{{$parent.$id}}-{{$id}};
')($rootScope); - $rootScope.$apply(); - expect(log).toEqual('compile: ; link; LOG; LOG; HIGH'); - expect(element.text()).toEqual('1-2;1-3;'); - }); - }); - it('should only allow one element transclusion per element', function() { - module(function() { - directive('first', valueFn({ - transclude: 'element' - })); - directive('second', valueFn({ - transclude: 'element' - })); - }); - inject(function($compile) { - expect(function() { - $compile('
'); - }).toThrowMinErr('$compile', 'multidir', 'Multiple directives [first, second] asking for transclusion on: ' + - ''); - }); - }); + it('should only allow one element transclusion per element', function() { + module(function() { + directive('first', valueFn({ + transclude: 'element' + })); + directive('second', valueFn({ + transclude: 'element' + })); + }); + inject(function($compile) { + expect(function() { + $compile('
'); + }).toThrowMinErr('$compile', 'multidir', 'Multiple directives [first, second] asking for transclusion on: ' + + ''); + }); + }); - it('should only allow one element transclusion per element when directives have different priorities', function() { - // we restart compilation in this case and we need to remember the duplicates during the second compile - // regression #3893 - module(function() { - directive('first', valueFn({ - transclude: 'element', - priority: 100 - })); - directive('second', valueFn({ - transclude: 'element' - })); - }); - inject(function($compile) { - expect(function() { - $compile('
'); - }).toThrowMinErr('$compile', 'multidir', /Multiple directives \[first, second\] asking for transclusion on:
'); + }).toThrowMinErr('$compile', 'multidir', /Multiple directives \[first, second\] asking for transclusion on:
template.html

'); - $compile('
'); - expect(function() { - $httpBackend.flush(); - }).toThrowMinErr('$compile', 'multidir', /Multiple directives \[first, second\] asking for transclusion on:

template.html

'); + $compile('
'); + expect(function() { + $httpBackend.flush(); + }).toThrowMinErr('$compile', 'multidir', /Multiple directives \[first, second\] asking for transclusion on:

', - replace: true - })); - directive('first', valueFn({ - transclude: 'element', - priority: 100 - })); - directive('second', valueFn({ - transclude: 'element' - })); - }); - inject(function($compile) { - expect(function() { - $compile('
'); - }).toThrowMinErr('$compile', 'multidir', /Multiple directives \[first, second\] asking for transclusion on:

', + replace: true + })); + directive('first', valueFn({ + transclude: 'element', + priority: 100 + })); + directive('second', valueFn({ + transclude: 'element' + })); + }); + inject(function($compile) { + expect(function() { + $compile('
'); + }).toThrowMinErr('$compile', 'multidir', /Multiple directives \[first, second\] asking for transclusion on:

before

after
').contents(); - expect(element.length).toEqual(3); - expect(nodeName_(element[1])).toBe('div'); - $compile(element)($rootScope); - expect(nodeName_(element[1])).toBe('#comment'); - expect(nodeName_(comment)).toBe('#comment'); - }); - }); + it('should support transcluded element on root content', function() { + var comment; + module(function() { + directive('transclude', valueFn({ + transclude: 'element', + compile: function(element, attr, linker) { + return function(scope, element, attr) { + comment = element; + }; + } + })); + }); + inject(function($compile, $rootScope) { + var element = jqLite('
before
after
').contents(); + expect(element.length).toEqual(3); + expect(nodeName_(element[1])).toBe('div'); + $compile(element)($rootScope); + expect(nodeName_(element[1])).toBe('#comment'); + expect(nodeName_(comment)).toBe('#comment'); + }); + }); - it('should terminate compilation only for element trasclusion', function() { - module(function() { - directive('elementTrans', function(log) { - return { - transclude: 'element', - priority: 50, - compile: log.fn('compile:elementTrans') - }; - }); - directive('regularTrans', function(log) { - return { - transclude: true, - priority: 50, - compile: log.fn('compile:regularTrans') - }; + it('should terminate compilation only for element trasclusion', function() { + module(function() { + directive('elementTrans', function(log) { + return { + transclude: 'element', + priority: 50, + compile: log.fn('compile:elementTrans') + }; + }); + directive('regularTrans', function(log) { + return { + transclude: true, + priority: 50, + compile: log.fn('compile:regularTrans') + }; + }); + }); + inject(function(log, $compile, $rootScope) { + $compile('
')($rootScope); + expect(log).toEqual('compile:elementTrans; compile:regularTrans; regular'); + }); }); - }); - inject(function(log, $compile, $rootScope) { - $compile('
')($rootScope); - expect(log).toEqual('compile:elementTrans; compile:regularTrans; regular'); - }); - }); - it('should instantiate high priority controllers only once, but low priority ones each time we transclude', - function() { - module(function() { - directive('elementTrans', function(log) { - return { - transclude: 'element', - priority: 50, - controller: function($transclude, $element) { - log('controller:elementTrans'); - $transclude(function(clone) { - $element.after(clone); - }); - $transclude(function(clone) { - $element.after(clone); - }); - $transclude(function(clone) { - $element.after(clone); - }); - } - }; + it('should instantiate high priority controllers only once, but low priority ones each time we transclude', + function() { + module(function() { + directive('elementTrans', function(log) { + return { + transclude: 'element', + priority: 50, + controller: function($transclude, $element) { + log('controller:elementTrans'); + $transclude(function(clone) { + $element.after(clone); + }); + $transclude(function(clone) { + $element.after(clone); + }); + $transclude(function(clone) { + $element.after(clone); + }); + } + }; + }); + directive('normalDir', function(log) { + return { + controller: function() { + log('controller:normalDir'); + } + }; + }); + }); + inject(function($compile, $rootScope, log) { + element = $compile('
')($rootScope); + expect(log).toEqual([ + 'controller:elementTrans', + 'controller:normalDir', + 'controller:normalDir', + 'controller:normalDir' + ]); + }); }); - directive('normalDir', function(log) { - return { - controller: function() { - log('controller:normalDir'); - } - }; + + it('should allow to access $transclude in the same directive', function() { + var _$transclude; + module(function() { + directive('transclude', valueFn({ + transclude: 'element', + controller: function($transclude) { + _$transclude = $transclude; + } + })); + }); + inject(function($compile) { + element = $compile('
')($rootScope); + expect(_$transclude).toBeDefined(); + }); }); - }); - inject(function($compile, $rootScope, log) { - element = $compile('
')($rootScope); - expect(log).toEqual([ - 'controller:elementTrans', - 'controller:normalDir', - 'controller:normalDir', - 'controller:normalDir' - ]); - }); - }); - it('should allow to access $transclude in the same directive', function() { - var _$transclude; - module(function() { - directive('transclude', valueFn({ - transclude: 'element', - controller: function($transclude) { - _$transclude = $transclude; - } - })); - }); - inject(function($compile) { - element = $compile('
')($rootScope); - expect(_$transclude).toBeDefined(); - }); - }); + it('should copy the directive controller to all clones', function() { + var transcludeCtrl, cloneCount = 2; + module(function() { + directive('transclude', valueFn({ + transclude: 'element', + controller: function() { + transcludeCtrl = this; + }, + link: function(scope, el, attr, ctrl, $transclude) { + var i; + for (i = 0; i < cloneCount; i++) { + $transclude(cloneAttach); + } - it('should copy the directive controller to all clones', function() { - var transcludeCtrl, cloneCount = 2; - module(function() { - directive('transclude', valueFn({ - transclude: 'element', - controller: function() { - transcludeCtrl = this; - }, - link: function(scope, el, attr, ctrl, $transclude) { - var i; + function cloneAttach(clone) { + el.after(clone); + } + } + })); + }); + inject(function($compile) { + element = $compile('
')($rootScope); + var children = element.children(), i; for (i = 0; i < cloneCount; i++) { - $transclude(cloneAttach); + expect(children.eq(i).data('$transcludeController')).toBe(transcludeCtrl); } + }); + }); - function cloneAttach(clone) { - el.after(clone); - } - } - })); - }); - inject(function($compile) { - element = $compile('
')($rootScope); - var children = element.children(), i; - for (i = 0; i < cloneCount; i++) { - expect(children.eq(i).data('$transcludeController')).toBe(transcludeCtrl); - } - }); - }); + it('should expose the directive controller to transcluded children', function() { + var capturedTranscludeCtrl; + module(function() { + directive('transclude', valueFn({ + transclude: 'element', + controller: function() { + }, + link: function(scope, element, attr, ctrl, $transclude) { + $transclude(scope, function(clone) { + element.after(clone); + }); + } + })); + directive('child', valueFn({ + require: '^transclude', + link: function(scope, element, attr, ctrl) { + capturedTranscludeCtrl = ctrl; + } + })); + }); + inject(function($compile) { + // We need to wrap the transclude directive's element in a parent element so that the + // cloned element gets deallocated/cleaned up correctly + element = $compile('
')($rootScope); + expect(capturedTranscludeCtrl).toBeTruthy(); + }); + }); - it('should expose the directive controller to transcluded children', function() { - var capturedTranscludeCtrl; - module(function() { - directive('transclude', valueFn({ - transclude: 'element', - controller: function() { - }, - link: function(scope, element, attr, ctrl, $transclude) { - $transclude(scope, function(clone) { - element.after(clone); + it('should allow access to $transclude in a templateUrl directive', function() { + var transclude; + module(function() { + directive('template', valueFn({ + templateUrl: 'template.html', + replace: true + })); + directive('transclude', valueFn({ + transclude: 'content', + controller: function($transclude) { + transclude = $transclude; + } + })); + }); + inject(function($compile, $httpBackend) { + $httpBackend.expectGET('template.html').respond('
'); + element = $compile('
')($rootScope); + $httpBackend.flush(); + expect(transclude).toBeDefined(); + }); + }); + + // issue #6006 + it('should link directive with $element as a comment node', function() { + module(function($provide) { + directive('innerAgain', function(log) { + return { + transclude: 'element', + link: function(scope, element, attr, controllers, transclude) { + log('innerAgain:' + lowercase(nodeName_(element)) + ':' + trim(element[0].data)); + transclude(scope, function(clone) { + element.parent().append(clone); + }); + } + }; + }); + directive('inner', function(log) { + return { + replace: true, + templateUrl: 'inner.html', + link: function(scope, element) { + log('inner:' + lowercase(nodeName_(element)) + ':' + trim(element[0].data)); + } + }; }); - } - })); - directive('child', valueFn({ - require: '^transclude', - link: function(scope, element, attr, ctrl) { - capturedTranscludeCtrl = ctrl; - } - })); - }); - inject(function($compile) { - // We need to wrap the transclude directive's element in a parent element so that the - // cloned element gets deallocated/cleaned up correctly - element = $compile('
')($rootScope); - expect(capturedTranscludeCtrl).toBeTruthy(); + directive('outer', function(log) { + return { + transclude: 'element', + link: function(scope, element, attrs, controllers, transclude) { + log('outer:' + lowercase(nodeName_(element)) + ':' + trim(element[0].data)); + transclude(scope, function(clone) { + element.parent().append(clone); + }); + } + }; + }); + }); + inject(function(log, $compile, $rootScope, $templateCache) { + $templateCache.put('inner.html', '

Content

'); + element = $compile('
')($rootScope); + $rootScope.$digest(); + var child = element.children(); + + expect(log.toArray()).toEqual([ + 'outer:#comment:outer:', + 'innerAgain:#comment:innerAgain:', + 'inner:#comment:innerAgain:' + ]); + expect(child.length).toBe(1); + expect(child.contents().length).toBe(2); + expect(lowercase(nodeName_(child.contents().eq(0)))).toBe('#comment'); + expect(lowercase(nodeName_(child.contents().eq(1)))).toBe('div'); + }); + }); }); - }); - it('should allow access to $transclude in a templateUrl directive', function() { - var transclude; - module(function() { - directive('template', valueFn({ - templateUrl: 'template.html', - replace: true - })); - directive('transclude', valueFn({ - transclude: 'content', - controller: function($transclude) { - transclude = $transclude; - } - })); - }); - inject(function($compile, $httpBackend) { - $httpBackend.expectGET('template.html').respond('
'); - element = $compile('
')($rootScope); - $httpBackend.flush(); - expect(transclude).toBeDefined(); - }); - }); - // issue #6006 - it('should link directive with $element as a comment node', function() { - module(function($provide) { - directive('innerAgain', function(log) { - return { - transclude: 'element', - link: function(scope, element, attr, controllers, transclude) { - log('innerAgain:' + lowercase(nodeName_(element)) + ':' + trim(element[0].data)); - transclude(scope, function(clone) { - element.parent().append(clone); - }); - } - }; - }); - directive('inner', function(log) { - return { - replace: true, - templateUrl: 'inner.html', - link: function(scope, element) { - log('inner:' + lowercase(nodeName_(element)) + ':' + trim(element[0].data)); - } - }; + it('should be possible to change the scope of a directive using $provide', function() { + module(function($provide) { + directive('foo', function() { + return { + scope: {}, + template: '
' + }; + }); + $provide.decorator('fooDirective', function($delegate) { + var directive = $delegate[0]; + directive.scope.something = '='; + directive.template = '{{something}}'; + return $delegate; + }); }); - directive('outer', function(log) { - return { - transclude: 'element', - link: function(scope, element, attrs, controllers, transclude) { - log('outer:' + lowercase(nodeName_(element)) + ':' + trim(element[0].data)); - transclude(scope, function(clone) { - element.parent().append(clone); - }); - } - }; + inject(function($compile, $rootScope) { + element = $compile('
')($rootScope); + $rootScope.bar = 'bar'; + $rootScope.$digest(); + expect(element.text()).toBe('bar'); }); }); - inject(function(log, $compile, $rootScope, $templateCache) { - $templateCache.put('inner.html', '

Content

'); - element = $compile('
')($rootScope); - $rootScope.$digest(); - var child = element.children(); - - expect(log.toArray()).toEqual([ - 'outer:#comment:outer:', - 'innerAgain:#comment:innerAgain:', - 'inner:#comment:innerAgain:' - ]); - expect(child.length).toBe(1); - expect(child.contents().length).toBe(2); - expect(lowercase(nodeName_(child.contents().eq(0)))).toBe('#comment'); - expect(lowercase(nodeName_(child.contents().eq(1)))).toBe('div'); - }); - }); - }); - it('should be possible to change the scope of a directive using $provide', function() { - module(function($provide) { - directive('foo', function() { - return { - scope: {}, - template: '
' - }; - }); - $provide.decorator('fooDirective', function($delegate) { - var directive = $delegate[0]; - directive.scope.something = '='; - directive.template = '{{something}}'; - return $delegate; + it('should distinguish different bindings with the same binding name', function() { + module(function() { + directive('foo', function() { + return { + scope: { + foo: '=', + bar: '=' + }, + template: '
{{foo}}
{{bar}}
' + }; + }); + }); + inject(function($compile, $rootScope) { + element = $compile('
')($rootScope); + $rootScope.$digest(); + expect(element.text()).toBe('foobar'); + }); }); - }); - inject(function($compile, $rootScope) { - element = $compile('
')($rootScope); - $rootScope.bar = 'bar'; - $rootScope.$digest(); - expect(element.text()).toBe('bar'); - }); - }); - - it('should distinguish different bindings with the same binding name', function() { - module(function() { - directive('foo', function() { - return { - scope: { - foo: '=', - bar: '=' - }, - template: '
{{foo}}
{{bar}}
' - }; - }); - }); - inject(function($compile, $rootScope) { - element = $compile('
')($rootScope); - $rootScope.$digest(); - expect(element.text()).toBe('foobar'); - }); - }); + it('should safely create transclude comment node and not break with "-->"', + inject(function($rootScope) { + // see: https://github.com/angular/angular.js/issues/1740 + element = $compile('
  • {{item}}|
')($rootScope); + $rootScope.$digest(); - it('should safely create transclude comment node and not break with "-->"', - inject(function($rootScope) { - // see: https://github.com/angular/angular.js/issues/1740 - element = $compile('
  • {{item}}|
')($rootScope); - $rootScope.$digest(); + expect(element.text()).toBe('-->|x|'); + })); - expect(element.text()).toBe('-->|x|'); - })); + describe('lazy compilation', function() { + // See https://github.com/angular/angular.js/issues/7183 + it('should pass transclusion through to template of a \'replace\' directive', function() { + module(function() { + directive('transSync', function() { + return { + transclude: true, + link: function(scope, element, attr, ctrl, transclude) { - describe('lazy compilation', function() { - // See https://github.com/angular/angular.js/issues/7183 - it('should pass transclusion through to template of a \'replace\' directive', function() { - module(function() { - directive('transSync', function() { - return { - transclude: true, - link: function(scope, element, attr, ctrl, transclude) { + expect(transclude).toEqual(jasmine.any(Function)); - expect(transclude).toEqual(jasmine.any(Function)); + transclude(function(child) { element.append(child); }); + } + }; + }); - transclude(function(child) { element.append(child); }); - } - }; - }); + directive('trans', function($timeout) { + return { + transclude: true, + link: function(scope, element, attrs, ctrl, transclude) { - directive('trans', function($timeout) { - return { - transclude: true, - link: function(scope, element, attrs, ctrl, transclude) { + // We use timeout here to simulate how ng-if works + $timeout(function() { + transclude(function(child) { element.append(child); }); + }); + } + }; + }); - // We use timeout here to simulate how ng-if works - $timeout(function() { - transclude(function(child) { element.append(child); }); - }); - } - }; - }); + directive('replaceWithTemplate', function() { + return { + templateUrl: 'template.html', + replace: true + }; + }); + }); - directive('replaceWithTemplate', function() { - return { - templateUrl: 'template.html', - replace: true - }; - }); - }); + inject(function($compile, $rootScope, $templateCache, $timeout) { - inject(function($compile, $rootScope, $templateCache, $timeout) { + $templateCache.put('template.html', '
Content To Be Transcluded
'); - $templateCache.put('template.html', '
Content To Be Transcluded
'); + expect(function() { + element = $compile('
')($rootScope); + $timeout.flush(); + }).not.toThrow(); - expect(function() { - element = $compile('
')($rootScope); - $timeout.flush(); - }).not.toThrow(); + expect(element.text()).toEqual('Content To Be Transcluded'); + }); - expect(element.text()).toEqual('Content To Be Transcluded'); - }); + }); - }); + it('should lazily compile the contents of directives that are transcluded', function() { + var innerCompilationCount = 0, transclude; - it('should lazily compile the contents of directives that are transcluded', function() { - var innerCompilationCount = 0, transclude; + module(function() { + directive('trans', valueFn({ + transclude: true, + controller: function($transclude) { + transclude = $transclude; + } + })); - module(function() { - directive('trans', valueFn({ - transclude: true, - controller: function($transclude) { - transclude = $transclude; - } - })); + directive('inner', valueFn({ + template: 'FooBar', + compile: function() { + innerCompilationCount += 1; + } + })); + }); - directive('inner', valueFn({ - template: 'FooBar', - compile: function() { - innerCompilationCount += 1; - } - })); - }); + inject(function($compile, $rootScope) { + element = $compile('')($rootScope); + expect(innerCompilationCount).toBe(0); + transclude(function(child) { element.append(child); }); + expect(innerCompilationCount).toBe(1); + expect(element.text()).toBe('FooBar'); + }); + }); - inject(function($compile, $rootScope) { - element = $compile('')($rootScope); - expect(innerCompilationCount).toBe(0); - transclude(function(child) { element.append(child); }); - expect(innerCompilationCount).toBe(1); - expect(element.text()).toBe('FooBar'); - }); - }); + it('should lazily compile the contents of directives that are transcluded with a template', function() { + var innerCompilationCount = 0, transclude; - it('should lazily compile the contents of directives that are transcluded with a template', function() { - var innerCompilationCount = 0, transclude; + module(function() { + directive('trans', valueFn({ + transclude: true, + template: '
Baz
', + controller: function($transclude) { + transclude = $transclude; + } + })); - module(function() { - directive('trans', valueFn({ - transclude: true, - template: '
Baz
', - controller: function($transclude) { - transclude = $transclude; - } - })); + directive('inner', valueFn({ + template: 'FooBar', + compile: function() { + innerCompilationCount += 1; + } + })); + }); - directive('inner', valueFn({ - template: 'FooBar', - compile: function() { - innerCompilationCount += 1; - } - })); - }); + inject(function($compile, $rootScope) { + element = $compile('')($rootScope); + expect(innerCompilationCount).toBe(0); + transclude(function(child) { element.append(child); }); + expect(innerCompilationCount).toBe(1); + expect(element.text()).toBe('BazFooBar'); + }); + }); - inject(function($compile, $rootScope) { - element = $compile('')($rootScope); - expect(innerCompilationCount).toBe(0); - transclude(function(child) { element.append(child); }); - expect(innerCompilationCount).toBe(1); - expect(element.text()).toBe('BazFooBar'); - }); - }); + it('should lazily compile the contents of directives that are transcluded with a templateUrl', function() { + var innerCompilationCount = 0, transclude; - it('should lazily compile the contents of directives that are transcluded with a templateUrl', function() { - var innerCompilationCount = 0, transclude; + module(function() { + directive('trans', valueFn({ + transclude: true, + templateUrl: 'baz.html', + controller: function($transclude) { + transclude = $transclude; + } + })); - module(function() { - directive('trans', valueFn({ - transclude: true, - templateUrl: 'baz.html', - controller: function($transclude) { - transclude = $transclude; - } - })); + directive('inner', valueFn({ + template: 'FooBar', + compile: function() { + innerCompilationCount += 1; + } + })); + }); - directive('inner', valueFn({ - template: 'FooBar', - compile: function() { - innerCompilationCount += 1; - } - })); - }); + inject(function($compile, $rootScope, $httpBackend) { + $httpBackend.expectGET('baz.html').respond('
Baz
'); + element = $compile('')($rootScope); + $httpBackend.flush(); - inject(function($compile, $rootScope, $httpBackend) { - $httpBackend.expectGET('baz.html').respond('
Baz
'); - element = $compile('')($rootScope); - $httpBackend.flush(); + expect(innerCompilationCount).toBe(0); + transclude(function(child) { element.append(child); }); + expect(innerCompilationCount).toBe(1); + expect(element.text()).toBe('BazFooBar'); + }); + }); - expect(innerCompilationCount).toBe(0); - transclude(function(child) { element.append(child); }); - expect(innerCompilationCount).toBe(1); - expect(element.text()).toBe('BazFooBar'); - }); - }); + it('should lazily compile the contents of directives that are transclude element', function() { + var innerCompilationCount = 0, transclude; - it('should lazily compile the contents of directives that are transclude element', function() { - var innerCompilationCount = 0, transclude; + module(function() { + directive('trans', valueFn({ + transclude: 'element', + controller: function($transclude) { + transclude = $transclude; + } + })); - module(function() { - directive('trans', valueFn({ - transclude: 'element', - controller: function($transclude) { - transclude = $transclude; - } - })); + directive('inner', valueFn({ + template: 'FooBar', + compile: function() { + innerCompilationCount += 1; + } + })); + }); - directive('inner', valueFn({ - template: 'FooBar', - compile: function() { - innerCompilationCount += 1; - } - })); - }); + inject(function($compile, $rootScope) { + element = $compile('
')($rootScope); + expect(innerCompilationCount).toBe(0); + transclude(function(child) { element.append(child); }); + expect(innerCompilationCount).toBe(1); + expect(element.text()).toBe('FooBar'); + }); + }); - inject(function($compile, $rootScope) { - element = $compile('
')($rootScope); - expect(innerCompilationCount).toBe(0); - transclude(function(child) { element.append(child); }); - expect(innerCompilationCount).toBe(1); - expect(element.text()).toBe('FooBar'); - }); - }); + it('should lazily compile transcluded directives with ngIf on them', function() { + var innerCompilationCount = 0, outerCompilationCount = 0, transclude; - it('should lazily compile transcluded directives with ngIf on them', function() { - var innerCompilationCount = 0, outerCompilationCount = 0, transclude; + module(function() { + directive('outer', valueFn({ + transclude: true, + compile: function() { + outerCompilationCount += 1; + }, + controller: function($transclude) { + transclude = $transclude; + } + })); - module(function() { - directive('outer', valueFn({ - transclude: true, - compile: function() { - outerCompilationCount += 1; - }, - controller: function($transclude) { - transclude = $transclude; - } - })); + directive('inner', valueFn({ + template: 'FooBar', + compile: function() { + innerCompilationCount += 1; + } + })); + }); - directive('inner', valueFn({ - template: 'FooBar', - compile: function() { - innerCompilationCount += 1; - } - })); - }); + inject(function($compile, $rootScope) { + $rootScope.shouldCompile = false; + + element = $compile('
')($rootScope); + expect(outerCompilationCount).toBe(0); + expect(innerCompilationCount).toBe(0); + expect(transclude).toBeUndefined(); + $rootScope.$apply('shouldCompile=true'); + expect(outerCompilationCount).toBe(1); + expect(innerCompilationCount).toBe(0); + expect(transclude).toBeDefined(); + transclude(function(child) { element.append(child); }); + expect(outerCompilationCount).toBe(1); + expect(innerCompilationCount).toBe(1); + expect(element.text()).toBe('FooBar'); + }); + }); - inject(function($compile, $rootScope) { - $rootScope.shouldCompile = false; + it('should eagerly compile multiple directives with transclusion and templateUrl/replace', function() { + var innerCompilationCount = 0; - element = $compile('
')($rootScope); - expect(outerCompilationCount).toBe(0); - expect(innerCompilationCount).toBe(0); - expect(transclude).toBeUndefined(); - $rootScope.$apply('shouldCompile=true'); - expect(outerCompilationCount).toBe(1); - expect(innerCompilationCount).toBe(0); - expect(transclude).toBeDefined(); - transclude(function(child) { element.append(child); }); - expect(outerCompilationCount).toBe(1); - expect(innerCompilationCount).toBe(1); - expect(element.text()).toBe('FooBar'); - }); - }); + module(function() { + directive('outer', valueFn({ + transclude: true + })); - it('should eagerly compile multiple directives with transclusion and templateUrl/replace', function() { - var innerCompilationCount = 0; + directive('outer', valueFn({ + templateUrl: 'inner.html', + replace: true + })); - module(function() { - directive('outer', valueFn({ - transclude: true - })); + directive('inner', valueFn({ + compile: function() { + innerCompilationCount += 1; + } + })); + }); - directive('outer', valueFn({ - templateUrl: 'inner.html', - replace: true - })); + inject(function($compile, $rootScope, $httpBackend) { + $httpBackend.expectGET('inner.html').respond(''); + element = $compile('')($rootScope); + $httpBackend.flush(); - directive('inner', valueFn({ - compile: function() { - innerCompilationCount += 1; - } - })); + expect(innerCompilationCount).toBe(1); + }); + }); }); - inject(function($compile, $rootScope, $httpBackend) { - $httpBackend.expectGET('inner.html').respond(''); - element = $compile('')($rootScope); - $httpBackend.flush(); - - expect(innerCompilationCount).toBe(1); - }); }); }); - }); - describe('multi-slot transclude', function() { it('should only include elements without a matching transclusion element in default transclusion slot', function() { module(function() { diff --git a/test/ng/directive/formSpec.js b/test/ng/directive/formSpec.js index f878c4826070..e1d681956b23 100644 --- a/test/ng/directive/formSpec.js +++ b/test/ng/directive/formSpec.js @@ -290,7 +290,7 @@ describe('form', function() { describe('triggering commit value on submit', function() { it('should trigger update on form submit', function() { var form = $compile( - '
' + + '' + '' + '
')(scope); scope.$digest(); @@ -305,7 +305,7 @@ describe('form', function() { it('should trigger update on form submit with nested forms', function() { var form = $compile( - '
' + + '' + '
' + '' + '
' + @@ -323,7 +323,7 @@ describe('form', function() { it('should trigger update before ng-submit is invoked', function() { var form = $compile( '' + + 'ng-model-options="{ updateOn: \'submit\' }" >' + '' + '
')(scope); scope.$digest(); @@ -342,7 +342,7 @@ describe('form', function() { describe('rollback view value', function() { it('should trigger rollback on form controls', function() { var form = $compile( - '
' + + '' + '' + '
c').toEqual('a
b
c'); }); + it('should handle large datasets', function() { + // Large is non-trivial to quantify, but handling ~100,000 should be sufficient for most purposes. + var largeNumber = 17; // 2^17 = 131,072 + var result = '
b
'; + // Ideally we would use repeat, but that isn't supported in IE. + for (var i = 0; i < largeNumber; i++) { + result += result; + } + expectHTML('a' + result + 'c').toEqual('a' + result + 'c'); + }); + it('should remove style', function() { expectHTML('ac.').toEqual('ac.'); });