diff --git a/CHANGELOG.md b/CHANGELOG.md index 76115091ad74..4e462be949f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,105 +1,142 @@ - -# 1.4.3 foam-acceleration (2015-07-06) + +# 1.4.4 pylon-requirement (2015-08-13) ## Bug Fixes -- **$animateCss:** ensure animations execute if only a keyframeStyle is provided - ([97d79eec](https://github.com/angular/angular.js/commit/97d79eec80092f5fae3336c23aa881a72436de55), - [#12124](https://github.com/angular/angular.js/issues/12124), [#12340](https://github.com/angular/angular.js/issues/12340)) -- **$browser:** prevent infinite digest if changing hash when there is no hashPrefix - ([f81ff3be](https://github.com/angular/angular.js/commit/f81ff3beb0c9d19d494c5878086fb57476442b8b), - [#10423](https://github.com/angular/angular.js/issues/10423), [#12145](https://github.com/angular/angular.js/issues/12145)) +- **$animate:** + - leave animation callback should not overridden by follow-up animation + ([92e41ac9](https://github.com/angular/angular.js/commit/92e41ac904b7d16e96fd31a49ac2ae15d606a665), + [#12271](https://github.com/angular/angular.js/issues/12271), [#12249](https://github.com/angular/angular.js/issues/12249), [#12161](https://github.com/angular/angular.js/issues/12161)) + - make sure to run a post-digest reflow for parentless animations + ([861636c6](https://github.com/angular/angular.js/commit/861636c62542252a54fb2d2fa8ea9e17eefee120), + [#12400](https://github.com/angular/angular.js/issues/12400), [#12401](https://github.com/angular/angular.js/issues/12401)) + - ensure that class-based animations are properly applied when cancelled + ([21d6db38](https://github.com/angular/angular.js/commit/21d6db382d8f3540fb0bb7280570fba8d88d9843), + [#12266](https://github.com/angular/angular.js/issues/12266), [#12007](https://github.com/angular/angular.js/issues/12007)) +- **$animateCss:** make sure that `skipBlocking` avoids the pre-emptive transition-delay styling + ([11695ca6](https://github.com/angular/angular.js/commit/11695ca6e2ce5b21bb944ee0de80892203155cbb)) - **$compile:** - - throw error when requestng new and isolate scopes (async) - ([6333d65b](https://github.com/angular/angular.js/commit/6333d65b76e0796cfbab8a2953af0c8014dba2e1), - [#12215](https://github.com/angular/angular.js/issues/12215), [#12217](https://github.com/angular/angular.js/issues/12217)) - - do not write @-bound properties if attribute is not present - ([8a1eb162](https://github.com/angular/angular.js/commit/8a1eb1625c080445ce1e519762e1f2d4fd842b72), - [#12151](https://github.com/angular/angular.js/issues/12151), [#12144](https://github.com/angular/angular.js/issues/12144)) - - workaround for IE11 MutationObserver - ([f3b1d0b7](https://github.com/angular/angular.js/commit/f3b1d0b723298a5f8ea21d0704405649cce1b5fc), - [#11781](https://github.com/angular/angular.js/issues/11781)) - - exception when using "watch" as isolated scope binding variable in Firefox - ([a6339d30](https://github.com/angular/angular.js/commit/a6339d30d1379689da5eec9647a953f64821f8b0), - [#11627](https://github.com/angular/angular.js/issues/11627)) -- **$location:** - - allow navigating outside the original base URL - ([6903b5ec](https://github.com/angular/angular.js/commit/6903b5ec4c04ed6b7c80ef7d638c48639ccdc4bb), - [#11302](https://github.com/angular/angular.js/issues/11302), [#4776](https://github.com/angular/angular.js/issues/4776)) - - do not get caught in infinite digest in IE9 - ([91b60226](https://github.com/angular/angular.js/commit/91b602263b96b6fce1331208462e18eb647f4d60), - [#11439](https://github.com/angular/angular.js/issues/11439), [#11675](https://github.com/angular/angular.js/issues/11675), [#11935](https://github.com/angular/angular.js/issues/11935), [#12083](https://github.com/angular/angular.js/issues/12083)) -- **$parse:** - - set null reference properties to `undefined` - ([71fc3f4f](https://github.com/angular/angular.js/commit/71fc3f4fa0cd12eff335d57efed7c033554749f4), - [#12099](https://github.com/angular/angular.js/issues/12099)) - - set null reference properties to `undefined` - ([d19504a1](https://github.com/angular/angular.js/commit/d19504a179355d7801d59a8db0285a1322e04601), - [#11959](https://github.com/angular/angular.js/issues/11959)) -- **$sanitize:** dont not remove tab index property - ([799353c7](https://github.com/angular/angular.js/commit/799353c75de28e6fbf52dac6e0721e85b578575a), - [#8371](https://github.com/angular/angular.js/issues/8371), [#5853](https://github.com/angular/angular.js/issues/5853)) -- **compile:** assign ctrl return values correctly for multiple directives - ([8caf1802](https://github.com/angular/angular.js/commit/8caf1802e0e93389dec626ef35e04a302aa6c39d), - [#12029](https://github.com/angular/angular.js/issues/12029), [#12036](https://github.com/angular/angular.js/issues/12036)) -- **copy:** do not copy the same object twice - ([0e622f7b](https://github.com/angular/angular.js/commit/0e622f7b5bc3d5d0ab0fbc1a1bc69404bd7216d5)) -- **forms:** parse exponential notation in numberInputType parser - ([ebd0fbba](https://github.com/angular/angular.js/commit/ebd0fbba8ff90bee0cd016d574643d56a7f81ed0), - [#12121](https://github.com/angular/angular.js/issues/12121), [#12122](https://github.com/angular/angular.js/issues/12122)) -- **linky:** allow case insensitive scheme detection - ([8dc09e6d](https://github.com/angular/angular.js/commit/8dc09e6dabb84c2c611cdc9e40adfac989648200), - [#12073](https://github.com/angular/angular.js/issues/12073), [#12073](https://github.com/angular/angular.js/issues/12073)) -- **loader:** define isFunction - ([9ea52d81](https://github.com/angular/angular.js/commit/9ea52d818bcd2fb3ea8ccc85bf47f9fd5af68843)) -- **merge:** treat dates as atomic values instead of objects. - ([6cbbd966](https://github.com/angular/angular.js/commit/6cbbd966479448591f819cbf904e0a3b757613dc), - [#11720](https://github.com/angular/angular.js/issues/11720), [#11720](https://github.com/angular/angular.js/issues/11720)) -- **ngAnimate:** ensure that orphaned elements do not throw errors when animated - ([e4aeae0c](https://github.com/angular/angular.js/commit/e4aeae0c7303b94135e6df20e6c5e25f2aa0f586), - [#11975](https://github.com/angular/angular.js/issues/11975), [#12338](https://github.com/angular/angular.js/issues/12338)) -- **ngAria:** - - update `aria-valuemin/max` when `min/max` change - ([ebaa0f59](https://github.com/angular/angular.js/commit/ebaa0f598501702ae64d59ada0ae492eaf0e2db6), - [#11770](https://github.com/angular/angular.js/issues/11770), [#11774](https://github.com/angular/angular.js/issues/11774)) - - ensure boolean values for aria-hidden and aria-disabled - ([59273354](https://github.com/angular/angular.js/commit/59273354b57dd8d1ad2cd2f4740ffa8923e480f9), - [#11365](https://github.com/angular/angular.js/issues/11365)) -- **ngModel:** form validation when there is an Object.prototype enumerable value - ([0934b76b](https://github.com/angular/angular.js/commit/0934b76b72cec86093414834ac4cb7f0946b651d), - [#12066](https://github.com/angular/angular.js/issues/12066)) -- **ngOptions:** - - only watch numeric properties of an array - ([14638f4a](https://github.com/angular/angular.js/commit/14638f4a60053b085565e597fc74bd31cf0d372b)) - - do not watch properties starting with $ - ([34a6da24](https://github.com/angular/angular.js/commit/34a6da24c17356d4ffc70aec3f621a140a9a61ab), - [#11930](https://github.com/angular/angular.js/issues/11930), [#12010](https://github.com/angular/angular.js/issues/12010)) - - use reference check only when not using trackBy - ([d7dc14dc](https://github.com/angular/angular.js/commit/d7dc14dc0cdeb9c187d227e19acc8aca7df9d740), - [#11936](https://github.com/angular/angular.js/issues/11936), [#11996](https://github.com/angular/angular.js/issues/11996)) -- **orderBy:** ensure correct ordering with arrays of objects and no predicate - ([48e1f560](https://github.com/angular/angular.js/commit/48e1f5605edd32a63318fd78f5165c7d1f1a20f9), - [#11866](https://github.com/angular/angular.js/issues/11866), [#11312](https://github.com/angular/angular.js/issues/11312), [#4282](https://github.com/angular/angular.js/issues/4282)) + - don't trigger $observer if initial value is `undefined` + ([6f3b8622](https://github.com/angular/angular.js/commit/6f3b8622adce2006df5cf7eed4bf9262539004bd), + [#12383](https://github.com/angular/angular.js/issues/12383), [#12464](https://github.com/angular/angular.js/issues/12464)) + - ignore optional =-bound properties with empty value + ([533d9b76](https://github.com/angular/angular.js/commit/533d9b76704368ba9700ab08589118abca9f598c), + [#12144](https://github.com/angular/angular.js/issues/12144), [#12259](https://github.com/angular/angular.js/issues/12259), [#12290](https://github.com/angular/angular.js/issues/12290)) +- **$injector:** Allows ES6 function syntax + ([44a96a4c](https://github.com/angular/angular.js/commit/44a96a4c140873d9fd8484d870af83a0bb9acabd), + [#12424](https://github.com/angular/angular.js/issues/12424), [#12425](https://github.com/angular/angular.js/issues/12425)) +- **$location:** don't crash if navigating outside the app base + ([9e492c35](https://github.com/angular/angular.js/commit/9e492c358c19549696577c86c2c61b93f50ab356), + [#11667](https://github.com/angular/angular.js/issues/11667)) +- **$q:** Use extend to avoid overwriting prototype + ([3abb3fef](https://github.com/angular/angular.js/commit/3abb3fefe653df2a4cb730cface0049939c18efd), + [#10697](https://github.com/angular/angular.js/issues/10697)) +- **$rootScope:** don't clear phase if $apply is re-entered + ([e0cf496f](https://github.com/angular/angular.js/commit/e0cf496f3cd6835db91546438def5bca1b6db4df), + [#12174](https://github.com/angular/angular.js/issues/12174)) +- **Angular:** allow unescaped `=` signs in values in `parseKeyValue` + ([f13852c1](https://github.com/angular/angular.js/commit/f13852c179ffd9ec18b7a94df27dec39eb5f19fc), + [#12351](https://github.com/angular/angular.js/issues/12351)) +- **httpParamSerializerJQLike:** Follow jQuery for index of arrays of objects + ([18a2e4fb](https://github.com/angular/angular.js/commit/18a2e4fbfc44216c31bbcdf7705ca87c53e6f1fa)) +- **i18n:** by default put negative sign before currency symbol + ([96f2e3be](https://github.com/angular/angular.js/commit/96f2e3bef5fc310edb2f6ed1addbcb7e1c1e71c2), + [#10158](https://github.com/angular/angular.js/issues/10158)) +- **injector:** check that modulesToLoad isArray. + ([5abf593e](https://github.com/angular/angular.js/commit/5abf593e6b3535cc836c99db4018a4e2fc2dbc3b), + [#12285](https://github.com/angular/angular.js/issues/12285)) +- **input:** Firefox validation trigger + ([e7423168](https://github.com/angular/angular.js/commit/e7423168fbf439a8798fdbbffb57955c272c2d74), + [#12102](https://github.com/angular/angular.js/issues/12102)) +- **merge:** regExp should not be treated as a objects when merging. + ([a5221f32](https://github.com/angular/angular.js/commit/a5221f320a8c1644354003c0e78201add44f11e6), + [#12419](https://github.com/angular/angular.js/issues/12419), [#12409](https://github.com/angular/angular.js/issues/12409)) +- **ng/$locale:** by default put negative sign before currency symbol + ([52986724](https://github.com/angular/angular.js/commit/5298672411cd7f5870e12185845cc2e9e3fe6949), + [#10158](https://github.com/angular/angular.js/issues/10158)) +- **ngAnimate:** + - always apply a preparation reflow for CSS-based animations + ([d33cedda](https://github.com/angular/angular.js/commit/d33cedda1624114d7e97a97b79705685c6cc40a2), + [#12553](https://github.com/angular/angular.js/issues/12553), [#12554](https://github.com/angular/angular.js/issues/12554), [#12267](https://github.com/angular/angular.js/issues/12267), [#12554](https://github.com/angular/angular.js/issues/12554)) + - ensure that only string-based addClass/removeClass values are applied + ([0d6fc2dc](https://github.com/angular/angular.js/commit/0d6fc2dce57ac60dfebba6eefb571ef9afcd2189), + [#12458](https://github.com/angular/angular.js/issues/12458), [#12459](https://github.com/angular/angular.js/issues/12459)) + - ensure that parent class-based animations are never closed by their children + ([32d3cbb3](https://github.com/angular/angular.js/commit/32d3cbb3aadf71492102f9318fcac570fb60bef8), + [#11975](https://github.com/angular/angular.js/issues/11975), [#12276](https://github.com/angular/angular.js/issues/12276)) + - allow animations on body and root elements + ([44ce9c82](https://github.com/angular/angular.js/commit/44ce9c8288fc6c12043567027271a09bd0594d74), + [#11956](https://github.com/angular/angular.js/issues/11956), [#12245](https://github.com/angular/angular.js/issues/12245)) + - $timeout without invokeApply + ([7db5f361](https://github.com/angular/angular.js/commit/7db5f361b0097a79255b90b26b5d700decf22f37), + [#12281](https://github.com/angular/angular.js/issues/12281), [#12282](https://github.com/angular/angular.js/issues/12282)) +- **ngCsp:** allow CSP to be configurable + ([618356e4](https://github.com/angular/angular.js/commit/618356e481fcfeac74bfc9086332e25062fd8133), + [#11933](https://github.com/angular/angular.js/issues/11933), [#8459](https://github.com/angular/angular.js/issues/8459), [#12346](https://github.com/angular/angular.js/issues/12346)) +- **ngModel:** correct minErr usage for correct doc creation + ([a268c29f](https://github.com/angular/angular.js/commit/a268c29fb019858155dac6692f351b64d43bb61c), + [#12386](https://github.com/angular/angular.js/issues/12386), [#12416](https://github.com/angular/angular.js/issues/12416)) +- **ngOptions:** allow empty option selection with multiple attribute + ([c11a7d67](https://github.com/angular/angular.js/commit/c11a7d676f21c39916243b13eeaf47f44b40c8eb), + [#12511](https://github.com/angular/angular.js/issues/12511), [#12541](https://github.com/angular/angular.js/issues/12541)) +- **ngSanitize:** escape the wide char quote marks in a regex in linky.js + ([39ff3332](https://github.com/angular/angular.js/commit/39ff3332a31b2db09e615ecea07634708cb46d7b), + [#11609](https://github.com/angular/angular.js/issues/11609)) ## Features -- **$compile:** show module name during multidir error - ([351fe4b7](https://github.com/angular/angular.js/commit/351fe4b79c50a45a11af2fcd2aa7b6fd3b70058d), - [#11775](https://github.com/angular/angular.js/issues/11775)) -- **$q:** $q.resolve as an alias for $q.when - ([3ef52980](https://github.com/angular/angular.js/commit/3ef529806fef28b41ca4af86a330f39a95699cf6), - [#11944](https://github.com/angular/angular.js/issues/11944), [#11987](https://github.com/angular/angular.js/issues/11987)) -- **ngAria:** add option to disable role=button - ([1f5e42e8](https://github.com/angular/angular.js/commit/1f5e42e8821217026ef36a46d36f84d7cd32830a), - [#11580](https://github.com/angular/angular.js/issues/11580), [#12234](https://github.com/angular/angular.js/issues/12234)) +- **$animateCss:** expose a core version of `$animateCss` + ([39b634e5](https://github.com/angular/angular.js/commit/39b634e50a9ed140649d4be119a291debe527d55), + [#12509](https://github.com/angular/angular.js/issues/12509)) +- **$httpProvider:** add 'useLegacyPromiseExtensions' configuration + ([a8f7e9cf](https://github.com/angular/angular.js/commit/a8f7e9cfde82ed7eaba3a868d8acafdf57f2d76f), + [#12112](https://github.com/angular/angular.js/issues/12112), [#10508](https://github.com/angular/angular.js/issues/10508)) +- **orderBy:** Stable sort the input + ([ed3a33a0](https://github.com/angular/angular.js/commit/ed3a33a063f09d7ca356d15c278d95ad82e680a0), + [#12408](https://github.com/angular/angular.js/issues/12408), [#12405](https://github.com/angular/angular.js/issues/12405)) ## Performance Improvements -- **$compile:** avoid jquery data calls when there is no data - ([9efb0d5e](https://github.com/angular/angular.js/commit/9efb0d5ee961b57c8fc144a3138a15955e4010e2)) +- **$q:** small $q performance optimization + ([6838c979](https://github.com/angular/angular.js/commit/6838c979451c109d959a15035177ccee715ccf19), + [#12535](https://github.com/angular/angular.js/issues/12535)) + + +## Breaking Changes + +- **ngAnimate:** due to [32d3cbb3](https://github.com/angular/angular.js/commit/32d3cbb3aadf71492102f9318fcac570fb60bef8), + CSS classes added/removed by ngAnimate are now applied synchronously once the first digest has passed. + +The previous behavior involved ngAnimate having to wait for one +requestAnimationFrame before CSS classes were added/removed. The CSS classes +are now applied directly after the first digest that is triggered after +`$animate.addClass`, `$animate.removeClass` or `$animate.setClass` is +called. If any of your code relies on waiting for one frame before +checking for CSS classes on the element then please change this +behavior. If a parent class-based animation, however, is run through a +JavaScript animation which triggers an animation for `beforeAddClass` +and/or `beforeRemoveClass` then the CSS classes will not be applied +in time for the children (and the parent class-based animation will not +be cancelled by any child animations). + + + +# 1.4.3 foam-acceleration (2015-07-15) + + +## Bug Fixes + +- **$animateCss:** ensure animations execute if only a keyframeStyle is provided + ([97d79eec](https://github.com/angular/angular.js/commit/97d79eec80092f5fae3336c23aa881a72436de55), + [#12124](https://github.com/angular/angular.js/issues/12124), [#12340](https://github.com/angular/angular.js/issues/12340)) +- **loader:** define isFunction + ([9ea52d81](https://github.com/angular/angular.js/commit/9ea52d818bcd2fb3ea8ccc85bf47f9fd5af68843)) +- **ngAnimate:** ensure that orphaned elements do not throw errors when animated + ([e4aeae0c](https://github.com/angular/angular.js/commit/e4aeae0c7303b94135e6df20e6c5e25f2aa0f586), + [#11975](https://github.com/angular/angular.js/issues/11975), [#12338](https://github.com/angular/angular.js/issues/12338)) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 362f3fd2e126..1fba85c339d9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -202,7 +202,7 @@ format that includes a **type**, a **scope** and a **subject**: The **header** is mandatory and the **scope** of the header is optional. Any line of the commit message cannot be longer 100 characters! This allows the message to be easier -to read on github as well as in various git tools. +to read on GitHub as well as in various git tools. ### Revert If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit .`, where the hash is the SHA of the commit being reverted. @@ -215,7 +215,7 @@ Must be one of the following: * **docs**: Documentation only changes * **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) -* **refactor**: A code change that neither fixes a bug or adds a feature +* **refactor**: A code change that neither fixes a bug nor adds a feature * **perf**: A code change that improves performance * **test**: Adding missing tests * **chore**: Changes to the build process or auxiliary tools and libraries such as documentation @@ -233,7 +233,7 @@ The subject contains succinct description of the change: * no dot (.) at the end ### Body -Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes" +Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes". The body should include the motivation for the change and contrast this with previous behavior. ### Footer diff --git a/Gruntfile.js b/Gruntfile.js index db263ea911bc..53aaf59a9ad6 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -115,7 +115,7 @@ module.exports = function(grunt) { files: { src: 'test/**/*.js' }, }, ng: { - files: { src: files['angularSrc'] }, + files: { src: files['angularSrc'].concat('!src/angular.bind.js') }, }, ngAnimate: { files: { src: 'src/ngAnimate/**/*.js' }, @@ -156,7 +156,11 @@ module.exports = function(grunt) { }, jscs: { - src: ['src/**/*.js', 'test/**/*.js'], + src: [ + 'src/**/*.js', + 'test/**/*.js', + '!src/angular.bind.js' // we ignore this file since contains an early return statement + ], options: { config: ".jscsrc" } diff --git a/angularFiles.js b/angularFiles.js index 702d5efd6290..f40b03ec241c 100755 --- a/angularFiles.js +++ b/angularFiles.js @@ -14,13 +14,14 @@ var angularFiles = { 'src/ng/anchorScroll.js', 'src/ng/animate.js', - 'src/ng/asyncCallback.js', + 'src/ng/animateCss.js', 'src/ng/browser.js', 'src/ng/cacheFactory.js', 'src/ng/compile.js', 'src/ng/controller.js', 'src/ng/document.js', 'src/ng/exceptionHandler.js', + 'src/ng/forceReflow.js', 'src/ng/http.js', 'src/ng/httpBackend.js', 'src/ng/interpolate.js', @@ -76,7 +77,10 @@ var angularFiles = { 'src/ng/directive/script.js', 'src/ng/directive/select.js', 'src/ng/directive/style.js', - 'src/ng/directive/validators.js' + 'src/ng/directive/validators.js', + 'src/angular.bind.js', + 'src/publishExternalApis.js', + 'src/ngLocale/angular-locale_en-us.js' ], 'angularLoader': [ @@ -88,7 +92,7 @@ var angularFiles = { 'angularModules': { 'ngAnimate': [ 'src/ngAnimate/shared.js', - 'src/ngAnimate/rafScheduler.js', + 'src/ngAnimate/body.js', 'src/ngAnimate/animateChildrenDirective.js', 'src/ngAnimate/animateCss.js', 'src/ngAnimate/animateCssDriver.js', @@ -179,7 +183,6 @@ var angularFiles = { 'bower_components/jquery/dist/jquery.js', 'test/jquery_remove.js', '@angularSrc', - 'src/publishExternalApis.js', '@angularSrcModules', '@angularScenario', '@angularTest' @@ -188,7 +191,8 @@ var angularFiles = { 'karmaExclude': [ 'test/jquery_alias.js', 'src/angular-bootstrap.js', - 'src/ngScenario/angular-bootstrap.js' + 'src/ngScenario/angular-bootstrap.js', + 'src/angular.bind.js' ], 'karmaScenario': [ @@ -215,7 +219,6 @@ var angularFiles = { 'bower_components/jquery/dist/jquery.js', 'test/jquery_alias.js', '@angularSrc', - 'src/publishExternalApis.js', '@angularSrcModules', '@angularScenario', '@angularTest' @@ -224,7 +227,8 @@ var angularFiles = { 'karmaJqueryExclude': [ 'src/angular-bootstrap.js', 'src/ngScenario/angular-bootstrap.js', - 'test/jquery_remove.js' + 'test/jquery_remove.js', + 'src/angular.bind.js' ] }; diff --git a/docs/config/index.js b/docs/config/index.js index 8a1833600790..3bba1f92a5e5 100644 --- a/docs/config/index.js +++ b/docs/config/index.js @@ -42,7 +42,7 @@ module.exports = new Package('angularjs', [ readFilesProcessor.basePath = path.resolve(__dirname,'../..'); readFilesProcessor.sourceFiles = [ - { include: 'src/**/*.js', basePath: 'src' }, + { include: 'src/**/*.js', exclude: 'src/angular.bind.js', basePath: 'src' }, { include: 'docs/content/**/*.ngdoc', basePath: 'docs/content' } ]; diff --git a/docs/content/error/$controller/ctrlfmt.ngdoc b/docs/content/error/$controller/ctrlfmt.ngdoc index 019598f7951e..effd9c04ace2 100644 --- a/docs/content/error/$controller/ctrlfmt.ngdoc +++ b/docs/content/error/$controller/ctrlfmt.ngdoc @@ -11,7 +11,7 @@ Supported formats: 1. `__name__` 2. `__name__ as __identifier__` -N'either `__name__` or `__identifier__` may contain spaces. +Neither `__name__` or `__identifier__` may contain spaces. Example of incorrect usage that leads to this error: ```html diff --git a/docs/content/error/$http/legacy.ngdoc b/docs/content/error/$http/legacy.ngdoc new file mode 100644 index 000000000000..1ff4282fc607 --- /dev/null +++ b/docs/content/error/$http/legacy.ngdoc @@ -0,0 +1,45 @@ +@ngdoc error +@name $http:legacy +@fullName The `success` and `error` methods on the promise returned from `$http` have been disabled. +@description + +This error occurs when the legacy promise extensions (`success` and `error`) +{@link $httpProvider#useLegacyPromiseExtensions legacy `$http` promise extensions} have been disabled. + +To resolve this error, either turn on the legacy extensions by adding +`$httpProvider.useLegacyPromiseExtensions(true);` to your application's configuration; or refactor you +use of `$http` to use `.then()` rather than `.success()` and `.error()`. + +For example if you code looked like this: + +```js +// Simple GET request example : +$http.get('/someUrl'). + success(function(data, status, headers, config) { + // This callback will be called asynchronously + // when the response is available + }). + error(function(data, status, headers, config) { + // called asynchronously if an error occurs + // or server returns response with an error status. + }); +``` + +then you would change it to look like: + +```js +// Simple GET request example : +$http.get('/someUrl'). + then(function(response) { + // (The response object contains the data, status, headers and config properties) + // This callback will be called asynchronously + // when the response is available. + }, function(response) { + // called asynchronously if an error occurs + // or server returns response with an error status. + }); +``` + +For more information, see the +{@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} +documentation. diff --git a/docs/content/error/ngOptions/trkslct.ngdoc b/docs/content/error/ngOptions/trkslct.ngdoc deleted file mode 100644 index 563ab7ea51b0..000000000000 --- a/docs/content/error/ngOptions/trkslct.ngdoc +++ /dev/null @@ -1,33 +0,0 @@ -@ngdoc error -@name ngOptions:trkslct -@fullName Comprehension expression cannot contain both `select as` and `track by` expressions. -@description - -NOTE: This error was introduced in 1.3.0-rc.5, and was removed for 1.3.0-rc.6 in order to -not break existing apps. - -This error occurs when 'ngOptions' is passed a comprehension expression that contains both a -`select as` expression and a `track by` expression. These two expressions are fundamentally -incompatible. - - * Example of bad expression: ` -``` - -Note: This would store the whole `item` as the model to `scope.selected` instead of `item.subItem`. - -For more information on valid expression syntax, see 'ngOptions' in {@link ng.directive:select select} directive docs. diff --git a/docs/content/guide/controller.ngdoc b/docs/content/guide/controller.ngdoc index 2f85d70f0ad3..c307555153bf 100644 --- a/docs/content/guide/controller.ngdoc +++ b/docs/content/guide/controller.ngdoc @@ -109,7 +109,7 @@ needed for a single view. The most common way to keep Controllers slim is by encapsulating work that doesn't belong to controllers into services and then using these services in Controllers via dependency injection. -This is discussed in the {@link di Dependency Injection} {@link services +This is discussed in the {@link di Dependency Injection} and {@link services Services} sections of this guide. @@ -165,7 +165,7 @@ scope is augmented (managed) by the `SpicyController` Controller. starts with capital letter and ends with "Controller". - Assigning a property to `$scope` creates or updates the model. - Controller methods can be created through direct assignment to scope (see the `chiliSpicy` method) -- The Controller methods and properties are available in the template (for the `
` element and +- The Controller methods and properties are available in the template (for both the `
` element and its children). ## Spicy Arguments Example @@ -305,7 +305,7 @@ describe('myController function', function() { ``` -If you need to test a nested Controller you need to create the same scope hierarchy +If you need to test a nested Controller you must create the same scope hierarchy in your test that exists in the DOM: ```js diff --git a/docs/content/guide/di.ngdoc b/docs/content/guide/di.ngdoc index c030ca32c801..0701ef6d1fc8 100644 --- a/docs/content/guide/di.ngdoc +++ b/docs/content/guide/di.ngdoc @@ -163,8 +163,8 @@ someModule.controller('MyController', function($scope, greeter) { }); ``` -Given a function the injector can infer the names of the services to inject by examining the -function declaration and extracting the parameter names. In the above example `$scope`, and +Given a function, the injector can infer the names of the services to inject by examining the +function declaration and extracting the parameter names. In the above example, `$scope` and `greeter` are two services which need to be injected into the function. One advantage of this approach is that there's no array of names to keep in sync with the @@ -293,7 +293,7 @@ Create a new injector that can provide components defined in our `myModule` modu `greeter` service from the injector. (This is usually done automatically by angular bootstrap). ```js -var injector = angular.injector(['myModule', 'ng']); +var injector = angular.injector(['ng', 'myModule']); var greeter = injector.get('greeter'); ``` diff --git a/docs/content/guide/expression.ngdoc b/docs/content/guide/expression.ngdoc index be71e941afda..b5d0d4c7a9f5 100644 --- a/docs/content/guide/expression.ngdoc +++ b/docs/content/guide/expression.ngdoc @@ -70,7 +70,7 @@ You can try evaluating different expressions here:
  • [ X ] - {{expr}} => + {{expr}} =>
@@ -141,6 +141,9 @@ provide mockable access to globals. } element(by.css('[ng-click="greet()"]')).click(); + // We need to give the browser time to display the alert + browser.wait(protractor.ExpectedConditions.alertIsPresent(), 1000); + var alertDialog = browser.switchTo().alert(); expect(alertDialog.getText()).toEqual('Hello World'); @@ -344,4 +347,4 @@ When using a directive that takes an expression: -``` \ No newline at end of file +``` diff --git a/docs/content/guide/forms.ngdoc b/docs/content/guide/forms.ngdoc index a58a3c445f41..dd51af985190 100644 --- a/docs/content/guide/forms.ngdoc +++ b/docs/content/guide/forms.ngdoc @@ -95,6 +95,8 @@ and failing to satisfy its validity. +
form = {{user | json}}
+
master = {{master | json}}
');"; + js = "!window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend('');"; state.js.push(js); return state; diff --git a/lib/promises-aplus/promises-aplus-test-adapter.js b/lib/promises-aplus/promises-aplus-test-adapter.js index ba1ad125902b..169d3403e065 100644 --- a/lib/promises-aplus/promises-aplus-test-adapter.js +++ b/lib/promises-aplus/promises-aplus-test-adapter.js @@ -4,6 +4,8 @@ var isFunction = function isFunction(value){return typeof value == 'function';}; var isPromiseLike = function isPromiseLike(obj) {return obj && isFunction(obj.then);}; var isObject = function isObject(value){return value != null && typeof value === 'object';}; +var isUndefined = function isUndefined(value) {return typeof value === 'undefined';}; + var minErr = function minErr (module, constructor) { return function (){ var ErrorConstructor = constructor || Error; @@ -11,6 +13,20 @@ var minErr = function minErr (module, constructor) { }; }; +var extend = function extend(dst) { + for (var i = 1, ii = arguments.length; i < ii; i++) { + var obj = arguments[i]; + if (obj) { + var keys = Object.keys(obj); + for (var j = 0, jj = keys.length; j < jj; j++) { + var key = keys[j]; + dst[key] = obj[key]; + } + } + } + return dst; +}; + var $q = qFactory(process.nextTick, function noopExceptionHandler() {}); exports.resolved = $q.resolve; diff --git a/src/.jshintrc b/src/.jshintrc index f5b8b6c54bfe..e5ce17e43234 100644 --- a/src/.jshintrc +++ b/src/.jshintrc @@ -163,9 +163,6 @@ /* ng/compile.js */ "directiveNormalize": false, - /* ng/parse.js */ - "setter": false, - /* ng/directive/directives.js */ "ngDirective": false, diff --git a/src/Angular.js b/src/Angular.js index e66b9938a9a3..485a53a25d7f 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -354,6 +354,8 @@ function baseExtend(dst, objs, deep) { if (deep && isObject(src)) { if (isDate(src)) { dst[key] = new Date(src.valueOf()); + } else if (isRegExp(src)) { + dst[key] = new RegExp(src); } else { if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {}; baseExtend(dst[key], [src], true); @@ -984,22 +986,39 @@ function equals(o1, o2) { } var csp = function() { - if (isDefined(csp.isActive_)) return csp.isActive_; + if (!isDefined(csp.rules)) { - var active = !!(document.querySelector('[ng-csp]') || - document.querySelector('[data-ng-csp]')); - if (!active) { + var ngCspElement = (document.querySelector('[ng-csp]') || + document.querySelector('[data-ng-csp]')); + + if (ngCspElement) { + var ngCspAttribute = ngCspElement.getAttribute('ng-csp') || + ngCspElement.getAttribute('data-ng-csp'); + csp.rules = { + noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1), + noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1) + }; + } else { + csp.rules = { + noUnsafeEval: noUnsafeEval(), + noInlineStyle: false + }; + } + } + + return csp.rules; + + function noUnsafeEval() { try { /* jshint -W031, -W054 */ new Function(''); /* jshint +W031, +W054 */ + return false; } catch (e) { - active = true; + return true; } } - - return (csp.isActive_ = active); }; /** @@ -1231,13 +1250,19 @@ function tryDecodeURIComponent(value) { * @returns {Object.} */ function parseKeyValue(/**string*/keyValue) { - var obj = {}, key_value, key; + var obj = {}; forEach((keyValue || "").split('&'), function(keyValue) { + var splitPoint, key, val; if (keyValue) { - key_value = keyValue.replace(/\+/g,'%20').split('='); - key = tryDecodeURIComponent(key_value[0]); + key = keyValue = keyValue.replace(/\+/g,'%20'); + splitPoint = keyValue.indexOf('='); + if (splitPoint !== -1) { + key = keyValue.substring(0, splitPoint); + val = keyValue.substring(splitPoint + 1); + } + key = tryDecodeURIComponent(key); if (isDefined(key)) { - var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true; + val = isDefined(val) ? tryDecodeURIComponent(val) : true; if (!hasOwnProperty.call(obj, key)) { obj[key] = val; } else if (isArray(obj[key])) { diff --git a/src/AngularPublic.js b/src/AngularPublic.js index dff1ce015ae1..c12403838bfe 100644 --- a/src/AngularPublic.js +++ b/src/AngularPublic.js @@ -3,7 +3,6 @@ /* global angularModule: true, version: true, - $LocaleProvider, $CompileProvider, htmlAnchorDirective, @@ -20,7 +19,6 @@ ngClassDirective, ngClassEvenDirective, ngClassOddDirective, - ngCspDirective, ngCloakDirective, ngControllerDirective, ngFormDirective, @@ -57,6 +55,7 @@ $AnchorScrollProvider, $AnimateProvider, + $CoreAnimateCssProvider, $$CoreAnimateQueueProvider, $$CoreAnimateRunnerProvider, $BrowserProvider, @@ -65,6 +64,7 @@ $DocumentProvider, $ExceptionHandlerProvider, $FilterProvider, + $$ForceReflowProvider, $InterpolateProvider, $IntervalProvider, $$HashMapProvider, @@ -151,11 +151,6 @@ function publishExternalAPI(angular) { }); angularModule = setupModuleLoader(window); - try { - angularModule('ngLocale'); - } catch (e) { - angularModule('ngLocale', []).provider('$locale', $LocaleProvider); - } angularModule('ng', ['ngLocale'], ['$provide', function ngModule($provide) { @@ -218,6 +213,7 @@ function publishExternalAPI(angular) { $provide.provider({ $anchorScroll: $AnchorScrollProvider, $animate: $AnimateProvider, + $animateCss: $CoreAnimateCssProvider, $$animateQueue: $$CoreAnimateQueueProvider, $$AnimateRunner: $$CoreAnimateRunnerProvider, $browser: $BrowserProvider, @@ -226,6 +222,7 @@ function publishExternalAPI(angular) { $document: $DocumentProvider, $exceptionHandler: $ExceptionHandlerProvider, $filter: $FilterProvider, + $$forceReflow: $$ForceReflowProvider, $interpolate: $InterpolateProvider, $interval: $IntervalProvider, $http: $HttpProvider, diff --git a/src/angular.bind.js b/src/angular.bind.js new file mode 100644 index 000000000000..33bc710d4342 --- /dev/null +++ b/src/angular.bind.js @@ -0,0 +1,10 @@ +if (window.angular.bootstrap) { + //AngularJS is already loaded, so we can return here... + console.log('WARNING: Tried to load angular more than once.'); + return; +} + +//try to bind to jquery now so that one can write jqLite(document).ready() +//but we will rebind on bootstrap again. +bindJQuery(); + diff --git a/src/angular.suffix b/src/angular.suffix index 4baa20e1b0b6..a79a3fb67841 100644 --- a/src/angular.suffix +++ b/src/angular.suffix @@ -1,15 +1,3 @@ - if (window.angular.bootstrap) { - //AngularJS is already loaded, so we can return here... - console.log('WARNING: Tried to load angular more than once.'); - return; - } - - //try to bind to jquery now so that one can write jqLite(document).ready() - //but we will rebind on bootstrap again. - bindJQuery(); - - publishExternalAPI(angular); - jqLite(document).ready(function() { angularInit(document, bootstrap); }); diff --git a/src/auto/injector.js b/src/auto/injector.js index f6980a671403..189421da8338 100644 --- a/src/auto/injector.js +++ b/src/auto/injector.js @@ -62,7 +62,7 @@ * Implicit module which gets automatically added to each {@link auto.$injector $injector}. */ -var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; +var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m; var FN_ARG_SPLIT = /,/; var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; @@ -718,6 +718,7 @@ function createInjector(modulesToLoad, strictDi) { // Module Loading //////////////////////////////////// function loadModules(modulesToLoad) { + assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array'); var runBlocks = [], moduleFn; forEach(modulesToLoad, function(module) { if (loadedModules.get(module)) return; diff --git a/src/loader.js b/src/loader.js index a6c14f54ae9d..71ff78269dcb 100644 --- a/src/loader.js +++ b/src/loader.js @@ -38,8 +38,8 @@ function setupModuleLoader(window) { * All modules (angular core or 3rd party) that should be available to an application must be * registered using this mechanism. * - * When passed two or more arguments, a new module is created. If passed only one argument, an - * existing module (the name passed as the first argument to `module`) is retrieved. + * Passing one argument retrieves an existing {@link angular.Module}, + * whereas passing more than one argument creates a new {@link angular.Module} * * * # Module diff --git a/src/ng/animate.js b/src/ng/animate.js index 142de7b87d01..88e0e160592c 100644 --- a/src/ng/animate.js +++ b/src/ng/animate.js @@ -106,31 +106,31 @@ var $$CoreAnimateQueueProvider = function() { }; function addRemoveClassesPostDigest(element, add, remove) { - var data = postDigestQueue.get(element); - var classVal; + var classVal, data = postDigestQueue.get(element); if (!data) { postDigestQueue.put(element, data = {}); postDigestElements.push(element); } - if (add) { - forEach(add.split(' '), function(className) { - if (className) { - data[className] = true; - } - }); - } - - if (remove) { - forEach(remove.split(' '), function(className) { - if (className) { - data[className] = false; - } - }); - } + var updateData = function(classes, value) { + var changed = false; + if (classes) { + classes = isString(classes) ? classes.split(' ') : + isArray(classes) ? classes : []; + forEach(classes, function(className) { + if (className) { + changed = true; + data[className] = value; + } + }); + } + return changed; + }; - if (postDigestElements.length > 1) return; + var classesAdded = updateData(add, true); + var classesRemoved = updateData(remove, false); + if ((!classesAdded && !classesRemoved) || postDigestElements.length > 1) return; $rootScope.$$postDigest(function() { forEach(postDigestElements, function(element) { diff --git a/src/ng/animateCss.js b/src/ng/animateCss.js new file mode 100644 index 000000000000..3da66f3d1def --- /dev/null +++ b/src/ng/animateCss.js @@ -0,0 +1,84 @@ +'use strict'; + +/** + * @ngdoc service + * @name $animateCss + * @kind object + * + * @description + * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included, + * then the `$animateCss` service will actually perform animations. + * + * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}. + */ +var $CoreAnimateCssProvider = function() { + this.$get = ['$$rAF', '$q', function($$rAF, $q) { + + var RAFPromise = function() {}; + RAFPromise.prototype = { + done: function(cancel) { + this.defer && this.defer[cancel === true ? 'reject' : 'resolve'](); + }, + end: function() { + this.done(); + }, + cancel: function() { + this.done(true); + }, + getPromise: function() { + if (!this.defer) { + this.defer = $q.defer(); + } + return this.defer.promise; + }, + then: function(f1,f2) { + return this.getPromise().then(f1,f2); + }, + 'catch': function(f1) { + return this.getPromise().catch(f1); + }, + 'finally': function(f1) { + return this.getPromise().finally(f1); + } + }; + + return function(element, options) { + if (options.from) { + element.css(options.from); + options.from = null; + } + + var closed, runner = new RAFPromise(); + return { + start: run, + end: run + }; + + function run() { + $$rAF(function() { + close(); + if (!closed) { + runner.done(); + } + closed = true; + }); + return runner; + } + + function close() { + if (options.addClass) { + element.addClass(options.addClass); + options.addClass = null; + } + if (options.removeClass) { + element.removeClass(options.removeClass); + options.removeClass = null; + } + if (options.to) { + element.css(options.to); + options.to = null; + } + } + }; + }]; +}; diff --git a/src/ng/asyncCallback.js b/src/ng/asyncCallback.js deleted file mode 100644 index 5cd9dec8413d..000000000000 --- a/src/ng/asyncCallback.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -function $$AsyncCallbackProvider() { - this.$get = ['$$rAF', '$timeout', function($$rAF, $timeout) { - return $$rAF.supported - ? function(fn) { return $$rAF(fn); } - : function(fn) { - return $timeout(fn, 0, false); - }; - }]; -} diff --git a/src/ng/compile.js b/src/ng/compile.js index 7a926f309fdc..d8ed0c5e0e3d 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -407,7 +407,7 @@ * otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown. * * Note that you can also require the directive's own controller - it will be made available like - * like any other controller. + * any other controller. * * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope. * This is the same as the `$transclude` @@ -433,7 +433,7 @@ * * ### Transclusion * - * Transclusion is the process of extracting a collection of DOM element from one part of the DOM and + * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and * copying them to another part of the DOM, while maintaining their connection to the original AngularJS * scope from where they were taken. * @@ -1188,7 +1188,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { listeners.push(fn); $rootScope.$evalAsync(function() { - if (!listeners.$$inter && attrs.hasOwnProperty(key)) { + if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) { // no one registered attribute interpolation function, so lets call it manually fn(attrs[key]); } @@ -2567,24 +2567,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { lastValue, parentGet, parentSet, compare; - if (!hasOwnProperty.call(attrs, attrName)) { - // In the case of user defined a binding with the same name as a method in Object.prototype but didn't set - // the corresponding attribute. We need to make sure subsequent code won't access to the prototype function - attrs[attrName] = undefined; - } - switch (mode) { case '@': - if (!attrs[attrName] && !optional) { - destination[scopeName] = undefined; + if (!optional && !hasOwnProperty.call(attrs, attrName)) { + destination[scopeName] = attrs[attrName] = void 0; } - attrs.$observe(attrName, function(value) { - destination[scopeName] = value; + if (isString(value)) { + destination[scopeName] = value; + } }); attrs.$$observers[attrName].$$scope = scope; - if (attrs[attrName]) { + if (isString(attrs[attrName])) { // If the attribute has been provided then we trigger an interpolation to ensure // the value is there for use in the link fn destination[scopeName] = $interpolate(attrs[attrName])(scope); @@ -2592,11 +2587,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { break; case '=': - if (optional && !attrs[attrName]) { - return; + if (!hasOwnProperty.call(attrs, attrName)) { + if (optional) break; + attrs[attrName] = void 0; } - parentGet = $parse(attrs[attrName]); + if (optional && !attrs[attrName]) break; + parentGet = $parse(attrs[attrName]); if (parentGet.literal) { compare = equals; } else { @@ -2635,7 +2632,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { break; case '&': - parentGet = $parse(attrs[attrName]); + // Don't assign Object.prototype method to scope + parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop; // Don't assign noop to destination if expression is not valid if (parentGet === noop && optional) break; diff --git a/src/ng/directive/form.js b/src/ng/directive/form.js index 9c7f52564c91..d540e0403953 100644 --- a/src/ng/directive/form.js +++ b/src/ng/directive/form.js @@ -449,7 +449,7 @@ function FormController(element, attrs, $scope, $animate, $interpolate) { * related scope, under this name. */ var formDirectiveFactory = function(isNgForm) { - return ['$timeout', function($timeout) { + return ['$timeout', '$parse', function($timeout, $parse) { var formDirective = { name: 'form', restrict: isNgForm ? 'EAC' : 'E', @@ -491,21 +491,21 @@ var formDirectiveFactory = function(isNgForm) { } var parentFormCtrl = controller.$$parentForm; + var setter = nameAttr ? getSetter(controller.$name) : noop; if (nameAttr) { - setter(scope, controller.$name, controller, controller.$name); + setter(scope, controller); attr.$observe(nameAttr, function(newValue) { if (controller.$name === newValue) return; - setter(scope, controller.$name, undefined, controller.$name); + setter(scope, undefined); parentFormCtrl.$$renameControl(controller, newValue); - setter(scope, controller.$name, controller, controller.$name); + setter = getSetter(controller.$name); + setter(scope, controller); }); } formElement.on('$destroy', function() { parentFormCtrl.$removeControl(controller); - if (nameAttr) { - setter(scope, attr[nameAttr], undefined, controller.$name); - } + setter(scope, undefined); extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards }); } @@ -514,6 +514,14 @@ var formDirectiveFactory = function(isNgForm) { }; return formDirective; + + function getSetter(expression) { + if (expression === '') { + //create an assignable expression, so forms with an empty name can be renamed later + return $parse('this[""]').assign; + } + return $parse(expression).assign || noop; + } }]; }; diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index 538f7e068511..f3bab623126c 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -6,7 +6,7 @@ DIRTY_CLASS: false, UNTOUCHED_CLASS: false, TOUCHED_CLASS: false, - $ngModelMinErr: false, + ngModelMinErr: false, */ // Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231 @@ -1128,7 +1128,11 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) { element.on('change', listener); ctrl.$render = function() { - element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue); + // Workaround for Firefox validation #12102. + var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue; + if (element.val() !== value) { + element.val(value); + } }; } @@ -1239,7 +1243,7 @@ function createDateInputType(type, regexp, parseDate, format) { ctrl.$formatters.push(function(value) { if (value && !isDate(value)) { - throw $ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value); + throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value); } if (isValidDate(value)) { previousDate = value; @@ -1315,7 +1319,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) { ctrl.$formatters.push(function(value) { if (!ctrl.$isEmpty(value)) { if (!isNumber(value)) { - throw $ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value); + throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value); } value = value.toString(); } @@ -1408,7 +1412,7 @@ function parseConstantExpr($parse, context, name, expression, fallback) { if (isDefined(expression)) { parseFn = $parse(expression); if (!parseFn.constant) { - throw minErr('ngModel')('constexpr', 'Expected constant expression for `{0}`, but saw ' + + throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' + '`{1}`.', name, expression); } return parseFn(context); diff --git a/src/ng/directive/ngCsp.js b/src/ng/directive/ngCsp.js index 974d2e9305fb..378333b4a854 100644 --- a/src/ng/directive/ngCsp.js +++ b/src/ng/directive/ngCsp.js @@ -6,27 +6,29 @@ * * @element html * @description - * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support. + * + * Angular has some features that can break certain + * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules. + * + * If you intend to implement these rules then you must tell Angular not to use these features. * * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps. * - * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things). - * For Angular to be CSP compatible there are only two things that we need to do differently: * - * - don't use `Function` constructor to generate optimized value getters - * - don't inject custom stylesheet into the document + * The following rules affect Angular: * - * AngularJS uses `Function(string)` generated functions as a speed optimization. Applying the `ngCsp` - * directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will - * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will - * be raised. + * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions + * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30% + * increase in the speed of evaluating Angular expressions. * - * CSP forbids JavaScript to inline stylesheet rules. In non CSP mode Angular automatically - * includes some CSS rules (e.g. {@link ng.directive:ngCloak ngCloak}). - * To make those directives work in CSP mode, include the `angular-csp.css` manually. + * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular + * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}). + * To make these directives work when a CSP rule is blocking inline styles, you must link to the + * `angular-csp.css` in your HTML manually. * - * Angular tries to autodetect if CSP is active and automatically turn on the CSP-safe mode. This - * autodetection however triggers a CSP error to be logged in the console: + * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval + * and automatically deactivates this feature in the {@link $parse} service. This autodetection, + * however, triggers a CSP error to be logged in the console: * * ``` * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of @@ -35,11 +37,39 @@ * ``` * * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp` - * directive on the root element of the application or on the `angular.js` script tag, whichever - * appears first in the html document. + * directive on an element of the HTML document that appears before the `