Compare commits
190 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3468ad1b61 | |||
| e9c79cad43 | |||
| e455e7d878 | |||
| 3410f65e79 | |||
| f3de5b6eac | |||
| fcd2a8131a | |||
| 62dbe85798 | |||
| 1d5e18b062 | |||
| a0ed371389 | |||
| 05e4fd3488 | |||
| 30a8b7d0b5 | |||
| f8944efe70 | |||
| 43072e3812 | |||
| 9396d55414 | |||
| 82e97cf53e | |||
| cf2a7614a4 | |||
| 9e538e7c31 | |||
| 4ac21ac039 | |||
| f69dc16241 | |||
| f1a8d419d5 | |||
| 8864e54f1f | |||
| dc4df93177 | |||
| 043190f397 | |||
| f4d850e168 | |||
| 8ec2743ca1 | |||
| ecbee8147b | |||
| f8c6ee3df5 | |||
| fe84f7bef8 | |||
| d653607162 | |||
| 082fe180ec | |||
| d3491083a5 | |||
| c3d6ca97e1 | |||
| a14266e464 | |||
| b4d44e1298 | |||
| ca116c35a6 | |||
| 78ba429e6a | |||
| dbf8c3c745 | |||
| 3602c9785b | |||
| acaac21fd1 | |||
| c98ef94706 | |||
| b0972a2e75 | |||
| 2dbb6f9a54 | |||
| 785a5fd7c1 | |||
| a55c1e79cf | |||
| d070450cd2 | |||
| 09648e4888 | |||
| 2adbcf189b | |||
| 39c5ffb2a6 | |||
| 04a570d31c | |||
| 958d3d56b1 | |||
| 0e50810c53 | |||
| 21e48abbc1 | |||
| b6d5439343 | |||
| 93901bdde4 | |||
| d802ed1b36 | |||
| e8f4305e9d | |||
| b38a2287f2 | |||
| 1e7675ad4c | |||
| 280b5ce3c0 | |||
| fbc5cf514b | |||
| f01087f802 | |||
| 4ac6424e87 | |||
| d3c486dd6d | |||
| 2d0f6ccba8 | |||
| 9a81b8668a | |||
| 9481d69d1c | |||
| 7615723547 | |||
| 338f949259 | |||
| d0192b31a3 | |||
| 6127528b50 | |||
| 0410572322 | |||
| fd2371cfc2 | |||
| 267fcc999c | |||
| 84187b6d94 | |||
| 5d6482bb3b | |||
| 023765c593 | |||
| 4a401bbcf3 | |||
| 7401c70718 | |||
| bb36bc7edf | |||
| bf1972dc1e | |||
| 689dfb1679 | |||
| 1169b54456 | |||
| 81b81856ee | |||
| fd4b99936e | |||
| 09271a8ab9 | |||
| 5a8d9acacb | |||
| 04d5a5072f | |||
| 55c30e1be6 | |||
| 97fc84c151 | |||
| 4ee0687f3f | |||
| ddff347b91 | |||
| 05ef1bd853 | |||
| d0f8bd30a6 | |||
| 1a8d3c8b3a | |||
| 753687e5c2 | |||
| 1a15c01b64 | |||
| 7f33e1ca89 | |||
| 28d00945ba | |||
| 6f40c88f47 | |||
| 68dd621082 | |||
| 3abfb4ef51 | |||
| 1014e52349 | |||
| cda061f723 | |||
| 1497c6c1fb | |||
| e41e445b51 | |||
| 7ab73190b7 | |||
| 450b3a5460 | |||
| 38fb542838 | |||
| 7ab5098c14 | |||
| bcca80548d | |||
| 736c8fbbae | |||
| 947562220d | |||
| 333523483f | |||
| 68ceb17272 | |||
| 5bd6596856 | |||
| b3f2a20832 | |||
| e8d8c7a8d7 | |||
| 7a91d7fa7e | |||
| c6bd58eb58 | |||
| c2e45c769e | |||
| b08427dde9 | |||
| ffd075b440 | |||
| 3fcd228441 | |||
| 8383ecfcdf | |||
| eed2333298 | |||
| a2809dacc4 | |||
| b837a31afa | |||
| 66b0fcd3c0 | |||
| 2efe82309a | |||
| a090400f09 | |||
| 84e0eea164 | |||
| bcf12e70e5 | |||
| 1ca98b2c09 | |||
| 3efdeebcb7 | |||
| 16febf8357 | |||
| 0f7c4ca671 | |||
| a3172a285f | |||
| 84c408ce63 | |||
| 40647b179c | |||
| 0421cb4200 | |||
| 6f1050df4f | |||
| 4d16472b91 | |||
| 9e89a31b12 | |||
| e6521e7491 | |||
| 0a7cbb33b0 | |||
| 579242346c | |||
| c42d0a0418 | |||
| 3fbb25e25c | |||
| 6760d7a315 | |||
| 062fbed8fc | |||
| 76e4db6f3d | |||
| 0cd7e8f227 | |||
| ba1b47f85b | |||
| 0a3481e23a | |||
| e33c365144 | |||
| e3ceb50b73 | |||
| 6b5772bbbd | |||
| 6288cf5ca4 | |||
| f6ecf9a3c9 | |||
| a4e6d962d7 | |||
| 7874a4d007 | |||
| 1d50663b38 | |||
| ec3c4f94c7 | |||
| 6b8bbe4d90 | |||
| 7067a8fb0b | |||
| c47abd0dd7 | |||
| 68d71bbc01 | |||
| 334303e485 | |||
| b95fd53c6e | |||
| c77dd040b4 | |||
| dc027f22e5 | |||
| 043500fb27 | |||
| 3ceb6ab477 | |||
| 999fa44616 | |||
| 5f9121ad56 | |||
| b4cf8483d7 | |||
| 8a9816e06b | |||
| c0e10683a6 | |||
| cad5a367c3 | |||
| f2453eabb3 | |||
| aa0b11d794 | |||
| b9fa5c5a67 | |||
| 751f058f30 | |||
| 29274e1d8d | |||
| 23ba287897 | |||
| 8f1e3606dd | |||
| ac56d1c9d9 | |||
| de2919cb9a | |||
| 61943276f0 | |||
| 88ce00a3cf |
@@ -17,3 +17,4 @@ angular.xcodeproj
|
||||
libpeerconnection.log
|
||||
npm-debug.log
|
||||
/tmp/
|
||||
/scripts/bower/bower-*
|
||||
|
||||
+9
-5
@@ -3,11 +3,16 @@ node_js:
|
||||
- 0.10
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- JOB=unit
|
||||
- JOB=e2e
|
||||
global:
|
||||
- SAUCE_USERNAME=angular-ci
|
||||
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||
- SAUCE_CONNECT_READY_FILE=/tmp/sauce-connect-ready
|
||||
- BROWSER_STACK_USERNAME=VojtaJina
|
||||
- BROWSER_STACK_ACCESS_KEY=HAfHZaypxAc3PEUrUU9a
|
||||
- LOGS_DIR=/tmp/angular-build/logs
|
||||
- BROWSER_PROVIDER_READY_FILE=/tmp/sauce-connect-ready
|
||||
|
||||
before_script:
|
||||
- mkdir -p $LOGS_DIR
|
||||
@@ -16,11 +21,10 @@ before_script:
|
||||
- grunt bower
|
||||
- grunt bower
|
||||
- grunt package-without-bower
|
||||
- grunt ci-checks
|
||||
- ./lib/sauce/sauce_connect_block.sh
|
||||
- ./scripts/travis/wait_for_browser_provider.sh
|
||||
|
||||
script:
|
||||
- ./travis_build.sh
|
||||
- ./scripts/travis/build.sh
|
||||
|
||||
after_script:
|
||||
- ./travis_print_logs.sh
|
||||
- ./scripts/travis/print_logs.sh
|
||||
|
||||
+216
@@ -1,3 +1,219 @@
|
||||
<a name="1.2.5"></a>
|
||||
# 1.2.5 singularity-expansion (2013-12-13)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:** allow literals in isolate scope references
|
||||
([43072e38](https://github.com/angular/angular.js/commit/43072e3812e32b89b97ad03144577cba50d4b776),
|
||||
[#5296](https://github.com/angular/angular.js/issues/5296))
|
||||
- **angular-mocks:** use copy of mock data in $httpBackend
|
||||
([f69dc162](https://github.com/angular/angular.js/commit/f69dc16241c8b631123ad0b09674f0a5e0ff32fe))
|
||||
- **closure:** add missing FormController extern definitions
|
||||
([1d5e18b0](https://github.com/angular/angular.js/commit/1d5e18b062c3e33b2a8d96aa58d905ed2cd48649),
|
||||
[#5303](https://github.com/angular/angular.js/issues/5303))
|
||||
- **ngInclude:** add template to DOM before linking other directives
|
||||
([30a8b7d0](https://github.com/angular/angular.js/commit/30a8b7d0b5d4882c2bf3b20eb696a02f5b667726),
|
||||
[#5247](https://github.com/angular/angular.js/issues/5247))
|
||||
- **ngView:** add template to DOM before linking other directives
|
||||
([f8944efe](https://github.com/angular/angular.js/commit/f8944efe70b81e02704df9b53ea2546c80c73d3b))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **$injector:** remove invoke optimization that doesn't work
|
||||
([05e4fd34](https://github.com/angular/angular.js/commit/05e4fd3488b89e670c36869f18defe26deac2efa),
|
||||
[#5388](https://github.com/angular/angular.js/issues/5388))
|
||||
- **$resource:** use shallow copy instead of angular.copy
|
||||
([fcd2a813](https://github.com/angular/angular.js/commit/fcd2a8131a3cb3e59a616bf31e61510b5c3a97d3),
|
||||
[#5300](https://github.com/angular/angular.js/issues/5300))
|
||||
- **a:** do not link when href or name exists in template
|
||||
([f3de5b6e](https://github.com/angular/angular.js/commit/f3de5b6eac90baf649506072162f36dbc6d2f028),
|
||||
[#5362](https://github.com/angular/angular.js/issues/5362))
|
||||
- **jqLite:** implement and use the `empty` method in place of `html(‘’)`
|
||||
([3410f65e](https://github.com/angular/angular.js/commit/3410f65e790a81d457b4f4601a1e760a6f8ede5e),
|
||||
[#4457](https://github.com/angular/angular.js/issues/4457))
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **angular-mocks:** due to [f69dc162](https://github.com/angular/angular.js/commit/f69dc16241c8b631123ad0b09674f0a5e0ff32fe),
|
||||
some tests that rely on identity comparison rather than equality comparison in checking mock http responses will be broken,
|
||||
since now each mock response is a copy of the original response. This is usually fixable by changing a `.toBe()` comparison
|
||||
to `toEqual()` inside of tests.
|
||||
|
||||
<a name="1.2.4"></a>
|
||||
# 1.2.4 wormhole-blaster (2013-12-06)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$animate:**
|
||||
- ensure animations work with directives that share a transclusion
|
||||
([958d3d56](https://github.com/angular/angular.js/commit/958d3d56b1899a2cfc7b18c0292e5a1d8c64d0a5),
|
||||
[#4716](https://github.com/angular/angular.js/issues/4716), [#4871](https://github.com/angular/angular.js/issues/4871), [#5021](https://github.com/angular/angular.js/issues/5021), [#5278](https://github.com/angular/angular.js/issues/5278))
|
||||
- ensure ms durations are properly rounded
|
||||
([93901bdd](https://github.com/angular/angular.js/commit/93901bdde4bb9f0ba114ebb33b8885808e1823e1),
|
||||
[#5113](https://github.com/angular/angular.js/issues/5113), [#5162](https://github.com/angular/angular.js/issues/5162))
|
||||
- **$compile:**
|
||||
- update cloned elements if the template arrives after the cloning
|
||||
([b0972a2e](https://github.com/angular/angular.js/commit/b0972a2e75909e41dbac6e4413ada7df2d51df3a))
|
||||
- ensure the isolated local watch `lastValue` is always in sync
|
||||
([2d0f6ccb](https://github.com/angular/angular.js/commit/2d0f6ccba896fe34141d6d4f59eef6fba580c5c2),
|
||||
[#5182](https://github.com/angular/angular.js/issues/5182))
|
||||
- **$rootScope:**
|
||||
- ensure that when the $destroy event is broadcast on $rootScope that it does something
|
||||
([d802ed1b](https://github.com/angular/angular.js/commit/d802ed1b3680cfc1751777fac465b92ee29944dc),
|
||||
[#5169](https://github.com/angular/angular.js/issues/5169))
|
||||
- ensure the phase is cleared within a digest if an exception is raised by a watcher
|
||||
([d3c486dd](https://github.com/angular/angular.js/commit/d3c486dd6dfa8d5dca32a3e28aa685fb7260c878))
|
||||
- **$sanitize:** don't rely on YARR regex engine executing immediately in order to prevent object mutation
|
||||
([81b81856](https://github.com/angular/angular.js/commit/81b81856ee43d2876927c4e1f774affa87e99707),
|
||||
[#5193](https://github.com/angular/angular.js/issues/5193), [#5192](https://github.com/angular/angular.js/issues/5192))
|
||||
- **closure:** closure compiler shouldn't rename .defaults.transformRequest
|
||||
([f01087f8](https://github.com/angular/angular.js/commit/f01087f802839637843115cbcf99702e09d866f6))
|
||||
- **input:** ensure ngModelWatch() triggers second digest pass when appropriate
|
||||
([b6d54393](https://github.com/angular/angular.js/commit/b6d5439343b9801f7f2a009d0de09cba9aa21a1d),
|
||||
[#5258](https://github.com/angular/angular.js/issues/5258), [#5282](https://github.com/angular/angular.js/issues/5282))
|
||||
- **isElement:** return boolean value rather than `truthy` value.
|
||||
([2dbb6f9a](https://github.com/angular/angular.js/commit/2dbb6f9a54eb5ff5847eed11c85ac4cf119eb41c),
|
||||
[#4519](https://github.com/angular/angular.js/issues/4519), [#4534](https://github.com/angular/angular.js/issues/4534))
|
||||
- **jqLite:** ignore incompatible nodes on find()
|
||||
([1169b544](https://github.com/angular/angular.js/commit/1169b5445691e1495354d235a3badf05240e3904),
|
||||
[#4120](https://github.com/angular/angular.js/issues/4120))
|
||||
- **ngInit:** evaluate ngInit before ngInclude
|
||||
([0e50810c](https://github.com/angular/angular.js/commit/0e50810c53428f4c1f5bfdba9599df54cb7a6c6e),
|
||||
[#5167](https://github.com/angular/angular.js/issues/5167), [#5208](https://github.com/angular/angular.js/issues/5208))
|
||||
- **ngSanitize:** prefer textContent to innerText to avoid layout trashing
|
||||
([bf1972dc](https://github.com/angular/angular.js/commit/bf1972dc1e8ffbeaddfa53df1d49bc5a2177f09c))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **$parse:** micro-optimization for ensureSafeObject function
|
||||
([689dfb16](https://github.com/angular/angular.js/commit/689dfb167924a61aef444ce7587fb987d8080990),
|
||||
[#5246](https://github.com/angular/angular.js/issues/5246))
|
||||
- **Scope:** short-circuit after dirty-checking last dirty watcher
|
||||
([d070450c](https://github.com/angular/angular.js/commit/d070450cd2b3b3a3aa34b69d3fa1f4cc3be025dd),
|
||||
[#5272](https://github.com/angular/angular.js/issues/5272), [#5287](https://github.com/angular/angular.js/issues/5287))
|
||||
|
||||
|
||||
|
||||
<a name="1.2.3"></a>
|
||||
# 1.2.3 unicorn-zapper (2013-11-27)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$animate:**
|
||||
- ensure blocked keyframe animations are unblocked before the DOM operation
|
||||
([2efe8230](https://github.com/angular/angular.js/commit/2efe82309ac8ff4f67df8b6e40a539ea31e15804),
|
||||
[#5106](https://github.com/angular/angular.js/issues/5106))
|
||||
- ensure animations are disabled during bootstrap to prevent unwanted structural animations
|
||||
([eed23332](https://github.com/angular/angular.js/commit/eed2333298412fbad04eda97ded3487c845b9eb9),
|
||||
[#5130](https://github.com/angular/angular.js/issues/5130))
|
||||
- **$sanitize:** use the same whitelist mechanism as `$compile` does
|
||||
([33352348](https://github.com/angular/angular.js/commit/333523483f3ce6dd3177b697a5e5a7177ca364c8),
|
||||
[#3748](https://github.com/angular/angular.js/issues/3748))
|
||||
- **input:** react to form auto completion, through the `change` event, on modern browsers
|
||||
([a090400f](https://github.com/angular/angular.js/commit/a090400f09d7993d102f527609879cdc74abae60),
|
||||
[#1460](https://github.com/angular/angular.js/issues/1460))
|
||||
- **$attrs:** add `$attrs.$attr` to externs so that it isn't renamed on js minification
|
||||
([bcca8054](https://github.com/angular/angular.js/commit/bcca80548dde85ffe3838c943ba8e5c2deb1c721))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
No new features in this release
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
There are no breaking changes in this release (promise!)
|
||||
|
||||
|
||||
|
||||
<a name="1.2.2"></a>
|
||||
# 1.2.2 consciousness-inertia (2013-11-22)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$animate:**
|
||||
- ensure keyframe animations are blocked around the reflow
|
||||
([6760d7a3](https://github.com/angular/angular.js/commit/6760d7a315d7ea5cbd4f8ab74b200f754a2041f4),
|
||||
[#5018](https://github.com/angular/angular.js/issues/5018))
|
||||
- ensure transition animations are unblocked before the dom operation occurs
|
||||
([062fbed8](https://github.com/angular/angular.js/commit/062fbed8fc3f7bc55433f8c6915c27520e6f63c5),
|
||||
[#5014](https://github.com/angular/angular.js/issues/5014),
|
||||
[#4265](https://github.com/angular/angular.js/issues/4265))
|
||||
- ensure addClass/removeClass animations do not snap during reflow
|
||||
([76e4db6f](https://github.com/angular/angular.js/commit/76e4db6f3d15199ac1fbe85f9cfa6079a1c4fa56),
|
||||
[#4892](https://github.com/angular/angular.js/issues/4892))
|
||||
- ensure the DOM operation isn't run twice
|
||||
([7067a8fb](https://github.com/angular/angular.js/commit/7067a8fb0b18d5b5489006e1960cee721a88b4d2),
|
||||
[#4949](https://github.com/angular/angular.js/issues/4949))
|
||||
- **$compile:**
|
||||
- secure form[action] & iframe[srcdoc]
|
||||
([0421cb42](https://github.com/angular/angular.js/commit/0421cb4200e672818ed10996e92311404c150c3a),
|
||||
[#4927](https://github.com/angular/angular.js/issues/4927),
|
||||
[#4933](https://github.com/angular/angular.js/issues/4933))
|
||||
- ensure CSS classes are added and removed only when necessary
|
||||
([0cd7e8f2](https://github.com/angular/angular.js/commit/0cd7e8f22721f62b62440bb059ae764ebbe7b42a))
|
||||
- **$httpBackend:** only IE8 and below can't use `script.onload` for JSONP
|
||||
([a3172a28](https://github.com/angular/angular.js/commit/a3172a285fd74b5aa6c8d68a4988c767c06f549c),
|
||||
[#4523](https://github.com/angular/angular.js/issues/4523),
|
||||
[#4527](https://github.com/angular/angular.js/issues/4527),
|
||||
[#4922](https://github.com/angular/angular.js/issues/4922))
|
||||
- **$parse:** allow for new lines in expr when promise unwrapping is on
|
||||
([40647b17](https://github.com/angular/angular.js/commit/40647b179c473f3f470bb1b3237d6f006269582f),
|
||||
[#4718](https://github.com/angular/angular.js/issues/4718))
|
||||
- **$resource:** Always return a resource instance when calling class methods on resources.
|
||||
([f6ecf9a3](https://github.com/angular/angular.js/commit/f6ecf9a3c9090593faf5fa50586c99a56b51c776),
|
||||
[#4545](https://github.com/angular/angular.js/issues/4545),
|
||||
[#5061](https://github.com/angular/angular.js/issues/5061))
|
||||
- **httpBackend:** should not read response data when request is aborted
|
||||
([6f1050df](https://github.com/angular/angular.js/commit/6f1050df4fa885bd59ce85adbef7350ea93911a3),
|
||||
[#4913](https://github.com/angular/angular.js/issues/4913),
|
||||
[#4940](https://github.com/angular/angular.js/issues/4940))
|
||||
- **loader:** expose `$$minErr` to modules such as`ngResource`
|
||||
([9e89a31b](https://github.com/angular/angular.js/commit/9e89a31b129e40c805178535c244899ffafb77d8),
|
||||
[#5050](https://github.com/angular/angular.js/issues/5050))
|
||||
- **ngAnimate:**
|
||||
- correctly retain and restore existing styles during and after animation
|
||||
([c42d0a04](https://github.com/angular/angular.js/commit/c42d0a041890b39fc98afd357ec1307a3a36208d),
|
||||
[#4869](https://github.com/angular/angular.js/issues/4869))
|
||||
- use a fallback CSS property that doesn't break existing styles
|
||||
([1d50663b](https://github.com/angular/angular.js/commit/1d50663b38ba042e8d748ffa6d48cfb5e93cfd7e),
|
||||
[#4902](https://github.com/angular/angular.js/issues/4902),
|
||||
[#5030](https://github.com/angular/angular.js/issues/5030))
|
||||
- **ngClass:** ensure that ngClass only adds/removes the changed classes
|
||||
([6b8bbe4d](https://github.com/angular/angular.js/commit/6b8bbe4d90640542eed5607a8c91f6b977b1d6c0),
|
||||
[#4960](https://github.com/angular/angular.js/issues/4960),
|
||||
[#4944](https://github.com/angular/angular.js/issues/4944))
|
||||
- **ngController:** fix issue with ngInclude on the same element
|
||||
([6288cf5c](https://github.com/angular/angular.js/commit/6288cf5ca471b0615a026fdb4db3ba242c9d8f88),
|
||||
[#4431](https://github.com/angular/angular.js/issues/4431))
|
||||
- **ngInclude:**
|
||||
- Don't throw when the ngInclude element contains content with directives.
|
||||
([0a7cbb33](https://github.com/angular/angular.js/commit/0a7cbb33b06778833a4d99b1868cc07690a827a7))
|
||||
- allow ngInclude to load scripts when jQuery is included
|
||||
([c47abd0d](https://github.com/angular/angular.js/commit/c47abd0dd7490576f4b84ee51ebaca385c1036da),
|
||||
[#3756](https://github.com/angular/angular.js/issues/3756))
|
||||
- **ngMock:** fixes httpBackend expectation with body object
|
||||
([4d16472b](https://github.com/angular/angular.js/commit/4d16472b918a3482942d76f1e273a5aa01f65e83),
|
||||
[#4956](https://github.com/angular/angular.js/issues/4956))
|
||||
- **ngView:** Don't throw when the ngView element contains content with directives.
|
||||
([e6521e74](https://github.com/angular/angular.js/commit/e6521e7491242504250b57dd0ee66af49e653c33),
|
||||
[#5069](https://github.com/angular/angular.js/issues/5069))
|
||||
- **tests:** Correct tests for IE11
|
||||
([57924234](https://github.com/angular/angular.js/commit/579242346c4202ea58fc2cae6df232289cbea0bb),
|
||||
[#5046](https://github.com/angular/angular.js/issues/5046))
|
||||
- **input:** hold listener during text composition
|
||||
([a4e6d962](https://github.com/angular/angular.js/commit/a4e6d962d78b26f5112d48c4f88c1e6234d0cae7),
|
||||
[#4684](https://github.com/angular/angular.js/issues/4684))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.2.1"></a>
|
||||
# 1.2.1 underscore-empathy (2013-11-14)
|
||||
|
||||
|
||||
+108
-64
@@ -29,32 +29,124 @@ duplication of work, and help you to craft the change so that it is successfully
|
||||
project.
|
||||
* **Small Changes** can be crafted and submitted to [GitHub Repository][github] as a Pull Request.
|
||||
|
||||
|
||||
## Want a Doc Fix?
|
||||
If you want to help improve the docs, it's a good idea to let others know what you're working on to
|
||||
minimize duplication of effort. Before starting, check out the issue queue for [Milestone:Docs Only](https://github.com/angular/angular.js/issues?milestone=24&state=open).
|
||||
Comment on an issue to let others know what you're working on, or create a new issue if your work
|
||||
doesn't fit within the scope of any of the existing doc fix projects.
|
||||
|
||||
For large fixes, please build and test the documentation before submitting the PR to be sure you haven't
|
||||
accidentally introduced any layout or formatting issues.You should also make sure that your commit message
|
||||
is labeled "docs:" and follows the **Git Commit Guidelines** outlined below.
|
||||
|
||||
If you're just making a small change, don't worry about filing an issue first. Use the friendly blue "Improve this doc" button at the top right of the doc page to fork the repository in-place and make a quick change on the fly.
|
||||
|
||||
## Submission Guidelines
|
||||
|
||||
### Submitting an Issue
|
||||
Before you submit your issue follow the following guidelines:
|
||||
|
||||
* Search the archive first, it's likely that your question was already answered.
|
||||
* A live example demonstrating the issue, will get an answer faster.
|
||||
* Create one using [Plunker][plunker] or [JSFiddle][jsfiddle].
|
||||
* If you get help, help others. Good karma rulez!
|
||||
Before you submit your issue search the archive, maybe your question was already answered.
|
||||
|
||||
If your issue appears to be a bug, and hasn't been reported, open a new issue.
|
||||
Help us to maximize the effort we can spend fixing issues and adding new
|
||||
features, by not reporting duplicate issues.
|
||||
features, by not reporting duplicate issues. Providing the following information will increase the
|
||||
chances of your issue being dealt with quickly:
|
||||
|
||||
* **Overview of the issue** - if an error is being thrown a non-minified stack trace helps
|
||||
* **Motivation for or Use Case** - explain why this is a bug for you
|
||||
* **Angular Version(s)** - is it a regression?
|
||||
* **Browsers and Operating System** - is this a problem with all browsers or only IE8?
|
||||
* **Reproduce the error** - provide a live example (using [Plunker][plunker] or
|
||||
[JSFiddle][jsfiddle]) or a unambiguous set of steps.
|
||||
* **Related issues** - has a similar issue been reported before?
|
||||
* **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
|
||||
causing the problem (line of code or commit)
|
||||
|
||||
Here is a great example of a well defined issue: https://github.com/angular/angular.js/issues/5069
|
||||
|
||||
**If you get help, help others. Good karma rulez!**
|
||||
|
||||
### Submitting a Pull Request
|
||||
Before you submit your pull request follow the following guidelines:
|
||||
Before you submit your pull request consider the following guidelines:
|
||||
|
||||
* Search GitHub for an open or closed Pull Request that relates to your submission. You don't want
|
||||
to duplicate effort.
|
||||
* Search [GitHub](https://github.com/angular/angular.js/pulls) for an open or closed Pull Request
|
||||
that relates to your submission. You don't want to duplicate effort.
|
||||
* Please sign our [Contributor License Agreement (CLA)](#signing-the-cla) before sending pull
|
||||
requests. We cannot accept code without this.
|
||||
* Make your changes in a new git branch
|
||||
|
||||
```shell
|
||||
git checkout -b my-fix-branch master
|
||||
```
|
||||
|
||||
* Create your patch, including appropriate test cases.
|
||||
* Follow our Coding Rules
|
||||
* Follow our Git Commit Guidelines
|
||||
* Build your changes locally and on Travis (by pushing to GitHub) to ensure all the tests pass.
|
||||
* Sign the Contributor License Agreement (CLA). We cannot accept code without this.
|
||||
* Commit your changes and create a descriptive commit message (the
|
||||
commit message is used to generate release notes, please check out our
|
||||
[commit message conventions](#commit-message-format) and our commit message presubmit hook
|
||||
`validate-commit-msg.js`):
|
||||
|
||||
```shell
|
||||
git commit -a
|
||||
```
|
||||
|
||||
* Build your changes locally to ensure all the tests pass
|
||||
|
||||
```shell
|
||||
grunt test
|
||||
```
|
||||
|
||||
* Push your branch to Github:
|
||||
|
||||
```shell
|
||||
git push origin my-fix-branch
|
||||
```
|
||||
|
||||
* In Github, send a pull request to `angular:master`.
|
||||
* If we suggest changes then you can modify your branch, rebase and force a new push to your GitHub
|
||||
repository to update the Pull Request.
|
||||
repository to update the Pull Request:
|
||||
|
||||
```shell
|
||||
git rebase master -i
|
||||
git push -f
|
||||
```
|
||||
|
||||
That's it! Thank you for your contribution!
|
||||
|
||||
When the patch is reviewed and merged, you can safely delete your branch and pull the changes
|
||||
from the main (upstream) repository:
|
||||
|
||||
* Delete the remote branch on Github:
|
||||
|
||||
```shell
|
||||
git push origin --delete my-fix-branch
|
||||
```
|
||||
|
||||
* Check out the master branch:
|
||||
|
||||
```shell
|
||||
git checkout master -f
|
||||
```
|
||||
|
||||
* Delete the local branch:
|
||||
|
||||
```shell
|
||||
git branch -D my-fix-branch
|
||||
```
|
||||
|
||||
* Update your master with the latest upstream version:
|
||||
|
||||
```shell
|
||||
git pull --ff upstream master
|
||||
```
|
||||
|
||||
### GitHub Pull Request Helper
|
||||
|
||||
We track Pull Requests by attaching labels and assigning to milestones. For some reason GitHub
|
||||
does not provide a good UI for managing labels on Pull Requests (unlike Issues). We have developed
|
||||
a simple Chrome Extension that enables you to view (and manage if you have permission) the labels
|
||||
on Pull Requests. You can get the extension from the Chrome WebStore -
|
||||
[GitHub PR Helper][github-pr-helper]
|
||||
|
||||
## Coding Rules
|
||||
To ensure consistency throughout the source code, keep these rules in mind as you are working:
|
||||
@@ -106,6 +198,7 @@ Must be one of the following:
|
||||
* **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
|
||||
* **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
|
||||
generation
|
||||
@@ -146,56 +239,6 @@ You can find out more detailed information about contributing in the
|
||||
[AngularJS documentation][contributing].
|
||||
|
||||
|
||||
## Submitting Your Changes
|
||||
|
||||
To create and submit a change:
|
||||
|
||||
1. Please sign our [Contributor License Agreement (CLA)](#signing-the-cla) before sending pull
|
||||
requests.
|
||||
|
||||
2. Create and checkout a new branch off the master branch for your changes:
|
||||
|
||||
```shell
|
||||
git checkout -b my-fix-branch master
|
||||
```
|
||||
|
||||
3. Create your patch, including appropriate test cases.
|
||||
|
||||
4. Commit your changes and create a descriptive commit message (the commit message is used to
|
||||
generate release notes, please check out our [commit message conventions](#commit-message-format)
|
||||
and our commit message presubmit hook `validate-commit-msg.js`):
|
||||
|
||||
```shell
|
||||
git commit -a
|
||||
```
|
||||
|
||||
5. Push your branch to Github:
|
||||
|
||||
```shell
|
||||
git push origin my-fix-branch
|
||||
```
|
||||
|
||||
6. In Github, send a pull request to `angular:master`.
|
||||
|
||||
That's it! Thank you for your contribution!
|
||||
|
||||
When the patch is reviewed and merged, you can safely delete your branch and pull the changes
|
||||
from the main (upstream) repository:
|
||||
|
||||
```shell
|
||||
# Delete the remote branch on Github:
|
||||
git push origin :my-fix-branch
|
||||
|
||||
# Check out the master branch:
|
||||
git checkout master
|
||||
|
||||
# Delete the local branch:
|
||||
git branch -D my-fix-branch
|
||||
|
||||
# Update your master with the latest upstream version:
|
||||
git pull --ff upstream master
|
||||
```
|
||||
|
||||
|
||||
[github]: https://github.com/angular/angular.js
|
||||
[Google Closure I18N library]: https://code.google.com/p/closure-library/source/browse/closure/goog/i18n/
|
||||
@@ -214,3 +257,4 @@ git pull --ff upstream master
|
||||
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
|
||||
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
|
||||
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
||||
[github-pr-helper]: https://chrome.google.com/webstore/detail/github-pr-helper/mokbklfnaddkkbolfldepnkfmanfhpen
|
||||
|
||||
+4
-11
@@ -106,45 +106,38 @@ module.exports = function(grunt) {
|
||||
},
|
||||
|
||||
jshint: {
|
||||
options: {
|
||||
jshintrc: true,
|
||||
},
|
||||
ng: {
|
||||
files: { src: files['angularSrc'] },
|
||||
options: { jshintrc: 'src/.jshintrc' }
|
||||
},
|
||||
ngAnimate: {
|
||||
files: { src: 'src/ngAnimate/**/*.js' },
|
||||
options: { jshintrc: 'src/ngAnimate/.jshintrc' }
|
||||
},
|
||||
ngCookies: {
|
||||
files: { src: 'src/ngCookies/**/*.js' },
|
||||
options: { jshintrc: 'src/ngCookies/.jshintrc' }
|
||||
},
|
||||
ngLocale: {
|
||||
files: { src: 'src/ngLocale/**/*.js' },
|
||||
options: { jshintrc: 'src/ngLocale/.jshintrc' }
|
||||
},
|
||||
ngMock: {
|
||||
files: { src: 'src/ngMock/**/*.js' },
|
||||
options: { jshintrc: 'src/ngMock/.jshintrc' }
|
||||
},
|
||||
ngResource: {
|
||||
files: { src: 'src/ngResource/**/*.js' },
|
||||
options: { jshintrc: 'src/ngResource/.jshintrc' }
|
||||
},
|
||||
ngRoute: {
|
||||
files: { src: 'src/ngRoute/**/*.js' },
|
||||
options: { jshintrc: 'src/ngRoute/.jshintrc' }
|
||||
},
|
||||
ngSanitize: {
|
||||
files: { src: 'src/ngSanitize/**/*.js' },
|
||||
options: { jshintrc: 'src/ngSanitize/.jshintrc' }
|
||||
},
|
||||
ngScenario: {
|
||||
files: { src: 'src/ngScenario/**/*.js' },
|
||||
options: { jshintrc: 'src/ngScenario/.jshintrc' }
|
||||
},
|
||||
ngTouch: {
|
||||
files: { src: 'src/ngTouch/**/*.js' },
|
||||
options: { jshintrc: 'src/ngTouch/.jshintrc' }
|
||||
}
|
||||
},
|
||||
|
||||
@@ -178,7 +171,7 @@ module.exports = function(grunt) {
|
||||
},
|
||||
mocks: {
|
||||
dest: 'build/angular-mocks.js',
|
||||
src: files['angularModules']['ngMock'],
|
||||
src: util.wrap(files['angularModules']['ngMock'], 'module'),
|
||||
strict: false
|
||||
},
|
||||
sanitize: {
|
||||
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
# Triage new issues/PRs on github
|
||||
|
||||
This document shows the steps the Angular team is using to triage issues.
|
||||
The labels are used later on for planning releases.
|
||||
|
||||
## Tips ##
|
||||
|
||||
* install [github pr helper extension](https://github.com/petebacondarwin/github-pr-helper) and become 356% more productive
|
||||
* Label "resolution:*"
|
||||
* these tags can be used for labeling a closed issue/PR with a reason why it was closed. (we can add reasons as we need them, right there are only a few rejection reasons. it doesn't make sense to label issues that were fixed or prs that were merged)
|
||||
|
||||
|
||||
## Automatic processing ##
|
||||
|
||||
We have automatic tools (e.g. Mary Poppins) that automatically add comments / labels to issues and PRs.
|
||||
The following is done automatically and should not be done manually:
|
||||
|
||||
* Label "cla: yes" or "cla: no" for pull requests
|
||||
|
||||
## Process ##
|
||||
|
||||
1. Open list of [non triaged issues](https://github.com/angular/angular.js/issues?direction=desc&milestone=none&page=1&sort=created&state=open)
|
||||
1. Assign yourself: Pick an issue that is not assigned to anyone and assign it to you
|
||||
1. Assign milestone:
|
||||
* "Docs only" milestone - for documentation PR -> **Done**.
|
||||
* Current/next milestone - regressions
|
||||
* 1.2.x - everything else
|
||||
1. Label "GH: *" (to be automated via Mary Poppins)
|
||||
* PR - issue is a PR
|
||||
* issue - otherwise
|
||||
1. Bugs:
|
||||
* Label "Type: Bug"
|
||||
* Label "Type: Regression" - if the bug is a regression
|
||||
* Duplicate? - Check if there are comments pointing out that this is a dupe, if they do exist verify that this is indeed a dupe and close it and go to the last step
|
||||
* Reproducible? - Steps to reproduce the bug are clear, if not ask for clarification (ideally plunker or fiddle)
|
||||
* Reproducible on master? - http://code.angularjs.org/snapshot/
|
||||
|
||||
1. Non bugs:
|
||||
* Label "Type: Feature" or "Type: Chore" or "Type: Perf"
|
||||
* Label "needs: breaking change" - if needed
|
||||
* Label "needs: public api" - if a new public api is needed
|
||||
* Understandable? - verify if the description of the request is clear. if not ask for clarification
|
||||
* Goals of angular core? - Often new features should be implemented as a third-party module rather than an addition to the core.
|
||||
|
||||
1. Label "component: *"
|
||||
* In rare cases, it's ok to have multiple components.
|
||||
1. Label "impact: *"
|
||||
* small - obscure issue affecting one or handful of developers
|
||||
* medium - impacts some usage patterns
|
||||
* large - impacts most or all of angular apps
|
||||
1. Label "complexity: *"
|
||||
* small - trivial change
|
||||
* medium - non-trivial but straightforward change
|
||||
* large - changes to many components in angular or any changes to $compile, ngRepeat or other "fun" components
|
||||
1. Label "PRs welcome" for "GH: issue"
|
||||
* if complexity is small or medium and the problem as well as solution are well captured in the issue
|
||||
1. Label "origin: google" for issues from Google
|
||||
1. Label "high priority" for security issues, major performance regressions or memory leaks
|
||||
|
||||
1. Unassign yourself from the issue
|
||||
|
||||
Vendored
+1
@@ -27,6 +27,7 @@ angularFiles = {
|
||||
'src/ng/parse.js',
|
||||
'src/ng/q.js',
|
||||
'src/ng/rootScope.js',
|
||||
'src/ng/sanitizeUri.js',
|
||||
'src/ng/sce.js',
|
||||
'src/ng/sniffer.js',
|
||||
'src/ng/timeout.js',
|
||||
|
||||
+3
-1
@@ -142,6 +142,7 @@ var writeChangelog = function(stream, commits, version) {
|
||||
var sections = {
|
||||
fix: {},
|
||||
feat: {},
|
||||
perf: {},
|
||||
breaks: {}
|
||||
};
|
||||
|
||||
@@ -169,6 +170,7 @@ var writeChangelog = function(stream, commits, version) {
|
||||
stream.write(util.format(HEADER_TPL, version, version, currentDate()));
|
||||
printSection(stream, 'Bug Fixes', sections.fix);
|
||||
printSection(stream, 'Features', sections.feat);
|
||||
printSection(stream, 'Performance Improvements', sections.perf);
|
||||
printSection(stream, 'Breaking Changes', sections.breaks, false);
|
||||
}
|
||||
|
||||
@@ -186,7 +188,7 @@ var getPreviousTag = function() {
|
||||
var generate = function(version, file) {
|
||||
getPreviousTag().then(function(tag) {
|
||||
console.log('Reading git log since', tag);
|
||||
readGitLog('^fix|^feat|BREAKING', tag).then(function(commits) {
|
||||
readGitLog('^fix|^feat|^perf|BREAKING', tag).then(function(commits) {
|
||||
console.log('Parsed', commits.length, 'commits');
|
||||
console.log('Generating changelog to', file || 'stdout', '(', version, ')');
|
||||
writeChangelog(file ? fs.createWriteStream(file) : process.stdout, commits, version);
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
<a name="v1.0.0rc3"></a>
|
||||
# v1.0.0rc3 (2012-03-27)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- create new (isolate) scopes for directives on root elements ([5390fb37](https://github.com/angular/angular.js/commit/5390fb37d2c01937922613fc57df4986af521787), closes [#817](https://github.com/angular/angular.js/issues/817))
|
||||
- don't touch static element attributes ([9cb2195e](https://github.com/angular/angular.js/commit/9cb2195e61a78e99020ec19d687a221ca88b5900))
|
||||
- Merge interpolated css class when replacing an element ([f49eaf8b](https://github.com/angular/angular.js/commit/f49eaf8bf2df5f4e0e82d6c89e849a4f82c8d414))
|
||||
- **$http:**
|
||||
- don't send Content-Type header when no data ([1a5bebd9](https://github.com/angular/angular.js/commit/1a5bebd927ecd22f9c34617642fdf58fe3f62efb), closes [#749](https://github.com/angular/angular.js/issues/749))
|
||||
- **$log:**
|
||||
- avoid console.log.apply calls in IE ([15213ec2](https://github.com/angular/angular.js/commit/15213ec212769837cb2b7e781ffc5bfd598d27ca), closes [#805](https://github.com/angular/angular.js/issues/805))
|
||||
- **$resource:**
|
||||
- support escaping of ':' in resource url ([6d6f8753](https://github.com/angular/angular.js/commit/6d6f875345e01f2c6c63ef95164f6f39e923da15))
|
||||
- **compiler:**
|
||||
- allow transclusion of root elements ([9918b748](https://github.com/angular/angular.js/commit/9918b748be01266eb10db39d51b4d3098d54ab66))
|
||||
- **e2e runner:**
|
||||
- fix typo that caused errors on IE8 ([ee5a5352](https://github.com/angular/angular.js/commit/ee5a5352fd4b94cedee6ef20d4bf2d43ce77e00b), closes [#806](https://github.com/angular/angular.js/issues/806))
|
||||
- **forEach:**
|
||||
- should ignore prototypically inherited properties ([8d7e6948](https://github.com/angular/angular.js/commit/8d7e6948496ff26ef1da8854ba02fcb8eebfed61), closes [#813](https://github.com/angular/angular.js/issues/813))
|
||||
- **forms:**
|
||||
- Remove double registering of form ([1faafa31](https://github.com/angular/angular.js/commit/1faafa31582c4e9413f48dc7d12f5b681f9fe9fd))
|
||||
- Set ng-valid/ng-invalid correctly ([08bfea18](https://github.com/angular/angular.js/commit/08bfea183a850b29da270eac47f80b598cbe600f))
|
||||
- **init:**
|
||||
- use jQuery#ready for init if available ([cb2ad9ab](https://github.com/angular/angular.js/commit/cb2ad9abf24e6f855cc749efe3155bd7987ece9d), closes [#818](https://github.com/angular/angular.js/issues/818))
|
||||
- **json:**
|
||||
- added support for iso8061 timezone ([5ac14f63](https://github.com/angular/angular.js/commit/5ac14f633a69f49973b5512780c6ec7752405967))
|
||||
- **matchers.toHaveClass:**
|
||||
- Correct reference to angular.mock.dump ([f701ce08](https://github.com/angular/angular.js/commit/f701ce08f9d63be05fc3b92f57ad473e1e749b2d))
|
||||
- **ng-switch:**
|
||||
- properly destroy child scopes ([2315d9b3](https://github.com/angular/angular.js/commit/2315d9b3610994b36c44e4a97fb1427d59471ce8))
|
||||
- **ngDocSpec:**
|
||||
- fix broken tests ([53b6f522](https://github.com/angular/angular.js/commit/53b6f522a56eea314cbd084816e08f24b2c7879f))
|
||||
- **ngForm:**
|
||||
- alias name||ngForm ([823adb23](https://github.com/angular/angular.js/commit/823adb231995e917bc060bfa49453e2a96bac2b6))
|
||||
- **ngRepeat:**
|
||||
- correct variable reference in error message ([935c1018](https://github.com/angular/angular.js/commit/935c1018da05dbf3124b2dd33619c4a3c82d7a2a))
|
||||
- **ngView:**
|
||||
- controller not published ([21e74c2d](https://github.com/angular/angular.js/commit/21e74c2d2e8e985b23711785287feb59965cbd90))
|
||||
- **q:**
|
||||
- resolve all of nothing to nothing ([ac75079e](https://github.com/angular/angular.js/commit/ac75079e2113949d5d64adbcf23d56f3cf295d41))
|
||||
- **select:**
|
||||
- multiselect failes to update view on selection insert ([6ecac8e7](https://github.com/angular/angular.js/commit/6ecac8e71a84792a434d21db2c245b3648c55f18))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$compile:**
|
||||
- do not interpolate boolean attributes, rather evaluate them ([a08cbc02](https://github.com/angular/angular.js/commit/a08cbc02e78e789a66e9af771c410e8ad1646e25))
|
||||
- **$controller:**
|
||||
- support controller registration via $controllerProvider ([d54dfecb](https://github.com/angular/angular.js/commit/d54dfecb00fba41455536c5ddd55310592fdaf84))
|
||||
- **$route:**
|
||||
- when matching consider trailing slash as optional ([a4fe51da](https://github.com/angular/angular.js/commit/a4fe51da3ba0dc297ecd389e230d6664f250c9a6), closes [#784](https://github.com/angular/angular.js/issues/784))
|
||||
- **assertArgFn:**
|
||||
- should support array annotated fns ([4b8d9260](https://github.com/angular/angular.js/commit/4b8d926062eb4d4483555bdbdec4656f585ab40b))
|
||||
- **http:**
|
||||
- added params parameter ([73c85930](https://github.com/angular/angular.js/commit/73c8593077155a9f2e8ef42efd4c497eba0bef4f))
|
||||
- **injector:**
|
||||
- infer _foo_ as foo ([f13dd339](https://github.com/angular/angular.js/commit/f13dd3393dfb7a33565c9360342c193bc0bddcb6))
|
||||
- **input.radio:**
|
||||
- Allow value attribute to be interpolated ([ade6c452](https://github.com/angular/angular.js/commit/ade6c452753145c84884d17027a7865bf4b34b0c))
|
||||
- **jqLite:**
|
||||
- make injector() and scope() work with the document object ([5fdab52d](https://github.com/angular/angular.js/commit/5fdab52dd7c269f99839f4fa6b5854d9548269fa))
|
||||
- add .controller() method ([6c5a05ad](https://github.com/angular/angular.js/commit/6c5a05ad49a1e083570c3dfe331403398f899dbe))
|
||||
- **ngValue:**
|
||||
- allow radio inputs to have non string values ([09e175f0](https://github.com/angular/angular.js/commit/09e175f02cca0f4a295fd0c9b980cd8f432e722b), closes [#816](https://github.com/angular/angular.js/issues/816))
|
||||
- **scope:**
|
||||
- broadcast $destroy event on scope destruction ([9b1aff90](https://github.com/angular/angular.js/commit/9b1aff905b638aa274a5fc8f88662df446d374bd))
|
||||
- **scope.$eval:**
|
||||
- Allow passing locals to the expression ([192ff61f](https://github.com/angular/angular.js/commit/192ff61f5d61899e667c6dbce4d3e6e399429d8b))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- boolean attrs are evaluated rather than interpolated ([a08cbc02](https://github.com/angular/angular.js/commit/a08cbc02e78e789a66e9af771c410e8ad1646e25))
|
||||
- ng-bind-attr directive removed ([55027132](https://github.com/angular/angular.js/commit/55027132f3d57e5dcf94683e6e6bd7b0aae0087d))
|
||||
- any app that depends on this service and its fallback to Modernizr, please ([aaedefb9](https://github.com/angular/angular.js/commit/aaedefb92e6bec6626e173e5155072c91471596a))
|
||||
|
||||
Vendored
+56
-2
@@ -225,6 +225,11 @@ angular.uppercase = function(s) {};
|
||||
*/
|
||||
angular.Attributes;
|
||||
|
||||
/**
|
||||
* @type {Object.<string, string>}
|
||||
*/
|
||||
angular.Attributes.$attr;
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @return {string}
|
||||
@@ -1048,6 +1053,10 @@ angular.$http;
|
||||
*/
|
||||
angular.$http.Config;
|
||||
|
||||
angular.$http.Config.transformRequest;
|
||||
|
||||
angular.$http.Config.transformResponse;
|
||||
|
||||
// /**
|
||||
// * This extern is currently incomplete as delete is a reserved word.
|
||||
// * To use delete, index $http.
|
||||
@@ -1154,6 +1163,13 @@ angular.$http.HttpPromise.error = function(callback) {};
|
||||
*/
|
||||
angular.$http.Response;
|
||||
|
||||
angular.$HttpProvider;
|
||||
|
||||
/**
|
||||
* @type {angular.$http.Config}
|
||||
*/
|
||||
angular.$HttpProvider.defaults;
|
||||
|
||||
/******************************************************************************
|
||||
* $injector Service
|
||||
*****************************************************************************/
|
||||
@@ -1425,6 +1441,11 @@ angular.NgModelController.prototype.$viewValue;
|
||||
*/
|
||||
angular.FormController = function() {};
|
||||
|
||||
/**
|
||||
* @param {*} control
|
||||
*/
|
||||
angular.FormController.prototype.$addControl = function(control) {};
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
@@ -1440,11 +1461,39 @@ angular.FormController.prototype.$error;
|
||||
*/
|
||||
angular.FormController.prototype.$invalid;
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
angular.FormController.prototype.$name;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
angular.FormController.prototype.$pristine;
|
||||
|
||||
/**
|
||||
* @param {*} control
|
||||
*/
|
||||
angular.FormController.prototype.$removeControl = function(control) {};
|
||||
|
||||
/**
|
||||
* @type {function()}
|
||||
*/
|
||||
angular.FormController.prototype.$setDirty = function() {};
|
||||
|
||||
/**
|
||||
* @type {function()}
|
||||
*/
|
||||
angular.FormController.prototype.$setPristine = function() {};
|
||||
|
||||
/**
|
||||
* @param {string} validationToken
|
||||
* @param {boolean} isValid
|
||||
* @param {*} control
|
||||
*/
|
||||
angular.FormController.prototype.$setValidity = function(
|
||||
validationToken, isValid, control) {};
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
@@ -1578,6 +1627,7 @@ angular.$q.when = function(value) {};
|
||||
* @typedef {{
|
||||
* resolve: function(*=),
|
||||
* reject: function(*=),
|
||||
* notify: function(*=),
|
||||
* promise: angular.$q.Promise
|
||||
* }}
|
||||
*/
|
||||
@@ -1589,6 +1639,9 @@ angular.$q.Deferred.resolve = function(opt_value) {};
|
||||
/** @param {*=} opt_reason */
|
||||
angular.$q.Deferred.reject = function(opt_reason) {};
|
||||
|
||||
/** @param {*=} opt_value */
|
||||
angular.$q.Deferred.notify = function(opt_value) {};
|
||||
|
||||
/** @type {angular.$q.Promise} */
|
||||
angular.$q.Deferred.promise;
|
||||
|
||||
@@ -1689,7 +1742,8 @@ angular.$routeProvider.when = function(path, route) {};
|
||||
* resolve: (Object.<string, (
|
||||
* string|Function|Array.<string|Function>|angular.$q.Promise
|
||||
* )>|undefined),
|
||||
* redirectTo: (string|function()|undefined),
|
||||
* redirectTo: (
|
||||
* string|function(Object.<string>, string, Object): string|undefined),
|
||||
* reloadOnSearch: (boolean|undefined)
|
||||
* }}
|
||||
*/
|
||||
@@ -1712,7 +1766,7 @@ angular.$routeProvider.Params.templateUrl;
|
||||
*/
|
||||
angular.$routeProvider.Params.resolve;
|
||||
|
||||
/** @type {string|function()} */
|
||||
/** @type {string|function(Object.<string>, string, Object): string} */
|
||||
angular.$routeProvider.Params.redirectTo;
|
||||
|
||||
/** @type {boolean} */
|
||||
|
||||
+4
-4
@@ -15,8 +15,8 @@ ng\:form {
|
||||
* when the active class isn't set, or if the active class doesn't
|
||||
* contain any styles to transition to, then, if ngAnimate is used,
|
||||
* it will appear as if the webpage is broken due to the forever hanging
|
||||
* animations. The clip (!ie) and zoom (ie) CSS properties are used
|
||||
* since they trigger a transition without making the browser
|
||||
* animations. The border-spacing (!ie) and zoom (ie) CSS properties are
|
||||
* used below since they trigger a transition without making the browser
|
||||
* animate anything and they're both highly underused CSS properties */
|
||||
.ng-animate-start { clip:rect(0, auto, auto, 0); -ms-zoom:1.0001; }
|
||||
.ng-animate-active { clip:rect(-1px, auto, auto, 0); -ms-zoom:1; }
|
||||
.ng-animate-start { border-spacing:1px 1px; -ms-zoom:1.0001; }
|
||||
.ng-animate-active { border-spacing:0px 0px; -ms-zoom:1; }
|
||||
|
||||
@@ -5,7 +5,7 @@ describe('Docs Annotations', function() {
|
||||
var body;
|
||||
beforeEach(function() {
|
||||
body = angular.element(document.body);
|
||||
body.html('');
|
||||
body.empty();
|
||||
});
|
||||
|
||||
var normalizeHtml = function(html) {
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ function escape(text) {
|
||||
function setHtmlIe8SafeWay(element, html) {
|
||||
var newElement = angular.element('<pre>' + html + '</pre>');
|
||||
|
||||
element.html('');
|
||||
element.empty();
|
||||
element.append(newElement.contents());
|
||||
return element;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
@fullName Orphan ngTransclude Directive
|
||||
@description
|
||||
|
||||
Occurs when an `ngTransclude` occurs without a transcluded ancesstor element.
|
||||
Occurs when an `ngTransclude` occurs without a transcluded ancestor element.
|
||||
|
||||
This error often occurs when you have forgotten to set `transclude: true` in some directive definition, and then used `ngTranslude` in the driective's template.
|
||||
This error often occurs when you have forgotten to set `transclude: true` in some directive definition, and then used `ngTransclude` in the directive's template.
|
||||
|
||||
To resolve, either remove the offending `ngTransclude` or check that `transclude: true` is included in the intended directive definition.
|
||||
|
||||
|
||||
@@ -190,6 +190,7 @@ This should help give you an idea of what Angular does internally.
|
||||
<pre>
|
||||
var $compile = ...; // injected into your code
|
||||
var scope = ...;
|
||||
var parent = ...; // DOM element where the compiled template can be appended
|
||||
|
||||
var html = '<div ng-bind="exp"></div>';
|
||||
|
||||
@@ -200,7 +201,10 @@ This should help give you an idea of what Angular does internally.
|
||||
var linkFn = $compile(template);
|
||||
|
||||
// Step 3: link the compiled template with the scope.
|
||||
linkFn(scope);
|
||||
var element = linkFn(scope);
|
||||
|
||||
// Step 4: Append to DOM (optional)
|
||||
parent.appendChild(element);
|
||||
</pre>
|
||||
|
||||
### The difference between Compile and Link
|
||||
@@ -350,7 +354,7 @@ Creating local properties on widget scope creates two problems:
|
||||
|
||||
|
||||
To solve the issue of lack of isolation, the directive declares a new `isolated` scope. An
|
||||
isolated scope does not prototypically inherit from the child scope, and therefore we don't have
|
||||
isolated scope does not prototypically inherit from the parent scope, and therefore we don't have
|
||||
to worry about accidentally clobbering any properties.
|
||||
|
||||
However `isolated` scope creates a new problem: if a transcluded DOM is a child of the widget
|
||||
|
||||
@@ -270,7 +270,7 @@ When Angular starts, it will use the configuration of the module with the name d
|
||||
including the configuration of all modules that this module depends on.
|
||||
|
||||
In the example above:
|
||||
The template contains the directive `ng-app="invoice"`. This tells Angular
|
||||
The template contains the directive `ng-app="invoice2"`. This tells Angular
|
||||
to use the `invoice` module as the main module for the application.
|
||||
The code snippet `angular.module('invoice', ['finance'])` specifies that the `invoice` module depends on the
|
||||
`finance` module. By this, Angular uses the `InvoiceController` as well as the `currencyConverter` service.
|
||||
|
||||
@@ -13,45 +13,44 @@ Unit testing as the name implies is about testing individual units of code. Unit
|
||||
answer questions such as "Did I think about the logic correctly?" or "Does the sort function order the list
|
||||
in the right order?"
|
||||
|
||||
In order to answer such question it is very important that we can isolate the unit of code under test.
|
||||
In order to answer such a question it is very important that we can isolate the unit of code under test.
|
||||
That is because when we are testing the sort function we don't want to be forced into creating
|
||||
related pieces such as the DOM elements, or making any XHR calls in getting the data to sort.
|
||||
|
||||
While
|
||||
this may seem obvious it usually is very difficult to be able to call an individual function on a
|
||||
typical project. The reason is that the developers often mix concerns, and they end up with a
|
||||
piece of code which does everything. It reads the data from XHR, it sorts it and then it
|
||||
While this may seem obvious it can be very difficult to call an individual function on a
|
||||
typical project. The reason is that the developers often mix concerns resulting in a
|
||||
piece of code which does everything. It makes an XHR request, it sorts the response data and then it
|
||||
manipulates the DOM.
|
||||
|
||||
With Angular we try to make it easy for you to do the right thing, and so we
|
||||
provide dependency injection for your XHR (which you can mock out) and we created abstraction which
|
||||
provide dependency injection for your XHR (which you can mock out) and we created abstractions which
|
||||
allow you to sort your model without having to resort to manipulating the DOM. So that in the end,
|
||||
it is easy to write a sort function which sorts some data, so that your test can create a data set,
|
||||
apply the function, and assert that the resulting model is in the correct order. The test does not
|
||||
have to wait for XHR, or create the right kind of DOM, or assert that your function has mutated the
|
||||
DOM in the right way.
|
||||
have to wait for the XHR response to arrive, create the right kind of test DOM, nor assert that your
|
||||
function has mutated the DOM in the right way.
|
||||
|
||||
## With great power comes great responsibility
|
||||
|
||||
Angular is written with testability in mind, but it still requires that you
|
||||
do the right thing. We tried to make the right thing easy, but Angular is not magic, which means if
|
||||
you don't follow these guidelines you may very well end up with an untestable application.
|
||||
Angular is written with testability in mind, but it still requires that you do the right thing.
|
||||
We tried to make the right thing easy, but Angular is not magic. If you don't follow these guidelines
|
||||
you may very well end up with an untestable application.
|
||||
|
||||
## Dependency Injection
|
||||
There are several ways in which you can get a hold of a dependency:
|
||||
1. You could create it using the `new` operator.
|
||||
2. You could look for it in a well known place, also known as global singleton.
|
||||
3. You could ask a registry (also known as service registry) for it. (But how do you get a hold of
|
||||
the registry? Most likely by looking it up in a well known place. See #2)
|
||||
4. You could expect that it be handed to you.
|
||||
There are several ways in which you can get a hold of a dependency. You can:
|
||||
1. Create it using the `new` operator.
|
||||
2. Look for it in a well-known place, also known as a global singleton.
|
||||
3. Ask a registry (also known as service registry) for it. (But how do you get a hold of
|
||||
the registry? Most likely by looking it up in a well known place. See #2.)
|
||||
4. Expect it to be handed to you.
|
||||
|
||||
Out of the four options in the list above, only the last one is testable. Let's look at why:
|
||||
|
||||
### Using the `new` operator
|
||||
|
||||
While there is nothing wrong with the `new` operator fundamentally the issue is that calling a new
|
||||
on a constructor permanently binds the call site to the type. For example lets say that we are
|
||||
trying to instantiate an `XHR` so that we can get some data from the server.
|
||||
While there is nothing wrong with the `new` operator fundamentally, a problem arises when calling `new`
|
||||
on a constructor. This permanently binds the call site to the type. For example, lets say that we try to
|
||||
instantiate an `XHR` that will retrieve data from the server.
|
||||
|
||||
<pre>
|
||||
function MyClass() {
|
||||
@@ -64,12 +63,12 @@ function MyClass() {
|
||||
}
|
||||
</pre>
|
||||
|
||||
The issue becomes that in tests, we would very much like to instantiate a `MockXHR` which would
|
||||
A problem surfaces in tests when we would like to instantiate a `MockXHR` that would
|
||||
allow us to return fake data and simulate network failures. By calling `new XHR()` we are
|
||||
permanently bound to the actual XHR, and there is no good way to replace it. Yes there is monkey
|
||||
patching. That is a bad idea for many reasons which are outside the scope of this document.
|
||||
permanently bound to the actual XHR and there is no way to replace it. Yes, we could monkey
|
||||
patch, but that is a bad idea for many reasons which are outside the scope of this document.
|
||||
|
||||
The class above is hard to test since we have to resort to monkey patching:
|
||||
Here's an example of how the class above becomes hard to test when resorting to monkey patching:
|
||||
<pre>
|
||||
var oldXHR = XHR;
|
||||
XHR = function MockXHR() {};
|
||||
@@ -81,7 +80,7 @@ XHR = oldXHR; // if you forget this bad things will happen
|
||||
|
||||
|
||||
### Global look-up:
|
||||
Another way to approach the problem is to look for the service in a well known location.
|
||||
Another way to approach the problem is to look for the service in a well-known location.
|
||||
|
||||
<pre>
|
||||
function MyClass() {
|
||||
@@ -95,14 +94,14 @@ function MyClass() {
|
||||
}
|
||||
</pre>
|
||||
|
||||
While no new instance of the dependency is being created, it is fundamentally the same as `new`, in
|
||||
that there is no good way to intercept the call to `global.xhr` for testing purposes, other then
|
||||
While no new dependency instance is created, it is fundamentally the same as `new` in
|
||||
that no way exists to intercept the call to `global.xhr` for testing purposes, other then
|
||||
through monkey patching. The basic issue for testing is that a global variable needs to be mutated in
|
||||
order to replace it with call to a mock method. For further explanation why this is bad see: {@link
|
||||
order to replace it with call to a mock method. For further explanation of why this is bad see: {@link
|
||||
http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/ Brittle Global
|
||||
State & Singletons}
|
||||
|
||||
The class above is hard to test since we have to change global state:
|
||||
The class above is hard to test since we have to change the global state:
|
||||
<pre>
|
||||
var oldXHR = global.xhr;
|
||||
global.xhr = function mockXHR() {};
|
||||
@@ -115,7 +114,7 @@ global.xhr = oldXHR; // if you forget this bad things will happen
|
||||
|
||||
### Service Registry:
|
||||
|
||||
It may seem as that this can be solved by having a registry for all of the services, and then
|
||||
It may seem that this can be solved by having a registry of all the services and then
|
||||
having the tests replace the services as needed.
|
||||
|
||||
<pre>
|
||||
@@ -131,12 +130,12 @@ function MyClass() {
|
||||
}
|
||||
</pre>
|
||||
|
||||
However, where does the serviceRegistry come from? if it is:
|
||||
* `new`-ed up, the test has no chance to reset the services for testing
|
||||
* global look-up, then the service returned is global as well (but resetting is easier, since
|
||||
there is only one global variable to be reset).
|
||||
However, where does the serviceRegistry come from? If it is:
|
||||
* `new`-ed up, the test has no chance to reset the services for testing.
|
||||
* a global look-up then the service returned is global as well (but resetting is easier, since
|
||||
only one global variable exists to be reset).
|
||||
|
||||
The class above is hard to test since we have to change global state:
|
||||
The class above is hard to test since we have to change the global state:
|
||||
<pre>
|
||||
var oldServiceLocator = global.serviceLocator;
|
||||
global.serviceLocator.set('xhr', function mockXHR() {});
|
||||
@@ -148,7 +147,7 @@ global.serviceLocator = oldServiceLocator; // if you forget this bad things will
|
||||
|
||||
|
||||
### Passing in Dependencies:
|
||||
Lastly the dependency can be passed in.
|
||||
Last, the dependency can be passed in.
|
||||
|
||||
<pre>
|
||||
function MyClass(xhr) {
|
||||
@@ -161,12 +160,12 @@ function MyClass(xhr) {
|
||||
}
|
||||
</pre>
|
||||
|
||||
This is the preferred way since the code makes no assumptions as to where the `xhr` comes from,
|
||||
rather that whoever created the class was responsible for passing it in. Since the creator of the
|
||||
This is the preferred method since the code makes no assumptions about the origin of `xhr` and cares
|
||||
instead about whoever created the class responsible for passing it in. Since the creator of the
|
||||
class should be different code than the user of the class, it separates the responsibility of
|
||||
creation from the logic, and that is what dependency-injection is in a nutshell.
|
||||
creation from the logic. This is dependency-injection is in a nutshell.
|
||||
|
||||
The class above is very testable, since in the test we can write:
|
||||
The class above is testable, since in the test we can write:
|
||||
<pre>
|
||||
function xhrMock(args) {...}
|
||||
var myClass = new MyClass(xhrMock);
|
||||
@@ -176,12 +175,12 @@ myClass.doWork();
|
||||
|
||||
Notice that no global variables were harmed in the writing of this test.
|
||||
|
||||
Angular comes with {@link di dependency injection} built in which makes the right thing
|
||||
Angular comes with {@link di dependency injection} built-in, making the right thing
|
||||
easy to do, but you still need to do it if you wish to take advantage of the testability story.
|
||||
|
||||
## Controllers
|
||||
What makes each application unique is its logic, which is what we would like to test. If the logic
|
||||
for your application is mixed in with DOM manipulation, it will be hard to test as in the example
|
||||
What makes each application unique is its logic, and the logic is what we would like to test. If the logic
|
||||
for your application contains DOM manipulation, it will be hard to test. See the example
|
||||
below:
|
||||
|
||||
<pre>
|
||||
@@ -209,7 +208,7 @@ function PasswordCtrl() {
|
||||
}
|
||||
</pre>
|
||||
|
||||
The code above is problematic from a testability point of view, since it requires your test to have the right kind
|
||||
The code above is problematic from a testability point of view since it requires your test to have the right kind
|
||||
of DOM present when the code executes. The test would look like this:
|
||||
|
||||
<pre>
|
||||
@@ -223,11 +222,11 @@ var pc = new PasswordCtrl();
|
||||
input.val('abc');
|
||||
pc.grade();
|
||||
expect(span.text()).toEqual('weak');
|
||||
$('body').html('');
|
||||
$('body').empty();
|
||||
</pre>
|
||||
|
||||
In angular the controllers are strictly separated from the DOM manipulation logic which results in
|
||||
a much easier testability story as can be seen in this example:
|
||||
In angular the controllers are strictly separated from the DOM manipulation logic and this results in
|
||||
a much easier testability story as the following example shows:
|
||||
|
||||
<pre>
|
||||
function PasswordCtrl($scope) {
|
||||
@@ -245,7 +244,7 @@ function PasswordCtrl($scope) {
|
||||
}
|
||||
</pre>
|
||||
|
||||
and the test is straight forward
|
||||
and the test is straight forward:
|
||||
|
||||
<pre>
|
||||
var $scope = {};
|
||||
@@ -255,11 +254,11 @@ $scope.grade();
|
||||
expect($scope.strength).toEqual('weak');
|
||||
</pre>
|
||||
|
||||
Notice that the test is not only much shorter but it is easier to follow what is going on. We say
|
||||
Notice that the test is not only much shorter, it is also easier to follow what is happening. We say
|
||||
that such a test tells a story, rather then asserting random bits which don't seem to be related.
|
||||
|
||||
## Filters
|
||||
{@link api/ng.$filterProvider Filters} are functions which transform the data into user readable
|
||||
{@link api/ng.$filterProvider Filters} are functions which transform the data into a user readable
|
||||
format. They are important because they remove the formatting responsibility from the application
|
||||
logic, further simplifying the application logic.
|
||||
|
||||
@@ -282,7 +281,7 @@ you create with directives may be used throughout your application and in many d
|
||||
|
||||
### Simple HTML Element Directive
|
||||
|
||||
Lets start with an angular app with no dependencies.
|
||||
Let's start with an angular app with no dependencies.
|
||||
|
||||
<pre>
|
||||
var app = angular.module('myApp', []);
|
||||
|
||||
@@ -146,7 +146,7 @@ of service names to inject.
|
||||
var MyController = function(renamed$scope, renamedGreeter) {
|
||||
...
|
||||
}
|
||||
MyController.$inject = ['$scope', 'greeter'];
|
||||
MyController['$inject'] = ['$scope', 'greeter'];
|
||||
</pre>
|
||||
|
||||
In this scenario the ordering of the values in the '$inject' array must match the ordering of the arguments to inject.
|
||||
|
||||
@@ -56,9 +56,9 @@ The following also **matches** `ngModel`:
|
||||
Angular **normalizes** an element's tag and attribute name to determine which elements match which
|
||||
directives. We typically refer to directives by their case-sensitive
|
||||
{@link http://en.wikipedia.org/wiki/CamelCase camelCase} **normalized** name (e.g. `ngModel`).
|
||||
However, in the DOM, we refer to directives by case-insensitive forms, typically using
|
||||
{@link http://en.wikipedia.org/wiki/Letter_case#Computers dash-delimited} attributes on DOM elements
|
||||
(e.g. `ng-model`).
|
||||
However, since HTML is case-insensitive, we refer to directives in the DOM by lower-case
|
||||
forms, typically using {@link http://en.wikipedia.org/wiki/Letter_case#Computers dash-delimited}
|
||||
attributes on DOM elements (e.g. `ng-model`).
|
||||
|
||||
The **normalization** process is as follows:
|
||||
|
||||
@@ -404,40 +404,43 @@ we call an **isolate scope**. To do this, we can use a directive's `scope` optio
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
customer: '=customer'
|
||||
customerInfo: '=info'
|
||||
},
|
||||
templateUrl: 'my-customer.html'
|
||||
templateUrl: 'my-customer-iso.html'
|
||||
};
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="Ctrl">
|
||||
<my-customer customer="naomi"></my-customer>
|
||||
<my-customer info="naomi"></my-customer>
|
||||
<hr>
|
||||
<my-customer customer="igor"></my-customer>
|
||||
<my-customer info="igor"></my-customer>
|
||||
</div>
|
||||
</file>
|
||||
<file name="my-customer.html">
|
||||
Name: {{customer.name}} Address: {{customer.address}}
|
||||
<file name="my-customer-iso.html">
|
||||
Name: {{customerInfo.name}} Address: {{customerInfo.address}}
|
||||
</file>
|
||||
</example>
|
||||
|
||||
Looking at `index.html`, the first `<my-customer>` element binds the inner scope's `customer` to
|
||||
`naomi`, which we have exposed on our controller's scope. The second binds `customer` to `igor`.
|
||||
Looking at `index.html`, the first `<my-customer>` element binds the `info` attribute to `naomi`,
|
||||
which we have exposed on our controller's scope. The second binds `info` to `igor`.
|
||||
|
||||
Let's take a closer look at the scope option:
|
||||
|
||||
```javascript
|
||||
//...
|
||||
scope: {
|
||||
customer: '=customer'
|
||||
customerInfo: '=info'
|
||||
},
|
||||
//...
|
||||
```
|
||||
|
||||
The property name (`customer`) corresponds to the variable name of the `myCustomer` directive's
|
||||
isolated scope. The value of the property (`=customer`) tells `$compile` to bind to the `customer`
|
||||
attribute.
|
||||
The **scope option** is an object that contains a property for each isolate scope binding. In this
|
||||
case it has just one property:
|
||||
|
||||
- Its name (`customerInfo`) corresponds to the
|
||||
directive's **isolate scope** property `customerInfo`.
|
||||
- Its value (`=info`) tells `$compile` to bind to the `info` attribute.
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**Note:** These `=attr` attributes in the `scope` option of directives are normalized just like
|
||||
@@ -449,12 +452,12 @@ For cases where the attribute name is the same as the value you want to bind to
|
||||
directive's scope, you can use this shorthand syntax:
|
||||
|
||||
```javascript
|
||||
//...
|
||||
...
|
||||
scope: {
|
||||
// same as '=customer'
|
||||
// same as '=customer'
|
||||
customer: '='
|
||||
},
|
||||
//...
|
||||
...
|
||||
```
|
||||
|
||||
Besides making it possible to bind different data to the scope inside a directive, using an isolated
|
||||
@@ -475,7 +478,7 @@ within our directive's template:
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
customer: '=customer'
|
||||
customerInfo: '=info'
|
||||
},
|
||||
templateUrl: 'my-customer-plus-vojta.html'
|
||||
};
|
||||
@@ -483,11 +486,11 @@ within our directive's template:
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="Ctrl">
|
||||
<my-customer customer="naomi"></my-customer>
|
||||
<my-customer info="naomi"></my-customer>
|
||||
</div>
|
||||
</file>
|
||||
<file name="my-customer-plus-vojta.html">
|
||||
Name: {{customer.name}} Address: {{customer.address}}
|
||||
Name: {{customerInfo.name}} Address: {{customerInfo.address}}
|
||||
<hr>
|
||||
Name: {{vojta.name}} Address: {{vojta.address}}
|
||||
</file>
|
||||
@@ -717,7 +720,7 @@ own behavior to it.
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="Ctrl">
|
||||
<my-dialog ng-hide="dialogIsHidden" on-close="dialogIsHidden = true">
|
||||
<my-dialog ng-hide="dialogIsHidden" on-close="hideDialog()">
|
||||
Check out the contents, {{name}}!
|
||||
</my-dialog>
|
||||
</div>
|
||||
|
||||
@@ -18,7 +18,7 @@ It might be tempting to think of Angular view expressions as JavaScript expressi
|
||||
not entirely correct, since Angular does not use a JavaScript `eval()` to evaluate expressions.
|
||||
You can think of Angular expressions as JavaScript expressions with following differences:
|
||||
|
||||
* **Attribute Evaluation:** evaluation of all properties are against the scope, doing the
|
||||
* **Attribute Evaluation:** evaluation of all properties are against the scope doing the
|
||||
evaluation, unlike in JavaScript where the expressions are evaluated against the global
|
||||
`window`.
|
||||
|
||||
|
||||
@@ -115,9 +115,14 @@ This ensures that the user is not distracted with an error until after interacti
|
||||
|
||||
A form is an instance of {@link api/ng.directive:form.FormController FormController}.
|
||||
The form instance can optionally be published into the scope using the `name` attribute.
|
||||
Similarly, control is an instance of {@link api/ng.directive:ngModel.NgModelController NgModelController}.
|
||||
The control instance can similarly be published into the form instance using the `name` attribute.
|
||||
This implies that the internal state of both the form and the control is available for binding in the view using the standard binding primitives.
|
||||
|
||||
Similarly, an input control that has the {@link api.ng.directive:ng-model} directive holds an
|
||||
instance of {@link api/ng.directive:ngModel.NgModelController NgModelController}.
|
||||
Such a control instance can be published as a property of the form instance using the `name` attribute
|
||||
on the input control. The name attribute specifies the name of the property on the form instance.
|
||||
|
||||
This implies that the internal state of both the form and the control is available for binding in
|
||||
the view using the standard binding primitives.
|
||||
|
||||
This allows us to extend the above example with these features:
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ http://userguide.icu-project.org/locale ICU } website for more information about
|
||||
**Supported locales in Angular**
|
||||
Angular separates number and datetime format rule sets into different files, each file for a
|
||||
particular locale. You can find a list of currently supported locales {@link
|
||||
https://github.com/angular/angular.js/tree/master/i18n/locale here}
|
||||
https://github.com/angular/angular.js/tree/master/src/ngLocale here}
|
||||
# Providing locale rules to Angular
|
||||
|
||||
There are two approaches to providing locale rules to Angular:
|
||||
|
||||
@@ -8,6 +8,17 @@ This document describes the Internet Explorer (IE) idiosyncrasies when dealing w
|
||||
attributes and tags. Read this document if you are planning on deploying your Angular application
|
||||
on IE v8.0 or earlier.
|
||||
|
||||
The project currently supports and will attempt to fix bugs for IE8 and above. The continuous
|
||||
integration server runs all the tests against IE8. See http://ci.angularjs.org.
|
||||
|
||||
IE7 and below are not tested and the project makes no guarantee that Angular will work on it.
|
||||
A subset of the AngularJS functionality may work. It is up to you to test and decide whether
|
||||
it works for your particular app.
|
||||
|
||||
It is very unlikely that issues specific to IE7 or earlier will be given any time by the core team.
|
||||
[GitHub](https://github.com/angular/angular.js/issues/4974)
|
||||
|
||||
|
||||
# Short Version
|
||||
|
||||
To make your Angular application work on IE please make sure that:
|
||||
@@ -80,7 +91,7 @@ The **important** parts are:
|
||||
IE has issues with element tag names which are not standard HTML tag names. These fall into two
|
||||
categories, and each category has its own fix.
|
||||
|
||||
* If the tag name starts with `my:` prefix than it is considered an XML namespace and must
|
||||
* If the tag name starts with `my:` prefix then it is considered an XML namespace and must
|
||||
have corresponding namespace declaration on `<html xmlns:my="ignored">`
|
||||
|
||||
* If the tag has no `:` but it is not a standard HTML tag, then it must be pre-created using
|
||||
|
||||
@@ -7,19 +7,14 @@
|
||||
Everything you need to know about AngularJS
|
||||
|
||||
* {@link guide/introduction What is AngularJS?}
|
||||
|
||||
* {@link guide/concepts Conceptual Overview}
|
||||
|
||||
## Tutorials
|
||||
|
||||
* {@link tutorial/index Official AngularJS Tutorial}
|
||||
|
||||
* [10 Reasons Why You Should Use AngularJS](http://www.sitepoint.com/10-reasons-use-angularjs/)
|
||||
|
||||
* [Design Principles of AngularJS (video)](https://www.youtube.com/watch?v=HCR7i5F5L8c)
|
||||
|
||||
* [Fundamentals in 60 Minutes (video)](http://www.youtube.com/watch?v=i9MHigUZKEM)
|
||||
|
||||
* [For folks with jQuery background](http://stackoverflow.com/questions/14994391/how-do-i-think-in-angularjs-if-i-have-a-jquery-background)
|
||||
|
||||
## Core Concepts
|
||||
@@ -29,63 +24,44 @@ Everything you need to know about AngularJS
|
||||
In Angular applications, you move the job of filling page templates with data from the server to the client. The result is a system better structured for dynamic page updates. Below are the core features you'll use.
|
||||
|
||||
* {@link guide/databinding Data binding}
|
||||
|
||||
* {@link guide/expression Expressions}
|
||||
|
||||
* {@link guide/directive Directives}
|
||||
|
||||
* {@link api/ngRoute.$route Views and routes (see the example)}
|
||||
|
||||
* {@link guide/filter Filters}
|
||||
|
||||
* {@link guide/forms Forms} and [Concepts of AngularJS Forms](http://mrbool.com/the-concepts-of-angularjs-forms/29117)
|
||||
|
||||
### Application Structure
|
||||
|
||||
* **Blog post: **[When to use directives, controllers or services](http://kirkbushell.me/when-to-use-directives-controllers-or-services-in-angular/)
|
||||
|
||||
* **App wiring:** {@link guide/di Dependency injection}
|
||||
|
||||
* **Exposing model to templates:** {@link guide/scope Scopes}
|
||||
|
||||
* **Communicating with servers:** {@link api/ng.$http $http}, {@link api/ngResource.$resource $resource}
|
||||
|
||||
### Other AngularJS Features
|
||||
|
||||
* **Animation:** {@link guide/animations Core concepts}, {@link api/ngAnimate ngAnimate API}, and [Animation in AngularJS 1.2](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html)
|
||||
|
||||
* **Security:** {@link api/ng.$sce Strict Contextual Escaping}, {@link api/ng.directive:ngCsp Content Security Policy}, {@link api/ngSanitize.$sanitize $sanitize}, [video](https://www.youtube.com/watch?v=18ifoT-Id54)
|
||||
|
||||
* **Internationalization and Localization:** {@link guide/i18n Angular Guide to i18n and l10n}, {@link api/ng.filter:date date filter}, {@link api/ng.filter:currency currency filter}, [Creating multilingual support](http://www.novanet.no/blog/hallstein-brotan/dates/2013/10/creating-multilingual-support-using-angularjs/)
|
||||
|
||||
* **Mobile:** {@link api/ngTouch Touch events}
|
||||
|
||||
### Testing
|
||||
|
||||
* **Unit testing:** [Using Karma (video)](http://www.youtube.com/watch?v=YG5DEzaQBIc), {@link guide/dev_guide.unit-testing Unit testing}, {@link guide/dev_guide.services.testing_services Testing services}, [Karma in Webstorm](http://blog.jetbrains.com/webstorm/2013/10/running-javascript-tests-with-karma-in-webstorm-7/)
|
||||
|
||||
* **Scenario testing:** [Protractor](https://github.com/angular/protractor)
|
||||
|
||||
## Specific Topics
|
||||
|
||||
* **Login: **[Google example](https://developers.google.com/+/photohunt/python), [Facebook example](http://blog.brunoscopelliti.com/facebook-authentication-in-your-angularjs-web-app), [authentication strategy](http://blog.brunoscopelliti.com/deal-with-users-authentication-in-an-angularjs-web-app), [unix-style authorization](http://frederiknakstad.com/authentication-in-single-page-applications-with-angular-js/)
|
||||
|
||||
* **Mobile:** [Angular on Mobile Guide](http://www.ng-newsletter.com/posts/angular-on-mobile.html), [PhoneGap](http://devgirl.org/2013/06/10/quick-start-guide-phonegap-and-angularjs/)
|
||||
|
||||
* **Other Languages:** [CoffeeScript](http://www.coffeescriptlove.com/2013/08/angularjs-and-coffeescript-tutorials.html), [Dart](https://github.com/angular/angular.dart.tutorial/wiki)
|
||||
|
||||
* **Realtime: **[Socket.io](http://www.creativebloq.com/javascript/angularjs-collaboration-board-socketio-2132885), [OmniBinder](https://github.com/jeffbcross/omnibinder)
|
||||
|
||||
* **Visualization:** [SVG](http://gaslight.co/blog/angular-backed-svgs), [D3.js](http://www.ng-newsletter.com/posts/d3-on-angular.html)
|
||||
|
||||
## Tools
|
||||
|
||||
* **Debugging:** [Batarang](https://chrome.google.com/webstore/detail/angularjs-batarang/ighdmehidhipcmcojjgiloacoafjmpfk?hl=en)
|
||||
|
||||
* **Testing:** [Karma](http://karma-runner.github.io), [Protractor](https://github.com/angular/protractor)
|
||||
|
||||
* **Editor support:** [Webstorm](http://plugins.jetbrains.com/plugin/6971) (and [video](http://www.youtube.com/watch?v=LJOyrSh1kDU)), [Sublime Text](https://github.com/angular-ui/AngularJS-sublime-package), [Visual Studio](http://madskristensen.net/post/angularjs-intellisense-in-visual-studio-2012)
|
||||
|
||||
* **Workflow:** [Yeoman.io](https://github.com/yeoman/generator-angular) and [Angular Yeoman Tutorial](http://www.sitepoint.com/kickstart-your-angularjs-development-with-yeoman-grunt-and-bower/)
|
||||
|
||||
## Complementary Libraries
|
||||
@@ -93,37 +69,26 @@ In Angular applications, you move the job of filling page templates with data fr
|
||||
This is a short list of libraries with specific support and documentation for working with Angular. You can find a full list of all known Angular external libraries at [ngmodules.org](http://ngmodules.org/).
|
||||
|
||||
* **Internationalization:** [angular-translate](http://pascalprecht.github.io/angular-translate/), [angular-gettext](http://angular-gettext.rocketeer.be/)
|
||||
|
||||
* **RESTful services:** [Restangular](https://github.com/mgonto/restangular)
|
||||
|
||||
* **SQL and NoSQL backends:** [BreezeJS](http://www.breezejs.com/), [AngularFire](http://angularfire.com/)
|
||||
|
||||
* **UI Widgets: **[KendoUI](http://kendo-labs.github.io/angular-kendo/#/), [UI Bootstrap](http://angular-ui.github.io/bootstrap/), [Wijmo](http://wijmo.com/tag/angularjs-2/)
|
||||
|
||||
## Deployment
|
||||
|
||||
### General
|
||||
### General
|
||||
|
||||
* **Javascript minification: **[Background](http://thegreenpizza.github.io/2013/05/25/building-minification-safe-angular.js-applications/), [ngmin automation tool](http://www.thinkster.io/pick/XlWneEZCqY/angularjs-ngmin)
|
||||
|
||||
* **Tracking:** [Angularyitcs (Google Analytics)](http://ngmodules.org/modules/angularytics), [Logging Client-Side Errors](http://www.bennadel.com/blog/2542-Logging-Client-Side-Errors-With-AngularJS-And-Stacktrace-js.htm)
|
||||
|
||||
* **SEO:** [By hand](http://www.yearofmoo.com/2012/11/angularjs-and-seo.html), [prerender.io](http://prerender.io/), [Brombone](http://www.brombone.com/), [SEO.js](http://getseojs.com/), [SEO4Ajax](http://www.seo4ajax.com/)
|
||||
|
||||
### Server-Specific
|
||||
|
||||
* **Django:** [Tutorial](http://blog.mourafiq.com/post/55034504632/end-to-end-web-app-with-django-rest-framework), [Integrating AngularJS with Django](http://django-angular.readthedocs.org/en/latest/integration.html)
|
||||
|
||||
* **FireBase:** [AngularFire](http://angularfire.com/), [Realtime Apps with AngularJS and FireBase (video)](http://www.youtube.com/watch?v=C7ZI7z7qnHU)
|
||||
|
||||
* **Google Cloud Platform: **[with Cloud Endpoints](https://cloud.google.com/resources/articles/angularjs-cloud-endpoints-recipe-for-building-modern-web-applications), [with Go](https://github.com/GoogleCloudPlatform/appengine-angular-gotodos)
|
||||
|
||||
* **Hood.ie:** [60 Minutes to Awesome](http://www.roberthorvick.com/2013/06/30/todomvc-angularjs-hood-ie-60-minutes-to-awesome/)
|
||||
|
||||
* **MEAN Stack: **[Blog post](http://blog.mongodb.org/post/49262866911/the-mean-stack-mongodb-expressjs-angularjs-and), [Setup](http://thecodebarbarian.wordpress.com/2013/07/22/introduction-to-the-mean-stack-part-one-setting-up-your-tools/), [GDL Video](https://developers.google.com/live/shows/913996610)
|
||||
|
||||
* **Rails: **[Tutorial](http://coderberry.me/blog/2013/04/22/angularjs-on-rails-4-part-1/), [AngularJS with Rails4](https://shellycloud.com/blog/2013/10/how-to-integrate-angularjs-with-rails-4), [angularjs-rails](https://github.com/hiravgandhi/angularjs-rails)
|
||||
|
||||
* **PHP: **[Building a RESTful web service](http://blog.brunoscopelliti.com/building-a-restful-web-service-with-angularjs-and-php-more-power-with-resource), [End to End with Laravel 4 (video)](http://www.youtube.com/watch?v=hqAyiqUs93c)
|
||||
|
||||
## Learning Resources
|
||||
@@ -137,18 +102,18 @@ This is a short list of libraries with specific support and documentation for wo
|
||||
* [ng-book: The Complete Book on AngularJS](http://ng-book.com/) by Ari Lerner
|
||||
|
||||
###Videos:
|
||||
* [egghead.io](http://egghead.io/),
|
||||
* [egghead.io](http://egghead.io/)
|
||||
* [Angular on YouTube](http://youtube.com/angularjs)
|
||||
|
||||
###Courses
|
||||
* **Free on-line:**
|
||||
### Courses
|
||||
* **Free online:**
|
||||
[thinkster.io](http://thinkster.io),
|
||||
[CodeAcademy](http://www.codecademy.com/courses/javascript-advanced-en-2hJ3J/0/1)
|
||||
* **Paid on-line:**
|
||||
* **Paid online:**
|
||||
[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)
|
||||
* **Paid on-site:**
|
||||
* **Paid onsite:**
|
||||
[angularbootcamp.com](http://angularbootcamp.com/)
|
||||
|
||||
## Getting Help
|
||||
@@ -156,19 +121,14 @@ This is a short list of libraries with specific support and documentation for wo
|
||||
The recipe for getting help on your unique issue is to create an example that could work (even if it doesn't) in a shareable example on [Plunker](http://plnkr.co/), [JSFiddle](http://jsfiddle.net/), or similar site and then post to one of the following:
|
||||
|
||||
* [Stackoverflow.com](http://stackoverflow.com/search?q=angularjs)
|
||||
|
||||
* [AngularJS mailing list](https://groups.google.com/forum/#!forum/angular)
|
||||
|
||||
* [AngularJS IRC channel](http://webchat.freenode.net/?channels=angularjs&uio=d4)
|
||||
|
||||
## Social Channels
|
||||
|
||||
* **Daily updates:** [Google+](https://plus.google.com/u/0/+AngularJS) or [Twitter](https://twitter.com/angularjs)
|
||||
|
||||
* **Weekly newsletter:** [ng-newsletter](http://www.ng-newsletter.com/)
|
||||
|
||||
* **Meetups: **[meetup.com](http://www.meetup.com/find/?keywords=angularJS&radius=Infinity&userFreeform=San+Francisco%2C+CA&mcId=z94108&mcName=San+Francisco%2C+CA&sort=member_count&eventFilter=mysugg)
|
||||
|
||||
* **Official news and releases: **[AngularJS Blog](http://blog.angularjs.org/)
|
||||
|
||||
## Contributing to AngularJS
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
@description
|
||||
|
||||
|
||||
AngularJS version 1.2 introduces several breaking changes that will may require changes to your
|
||||
AngularJS version 1.2 introduces several breaking changes that may require changes to your
|
||||
application's source code.
|
||||
|
||||
Although we try to avoid breaking changes, there are some cases where it is unavoidable.
|
||||
@@ -35,7 +35,7 @@ below should still apply, but you may want to consult the
|
||||
<li>{@link guide/migration#resource-methods-return-the-promise Resource methods return the promise}</li>
|
||||
<li>{@link guide/migration#resource-promises-are-resolved-with-the-resource-instance Resource promises are resolved with the resource instance}</li>
|
||||
<li>{@link guide/migration#$locationsearch-supports-multiple-keys $location.search supports multiple keys}</li>
|
||||
<li>{@link guide/migration#nghtmlbindunsafe-has-been-removed-and-replaced-by-nghtmlbind ngHtmlBindUnsafe has been removed and replaced by ngHtmlBind}</li>
|
||||
<li>{@link guide/migration#ngbindhtmlunsafe-has-been-removed-and-replaced-by-ngbindhtml ngBindHtmlUnsafe has been removed and replaced by ngBindHtml}</li>
|
||||
<li>{@link guide/migration#form-names-that-are-expressions-are-evaluated Form names that are expressions are evaluated}</li>
|
||||
<li>{@link guide/migration#hasownproperty-disallowed-as-an-input-name hasOwnProperty disallowed as an input name}</li>
|
||||
<li>{@link guide/migration#directives-order-of-postlink-functions-reversed Directives: Order of postLink functions reversed}</li>
|
||||
@@ -45,7 +45,7 @@ below should still apply, but you may want to consult the
|
||||
<li>{@link guide/migration#urls-are-now-sanitized-against-a-whitelist URLs are now sanitized against a whitelist}</li>
|
||||
<li>{@link guide/migration#isolate-scope-only-exposed-to-directives-with-property Isolate scope only exposed to directives with <code>scope</code> property}</li>
|
||||
<li>{@link guide/migration#change-to-interpolation-priority Change to interpolation priority}</li>
|
||||
<li>{@link guide/migration#underscore-prefixed/suffixed-prorperties-are-non-bindable Underscore-prefixed/suffixed prorperties are non-bindable}</li>
|
||||
<li>{@link guide/migration#underscore-prefixed/suffixed-properties-are-non-bindable Underscore-prefixed/suffixed properties are non-bindable}</li>
|
||||
<li>{@link guide/migration#you-cannot-bind-to-select[multiple] You cannot bind to select[multiple]}</li>
|
||||
<li>{@link guide/migration#uncommon-region-specific-local-files-were-removed-from-i18n Uncommon region-specific local files were removed from i18n}</li>
|
||||
</ul>
|
||||
@@ -376,11 +376,11 @@ passing it to `$location`.
|
||||
See [80739409](https://github.com/angular/angular.js/commit/807394095b991357225a03d5fed81fea5c9a1abe).
|
||||
|
||||
|
||||
## ngHtmlBindUnsafe has been removed and replaced by ngHtmlBind
|
||||
## ngBindHtmlUnsafe has been removed and replaced by ngBindHtml
|
||||
|
||||
`ngHtmlBind` which has been moved from `ngSanitize` module to the core `ng` module.
|
||||
`ngBindHtml` which has been moved from `ngSanitize` module to the core `ng` module.
|
||||
|
||||
`ngBindHtml` provides `ngHtmlBindUnsafe` like
|
||||
`ngBindHtml` provides `ngBindHtmlUnsafe` like
|
||||
behavior (evaluate an expression and innerHTML the result into the DOM) when bound to the result
|
||||
of `$sce.trustAsHtml(string)`. When bound to a plain string, the string is sanitized via
|
||||
`$sanitize` before being innerHTML'd. If the `$sanitize` service isn't available (`ngSanitize`
|
||||
@@ -588,7 +588,11 @@ See [79223eae](https://github.com/angular/angular.js/commit/79223eae502283889334
|
||||
[#4528](https://github.com/angular/angular.js/issues/4528), and
|
||||
[#4649](https://github.com/angular/angular.js/issues/4649)
|
||||
|
||||
## Underscore-prefixed/suffixed prorperties are non-bindable
|
||||
## Underscore-prefixed/suffixed properties are non-bindable
|
||||
|
||||
<div class="alert alert-info">
|
||||
<p>**Reverted**: This breaking change has been reverted in 1.2.1, and so can be ignored if you're using **version 1.2.1 or higher**</p>
|
||||
</div>
|
||||
|
||||
This change introduces the notion of "private" properties (properties
|
||||
whose names begin and/or end with an underscore) on the scope chain.
|
||||
|
||||
@@ -110,7 +110,7 @@ myApp.factory('clientId', function clientIdFactory() {
|
||||
But given that the token is just a string literal, sticking with the Value recipe is still more
|
||||
appropriate as it makes the code easier to follow.
|
||||
|
||||
Let's say, however, that we would also like create a service that computes a token used for
|
||||
Let's say, however, that we would also like to create a service that computes a token used for
|
||||
authentication against a remote API. This token will be called 'apiToken' and will be computed
|
||||
based on the `clientId` value and a secret stored in browser's local storage:
|
||||
|
||||
@@ -228,7 +228,7 @@ myApp.provider('unicornLauncher', function UnicornLauncherProvider() {
|
||||
// let's assume that the UnicornLauncher constructor was also changed to
|
||||
// accept and use the useTinfoilShielding argument
|
||||
return new UnicornLauncher(apiToken, useTinfoilShielding);
|
||||
}]);
|
||||
}];
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ To examine the scope in the debugger:
|
||||
2. The debugger allows you to access the currently selected element in the console as `$0`
|
||||
variable.
|
||||
|
||||
3. To retrieve the associated scope in console execute: `angular.element($0).scope()`
|
||||
3. To retrieve the associated scope in console execute: `angular.element($0).scope()` or just type $scope
|
||||
|
||||
|
||||
## Scope Events Propagation
|
||||
|
||||
@@ -23,7 +23,7 @@ for how to contribute your own code to AngularJS.
|
||||
Before you can build AngularJS, you must install and configure the following dependencies on your
|
||||
machine:
|
||||
|
||||
* {@link http://git-scm.com/ Git}: The {@link http://help.github.com/mac-git-installation Github Guide to
|
||||
* {@link http://git-scm.com/ Git}: The {@link https://help.github.com/articles/set-up-git Github Guide to
|
||||
Installing Git} is a good source of information.
|
||||
|
||||
* {@link http://nodejs.org Node.js}: We use Node to generate the documentation, run a
|
||||
|
||||
@@ -67,7 +67,7 @@ illustration we typically build snappy apps with hundreds or thousands of active
|
||||
|
||||
### How big is the angular.js file that I need to include?
|
||||
|
||||
The size of the file is < 29KB compressed and minified.
|
||||
The size of the file is < 36KB compressed and minified.
|
||||
|
||||
|
||||
### Can I use the open-source Closure Library with Angular?
|
||||
|
||||
@@ -85,7 +85,7 @@ to run <code>scripts/web-server.js</code>, a simple bundled http server.</p></l
|
||||
<div class="tab-pane well" id="git-win" title="Git on Windows">
|
||||
<ol>
|
||||
<li><p>You will need Node.js and Karma to run unit tests, so please verify that you have
|
||||
<a href="http://nodejs.org/">Node.js</a> v0.8 or better installed
|
||||
<a href="http://nodejs.org/">Node.js</a> v0.10 or better installed
|
||||
and that the <code>node</code> executable is on your <code>PATH</code> by running the following
|
||||
command in a terminal window:</p>
|
||||
<pre>node --version</pre>
|
||||
|
||||
@@ -26,7 +26,7 @@ angular-seed, and run the application in the browser.
|
||||
<ol>
|
||||
<li>In a <i>separate</i> terminal tab or window, run <code>node ./scripts/web-server.js</code> to start the web server.</li>
|
||||
<li>Open a browser window for the app and navigate to <a
|
||||
href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html</a></li>
|
||||
href="http://localhost:8000/app/index.html" target="_blank">`http://localhost:8000/app/index.html`</a></li>
|
||||
</ol>
|
||||
</li>
|
||||
<li><b>For other http servers:</b>
|
||||
@@ -54,7 +54,7 @@ angular-seed, and run the application in the browser.
|
||||
<li><b>For node.js users:</b>
|
||||
<ol>
|
||||
<li>In a <i>separate</i> terminal tab or window, run <code>node scripts\web-server.js</code> to start the web server.</li>
|
||||
<li>Open a browser window for the app and navigate to <a href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html</a></li>
|
||||
<li>Open a browser window for the app and navigate to <a href="http://localhost:8000/app/index.html" target="_blank">`http://localhost:8000/app/index.html`</a></li>
|
||||
</ol>
|
||||
</li>
|
||||
<li><b>For other http servers:</b>
|
||||
|
||||
@@ -180,11 +180,15 @@ is available to be injected.
|
||||
Angular developers prefer the syntax of Jasmine's Behavior-driven Development (BDD) framework when
|
||||
writing tests. Although Angular does not require you to use Jasmine, we wrote all of the tests in
|
||||
this tutorial in Jasmine. You can learn about Jasmine on the {@link
|
||||
http://pivotal.github.com/jasmine/ Jasmine home page} and on the {@link
|
||||
https://github.com/pivotal/jasmine/wiki Jasmine wiki}.
|
||||
http://pivotal.github.com/jasmine/ Jasmine home page} and at the {@link
|
||||
http://pivotal.github.io/jasmine/ Jasmine docs}.
|
||||
|
||||
The angular-seed project is pre-configured to run all unit tests using {@link
|
||||
http://karma-runner.github.io/ Karma}. To run the test, do the following:
|
||||
http://karma-runner.github.io/ Karma}. Ensure that the necessary karma plugins are installed.
|
||||
You can do this by issuing `npm install` into your terminal.
|
||||
|
||||
|
||||
To run the test, do the following:
|
||||
|
||||
1. In a _separate_ terminal window or tab, go to the `angular-phonecat` directory and run
|
||||
`./scripts/test.sh` to start the Karma server (the config file necessary to start the server
|
||||
|
||||
@@ -127,8 +127,8 @@ end-to-end tests! Use `./scripts/e2e-test.sh` script for that. End-to-end tests
|
||||
with unit tests, Karma will exit after the test run and will not automatically rerun the test
|
||||
suite on every file change. To rerun the test suite, execute the `e2e-test.sh` script again.
|
||||
|
||||
Note: You must ensure you've installed karma-ng-scenario prior to running the `e2e-test.sh` script.
|
||||
You can do this by issuing `npm install karma-ng-scenario` into your terminal.
|
||||
Note: You must ensure you've installed the karma-ng-scenario framework plugin prior to running the
|
||||
`e2e-test.sh` script. You can do this by issuing `npm install` into your terminal.
|
||||
|
||||
This test verifies that the search box and the repeater are correctly wired together. Notice how
|
||||
easy it is to write end-to-end tests in Angular. Although this example is for a simple test, it
|
||||
@@ -154,7 +154,7 @@ really is that easy to set up any functional, readable, end-to-end test.
|
||||
`ngController` declaration to the HTML element because it is the common parent of both the body
|
||||
and title elements:
|
||||
|
||||
<html ng-app ng-controller="PhoneListCtrl">
|
||||
<html ng-app="phonecatApp" ng-controller="PhoneListCtrl">
|
||||
|
||||
Be sure to __remove__ the `ng-controller` declaration from the body element.
|
||||
|
||||
|
||||
@@ -112,11 +112,12 @@ describe('PhoneCat controllers', function() {
|
||||
describe('PhoneListCtrl', function(){
|
||||
var scope, ctrl;
|
||||
|
||||
beforeEach(function() {
|
||||
scope = {},
|
||||
ctrl = new PhoneListCtrl(scope);
|
||||
});
|
||||
|
||||
beforeEach(module('phonecatApp'));
|
||||
|
||||
beforeEach(inject(function($controller) {
|
||||
scope = {};
|
||||
ctrl = $controller('PhoneListCtrl', {$scope:scope});
|
||||
}));
|
||||
|
||||
it('should create "phones" model with 3 phones', function() {
|
||||
expect(scope.phones.length).toBe(3);
|
||||
|
||||
@@ -64,7 +64,7 @@ We also added phone images next to each record using an image tag with the {@lin
|
||||
api/ng.directive:ngSrc ngSrc} directive. That directive prevents the
|
||||
browser from treating the angular `{{ expression }}` markup literally, and initiating a request to
|
||||
invalid url `http://localhost:8000/app/{{phone.imageUrl}}`, which it would have done if we had only
|
||||
specified an attribute binding in a regular `src` attribute (`<img class="diagram" src="{{phone.imageUrl}}">`).
|
||||
specified an attribute binding in a regular `src` attribute (`<img src="{{phone.imageUrl}}">`).
|
||||
Using the `ngSrc` directive prevents the browser from making an http request to an invalid location.
|
||||
|
||||
|
||||
|
||||
@@ -258,7 +258,7 @@ to various URLs and verify that the correct view was rendered.
|
||||
<pre>
|
||||
...
|
||||
it('should redirect index.html to index.html#/phones', function() {
|
||||
browser().navigateTo('../../app/index.html');
|
||||
browser().navigateTo('app/index.html');
|
||||
expect(browser().location().url()).toBe('/phones');
|
||||
});
|
||||
...
|
||||
@@ -266,7 +266,7 @@ to various URLs and verify that the correct view was rendered.
|
||||
describe('Phone detail view', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
browser().navigateTo('../../app/index.html#/phones/nexus-s');
|
||||
browser().navigateTo('app/index.html#/phones/nexus-s');
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ Angular's server}.
|
||||
|
||||
<button ng-click="hello('Elmo')">Hello</button>
|
||||
|
||||
to the `phone-details.html` template.
|
||||
to the `phone-detail.html` template.
|
||||
|
||||
<div style="display: none">
|
||||
TODO!
|
||||
|
||||
@@ -43,7 +43,7 @@ __`app/index.html`.__
|
||||
<pre>
|
||||
...
|
||||
<!-- jQuery is used for JavaScript animations (include this before angular.js) -->
|
||||
<script src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
|
||||
<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
|
||||
|
||||
<!-- required module to enable animation support in AngularJS -->
|
||||
<script src="lib/angular/angular-animate.js"></script>
|
||||
@@ -56,6 +56,10 @@ __`app/index.html`.__
|
||||
...
|
||||
</pre>
|
||||
|
||||
<div class="alert alert-error">
|
||||
**Important:** Be sure to use jQuery version `1.10.x`. AngularJS does not yet support jQuery `2.x`.
|
||||
</div>
|
||||
|
||||
Animations can now be created within the CSS code (`animations.css`) as well as the JavaScript code (`animations.js`).
|
||||
But before we start, let's create a new module which uses the ngAnimate module as a dependency just like we did before
|
||||
with `ngResource`.
|
||||
@@ -153,7 +157,7 @@ __`app/css/animations.css`__
|
||||
</pre>
|
||||
|
||||
As you can see our `phone-listing` CSS class is combined together with the animation hooks that occur when items are
|
||||
inserted info and removed from the list:
|
||||
inserted into and removed from the list:
|
||||
|
||||
* The `ng-enter` class is applied to the element when a new phone is added to the list and rendered on the page.
|
||||
* The `ng-move` class is applied when items are moved around in the list.
|
||||
@@ -383,10 +387,6 @@ isn't required to do JavaScript animations with AngularJS, but we're going to us
|
||||
your own JavaScript animation library is beyond the scope of this tutorial. For more on
|
||||
`jQuery.animate`, see the {@link http://api.jquery.com/animate/ jQuery documentation}.
|
||||
|
||||
<div class="alert alert-error">
|
||||
**Important:** Be sure to use jQuery version `1.10.x`. AngularJS does not yet support jQuery `2.x`.
|
||||
</div>
|
||||
|
||||
The `addClass` and `removeClass` callback functions are called whenever an a class is added or removed
|
||||
on the element that contains the class we registered, which is in this case `.phone`. When the `.active`
|
||||
class is added to the element (via the `ng-class` directive) the `addClass` JavaScript callback will
|
||||
|
||||
@@ -34,8 +34,8 @@ describe('ngdoc', function() {
|
||||
var d1 = new Doc('@name a.b.c').parse();
|
||||
var d2 = new Doc('@name a.b.ng-c').parse();
|
||||
var d3 = new Doc('@name some text: more text').parse();
|
||||
expect(ngdoc.metadata([d1])[0].shortName).toEqual('c');
|
||||
expect(ngdoc.metadata([d2])[0].shortName).toEqual('ng-c');
|
||||
expect(ngdoc.metadata([d1])[0].shortName).toEqual('a.b.c');
|
||||
expect(ngdoc.metadata([d2])[0].shortName).toEqual('a.b.ng-c');
|
||||
expect(ngdoc.metadata([d3])[0].shortName).toEqual('more text');
|
||||
});
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ describe('Docs Links', function() {
|
||||
});
|
||||
|
||||
it('should have an "view source" button', function() {
|
||||
spyOn(gruntUtil, 'getVersion').andReturn({cdn: '1.2.299'});
|
||||
spyOn(gruntUtil, 'getVersion').andReturn({full: '1.2.299'});
|
||||
|
||||
expect(doc.html()).
|
||||
toContain('<a href="http://github.com/angular/angular.js/tree/v1.2.299/test.js#L42" class="view-source btn btn-action"><i class="icon-zoom-in"> </i> View source</a>');
|
||||
|
||||
@@ -144,6 +144,27 @@
|
||||
.content h4,
|
||||
.content h5 {
|
||||
margin-top: 1em;
|
||||
letter-spacing: -0.06em;
|
||||
}
|
||||
|
||||
.content h2 {
|
||||
font-size: 36px;
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
.content h3 {
|
||||
font-size: 24px;
|
||||
border-top: 1px solid #eee;
|
||||
padding-top: .5em;
|
||||
}
|
||||
|
||||
.content h4 {
|
||||
font-size: 16px;
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
|
||||
.content ul {
|
||||
margin-top: .5em;
|
||||
}
|
||||
|
||||
ul.parameters > li > p,
|
||||
|
||||
@@ -335,9 +335,8 @@
|
||||
|
||||
<div ng-hide="loading" ng-include src="currentPage.partialUrl" onload="afterPartialLoaded()" autoscroll class="content slide-reveal"></div>
|
||||
|
||||
<div id="disqus" class="disqus">
|
||||
<h2>Discussion</h2>
|
||||
<div id="disqus_thread" class="content-panel-content"></div>
|
||||
<div class="alert alert-info">
|
||||
<a href="http://blog.angularjs.org/2013/11/farewell-disqus.html">Where did Disqus go?</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -365,7 +364,7 @@
|
||||
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<p class="pull-right"><a href="#">Back to top</a></p>
|
||||
<p class="pull-right"><a back-to-top href="#">Back to top</a></p>
|
||||
|
||||
<p>
|
||||
Super-powered by Google ©2010-2012
|
||||
|
||||
@@ -273,10 +273,10 @@ docsApp.directive.docTutorialNav = function(templateMerge) {
|
||||
element.addClass('btn-group');
|
||||
element.addClass('tutorial-nav');
|
||||
element.append(templateMerge(
|
||||
'<li class="btn btn-primary"><a href="tutorial/{{prev}}"><i class="icon-step-backward"></i> Previous</a></li>\n' +
|
||||
'<li class="btn btn-primary"><a href="http://angular.github.com/angular-phonecat/step-{{seq}}/app"><i class="icon-play"></i> Live Demo</a></li>\n' +
|
||||
'<li class="btn btn-primary"><a href="https://github.com/angular/angular-phonecat/compare/step-{{diffLo}}...step-{{diffHi}}"><i class="icon-search"></i> Code Diff</a></li>\n' +
|
||||
'<li class="btn btn-primary"><a href="tutorial/{{next}}">Next <i class="icon-step-forward"></i></a></li>', props));
|
||||
'<a href="tutorial/{{prev}}"><li class="btn btn-primary"><i class="icon-step-backward"></i> Previous</li></a>\n' +
|
||||
'<a href="http://angular.github.com/angular-phonecat/step-{{seq}}/app"><li class="btn btn-primary"><i class="icon-play"></i> Live Demo</li></a>\n' +
|
||||
'<a href="https://github.com/angular/angular-phonecat/compare/step-{{diffLo}}...step-{{diffHi}}"><li class="btn btn-primary"><i class="icon-search"></i> Code Diff</li></a>\n' +
|
||||
'<a href="tutorial/{{next}}"><li class="btn btn-primary">Next <i class="icon-step-forward"></i></li></a>', props));
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -372,6 +372,21 @@ docsApp.directive.errorDisplay = ['$location', 'errorLinkFilter', function ($loc
|
||||
}];
|
||||
|
||||
|
||||
/**
|
||||
* backToTop Directive
|
||||
* @param {Function} $anchorScroll
|
||||
*
|
||||
* @description Ensure that the browser scrolls when the anchor is clicked
|
||||
*/
|
||||
docsApp.directive.backToTop = ['$anchorScroll', function($anchorScroll) {
|
||||
return function link(scope, element) {
|
||||
element.on('click', function(event) {
|
||||
scope.$apply($anchorScroll);
|
||||
});
|
||||
};
|
||||
}];
|
||||
|
||||
|
||||
docsApp.serviceFactory.angularUrls = function($document) {
|
||||
var urls = {};
|
||||
|
||||
@@ -680,7 +695,6 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
|
||||
var currentPageId = $location.path();
|
||||
$scope.partialTitle = $scope.currentPage.shortName;
|
||||
$window._gaq.push(['_trackPageview', currentPageId]);
|
||||
loadDisqus(currentPageId);
|
||||
};
|
||||
|
||||
/** stores a cookie that is used by apache to decide which manifest ot send */
|
||||
@@ -892,29 +906,6 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
|
||||
return namespace;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function loadDisqus(currentPageId) {
|
||||
// http://docs.disqus.com/help/2/
|
||||
window.disqus_shortname = 'angularjs-next';
|
||||
window.disqus_identifier = currentPageId;
|
||||
window.disqus_url = 'http://docs.angularjs.org' + currentPageId;
|
||||
|
||||
if ($location.host() == 'localhost') {
|
||||
return; // don't display disqus on localhost, comment this out if needed
|
||||
//window.disqus_developer = 1;
|
||||
}
|
||||
|
||||
// http://docs.disqus.com/developers/universal/
|
||||
(function() {
|
||||
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
|
||||
dsq.src = 'http://angularjs.disqus.com/embed.js';
|
||||
(document.getElementsByTagName('head')[0] ||
|
||||
document.getElementsByTagName('body')[0]).appendChild(dsq);
|
||||
})();
|
||||
|
||||
angular.element(document.getElementById('disqus_thread')).html('');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
+3
-1
@@ -20,6 +20,8 @@ module.exports = function(config) {
|
||||
junitReporter: {
|
||||
outputFile: 'test_out/e2e.xml',
|
||||
suite: 'E2E'
|
||||
}
|
||||
},
|
||||
|
||||
browserNoActivityTimeout: 90000
|
||||
});
|
||||
};
|
||||
|
||||
+119
-6
@@ -5,14 +5,23 @@ module.exports = function(config, specificOptions) {
|
||||
logLevel: config.LOG_INFO,
|
||||
logColors: true,
|
||||
browsers: ['Chrome'],
|
||||
browserDisconnectTimeout: 5000,
|
||||
browserDisconnectTimeout: 10000,
|
||||
browserDisconnectTolerance: 2,
|
||||
browserNoActivityTimeout: 20000,
|
||||
|
||||
|
||||
// config for Travis CI
|
||||
// SauceLabs config for local development.
|
||||
sauceLabs: {
|
||||
testName: specificOptions.testName || 'AngularJS',
|
||||
startConnect: false,
|
||||
tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER
|
||||
startConnect: true
|
||||
},
|
||||
|
||||
// BrowserStack config for local development.
|
||||
browserStack: {
|
||||
project: 'AngularJS',
|
||||
name: specificOptions.testName,
|
||||
startTunnel: true,
|
||||
timeout: 600 // 10min
|
||||
},
|
||||
|
||||
// For more browsers on Sauce Labs see:
|
||||
@@ -49,12 +58,77 @@ module.exports = function(config, specificOptions) {
|
||||
browserName: 'internet explorer',
|
||||
platform: 'Windows 2012',
|
||||
version: '10'
|
||||
},
|
||||
'SL_IE_11': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'internet explorer',
|
||||
platform: 'Windows 8.1',
|
||||
version: '11'
|
||||
},
|
||||
|
||||
'BS_Chrome': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'chrome',
|
||||
os: 'OS X',
|
||||
os_version: 'Mountain Lion'
|
||||
},
|
||||
'BS_Safari': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'safari',
|
||||
os: 'OS X',
|
||||
os_version: 'Mountain Lion'
|
||||
},
|
||||
'BS_Firefox': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'firefox',
|
||||
os: 'Windows',
|
||||
os_version: '8'
|
||||
},
|
||||
'BS_IE_8': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'ie',
|
||||
browser_version: '8.0',
|
||||
os: 'Windows',
|
||||
os_version: '7'
|
||||
},
|
||||
'BS_IE_9': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'ie',
|
||||
browser_version: '9.0',
|
||||
os: 'Windows',
|
||||
os_version: '7'
|
||||
},
|
||||
'BS_IE_10': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'ie',
|
||||
browser_version: '10.0',
|
||||
os: 'Windows',
|
||||
os_version: '8'
|
||||
},
|
||||
'BS_IE_11': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'ie',
|
||||
browser_version: '11.0',
|
||||
os: 'Windows',
|
||||
os_version: '8.1'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (process.env.TRAVIS) {
|
||||
var buildLabel = 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';
|
||||
|
||||
config.logLevel = config.LOG_DEBUG;
|
||||
config.transports = ['websocket', 'xhr-polling'];
|
||||
|
||||
config.browserStack.build = buildLabel;
|
||||
config.browserStack.startTunnel = false;
|
||||
|
||||
config.sauceLabs.build = buildLabel;
|
||||
config.sauceLabs.startConnect = false;
|
||||
config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
|
||||
|
||||
// TODO(vojta): remove once SauceLabs supports websockets.
|
||||
// This speeds up the capturing a bit, as browsers don't even try to use websocket.
|
||||
config.transports = ['xhr-polling'];
|
||||
@@ -62,8 +136,47 @@ module.exports = function(config, specificOptions) {
|
||||
// Debug logging into a file, that we print out at the end of the build.
|
||||
config.loggers.push({
|
||||
type: 'file',
|
||||
filename: process.env.LOGS_DIR + '/' + (specificOptions.logFile || 'karma.log'),
|
||||
level: config.LOG_DEBUG
|
||||
filename: process.env.LOGS_DIR + '/' + (specificOptions.logFile || 'karma.log')
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Terrible hack to workaround inflexibility of log4js:
|
||||
// - ignore web-server's 404 warnings,
|
||||
// - ignore DEBUG logs (on Travis), we log them into a file instead.
|
||||
var IGNORED_404 = [
|
||||
'/favicon.ico',
|
||||
'/%7B%7BtestUrl%7D%7D',
|
||||
'/someSanitizedUrl',
|
||||
'/{{testUrl}}'
|
||||
];
|
||||
var log4js = require('./node_modules/karma/node_modules/log4js');
|
||||
var layouts = require('./node_modules/karma/node_modules/log4js/lib/layouts');
|
||||
var originalConfigure = log4js.configure;
|
||||
log4js.configure = function(log4jsConfig) {
|
||||
var consoleAppender = log4jsConfig.appenders.shift();
|
||||
var originalResult = originalConfigure.call(log4js, log4jsConfig);
|
||||
var layout = layouts.layout(consoleAppender.layout.type, consoleAppender.layout);
|
||||
|
||||
|
||||
|
||||
log4js.addAppender(function(log) {
|
||||
var msg = log.data[0];
|
||||
|
||||
// ignore web-server's 404s
|
||||
if (log.categoryName === 'web-server' && log.level.levelStr === config.LOG_WARN &&
|
||||
IGNORED_404.some(function(ignoredLog) {return msg.indexOf(ignoredLog) !== -1})) {
|
||||
return;
|
||||
}
|
||||
|
||||
// on Travis, ignore DEBUG statements
|
||||
if (process.env.TRAVIS && log.level.levelStr === config.LOG_DEBUG) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(layout(log));
|
||||
});
|
||||
|
||||
return originalResult;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
var fs = require('fs');
|
||||
var http = require('http');
|
||||
var BrowserStackTunnel = require('browserstacktunnel-wrapper');
|
||||
|
||||
var HOSTNAME = 'localhost';
|
||||
var PORTS = require('../grunt/utils').availablePorts;
|
||||
var ACCESS_KEY = process.env.BROWSER_STACK_ACCESS_KEY;
|
||||
var READY_FILE = process.env.SAUCE_CONNECT_READY_FILE;
|
||||
|
||||
// We need to start fake servers, otherwise the tunnel does not start.
|
||||
var fakeServers = [];
|
||||
var hosts = [];
|
||||
|
||||
PORTS.forEach(function(port) {
|
||||
fakeServers.push(http.createServer(function() {}).listen(port));
|
||||
hosts.push({
|
||||
name: HOSTNAME,
|
||||
port: port,
|
||||
sslFlag: 0
|
||||
});
|
||||
});
|
||||
|
||||
var tunnel = new BrowserStackTunnel({
|
||||
key: ACCESS_KEY,
|
||||
hosts: hosts
|
||||
});
|
||||
|
||||
console.log('Starting tunnel on ports', PORTS.join(', '));
|
||||
tunnel.start(function(error) {
|
||||
if (error) {
|
||||
console.error('Can not establish the tunnel', error);
|
||||
} else {
|
||||
console.log('Tunnel established.');
|
||||
fakeServers.forEach(function(server) {
|
||||
server.close();
|
||||
});
|
||||
|
||||
if (READY_FILE) {
|
||||
fs.writeFile(READY_FILE, '');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tunnel.on('error', function(error) {
|
||||
console.error(error);
|
||||
});
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
node ./lib/browser-stack/start-tunnel.js &
|
||||
+19
-1
@@ -5,6 +5,22 @@ var spawn = require('child_process').spawn;
|
||||
var version;
|
||||
var CSP_CSS_HEADER = '/* Include this file in your html if you are using the CSP mode. */\n\n';
|
||||
|
||||
var PORT_MIN = 8000;
|
||||
var PORT_MAX = 9999;
|
||||
var TRAVIS_BUILD_NUMBER = parseInt(process.env.TRAVIS_BUILD_NUMBER, 10);
|
||||
var getRandomPorts = function() {
|
||||
if (!process.env.TRAVIS) {
|
||||
return [9876, 9877];
|
||||
}
|
||||
|
||||
// Generate two numbers between PORT_MIN and PORT_MAX, based on TRAVIS_BUILD_NUMBER.
|
||||
return [
|
||||
PORT_MIN + (TRAVIS_BUILD_NUMBER % (PORT_MAX - PORT_MIN)),
|
||||
PORT_MIN + ((TRAVIS_BUILD_NUMBER + 100) % (PORT_MAX - PORT_MIN))
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
|
||||
init: function() {
|
||||
@@ -295,5 +311,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
// see http://saucelabs.com/docs/connect#localhost
|
||||
sauceLabsAvailablePorts: [9000, 9001, 9080, 9090, 9876]
|
||||
sauceLabsAvailablePorts: [9000, 9001, 9080, 9090, 9876],
|
||||
// pseudo-random port numbers for BrowserStack
|
||||
availablePorts: getRandomPorts()
|
||||
};
|
||||
|
||||
@@ -36,8 +36,8 @@ ARGS=""
|
||||
if [ ! -z "$TRAVIS_JOB_NUMBER" ]; then
|
||||
ARGS="$ARGS --tunnel-identifier $TRAVIS_JOB_NUMBER"
|
||||
fi
|
||||
if [ ! -z "$SAUCE_CONNECT_READY_FILE" ]; then
|
||||
ARGS="$ARGS --readyfile $SAUCE_CONNECT_READY_FILE"
|
||||
if [ ! -z "$BROWSER_PROVIDER_READY_FILE" ]; then
|
||||
ARGS="$ARGS --readyfile $BROWSER_PROVIDER_READY_FILE"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
+9
-7
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "angularjs",
|
||||
"version": "1.2.1",
|
||||
"cdnVersion": "1.2.0",
|
||||
"codename": "underscore-empathy",
|
||||
"version": "1.2.5",
|
||||
"cdnVersion": "1.2.4",
|
||||
"codename": "singularity-expansion",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
@@ -24,20 +24,22 @@
|
||||
"karma-chrome-launcher": "~0.1.0",
|
||||
"karma-firefox-launcher": "~0.1.0",
|
||||
"karma-ng-scenario": "~0.1.0",
|
||||
"karma-junit-reporter": "git://github.com/karma-runner/karma-junit-reporter#karma-0.11",
|
||||
"karma-junit-reporter": "~0.2.1",
|
||||
"karma-sauce-launcher": "~0.1.1",
|
||||
"karma-script-launcher": "~0.1.0",
|
||||
"yaml-js": "~0.0.8",
|
||||
"marked": "0.2.9",
|
||||
"rewire": "1.1.3",
|
||||
"grunt-jasmine-node": "~0.1.0",
|
||||
"grunt-jasmine-node": "git://github.com/vojtajina/grunt-jasmine-node.git#fix-grunt-exit-code",
|
||||
"grunt-parallel": "~0.3.1",
|
||||
"grunt-ddescribe-iit": "~0.0.1",
|
||||
"grunt-merge-conflict": "~0.0.1",
|
||||
"promises-aplus-tests": "~1.3.2",
|
||||
"grunt-shell": "~0.4.0",
|
||||
"semver": "~2.1.0",
|
||||
"lodash": "~2.1.0"
|
||||
"lodash": "~2.1.0",
|
||||
"karma-browserstack-launcher": "git://github.com/karma-runner/karma-browserstack-launcher.git#master",
|
||||
"browserstacktunnel-wrapper": "~1.1.1"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
@@ -46,6 +48,6 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"grunt-contrib-jshint": "~0.6.4"
|
||||
"grunt-contrib-jshint": "~0.7.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Angular Bower Script
|
||||
|
||||
Script for updating the Angular bower repos from a code.angularjs.org package
|
||||
|
||||
Requires `node` (for parsing `bower.json`) and `wget` (for fetching the `angular.zip` from `code.angularjs.org`)
|
||||
|
||||
|
||||
## Instructions
|
||||
|
||||
You need to run `./init.sh` the first time you use this script to clone all the repos.
|
||||
|
||||
For subsequent updates:
|
||||
|
||||
```shell
|
||||
./publish.sh NEW_VERSION
|
||||
```
|
||||
|
||||
Where `NEW_VERSION` is a version number like `1.2.3`.
|
||||
|
||||
|
||||
## License
|
||||
MIT
|
||||
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# init all of the bower repos
|
||||
#
|
||||
|
||||
set -e # fail if any command fails
|
||||
|
||||
REPOS=(
|
||||
angular \
|
||||
angular-animate \
|
||||
angular-cookies \
|
||||
angular-i18n \
|
||||
angular-loader \
|
||||
angular-mocks \
|
||||
angular-route \
|
||||
angular-resource \
|
||||
angular-sanitize \
|
||||
angular-scenario \
|
||||
angular-touch \
|
||||
)
|
||||
|
||||
cd `dirname $0`
|
||||
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
git clone git@github.com:angular/bower-$repo.git
|
||||
done
|
||||
Executable
+90
@@ -0,0 +1,90 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# update all the things
|
||||
#
|
||||
|
||||
set -e # fail if any command fails
|
||||
|
||||
cd `dirname $0`
|
||||
|
||||
NEW_VERSION=$1
|
||||
|
||||
ZIP_FILE=angular-$NEW_VERSION.zip
|
||||
ZIP_FILE_URL=http://code.angularjs.org/$NEW_VERSION/angular-$NEW_VERSION.zip
|
||||
ZIP_DIR=angular-$NEW_VERSION
|
||||
|
||||
REPOS=(
|
||||
angular \
|
||||
angular-animate \
|
||||
angular-cookies \
|
||||
angular-i18n \
|
||||
angular-loader \
|
||||
angular-mocks \
|
||||
angular-route \
|
||||
angular-resource \
|
||||
angular-sanitize \
|
||||
angular-scenario \
|
||||
angular-touch \
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# download and unzip the file
|
||||
#
|
||||
|
||||
if [ ! -f $ZIP_FILE ]; then
|
||||
wget $ZIP_FILE_URL
|
||||
unzip $ZIP_FILE
|
||||
fi
|
||||
|
||||
|
||||
#
|
||||
# move the files from the zip
|
||||
#
|
||||
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
if [ -f $ZIP_DIR/$repo.js ] # ignore i18l
|
||||
then
|
||||
cd bower-$repo
|
||||
git reset --hard HEAD
|
||||
git checkout master
|
||||
git fetch --all
|
||||
git reset --hard origin/master
|
||||
cd ..
|
||||
mv $ZIP_DIR/$repo.* bower-$repo/
|
||||
fi
|
||||
done
|
||||
|
||||
# move i18n files
|
||||
mv $ZIP_DIR/i18n/*.js bower-angular-i18n/
|
||||
|
||||
# move csp.css
|
||||
mv $ZIP_DIR/angular-csp.css bower-angular
|
||||
|
||||
|
||||
#
|
||||
# get the old version number
|
||||
#
|
||||
|
||||
OLD_VERSION=$(node -e "console.log(require('./bower-angular/bower').version)" | sed -e 's/\r//g')
|
||||
echo $OLD_VERSION
|
||||
echo $NEW_VERSION
|
||||
|
||||
#
|
||||
# update bower.json
|
||||
# tag each repo
|
||||
#
|
||||
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
cd bower-$repo
|
||||
sed -i '' -e "s/$OLD_VERSION/$NEW_VERSION/g" bower.json
|
||||
git add -A
|
||||
git commit -m "v$NEW_VERSION"
|
||||
git tag v$NEW_VERSION
|
||||
git push origin master
|
||||
git push origin v$NEW_VERSION
|
||||
cd ..
|
||||
done
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
export SAUCE_ACCESS_KEY=`echo $SAUCE_ACCESS_KEY | rev`
|
||||
|
||||
if [ $JOB = "unit" ]; then
|
||||
grunt ci-checks
|
||||
grunt test:docgen
|
||||
grunt test:promises-aplus
|
||||
grunt test:unit --browsers SL_Chrome,SL_Safari,SL_Firefox,SL_IE_8,SL_IE_9,SL_IE_10,SL_IE_11 --reporters dots
|
||||
elif [ $JOB = "e2e" ]; then
|
||||
grunt test:e2e --browsers SL_Chrome --reporters dots
|
||||
else
|
||||
echo "Unknown job type. Please set JOB=unit or JOB=e2e."
|
||||
fi
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
|
||||
# Wait for Connect to be ready before exiting
|
||||
while [ ! -f $SAUCE_CONNECT_READY_FILE ]; do
|
||||
while [ ! -f $BROWSER_PROVIDER_READY_FILE ]; do
|
||||
sleep .5
|
||||
done
|
||||
+1
-1
@@ -162,4 +162,4 @@
|
||||
"nullFormCtrl": false
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+55
-41
@@ -80,7 +80,7 @@
|
||||
-assertArgFn,
|
||||
-assertNotHasOwnProperty,
|
||||
-getter,
|
||||
-getBlockElements
|
||||
-getBlockElements,
|
||||
|
||||
*/
|
||||
|
||||
@@ -393,7 +393,7 @@ function valueFn(value) {return function() {return value;};}
|
||||
* @param {*} value Reference to check.
|
||||
* @returns {boolean} True if `value` is undefined.
|
||||
*/
|
||||
function isUndefined(value){return typeof value == 'undefined';}
|
||||
function isUndefined(value){return typeof value === 'undefined';}
|
||||
|
||||
|
||||
/**
|
||||
@@ -407,7 +407,7 @@ function isUndefined(value){return typeof value == 'undefined';}
|
||||
* @param {*} value Reference to check.
|
||||
* @returns {boolean} True if `value` is defined.
|
||||
*/
|
||||
function isDefined(value){return typeof value != 'undefined';}
|
||||
function isDefined(value){return typeof value !== 'undefined';}
|
||||
|
||||
|
||||
/**
|
||||
@@ -422,7 +422,7 @@ function isDefined(value){return typeof value != 'undefined';}
|
||||
* @param {*} value Reference to check.
|
||||
* @returns {boolean} True if `value` is an `Object` but not `null`.
|
||||
*/
|
||||
function isObject(value){return value != null && typeof value == 'object';}
|
||||
function isObject(value){return value != null && typeof value === 'object';}
|
||||
|
||||
|
||||
/**
|
||||
@@ -436,7 +436,7 @@ function isObject(value){return value != null && typeof value == 'object';}
|
||||
* @param {*} value Reference to check.
|
||||
* @returns {boolean} True if `value` is a `String`.
|
||||
*/
|
||||
function isString(value){return typeof value == 'string';}
|
||||
function isString(value){return typeof value === 'string';}
|
||||
|
||||
|
||||
/**
|
||||
@@ -450,7 +450,7 @@ function isString(value){return typeof value == 'string';}
|
||||
* @param {*} value Reference to check.
|
||||
* @returns {boolean} True if `value` is a `Number`.
|
||||
*/
|
||||
function isNumber(value){return typeof value == 'number';}
|
||||
function isNumber(value){return typeof value === 'number';}
|
||||
|
||||
|
||||
/**
|
||||
@@ -465,7 +465,7 @@ function isNumber(value){return typeof value == 'number';}
|
||||
* @returns {boolean} True if `value` is a `Date`.
|
||||
*/
|
||||
function isDate(value){
|
||||
return toString.apply(value) == '[object Date]';
|
||||
return toString.call(value) === '[object Date]';
|
||||
}
|
||||
|
||||
|
||||
@@ -481,7 +481,7 @@ function isDate(value){
|
||||
* @returns {boolean} True if `value` is an `Array`.
|
||||
*/
|
||||
function isArray(value) {
|
||||
return toString.apply(value) == '[object Array]';
|
||||
return toString.call(value) === '[object Array]';
|
||||
}
|
||||
|
||||
|
||||
@@ -496,7 +496,7 @@ function isArray(value) {
|
||||
* @param {*} value Reference to check.
|
||||
* @returns {boolean} True if `value` is a `Function`.
|
||||
*/
|
||||
function isFunction(value){return typeof value == 'function';}
|
||||
function isFunction(value){return typeof value === 'function';}
|
||||
|
||||
|
||||
/**
|
||||
@@ -507,7 +507,7 @@ function isFunction(value){return typeof value == 'function';}
|
||||
* @returns {boolean} True if `value` is a `RegExp`.
|
||||
*/
|
||||
function isRegExp(value) {
|
||||
return toString.apply(value) == '[object RegExp]';
|
||||
return toString.call(value) === '[object RegExp]';
|
||||
}
|
||||
|
||||
|
||||
@@ -529,12 +529,12 @@ function isScope(obj) {
|
||||
|
||||
|
||||
function isFile(obj) {
|
||||
return toString.apply(obj) === '[object File]';
|
||||
return toString.call(obj) === '[object File]';
|
||||
}
|
||||
|
||||
|
||||
function isBoolean(value) {
|
||||
return typeof value == 'boolean';
|
||||
return typeof value === 'boolean';
|
||||
}
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@ var trim = (function() {
|
||||
// TODO: we should move this into IE/ES5 polyfill
|
||||
if (!String.prototype.trim) {
|
||||
return function(value) {
|
||||
return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value;
|
||||
return isString(value) ? value.replace(/^\s\s*/, '').replace(/\s\s*$/, '') : value;
|
||||
};
|
||||
}
|
||||
return function(value) {
|
||||
@@ -565,9 +565,9 @@ var trim = (function() {
|
||||
* @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
|
||||
*/
|
||||
function isElement(node) {
|
||||
return node &&
|
||||
return !!(node &&
|
||||
(node.nodeName // we are a direct element
|
||||
|| (node.on && node.find)); // we have an on and find method part of jQuery API
|
||||
|| (node.on && node.find))); // we have an on and find method part of jQuery API
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -638,7 +638,7 @@ function includes(array, obj) {
|
||||
function indexOf(array, obj) {
|
||||
if (array.indexOf) return array.indexOf(obj);
|
||||
|
||||
for ( var i = 0; i < array.length; i++) {
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
if (obj === array[i]) return i;
|
||||
}
|
||||
return -1;
|
||||
@@ -768,7 +768,7 @@ function shallowCopy(src, dst) {
|
||||
|
||||
for(var key in src) {
|
||||
// shallowCopy is only ever called by $compile nodeLinkFn, which has control over src
|
||||
// so we don't need to worry hasOwnProperty here
|
||||
// so we don't need to worry about using our custom hasOwnProperty here
|
||||
if (src.hasOwnProperty(key) && key.substr(0, 2) !== '$$') {
|
||||
dst[key] = src[key];
|
||||
}
|
||||
@@ -974,7 +974,7 @@ function startingTag(element) {
|
||||
try {
|
||||
// turns out IE does not let you set .html() on elements which
|
||||
// are not allowed to have children. So we just ignore it.
|
||||
element.html('');
|
||||
element.empty();
|
||||
} catch(e) {}
|
||||
// As Per DOM Standards
|
||||
var TEXT_NODE = 3;
|
||||
@@ -1102,26 +1102,38 @@ function encodeUriQuery(val, pctEncodeSpaces) {
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Use this directive to auto-bootstrap an application. Only
|
||||
* one ngApp directive can be used per HTML document. The directive
|
||||
* designates the root of the application and is typically placed
|
||||
* at the root of the page.
|
||||
* Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
|
||||
* designates the **root element** of the application and is typically placed near the root element
|
||||
* of the page - e.g. on the `<body>` or `<html>` tags.
|
||||
*
|
||||
* The first ngApp found in the document will be auto-bootstrapped. To use multiple applications in
|
||||
* an HTML document you must manually bootstrap them using {@link angular.bootstrap}.
|
||||
* Applications cannot be nested.
|
||||
* Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
|
||||
* found in the document will be used to define the root element to auto-bootstrap as an
|
||||
* application. To run multiple applications in an HTML document you must manually bootstrap them using
|
||||
* {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
|
||||
*
|
||||
* In the example below if the `ngApp` directive were not placed
|
||||
* on the `html` element then the document would not be compiled
|
||||
* and the `{{ 1+2 }}` would not be resolved to `3`.
|
||||
* You can specify an **AngularJS module** to be used as the root module for the application. This
|
||||
* module will be loaded into the {@link AUTO.$injector} when the application is bootstrapped and
|
||||
* should contain the application code needed or have dependencies on other modules that will
|
||||
* contain the code. See {@link angular.module} for more information.
|
||||
*
|
||||
* `ngApp` is the easiest way to bootstrap an application.
|
||||
* In the example below if the `ngApp` directive were not placed on the `html` element then the
|
||||
* document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
|
||||
* would not be resolved to `3`.
|
||||
*
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
I can add: 1 + 2 = {{ 1+2 }}
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
* `ngApp` is the easiest, and most common, way to bootstrap an application.
|
||||
*
|
||||
<example module="ngAppDemo">
|
||||
<file name="index.html">
|
||||
<div ng-controller="ngAppDemoController">
|
||||
I can add: {{a}} + {{b}} = {{ a+b }}
|
||||
</file>
|
||||
<file name="script.js">
|
||||
angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
|
||||
$scope.a = 1;
|
||||
$scope.b = 2;
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*
|
||||
*/
|
||||
function angularInit(element, bootstrap) {
|
||||
@@ -1318,23 +1330,25 @@ function getter(obj, path, bindFnToScope) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the siblings between `startNode` and `endNode`, inclusive
|
||||
* @param {Object} object with `startNode` and `endNode` properties
|
||||
* Return the DOM siblings between the first and last node in the given array.
|
||||
* @param {Array} array like object
|
||||
* @returns jQlite object containing the elements
|
||||
*/
|
||||
function getBlockElements(block) {
|
||||
if (block.startNode === block.endNode) {
|
||||
return jqLite(block.startNode);
|
||||
function getBlockElements(nodes) {
|
||||
var startNode = nodes[0],
|
||||
endNode = nodes[nodes.length - 1];
|
||||
if (startNode === endNode) {
|
||||
return jqLite(startNode);
|
||||
}
|
||||
|
||||
var element = block.startNode;
|
||||
var element = startNode;
|
||||
var elements = [element];
|
||||
|
||||
do {
|
||||
element = element.nextSibling;
|
||||
if (!element) break;
|
||||
elements.push(element);
|
||||
} while (element !== block.endNode);
|
||||
} while (element !== endNode);
|
||||
|
||||
return jqLite(elements);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
ngHideDirective,
|
||||
ngIfDirective,
|
||||
ngIncludeDirective,
|
||||
ngIncludeFillContentDirective,
|
||||
ngInitDirective,
|
||||
ngNonBindableDirective,
|
||||
ngPluralizeDirective,
|
||||
@@ -65,6 +66,7 @@
|
||||
$ParseProvider,
|
||||
$RootScopeProvider,
|
||||
$QProvider,
|
||||
$$SanitizeUriProvider,
|
||||
$SceProvider,
|
||||
$SceDelegateProvider,
|
||||
$SnifferProvider,
|
||||
@@ -136,6 +138,10 @@ function publishExternalAPI(angular){
|
||||
|
||||
angularModule('ng', ['ngLocale'], ['$provide',
|
||||
function ngModule($provide) {
|
||||
// $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
|
||||
$provide.provider({
|
||||
$$sanitizeUri: $$SanitizeUriProvider
|
||||
});
|
||||
$provide.provider('$compile', $CompileProvider).
|
||||
directive({
|
||||
a: htmlAnchorDirective,
|
||||
@@ -176,6 +182,9 @@ function publishExternalAPI(angular){
|
||||
ngRequired: requiredDirective,
|
||||
ngValue: ngValueDirective
|
||||
}).
|
||||
directive({
|
||||
ngInclude: ngIncludeFillContentDirective
|
||||
}).
|
||||
directive(ngAttributeAliasDirectives).
|
||||
directive(ngEventDirectives);
|
||||
$provide.provider({
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, document, undefined) {
|
||||
|
||||
+29
-22
@@ -27,6 +27,28 @@
|
||||
* $rootScope.$digest();
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* Sometimes you want to get access to the injector of a currently running Angular app
|
||||
* from outside Angular. Perhaps, you want to inject and compile some markup after the
|
||||
* application has been bootstrapped. You can do this using extra `injector()` added
|
||||
* to JQuery/jqLite elements. See {@link angular.element}.
|
||||
*
|
||||
* *This is fairly rare but could be the case if a third party library is injecting the
|
||||
* markup.*
|
||||
*
|
||||
* In the following example a new block of HTML containing a `ng-controller`
|
||||
* directive is added to the end of the document body by JQuery. We then compile and link
|
||||
* it into the current AngularJS scope.
|
||||
*
|
||||
* <pre>
|
||||
* var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
|
||||
* $(document.body).append($div);
|
||||
*
|
||||
* angular.element(document).injector().invoke(function($compile) {
|
||||
* var scope = angular.element($div).scope();
|
||||
* $compile($div)(scope);
|
||||
* });
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
|
||||
@@ -221,7 +243,7 @@ function annotate(fn) {
|
||||
* // ...
|
||||
* }
|
||||
* // Define function dependencies
|
||||
* MyController.$inject = ['$scope', '$route'];
|
||||
* MyController['$inject'] = ['$scope', '$route'];
|
||||
*
|
||||
* // Then
|
||||
* expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
|
||||
@@ -501,11 +523,11 @@ function annotate(fn) {
|
||||
* @example
|
||||
* Here are some examples of creating value services.
|
||||
* <pre>
|
||||
* $provide.constant('ADMIN_USER', 'admin');
|
||||
* $provide.value('ADMIN_USER', 'admin');
|
||||
*
|
||||
* $provide.constant('RoleLookup', { admin: 0, writer: 1, reader: 2 });
|
||||
* $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
|
||||
*
|
||||
* $provide.constant('halfOf', function(value) {
|
||||
* $provide.value('halfOf', function(value) {
|
||||
* return value / 2;
|
||||
* });
|
||||
* </pre>
|
||||
@@ -747,24 +769,9 @@ function createInjector(modulesToLoad) {
|
||||
fn = fn[length];
|
||||
}
|
||||
|
||||
|
||||
// Performance optimization: http://jsperf.com/apply-vs-call-vs-invoke
|
||||
switch (self ? -1 : args.length) {
|
||||
case 0: return fn();
|
||||
case 1: return fn(args[0]);
|
||||
case 2: return fn(args[0], args[1]);
|
||||
case 3: return fn(args[0], args[1], args[2]);
|
||||
case 4: return fn(args[0], args[1], args[2], args[3]);
|
||||
case 5: return fn(args[0], args[1], args[2], args[3], args[4]);
|
||||
case 6: return fn(args[0], args[1], args[2], args[3], args[4], args[5]);
|
||||
case 7: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
|
||||
case 8: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
|
||||
case 9: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7],
|
||||
args[8]);
|
||||
case 10: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7],
|
||||
args[8], args[9]);
|
||||
default: return fn.apply(self, args);
|
||||
}
|
||||
// http://jsperf.com/angularjs-invoke-apply-vs-switch
|
||||
// #5388
|
||||
return fn.apply(self, args);
|
||||
}
|
||||
|
||||
function instantiate(Type, locals) {
|
||||
|
||||
+23
-5
@@ -46,6 +46,7 @@
|
||||
* - [`contents()`](http://api.jquery.com/contents/)
|
||||
* - [`css()`](http://api.jquery.com/css/)
|
||||
* - [`data()`](http://api.jquery.com/data/)
|
||||
* - [`empty()`](http://api.jquery.com/empty/)
|
||||
* - [`eq()`](http://api.jquery.com/eq/)
|
||||
* - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
|
||||
* - [`hasClass()`](http://api.jquery.com/hasClass/)
|
||||
@@ -358,6 +359,15 @@ function jqLiteInheritedData(element, name, value) {
|
||||
}
|
||||
}
|
||||
|
||||
function jqLiteEmpty(element) {
|
||||
for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) {
|
||||
jqLiteDealoc(childNodes[i]);
|
||||
}
|
||||
while (element.firstChild) {
|
||||
element.removeChild(element.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
// Functions which are declared directly.
|
||||
//////////////////////////////////////////
|
||||
@@ -552,7 +562,9 @@ forEach({
|
||||
jqLiteDealoc(childNodes[i]);
|
||||
}
|
||||
element.innerHTML = value;
|
||||
}
|
||||
},
|
||||
|
||||
empty: jqLiteEmpty
|
||||
}, function(fn, name){
|
||||
/**
|
||||
* Properties: writes return selection, reads return first value
|
||||
@@ -562,11 +574,13 @@ forEach({
|
||||
|
||||
// jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
|
||||
// in a way that survives minification.
|
||||
if (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined) {
|
||||
// jqLiteEmpty takes no arguments but is a setter.
|
||||
if (fn !== jqLiteEmpty &&
|
||||
(((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined)) {
|
||||
if (isObject(arg1)) {
|
||||
|
||||
// we are a write, but the object properties are the key/values
|
||||
for(i=0; i < this.length; i++) {
|
||||
for (i = 0; i < this.length; i++) {
|
||||
if (fn === jqLiteData) {
|
||||
// data() takes the whole object in jQuery
|
||||
fn(this[i], arg1);
|
||||
@@ -591,7 +605,7 @@ forEach({
|
||||
}
|
||||
} else {
|
||||
// we are a write, so apply to all children
|
||||
for(i=0; i < this.length; i++) {
|
||||
for (i = 0; i < this.length; i++) {
|
||||
fn(this[i], arg1, arg2);
|
||||
}
|
||||
// return self for chaining
|
||||
@@ -822,7 +836,11 @@ forEach({
|
||||
},
|
||||
|
||||
find: function(element, selector) {
|
||||
return element.getElementsByTagName(selector);
|
||||
if (element.getElementsByTagName) {
|
||||
return element.getElementsByTagName(selector);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
clone: jqLiteClone,
|
||||
|
||||
+6
-1
@@ -17,7 +17,12 @@ function setupModuleLoader(window) {
|
||||
return obj[name] || (obj[name] = factory());
|
||||
}
|
||||
|
||||
return ensure(ensure(window, 'angular', Object), 'module', function() {
|
||||
var angular = ensure(window, 'angular', Object);
|
||||
|
||||
// We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
|
||||
angular.$$minErr = angular.$$minErr || minErr;
|
||||
|
||||
return ensure(angular, 'module', function() {
|
||||
/** @type {Object.<string, angular.Module>} */
|
||||
var modules = {};
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular, undefined) {
|
||||
|
||||
+8
-7
@@ -99,13 +99,14 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
* inserted into the DOM
|
||||
*/
|
||||
enter : function(element, parent, after, done) {
|
||||
var afterNode = after && after[after.length - 1];
|
||||
var parentNode = parent && parent[0] || afterNode && afterNode.parentNode;
|
||||
// IE does not like undefined so we have to pass null.
|
||||
var afterNextSibling = (afterNode && afterNode.nextSibling) || null;
|
||||
forEach(element, function(node) {
|
||||
parentNode.insertBefore(node, afterNextSibling);
|
||||
});
|
||||
if (after) {
|
||||
after.after(element);
|
||||
} else {
|
||||
if (!parent || !parent[0]) {
|
||||
parent = after.parent();
|
||||
}
|
||||
parent.append(element);
|
||||
}
|
||||
done && $timeout(done, 0, false);
|
||||
},
|
||||
|
||||
|
||||
+115
-94
@@ -192,7 +192,7 @@
|
||||
* * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
|
||||
* * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
|
||||
* * `^` - Locate the required controller by searching the element's parents. Throw an error if not found.
|
||||
* * `?^` - Attempt to locate the required controller by searching the element's parentsor pass `null` to the
|
||||
* * `?^` - Attempt to locate the required controller by searching the element's parents or pass `null` to the
|
||||
* `link` fn if not found.
|
||||
*
|
||||
*
|
||||
@@ -283,7 +283,7 @@
|
||||
* </div>
|
||||
*
|
||||
* <div class="alert alert-error">
|
||||
* **Note:** The `transclude` function that is passed to the compile function is deperecated, as it
|
||||
* **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
|
||||
* e.g. does not know about the right outer scope. Please use the transclude function that is passed
|
||||
* to the link function instead.
|
||||
* </div>
|
||||
@@ -493,14 +493,12 @@ var $compileMinErr = minErr('$compile');
|
||||
*
|
||||
* @description
|
||||
*/
|
||||
$CompileProvider.$inject = ['$provide'];
|
||||
function $CompileProvider($provide) {
|
||||
$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
|
||||
function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
var hasDirectives = {},
|
||||
Suffix = 'Directive',
|
||||
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
|
||||
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
|
||||
aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
|
||||
imgSrcSanitizationWhitelist = /^\s*(https?|ftp|file):|data:image\//;
|
||||
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/;
|
||||
|
||||
// Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
|
||||
// The assumption is that future DOM event attribute names will begin with
|
||||
@@ -584,10 +582,11 @@ function $CompileProvider($provide) {
|
||||
*/
|
||||
this.aHrefSanitizationWhitelist = function(regexp) {
|
||||
if (isDefined(regexp)) {
|
||||
aHrefSanitizationWhitelist = regexp;
|
||||
$$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
|
||||
return this;
|
||||
} else {
|
||||
return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
|
||||
}
|
||||
return aHrefSanitizationWhitelist;
|
||||
};
|
||||
|
||||
|
||||
@@ -614,18 +613,18 @@ function $CompileProvider($provide) {
|
||||
*/
|
||||
this.imgSrcSanitizationWhitelist = function(regexp) {
|
||||
if (isDefined(regexp)) {
|
||||
imgSrcSanitizationWhitelist = regexp;
|
||||
$$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
|
||||
return this;
|
||||
} else {
|
||||
return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
|
||||
}
|
||||
return imgSrcSanitizationWhitelist;
|
||||
};
|
||||
|
||||
|
||||
this.$get = [
|
||||
'$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
|
||||
'$controller', '$rootScope', '$document', '$sce', '$animate',
|
||||
'$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
|
||||
function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse,
|
||||
$controller, $rootScope, $document, $sce, $animate) {
|
||||
$controller, $rootScope, $document, $sce, $animate, $$sanitizeUri) {
|
||||
|
||||
var Attributes = function(element, attr) {
|
||||
this.$$element = element;
|
||||
@@ -672,6 +671,24 @@ function $CompileProvider($provide) {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.$compile.directive.Attributes#$updateClass
|
||||
* @methodOf ng.$compile.directive.Attributes
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Adds and removes the appropriate CSS class values to the element based on the difference
|
||||
* between the new and old CSS class values (specified as newClasses and oldClasses).
|
||||
*
|
||||
* @param {string} newClasses The current CSS className value
|
||||
* @param {string} oldClasses The former CSS className value
|
||||
*/
|
||||
$updateClass : function(newClasses, oldClasses) {
|
||||
this.$removeClass(tokenDifference(oldClasses, newClasses));
|
||||
this.$addClass(tokenDifference(newClasses, oldClasses));
|
||||
},
|
||||
|
||||
/**
|
||||
* Set a normalized attribute on the element in a way such that all directives
|
||||
* can share the attribute. This function properly handles boolean attributes.
|
||||
@@ -682,59 +699,44 @@ function $CompileProvider($provide) {
|
||||
* @param {string=} attrName Optional none normalized name. Defaults to key.
|
||||
*/
|
||||
$set: function(key, value, writeAttr, attrName) {
|
||||
//special case for class attribute addition + removal
|
||||
//so that class changes can tap into the animation
|
||||
//hooks provided by the $animate service
|
||||
if(key == 'class') {
|
||||
value = value || '';
|
||||
var current = this.$$element.attr('class') || '';
|
||||
this.$removeClass(tokenDifference(current, value).join(' '));
|
||||
this.$addClass(tokenDifference(value, current).join(' '));
|
||||
// TODO: decide whether or not to throw an error if "class"
|
||||
//is set through this function since it may cause $updateClass to
|
||||
//become unstable.
|
||||
|
||||
var booleanKey = getBooleanAttrName(this.$$element[0], key),
|
||||
normalizedVal,
|
||||
nodeName;
|
||||
|
||||
if (booleanKey) {
|
||||
this.$$element.prop(key, value);
|
||||
attrName = booleanKey;
|
||||
}
|
||||
|
||||
this[key] = value;
|
||||
|
||||
// translate normalized key to actual key
|
||||
if (attrName) {
|
||||
this.$attr[key] = attrName;
|
||||
} else {
|
||||
var booleanKey = getBooleanAttrName(this.$$element[0], key),
|
||||
normalizedVal,
|
||||
nodeName;
|
||||
|
||||
if (booleanKey) {
|
||||
this.$$element.prop(key, value);
|
||||
attrName = booleanKey;
|
||||
attrName = this.$attr[key];
|
||||
if (!attrName) {
|
||||
this.$attr[key] = attrName = snake_case(key, '-');
|
||||
}
|
||||
}
|
||||
|
||||
this[key] = value;
|
||||
nodeName = nodeName_(this.$$element);
|
||||
|
||||
// translate normalized key to actual key
|
||||
if (attrName) {
|
||||
this.$attr[key] = attrName;
|
||||
// sanitize a[href] and img[src] values
|
||||
if ((nodeName === 'A' && key === 'href') ||
|
||||
(nodeName === 'IMG' && key === 'src')) {
|
||||
this[key] = value = $$sanitizeUri(value, key === 'src');
|
||||
}
|
||||
|
||||
if (writeAttr !== false) {
|
||||
if (value === null || value === undefined) {
|
||||
this.$$element.removeAttr(attrName);
|
||||
} else {
|
||||
attrName = this.$attr[key];
|
||||
if (!attrName) {
|
||||
this.$attr[key] = attrName = snake_case(key, '-');
|
||||
}
|
||||
}
|
||||
|
||||
nodeName = nodeName_(this.$$element);
|
||||
|
||||
// sanitize a[href] and img[src] values
|
||||
if ((nodeName === 'A' && key === 'href') ||
|
||||
(nodeName === 'IMG' && key === 'src')) {
|
||||
// NOTE: urlResolve() doesn't support IE < 8 so we don't sanitize for that case.
|
||||
if (!msie || msie >= 8 ) {
|
||||
normalizedVal = urlResolve(value).href;
|
||||
if (normalizedVal !== '') {
|
||||
if ((key === 'href' && !normalizedVal.match(aHrefSanitizationWhitelist)) ||
|
||||
(key === 'src' && !normalizedVal.match(imgSrcSanitizationWhitelist))) {
|
||||
this[key] = value = 'unsafe:' + normalizedVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (writeAttr !== false) {
|
||||
if (value === null || value === undefined) {
|
||||
this.$$element.removeAttr(attrName);
|
||||
} else {
|
||||
this.$$element.attr(attrName, value);
|
||||
}
|
||||
this.$$element.attr(attrName, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -747,22 +749,6 @@ function $CompileProvider($provide) {
|
||||
$exceptionHandler(e);
|
||||
}
|
||||
});
|
||||
|
||||
function tokenDifference(str1, str2) {
|
||||
var values = [],
|
||||
tokens1 = str1.split(/\s+/),
|
||||
tokens2 = str2.split(/\s+/);
|
||||
|
||||
outer:
|
||||
for(var i=0;i<tokens1.length;i++) {
|
||||
var token = tokens1[i];
|
||||
for(var j=0;j<tokens2.length;j++) {
|
||||
if(token == tokens2[j]) continue outer;
|
||||
}
|
||||
values.push(token);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -945,7 +931,7 @@ function $CompileProvider($provide) {
|
||||
createBoundTranscludeFn(scope, childTranscludeFn || transcludeFn)
|
||||
);
|
||||
} else {
|
||||
nodeLinkFn(childLinkFn, childScope, node, undefined, boundTranscludeFn);
|
||||
nodeLinkFn(childLinkFn, childScope, node, $rootElement, boundTranscludeFn);
|
||||
}
|
||||
} else if (childLinkFn) {
|
||||
childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn);
|
||||
@@ -1233,7 +1219,7 @@ function $CompileProvider($provide) {
|
||||
});
|
||||
} else {
|
||||
$template = jqLite(jqLiteClone(compileNode)).contents();
|
||||
$compileNode.html(''); // clear contents
|
||||
$compileNode.empty(); // clear contents
|
||||
childTranscludeFn = compile($template, transcludeFn);
|
||||
}
|
||||
}
|
||||
@@ -1414,7 +1400,7 @@ function $CompileProvider($provide) {
|
||||
optional = (match[2] == '?'),
|
||||
mode = match[1], // @, =, or &
|
||||
lastValue,
|
||||
parentGet, parentSet;
|
||||
parentGet, parentSet, compare;
|
||||
|
||||
isolateScope.$$isolateBindings[scopeName] = mode + attrName;
|
||||
|
||||
@@ -1437,6 +1423,11 @@ function $CompileProvider($provide) {
|
||||
return;
|
||||
}
|
||||
parentGet = $parse(attrs[attrName]);
|
||||
if (parentGet.literal) {
|
||||
compare = equals;
|
||||
} else {
|
||||
compare = function(a,b) { return a === b; };
|
||||
}
|
||||
parentSet = parentGet.assign || function() {
|
||||
// reset the change, or we will throw this exception on every $digest
|
||||
lastValue = isolateScope[scopeName] = parentGet(scope);
|
||||
@@ -1447,19 +1438,18 @@ function $CompileProvider($provide) {
|
||||
lastValue = isolateScope[scopeName] = parentGet(scope);
|
||||
isolateScope.$watch(function parentValueWatch() {
|
||||
var parentValue = parentGet(scope);
|
||||
|
||||
if (parentValue !== isolateScope[scopeName]) {
|
||||
if (!compare(parentValue, isolateScope[scopeName])) {
|
||||
// we are out of sync and need to copy
|
||||
if (parentValue !== lastValue) {
|
||||
if (!compare(parentValue, lastValue)) {
|
||||
// parent changed and it has precedence
|
||||
lastValue = isolateScope[scopeName] = parentValue;
|
||||
isolateScope[scopeName] = parentValue;
|
||||
} else {
|
||||
// if the parent can be assigned then do so
|
||||
parentSet(scope, parentValue = lastValue = isolateScope[scopeName]);
|
||||
parentSet(scope, parentValue = isolateScope[scopeName]);
|
||||
}
|
||||
}
|
||||
return parentValue;
|
||||
});
|
||||
return lastValue = parentValue;
|
||||
}, null, parentGet.literal);
|
||||
break;
|
||||
|
||||
case '&':
|
||||
@@ -1661,7 +1651,7 @@ function $CompileProvider($provide) {
|
||||
? origAsyncDirective.templateUrl($compileNode, tAttrs)
|
||||
: origAsyncDirective.templateUrl;
|
||||
|
||||
$compileNode.html('');
|
||||
$compileNode.empty();
|
||||
|
||||
$http.get($sce.getTrustedResourceUrl(templateUrl), {cache: $templateCache}).
|
||||
success(function(content) {
|
||||
@@ -1784,10 +1774,15 @@ function $CompileProvider($provide) {
|
||||
|
||||
|
||||
function getTrustedContext(node, attrNormalizedName) {
|
||||
if (attrNormalizedName == "srcdoc") {
|
||||
return $sce.HTML;
|
||||
}
|
||||
var tag = nodeName_(node);
|
||||
// maction[xlink:href] can source SVG. It's not limited to <maction>.
|
||||
if (attrNormalizedName == "xlinkHref" ||
|
||||
(nodeName_(node) != "IMG" && (attrNormalizedName == "src" ||
|
||||
attrNormalizedName == "ngSrc"))) {
|
||||
(tag == "FORM" && attrNormalizedName == "action") ||
|
||||
(tag != "IMG" && (attrNormalizedName == "src" ||
|
||||
attrNormalizedName == "ngSrc"))) {
|
||||
return $sce.RESOURCE_URL;
|
||||
}
|
||||
}
|
||||
@@ -1832,9 +1827,19 @@ function $CompileProvider($provide) {
|
||||
attr[name] = interpolateFn(scope);
|
||||
($$observers[name] || ($$observers[name] = [])).$$inter = true;
|
||||
(attr.$$observers && attr.$$observers[name].$$scope || scope).
|
||||
$watch(interpolateFn, function interpolateFnWatchAction(value) {
|
||||
attr.$set(name, value);
|
||||
});
|
||||
$watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
|
||||
//special case for class attribute addition + removal
|
||||
//so that class changes can tap into the animation
|
||||
//hooks provided by the $animate service. Be sure to
|
||||
//skip animations when the first digest occurs (when
|
||||
//both the new and the old values are the same) since
|
||||
//the CSS classes are the non-interpolated values
|
||||
if(name === 'class' && newValue != oldValue) {
|
||||
attr.$updateClass(newValue, oldValue);
|
||||
} else {
|
||||
attr.$set(name, newValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1974,3 +1979,19 @@ function directiveLinkingFn(
|
||||
/* Element */ rootElement,
|
||||
/* function(Function) */ boundTranscludeFn
|
||||
){}
|
||||
|
||||
function tokenDifference(str1, str2) {
|
||||
var values = '',
|
||||
tokens1 = str1.split(/\s+/),
|
||||
tokens2 = str2.split(/\s+/);
|
||||
|
||||
outer:
|
||||
for(var i = 0; i < tokens1.length; i++) {
|
||||
var token = tokens1[i];
|
||||
for(var j = 0; j < tokens2.length; j++) {
|
||||
if(token == tokens2[j]) continue outer;
|
||||
}
|
||||
values += (values.length > 0 ? ' ' : '') + token;
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
+10
-8
@@ -32,13 +32,15 @@ var htmlAnchorDirective = valueFn({
|
||||
element.append(document.createComment('IE fix'));
|
||||
}
|
||||
|
||||
return function(scope, element) {
|
||||
element.on('click', function(event){
|
||||
// if we have no href url, then don't navigate anywhere.
|
||||
if (!element.attr('href')) {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
};
|
||||
if (!attr.href && !attr.name) {
|
||||
return function(scope, element) {
|
||||
element.on('click', function(event){
|
||||
// if we have no href url, then don't navigate anywhere.
|
||||
if (!element.attr('href')) {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -149,8 +149,11 @@
|
||||
*
|
||||
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
||||
* such as disabled. (Their presence means true and their absence means false.)
|
||||
* This prevents the Angular compiler from retrieving the binding expression.
|
||||
* If we put an Angular interpolation expression into such an attribute then the
|
||||
* binding information would be lost when the browser removes the attribute.
|
||||
* The `ngDisabled` directive solves this problem for the `disabled` attribute.
|
||||
* This complementary directive is not removed by the browser and so provides
|
||||
* a permanent reliable place to store the binding information.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
@@ -181,8 +184,11 @@
|
||||
* @description
|
||||
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
||||
* such as checked. (Their presence means true and their absence means false.)
|
||||
* This prevents the Angular compiler from retrieving the binding expression.
|
||||
* If we put an Angular interpolation expression into such an attribute then the
|
||||
* binding information would be lost when the browser removes the attribute.
|
||||
* The `ngChecked` directive solves this problem for the `checked` attribute.
|
||||
* This complementary directive is not removed by the browser and so provides
|
||||
* a permanent reliable place to store the binding information.
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
@@ -212,8 +218,12 @@
|
||||
* @description
|
||||
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
||||
* such as readonly. (Their presence means true and their absence means false.)
|
||||
* This prevents the Angular compiler from retrieving the binding expression.
|
||||
* If we put an Angular interpolation expression into such an attribute then the
|
||||
* binding information would be lost when the browser removes the attribute.
|
||||
* The `ngReadonly` directive solves this problem for the `readonly` attribute.
|
||||
* This complementary directive is not removed by the browser and so provides
|
||||
* a permanent reliable place to store the binding information.
|
||||
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
@@ -243,8 +253,11 @@
|
||||
* @description
|
||||
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
||||
* such as selected. (Their presence means true and their absence means false.)
|
||||
* This prevents the Angular compiler from retrieving the binding expression.
|
||||
* If we put an Angular interpolation expression into such an attribute then the
|
||||
* binding information would be lost when the browser removes the attribute.
|
||||
* The `ngSelected` directive solves this problem for the `selected` atttribute.
|
||||
* This complementary directive is not removed by the browser and so provides
|
||||
* a permanent reliable place to store the binding information.
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
@@ -276,8 +289,12 @@
|
||||
* @description
|
||||
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
||||
* such as open. (Their presence means true and their absence means false.)
|
||||
* This prevents the Angular compiler from retrieving the binding expression.
|
||||
* If we put an Angular interpolation expression into such an attribute then the
|
||||
* binding information would be lost when the browser removes the attribute.
|
||||
* The `ngOpen` directive solves this problem for the `open` attribute.
|
||||
* This complementary directive is not removed by the browser and so provides
|
||||
* a permanent reliable place to store the binding information.
|
||||
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
|
||||
@@ -21,9 +21,22 @@ var nullFormCtrl = {
|
||||
* @property {Object} $error Is an object hash, containing references to all invalid controls or
|
||||
* forms, where:
|
||||
*
|
||||
* - keys are validation tokens (error names) — such as `required`, `url` or `email`,
|
||||
* - values are arrays of controls or forms that are invalid with given error.
|
||||
* - keys are validation tokens (error names),
|
||||
* - values are arrays of controls or forms that are invalid for given error name.
|
||||
*
|
||||
*
|
||||
* Built-in validation tokens:
|
||||
*
|
||||
* - `email`
|
||||
* - `max`
|
||||
* - `maxlength`
|
||||
* - `min`
|
||||
* - `minlength`
|
||||
* - `number`
|
||||
* - `pattern`
|
||||
* - `required`
|
||||
* - `url`
|
||||
*
|
||||
* @description
|
||||
* `FormController` keeps track of all its controls and nested forms as well as state of them,
|
||||
* such as being valid/invalid or dirty/pristine.
|
||||
|
||||
+19
-38
@@ -392,8 +392,21 @@ var inputType = {
|
||||
|
||||
|
||||
function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
// In composition mode, users are still inputing intermediate text buffer,
|
||||
// hold the listener until composition is done.
|
||||
// More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
|
||||
var composing = false;
|
||||
|
||||
element.on('compositionstart', function() {
|
||||
composing = true;
|
||||
});
|
||||
|
||||
element.on('compositionend', function() {
|
||||
composing = false;
|
||||
});
|
||||
|
||||
var listener = function() {
|
||||
if (composing) return;
|
||||
var value = element.val();
|
||||
|
||||
// By default we will trim the value
|
||||
@@ -436,15 +449,15 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
deferListener();
|
||||
});
|
||||
|
||||
// if user paste into input using mouse, we need "change" event to catch it
|
||||
element.on('change', listener);
|
||||
|
||||
// if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
|
||||
if ($sniffer.hasEvent('paste')) {
|
||||
element.on('paste cut', deferListener);
|
||||
}
|
||||
}
|
||||
|
||||
// if user paste into input using mouse on older browser
|
||||
// or form autocomplete on newer browser, we need "change" event to catch it
|
||||
element.on('change', listener);
|
||||
|
||||
ctrl.$render = function() {
|
||||
element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue);
|
||||
@@ -938,39 +951,6 @@ var VALID_CLASS = 'ng-valid',
|
||||
</file>
|
||||
* </example>
|
||||
*
|
||||
* ## Isolated Scope Pitfall
|
||||
*
|
||||
* Note that if you have a directive with an isolated scope, you cannot require `ngModel`
|
||||
* since the model value will be looked up on the isolated scope rather than the outer scope.
|
||||
* When the directive updates the model value, calling `ngModel.$setViewValue()` the property
|
||||
* on the outer scope will not be updated. However you can get around this by using $parent.
|
||||
*
|
||||
* Here is an example of this situation. You'll notice that the first div is not updating the input.
|
||||
* However the second div can update the input properly.
|
||||
*
|
||||
* <example module="badIsolatedDirective">
|
||||
<file name="script.js">
|
||||
angular.module('badIsolatedDirective', []).directive('isolate', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
scope: { },
|
||||
template: '<input ng-model="innerModel">',
|
||||
link: function(scope, element, attrs, ngModel) {
|
||||
scope.$watch('innerModel', function(value) {
|
||||
console.log(value);
|
||||
ngModel.$setViewValue(value);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<input ng-model="someModel"/>
|
||||
<div isolate ng-model="someModel"></div>
|
||||
<div isolate ng-model="$parent.someModel"></div>
|
||||
</file>
|
||||
* </example>
|
||||
*
|
||||
*
|
||||
*/
|
||||
var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse',
|
||||
@@ -1117,7 +1097,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
* It will update the $viewValue, then pass this value through each of the functions in `$parsers`,
|
||||
* which includes any validators. The value that comes out of this `$parsers` pipeline, be applied to
|
||||
* `$modelValue` and the **expression** specified in the `ng-model` attribute.
|
||||
*
|
||||
*
|
||||
* Lastly, all the registered change listeners, in the `$viewChangeListeners` list, are called.
|
||||
*
|
||||
* Note that calling this function does not trigger a `$digest`.
|
||||
@@ -1174,6 +1154,8 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
ctrl.$render();
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
}];
|
||||
|
||||
@@ -1450,7 +1432,6 @@ var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
|
||||
id="{{name}}"
|
||||
name="favorite">
|
||||
</label>
|
||||
</span>
|
||||
<div>You chose {{my.favorite}}</div>
|
||||
</form>
|
||||
</doc:source>
|
||||
|
||||
@@ -20,11 +20,10 @@ function classDirective(name, selector) {
|
||||
// jshint bitwise: false
|
||||
var mod = $index & 1;
|
||||
if (mod !== old$index & 1) {
|
||||
if (mod === selector) {
|
||||
addClass(scope.$eval(attr[name]));
|
||||
} else {
|
||||
removeClass(scope.$eval(attr[name]));
|
||||
}
|
||||
var classes = flattenClasses(scope.$eval(attr[name]));
|
||||
mod === selector ?
|
||||
attr.$addClass(classes) :
|
||||
attr.$removeClass(classes);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -32,24 +31,17 @@ function classDirective(name, selector) {
|
||||
|
||||
function ngClassWatchAction(newVal) {
|
||||
if (selector === true || scope.$index % 2 === selector) {
|
||||
if (oldVal && !equals(newVal,oldVal)) {
|
||||
removeClass(oldVal);
|
||||
var newClasses = flattenClasses(newVal || '');
|
||||
if(!oldVal) {
|
||||
attr.$addClass(newClasses);
|
||||
} else if(!equals(newVal,oldVal)) {
|
||||
attr.$updateClass(newClasses, flattenClasses(oldVal));
|
||||
}
|
||||
addClass(newVal);
|
||||
}
|
||||
oldVal = copy(newVal);
|
||||
}
|
||||
|
||||
|
||||
function removeClass(classVal) {
|
||||
attr.$removeClass(flattenClasses(classVal));
|
||||
}
|
||||
|
||||
|
||||
function addClass(classVal) {
|
||||
attr.$addClass(flattenClasses(classVal));
|
||||
}
|
||||
|
||||
function flattenClasses(classVal) {
|
||||
if(isArray(classVal)) {
|
||||
return classVal.join(' ');
|
||||
|
||||
@@ -167,6 +167,7 @@
|
||||
var ngControllerDirective = [function() {
|
||||
return {
|
||||
scope: true,
|
||||
controller: '@'
|
||||
controller: '@',
|
||||
priority: 500
|
||||
};
|
||||
}];
|
||||
|
||||
@@ -94,9 +94,12 @@ var ngIfDirective = ['$animate', function($animate) {
|
||||
if (!childScope) {
|
||||
childScope = $scope.$new();
|
||||
$transclude(childScope, function (clone) {
|
||||
clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
|
||||
// Note: We only need the first/last node of the cloned nodes.
|
||||
// However, we need to keep the reference to the jqlite wrapper as it might be changed later
|
||||
// by a directive with templateUrl when it's template arrives.
|
||||
block = {
|
||||
startNode: clone[0],
|
||||
endNode: clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ')
|
||||
clone: clone
|
||||
};
|
||||
$animate.enter(clone, $element.parent(), $element);
|
||||
});
|
||||
@@ -109,7 +112,7 @@ var ngIfDirective = ['$animate', function($animate) {
|
||||
}
|
||||
|
||||
if (block) {
|
||||
$animate.leave(getBlockElements(block));
|
||||
$animate.leave(getBlockElements(block.clone));
|
||||
block = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,13 +147,14 @@
|
||||
* @description
|
||||
* Emitted every time the ngInclude content is reloaded.
|
||||
*/
|
||||
var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile', '$animate', '$sce',
|
||||
function($http, $templateCache, $anchorScroll, $compile, $animate, $sce) {
|
||||
var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$animate', '$sce',
|
||||
function($http, $templateCache, $anchorScroll, $animate, $sce) {
|
||||
return {
|
||||
restrict: 'ECA',
|
||||
priority: 400,
|
||||
terminal: true,
|
||||
transclude: 'element',
|
||||
controller: angular.noop,
|
||||
compile: function(element, attr) {
|
||||
var srcExp = attr.ngInclude || attr.src,
|
||||
onloadExp = attr.onload || '',
|
||||
@@ -187,28 +188,52 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
|
||||
$http.get(src, {cache: $templateCache}).success(function(response) {
|
||||
if (thisChangeId !== changeCounter) return;
|
||||
var newScope = scope.$new();
|
||||
ctrl.template = response;
|
||||
|
||||
$transclude(newScope, function(clone) {
|
||||
// Note: This will also link all children of ng-include that were contained in the original
|
||||
// html. If that content contains controllers, ... they could pollute/change the scope.
|
||||
// However, using ng-include on an element with additional content does not make sense...
|
||||
// Note: We can't remove them in the cloneAttchFn of $transclude as that
|
||||
// function is called before linking the content, which would apply child
|
||||
// directives to non existing elements.
|
||||
var clone = $transclude(newScope, function(clone) {
|
||||
cleanupLastIncludeContent();
|
||||
|
||||
currentScope = newScope;
|
||||
currentElement = clone;
|
||||
|
||||
currentElement.html(response);
|
||||
$animate.enter(currentElement, null, $element, afterAnimation);
|
||||
$compile(currentElement.contents())(currentScope);
|
||||
currentScope.$emit('$includeContentLoaded');
|
||||
scope.$eval(onloadExp);
|
||||
$animate.enter(clone, null, $element, afterAnimation);
|
||||
});
|
||||
|
||||
currentScope = newScope;
|
||||
currentElement = clone;
|
||||
|
||||
currentScope.$emit('$includeContentLoaded');
|
||||
scope.$eval(onloadExp);
|
||||
}).error(function() {
|
||||
if (thisChangeId === changeCounter) cleanupLastIncludeContent();
|
||||
});
|
||||
scope.$emit('$includeContentRequested');
|
||||
} else {
|
||||
cleanupLastIncludeContent();
|
||||
ctrl.template = null;
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
||||
// This directive is called during the $transclude call of the first `ngInclude` directive.
|
||||
// It will replace and compile the content of the element with the loaded template.
|
||||
// We need this directive so that the element content is already filled when
|
||||
// the link function of another directive on the same element as ngInclude
|
||||
// is called.
|
||||
var ngIncludeFillContentDirective = ['$compile',
|
||||
function($compile) {
|
||||
return {
|
||||
restrict: 'ECA',
|
||||
priority: -400,
|
||||
require: 'ngInclude',
|
||||
link: function(scope, $element, $attr, ctrl) {
|
||||
$element.html(ctrl.template);
|
||||
$compile($element.contents())(scope);
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
* to initialize values on a scope.
|
||||
* </div>
|
||||
*
|
||||
* @priority 450
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} ngInit {@link guide/expression Expression} to eval.
|
||||
*
|
||||
@@ -47,6 +49,7 @@
|
||||
</doc:example>
|
||||
*/
|
||||
var ngInitDirective = ngDirective({
|
||||
priority: 450,
|
||||
compile: function() {
|
||||
return {
|
||||
pre: function(scope, element, attrs) {
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
* other numbers, for example 12, so that instead of showing "12 people are viewing", you can
|
||||
* show "a dozen people are viewing".
|
||||
*
|
||||
* You can use a set of closed braces(`{}`) as a placeholder for the number that you want substituted
|
||||
* You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
|
||||
* into pluralized strings. In the previous example, Angular will replace `{}` with
|
||||
* <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
|
||||
* for <span ng-non-bindable>{{numberExpression}}</span>.
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
* For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
|
||||
* `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
|
||||
* with the corresponding item in the array by identity. Moving the same object in array would move the DOM
|
||||
* element in the same way ian the DOM.
|
||||
* element in the same way in the DOM.
|
||||
*
|
||||
* For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
|
||||
* case the object identity does not matter. Two objects are considered equivalent as long as their `id`
|
||||
@@ -301,7 +301,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
||||
} else if (nextBlockMap.hasOwnProperty(trackById)) {
|
||||
// restore lastBlockMap
|
||||
forEach(nextBlockOrder, function(block) {
|
||||
if (block && block.startNode) lastBlockMap[block.id] = block;
|
||||
if (block && block.scope) lastBlockMap[block.id] = block;
|
||||
});
|
||||
// This is a duplicate and we need to throw an error
|
||||
throw ngRepeatMinErr('dupes', "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}",
|
||||
@@ -318,7 +318,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
||||
// lastBlockMap is our own object so we don't need to use special hasOwnPropertyFn
|
||||
if (lastBlockMap.hasOwnProperty(key)) {
|
||||
block = lastBlockMap[key];
|
||||
elementsToRemove = getBlockElements(block);
|
||||
elementsToRemove = getBlockElements(block.clone);
|
||||
$animate.leave(elementsToRemove);
|
||||
forEach(elementsToRemove, function(element) { element[NG_REMOVED] = true; });
|
||||
block.scope.$destroy();
|
||||
@@ -330,9 +330,9 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
||||
key = (collection === collectionKeys) ? index : collectionKeys[index];
|
||||
value = collection[key];
|
||||
block = nextBlockOrder[index];
|
||||
if (nextBlockOrder[index - 1]) previousNode = nextBlockOrder[index - 1].endNode;
|
||||
if (nextBlockOrder[index - 1]) previousNode = getBlockEnd(nextBlockOrder[index - 1]);
|
||||
|
||||
if (block.startNode) {
|
||||
if (block.scope) {
|
||||
// if we have already seen this object, then we need to reuse the
|
||||
// associated scope/element
|
||||
childScope = block.scope;
|
||||
@@ -342,11 +342,11 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
||||
nextNode = nextNode.nextSibling;
|
||||
} while(nextNode && nextNode[NG_REMOVED]);
|
||||
|
||||
if (block.startNode != nextNode) {
|
||||
if (getBlockStart(block) != nextNode) {
|
||||
// existing item which got moved
|
||||
$animate.move(getBlockElements(block), null, jqLite(previousNode));
|
||||
$animate.move(getBlockElements(block.clone), null, jqLite(previousNode));
|
||||
}
|
||||
previousNode = block.endNode;
|
||||
previousNode = getBlockEnd(block);
|
||||
} else {
|
||||
// new item which we don't know about
|
||||
childScope = $scope.$new();
|
||||
@@ -362,14 +362,16 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
||||
childScope.$odd = !(childScope.$even = (index&1) === 0);
|
||||
// jshint bitwise: true
|
||||
|
||||
if (!block.startNode) {
|
||||
if (!block.scope) {
|
||||
$transclude(childScope, function(clone) {
|
||||
clone[clone.length++] = document.createComment(' end ngRepeat: ' + expression + ' ');
|
||||
$animate.enter(clone, null, jqLite(previousNode));
|
||||
previousNode = clone;
|
||||
block.scope = childScope;
|
||||
block.startNode = previousNode && previousNode.endNode ? previousNode.endNode : clone[0];
|
||||
block.endNode = clone[clone.length - 1];
|
||||
// Note: We only need the first/last node of the cloned nodes.
|
||||
// However, we need to keep the reference to the jqlite wrapper as it might be changed later
|
||||
// by a directive with templateUrl when it's template arrives.
|
||||
block.clone = clone;
|
||||
nextBlockMap[block.id] = block;
|
||||
});
|
||||
}
|
||||
@@ -378,5 +380,13 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function getBlockStart(block) {
|
||||
return block.clone[0];
|
||||
}
|
||||
|
||||
function getBlockEnd(block) {
|
||||
return block.clone[block.clone.length - 1];
|
||||
}
|
||||
}];
|
||||
|
||||
|
||||
@@ -6,19 +6,26 @@
|
||||
* @restrict EA
|
||||
*
|
||||
* @description
|
||||
* The ngSwitch directive is used to conditionally swap DOM structure on your template based on a scope expression.
|
||||
* Elements within ngSwitch but without ngSwitchWhen or ngSwitchDefault directives will be preserved at the location
|
||||
* The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
|
||||
* Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
|
||||
* as specified in the template.
|
||||
*
|
||||
* The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
|
||||
* from the template cache), ngSwitch simply choses one of the nested elements and makes it visible based on which element
|
||||
* from the template cache), `ngSwitch` simply choses one of the nested elements and makes it visible based on which element
|
||||
* matches the value obtained from the evaluated expression. In other words, you define a container element
|
||||
* (where you place the directive), place an expression on the **on="..." attribute**
|
||||
* (or the **ng-switch="..." attribute**), define any inner elements inside of the directive and place
|
||||
* (where you place the directive), place an expression on the **`on="..."` attribute**
|
||||
* (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
|
||||
* a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
|
||||
* expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
|
||||
* attribute is displayed.
|
||||
*
|
||||
* <div class="alert alert-info">
|
||||
* Be aware that the attribute values to match against cannot be expressions. They are interpreted
|
||||
* as literal string values to match against.
|
||||
* For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
|
||||
* value of the expression `$scope.someVal`.
|
||||
* </div>
|
||||
|
||||
* @animations
|
||||
* enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
|
||||
* leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
|
||||
@@ -30,6 +37,7 @@
|
||||
* <ANY ng-switch-default>...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
*
|
||||
* @scope
|
||||
* @priority 800
|
||||
* @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.
|
||||
|
||||
@@ -69,7 +69,7 @@ var ngTranscludeDirective = ngDirective({
|
||||
|
||||
link: function($scope, $element, $attrs, controller) {
|
||||
controller.$transclude(function(clone) {
|
||||
$element.html('');
|
||||
$element.empty();
|
||||
$element.append(clone);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -333,13 +333,13 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
// becomes the compilation root
|
||||
nullOption.removeClass('ng-scope');
|
||||
|
||||
// we need to remove it before calling selectElement.html('') because otherwise IE will
|
||||
// we need to remove it before calling selectElement.empty() because otherwise IE will
|
||||
// remove the label from the element. wtf?
|
||||
nullOption.remove();
|
||||
}
|
||||
|
||||
// clear contents, we'll add what's needed based on the model
|
||||
selectElement.html('');
|
||||
selectElement.empty();
|
||||
|
||||
selectElement.on('change', function() {
|
||||
scope.$apply(function() {
|
||||
|
||||
+23
-11
@@ -28,12 +28,13 @@ var XHR = window.XMLHttpRequest || function() {
|
||||
*/
|
||||
function $HttpBackendProvider() {
|
||||
this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
|
||||
return createHttpBackend($browser, XHR, $browser.defer, $window.angular.callbacks,
|
||||
$document[0], $window.location.protocol.replace(':', ''));
|
||||
return createHttpBackend($browser, XHR, $browser.defer, $window.angular.callbacks, $document[0]);
|
||||
}];
|
||||
}
|
||||
|
||||
function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument, locationProtocol) {
|
||||
function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument) {
|
||||
var ABORTED = -1;
|
||||
|
||||
// TODO(vojta): fix the signature
|
||||
return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
|
||||
var status;
|
||||
@@ -69,13 +70,19 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
|
||||
// always async
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState == 4) {
|
||||
var responseHeaders = xhr.getAllResponseHeaders();
|
||||
var responseHeaders = null,
|
||||
response = null;
|
||||
|
||||
if(status !== ABORTED) {
|
||||
responseHeaders = xhr.getAllResponseHeaders();
|
||||
response = xhr.responseType ? xhr.response : xhr.responseText;
|
||||
}
|
||||
|
||||
// responseText is the old-school way of retrieving response (supported by IE8 & 9)
|
||||
// response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
|
||||
completeRequest(callback,
|
||||
status || xhr.status,
|
||||
(xhr.responseType ? xhr.response : xhr.responseText),
|
||||
response,
|
||||
responseHeaders);
|
||||
}
|
||||
};
|
||||
@@ -99,20 +106,20 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
|
||||
|
||||
|
||||
function timeoutRequest() {
|
||||
status = -1;
|
||||
status = ABORTED;
|
||||
jsonpDone && jsonpDone();
|
||||
xhr && xhr.abort();
|
||||
}
|
||||
|
||||
function completeRequest(callback, status, response, headersString) {
|
||||
var protocol = locationProtocol || urlResolve(url).protocol;
|
||||
var protocol = urlResolve(url).protocol;
|
||||
|
||||
// cancel timeout and subsequent timeout promise resolution
|
||||
timeoutId && $browserDefer.cancel(timeoutId);
|
||||
jsonpDone = xhr = null;
|
||||
|
||||
// fix status code for file protocol (it's always 0)
|
||||
status = (protocol == 'file') ? (response ? 200 : 404) : status;
|
||||
status = (protocol == 'file' && status === 0) ? (response ? 200 : 404) : status;
|
||||
|
||||
// normalize IE bug (http://bugs.jquery.com/ticket/1450)
|
||||
status = status == 1223 ? 204 : status;
|
||||
@@ -128,6 +135,7 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
|
||||
// - adds and immediately removes script elements from the document
|
||||
var script = rawDocument.createElement('script'),
|
||||
doneWrapper = function() {
|
||||
script.onreadystatechange = script.onload = script.onerror = null;
|
||||
rawDocument.body.removeChild(script);
|
||||
if (done) done();
|
||||
};
|
||||
@@ -135,12 +143,16 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
|
||||
script.type = 'text/javascript';
|
||||
script.src = url;
|
||||
|
||||
if (msie) {
|
||||
if (msie && msie <= 8) {
|
||||
script.onreadystatechange = function() {
|
||||
if (/loaded|complete/.test(script.readyState)) doneWrapper();
|
||||
if (/loaded|complete/.test(script.readyState)) {
|
||||
doneWrapper();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
script.onload = script.onerror = doneWrapper;
|
||||
script.onload = script.onerror = function() {
|
||||
doneWrapper();
|
||||
};
|
||||
}
|
||||
|
||||
rawDocument.body.appendChild(script);
|
||||
|
||||
@@ -103,8 +103,8 @@ function $InterpolateProvider() {
|
||||
*
|
||||
<pre>
|
||||
var $interpolate = ...; // injected
|
||||
var exp = $interpolate('Hello {{name}}!');
|
||||
expect(exp({name:'Angular'}).toEqual('Hello Angular!');
|
||||
var exp = $interpolate('Hello {{name | uppercase}}!');
|
||||
expect(exp({name:'Angular'}).toEqual('Hello ANGULAR!');
|
||||
</pre>
|
||||
*
|
||||
*
|
||||
|
||||
@@ -179,7 +179,47 @@ function LocationHashbangUrl(appBase, hashPrefix) {
|
||||
hashPrefix);
|
||||
}
|
||||
parseAppUrl(withoutHashUrl, this, appBase);
|
||||
|
||||
this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
|
||||
|
||||
this.$$compose();
|
||||
|
||||
/*
|
||||
* In Windows, on an anchor node on documents loaded from
|
||||
* the filesystem, the browser will return a pathname
|
||||
* prefixed with the drive name ('/C:/path') when a
|
||||
* pathname without a drive is set:
|
||||
* * a.setAttribute('href', '/foo')
|
||||
* * a.pathname === '/C:/foo' //true
|
||||
*
|
||||
* Inside of Angular, we're always using pathnames that
|
||||
* do not include drive names for routing.
|
||||
*/
|
||||
function removeWindowsDriveName (path, url, base) {
|
||||
/*
|
||||
Matches paths for file protocol on windows,
|
||||
such as /C:/foo/bar, and captures only /foo/bar.
|
||||
*/
|
||||
var windowsFilePathExp = /^\/?.*?:(\/.*)/;
|
||||
|
||||
var firstPathSegmentMatch;
|
||||
|
||||
//Get the relative path from the input URL.
|
||||
if (url.indexOf(base) === 0) {
|
||||
url = url.replace(base, '');
|
||||
}
|
||||
|
||||
/*
|
||||
* The input URL intentionally contains a
|
||||
* first path segment that ends with a colon.
|
||||
*/
|
||||
if (windowsFilePathExp.exec(url)) {
|
||||
return path;
|
||||
}
|
||||
|
||||
firstPathSegmentMatch = windowsFilePathExp.exec(path);
|
||||
return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
*
|
||||
* The main purpose of this service is to simplify debugging and troubleshooting.
|
||||
*
|
||||
* The default is not to log `debug` messages. You can use
|
||||
* The default is to log `debug` messages. You can use
|
||||
* {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
|
||||
*
|
||||
* @example
|
||||
|
||||
+18
-17
@@ -44,23 +44,24 @@ function ensureSafeMemberName(name, fullExpression) {
|
||||
|
||||
function ensureSafeObject(obj, fullExpression) {
|
||||
// nifty check if obj is Function that is fast and works across iframes and other contexts
|
||||
if (obj && obj.constructor === obj) {
|
||||
throw $parseMinErr('isecfn',
|
||||
'Referencing Function in Angular expressions is disallowed! Expression: {0}',
|
||||
fullExpression);
|
||||
} else if (// isWindow(obj)
|
||||
obj && obj.document && obj.location && obj.alert && obj.setInterval) {
|
||||
throw $parseMinErr('isecwindow',
|
||||
'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
|
||||
fullExpression);
|
||||
} else if (// isElement(obj)
|
||||
obj && (obj.nodeName || (obj.on && obj.find))) {
|
||||
throw $parseMinErr('isecdom',
|
||||
'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
|
||||
fullExpression);
|
||||
} else {
|
||||
return obj;
|
||||
if (obj) {
|
||||
if (obj.constructor === obj) {
|
||||
throw $parseMinErr('isecfn',
|
||||
'Referencing Function in Angular expressions is disallowed! Expression: {0}',
|
||||
fullExpression);
|
||||
} else if (// isWindow(obj)
|
||||
obj.document && obj.location && obj.alert && obj.setInterval) {
|
||||
throw $parseMinErr('isecwindow',
|
||||
'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
|
||||
fullExpression);
|
||||
} else if (// isElement(obj)
|
||||
obj.children && (obj.nodeName || (obj.on && obj.find))) {
|
||||
throw $parseMinErr('isecdom',
|
||||
'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
|
||||
fullExpression);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
var OPERATORS = {
|
||||
@@ -1017,7 +1018,7 @@ function getterFn(path, options, fullExp) {
|
||||
: '((k&&k.hasOwnProperty("' + key + '"))?k:s)') + '["' + key + '"]' + ';\n' +
|
||||
(options.unwrapPromises
|
||||
? 'if (s && s.then) {\n' +
|
||||
' pw("' + fullExp.replace(/\"/g, '\\"') + '");\n' +
|
||||
' pw("' + fullExp.replace(/(["\r\n])/g, '\\$1') + '");\n' +
|
||||
' if (!("$$v" in s)) {\n' +
|
||||
' p=s;\n' +
|
||||
' p.$$v = undefined;\n' +
|
||||
|
||||
+1
-1
@@ -136,7 +136,7 @@
|
||||
*
|
||||
* # Differences between Kris Kowal's Q and $q
|
||||
*
|
||||
* There are three main differences:
|
||||
* There are two main differences:
|
||||
*
|
||||
* - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
|
||||
* mechanism in angular, which means faster propagation of resolution or rejection into your
|
||||
|
||||
+79
-57
@@ -71,6 +71,7 @@
|
||||
function $RootScopeProvider(){
|
||||
var TTL = 10;
|
||||
var $rootScopeMinErr = minErr('$rootScope');
|
||||
var lastDirtyWatch = null;
|
||||
|
||||
this.digestTtl = function(value) {
|
||||
if (arguments.length) {
|
||||
@@ -155,11 +156,11 @@ function $RootScopeProvider(){
|
||||
* @description
|
||||
* Creates a new child {@link ng.$rootScope.Scope scope}.
|
||||
*
|
||||
* The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} and
|
||||
* {@link ng.$rootScope.Scope#$digest $digest()} events. The scope can be removed from the
|
||||
* scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
|
||||
* The parent scope will propagate the {@link ng.$rootScope.Scope#methods_$digest $digest()} and
|
||||
* {@link ng.$rootScope.Scope#methods_$digest $digest()} events. The scope can be removed from the
|
||||
* scope hierarchy using {@link ng.$rootScope.Scope#methods_$destroy $destroy()}.
|
||||
*
|
||||
* {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
|
||||
* {@link ng.$rootScope.Scope#methods_$destroy $destroy()} must be called on a scope when it is
|
||||
* desired for the scope and its child scopes to be permanently detached from the parent and
|
||||
* thus stop participating in model change detection and listener notification by invoking.
|
||||
*
|
||||
@@ -172,7 +173,7 @@ function $RootScopeProvider(){
|
||||
*
|
||||
*/
|
||||
$new: function(isolate) {
|
||||
var Child,
|
||||
var ChildScope,
|
||||
child;
|
||||
|
||||
if (isolate) {
|
||||
@@ -182,11 +183,11 @@ function $RootScopeProvider(){
|
||||
child.$$asyncQueue = this.$$asyncQueue;
|
||||
child.$$postDigestQueue = this.$$postDigestQueue;
|
||||
} else {
|
||||
Child = function() {}; // should be anonymous; This is so that when the minifier munges
|
||||
ChildScope = function() {}; // should be anonymous; This is so that when the minifier munges
|
||||
// the name it does not become random set of chars. This will then show up as class
|
||||
// name in the debugger.
|
||||
Child.prototype = this;
|
||||
child = new Child();
|
||||
ChildScope.prototype = this;
|
||||
child = new ChildScope();
|
||||
child.$id = nextUid();
|
||||
}
|
||||
child['this'] = child;
|
||||
@@ -212,11 +213,11 @@ function $RootScopeProvider(){
|
||||
* @description
|
||||
* Registers a `listener` callback to be executed whenever the `watchExpression` changes.
|
||||
*
|
||||
* - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
|
||||
* - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#methods_$digest
|
||||
* $digest()} and should return the value that will be watched. (Since
|
||||
* {@link ng.$rootScope.Scope#$digest $digest()} reruns when it detects changes the
|
||||
* {@link ng.$rootScope.Scope#methods_$digest $digest()} reruns when it detects changes the
|
||||
* `watchExpression` can execute multiple times per
|
||||
* {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.)
|
||||
* {@link ng.$rootScope.Scope#methods_$digest $digest()} and should be idempotent.)
|
||||
* - The `listener` is called only when the value from the current `watchExpression` and the
|
||||
* previous call to `watchExpression` are not equal (with the exception of the initial run,
|
||||
* see below). The inequality is determined according to
|
||||
@@ -228,13 +229,13 @@ function $RootScopeProvider(){
|
||||
* iteration limit is 10 to prevent an infinite loop deadlock.
|
||||
*
|
||||
*
|
||||
* If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
|
||||
* If you want to be notified whenever {@link ng.$rootScope.Scope#methods_$digest $digest} is called,
|
||||
* you can register a `watchExpression` function with no `listener`. (Since `watchExpression`
|
||||
* can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a
|
||||
* can execute multiple times per {@link ng.$rootScope.Scope#methods_$digest $digest} cycle when a
|
||||
* change is detected, be prepared for multiple calls to your listener.)
|
||||
*
|
||||
* After a watcher is registered with the scope, the `listener` fn is called asynchronously
|
||||
* (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
|
||||
* (via {@link ng.$rootScope.Scope#methods_$evalAsync $evalAsync}) to initialize the
|
||||
* watcher. In rare cases, this is undesirable because the listener is called when the result
|
||||
* of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
|
||||
* can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
|
||||
@@ -266,7 +267,7 @@ function $RootScopeProvider(){
|
||||
|
||||
|
||||
|
||||
// Using a listener function
|
||||
// Using a listener function
|
||||
var food;
|
||||
scope.foodCounter = 0;
|
||||
expect(scope.foodCounter).toEqual(0);
|
||||
@@ -291,14 +292,14 @@ function $RootScopeProvider(){
|
||||
// Update food and run digest. Now the counter will increment
|
||||
food = 'cheeseburger';
|
||||
scope.$digest();
|
||||
expect(scope.foodCounter).toEqual(1);
|
||||
expect(scope.foodCounter).toEqual(1);
|
||||
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param {(function()|string)} watchExpression Expression that is evaluated on each
|
||||
* {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
|
||||
* {@link ng.$rootScope.Scope#methods_$digest $digest} cycle. A change in the return value triggers
|
||||
* a call to the `listener`.
|
||||
*
|
||||
* - `string`: Evaluated as {@link guide/expression expression}
|
||||
@@ -325,6 +326,8 @@ function $RootScopeProvider(){
|
||||
eq: !!objectEquality
|
||||
};
|
||||
|
||||
lastDirtyWatch = null;
|
||||
|
||||
// in the case user pass string, we need to compile it, do we really need this ?
|
||||
if (!isFunction(listener)) {
|
||||
var listenFn = compileToFn(listener || noop, 'listener');
|
||||
@@ -394,7 +397,7 @@ function $RootScopeProvider(){
|
||||
*
|
||||
* @param {string|Function(scope)} obj Evaluated as {@link guide/expression expression}. The
|
||||
* expression value should evaluate to an object or an array which is observed on each
|
||||
* {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
|
||||
* {@link ng.$rootScope.Scope#methods_$digest $digest} cycle. Any shallow change within the
|
||||
* collection will trigger a call to the `listener`.
|
||||
*
|
||||
* @param {function(newCollection, oldCollection, scope)} listener a callback function that is
|
||||
@@ -499,9 +502,9 @@ function $RootScopeProvider(){
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
|
||||
* its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
|
||||
* the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
|
||||
* Processes all of the {@link ng.$rootScope.Scope#methods_$watch watchers} of the current scope and
|
||||
* its children. Because a {@link ng.$rootScope.Scope#methods_$watch watcher}'s listener can change
|
||||
* the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#methods_$watch watchers}
|
||||
* until no more listeners are firing. This means that it is possible to get into an infinite
|
||||
* loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
|
||||
* iterations exceeds 10.
|
||||
@@ -509,12 +512,12 @@ function $RootScopeProvider(){
|
||||
* Usually, you don't call `$digest()` directly in
|
||||
* {@link ng.directive:ngController controllers} or in
|
||||
* {@link ng.$compileProvider#methods_directive directives}.
|
||||
* Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
|
||||
* Instead, you should call {@link ng.$rootScope.Scope#methods_$apply $apply()} (typically from within
|
||||
* a {@link ng.$compileProvider#methods_directive directives}), which will force a `$digest()`.
|
||||
*
|
||||
* If you want to be notified whenever `$digest()` is called,
|
||||
* you can register a `watchExpression` function with
|
||||
* {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
|
||||
* {@link ng.$rootScope.Scope#methods_$watch $watch()} with no `listener`.
|
||||
*
|
||||
* In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
|
||||
*
|
||||
@@ -553,6 +556,8 @@ function $RootScopeProvider(){
|
||||
|
||||
beginPhase('$digest');
|
||||
|
||||
lastDirtyWatch = null;
|
||||
|
||||
do { // "while dirty" loop
|
||||
dirty = false;
|
||||
current = target;
|
||||
@@ -562,10 +567,13 @@ function $RootScopeProvider(){
|
||||
asyncTask = asyncQueue.shift();
|
||||
asyncTask.scope.$eval(asyncTask.expression);
|
||||
} catch (e) {
|
||||
clearPhase();
|
||||
$exceptionHandler(e);
|
||||
}
|
||||
lastDirtyWatch = null;
|
||||
}
|
||||
|
||||
traverseScopesLoop:
|
||||
do { // "traverse the scopes" loop
|
||||
if ((watchers = current.$$watchers)) {
|
||||
// process our watches
|
||||
@@ -575,25 +583,34 @@ function $RootScopeProvider(){
|
||||
watch = watchers[length];
|
||||
// Most common watches are on primitives, in which case we can short
|
||||
// circuit it with === operator, only when === fails do we use .equals
|
||||
if (watch && (value = watch.get(current)) !== (last = watch.last) &&
|
||||
!(watch.eq
|
||||
? equals(value, last)
|
||||
: (typeof value == 'number' && typeof last == 'number'
|
||||
&& isNaN(value) && isNaN(last)))) {
|
||||
dirty = true;
|
||||
watch.last = watch.eq ? copy(value) : value;
|
||||
watch.fn(value, ((last === initWatchVal) ? value : last), current);
|
||||
if (ttl < 5) {
|
||||
logIdx = 4 - ttl;
|
||||
if (!watchLog[logIdx]) watchLog[logIdx] = [];
|
||||
logMsg = (isFunction(watch.exp))
|
||||
? 'fn: ' + (watch.exp.name || watch.exp.toString())
|
||||
: watch.exp;
|
||||
logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last);
|
||||
watchLog[logIdx].push(logMsg);
|
||||
if (watch) {
|
||||
if ((value = watch.get(current)) !== (last = watch.last) &&
|
||||
!(watch.eq
|
||||
? equals(value, last)
|
||||
: (typeof value == 'number' && typeof last == 'number'
|
||||
&& isNaN(value) && isNaN(last)))) {
|
||||
dirty = true;
|
||||
lastDirtyWatch = watch;
|
||||
watch.last = watch.eq ? copy(value) : value;
|
||||
watch.fn(value, ((last === initWatchVal) ? value : last), current);
|
||||
if (ttl < 5) {
|
||||
logIdx = 4 - ttl;
|
||||
if (!watchLog[logIdx]) watchLog[logIdx] = [];
|
||||
logMsg = (isFunction(watch.exp))
|
||||
? 'fn: ' + (watch.exp.name || watch.exp.toString())
|
||||
: watch.exp;
|
||||
logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last);
|
||||
watchLog[logIdx].push(logMsg);
|
||||
}
|
||||
} else if (watch === lastDirtyWatch) {
|
||||
// If the most recently dirty watcher is now clean, short circuit since the remaining watchers
|
||||
// have already been tested.
|
||||
dirty = false;
|
||||
break traverseScopesLoop;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
clearPhase();
|
||||
$exceptionHandler(e);
|
||||
}
|
||||
}
|
||||
@@ -602,13 +619,16 @@ function $RootScopeProvider(){
|
||||
// Insanity Warning: scope depth-first traversal
|
||||
// yes, this code is a bit crazy, but it works and we have tests to prove it!
|
||||
// this piece should be kept in sync with the traversal in $broadcast
|
||||
if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) {
|
||||
if (!(next = (current.$$childHead ||
|
||||
(current !== target && current.$$nextSibling)))) {
|
||||
while(current !== target && !(next = current.$$nextSibling)) {
|
||||
current = current.$parent;
|
||||
}
|
||||
}
|
||||
} while ((current = next));
|
||||
|
||||
// `break traverseScopesLoop;` takes us to here
|
||||
|
||||
if(dirty && !(ttl--)) {
|
||||
clearPhase();
|
||||
throw $rootScopeMinErr('infdig',
|
||||
@@ -616,6 +636,7 @@ function $RootScopeProvider(){
|
||||
'Watchers fired in the last 5 iterations: {1}',
|
||||
TTL, toJson(watchLog));
|
||||
}
|
||||
|
||||
} while (dirty || asyncQueue.length);
|
||||
|
||||
clearPhase();
|
||||
@@ -651,7 +672,7 @@ function $RootScopeProvider(){
|
||||
*
|
||||
* @description
|
||||
* Removes the current scope (and all of its children) from the parent scope. Removal implies
|
||||
* that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
|
||||
* that calls to {@link ng.$rootScope.Scope#methods_$digest $digest()} will no longer
|
||||
* propagate to the current scope and its children. Removal also implies that the current
|
||||
* scope is eligible for garbage collection.
|
||||
*
|
||||
@@ -668,11 +689,12 @@ function $RootScopeProvider(){
|
||||
*/
|
||||
$destroy: function() {
|
||||
// we can't destroy the root scope or a scope that has been already destroyed
|
||||
if ($rootScope == this || this.$$destroyed) return;
|
||||
if (this.$$destroyed) return;
|
||||
var parent = this.$parent;
|
||||
|
||||
this.$broadcast('$destroy');
|
||||
this.$$destroyed = true;
|
||||
if (this === $rootScope) return;
|
||||
|
||||
if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
|
||||
if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
|
||||
@@ -710,7 +732,7 @@ function $RootScopeProvider(){
|
||||
*
|
||||
* - `string`: execute using the rules as defined in {@link guide/expression expression}.
|
||||
* - `function(scope)`: execute the function with the current `scope` parameter.
|
||||
*
|
||||
*
|
||||
* @param {(object)=} locals Local variables object, useful for overriding values in scope.
|
||||
* @returns {*} The result of evaluating the expression.
|
||||
*/
|
||||
@@ -732,7 +754,7 @@ function $RootScopeProvider(){
|
||||
*
|
||||
* - it will execute after the function that scheduled the evaluation (preferably before DOM
|
||||
* rendering).
|
||||
* - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
|
||||
* - at least one {@link ng.$rootScope.Scope#methods_$digest $digest cycle} will be performed after
|
||||
* `expression` execution.
|
||||
*
|
||||
* Any exceptions from the execution of the expression are forwarded to the
|
||||
@@ -777,7 +799,7 @@ function $RootScopeProvider(){
|
||||
* framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
|
||||
* Because we are calling into the angular framework we need to perform proper scope life
|
||||
* cycle of {@link ng.$exceptionHandler exception handling},
|
||||
* {@link ng.$rootScope.Scope#$digest executing watches}.
|
||||
* {@link ng.$rootScope.Scope#methods_$digest executing watches}.
|
||||
*
|
||||
* ## Life cycle
|
||||
*
|
||||
@@ -798,11 +820,11 @@ function $RootScopeProvider(){
|
||||
* Scope's `$apply()` method transitions through the following stages:
|
||||
*
|
||||
* 1. The {@link guide/expression expression} is executed using the
|
||||
* {@link ng.$rootScope.Scope#$eval $eval()} method.
|
||||
* {@link ng.$rootScope.Scope#methods_$eval $eval()} method.
|
||||
* 2. Any exceptions from the execution of the expression are forwarded to the
|
||||
* {@link ng.$exceptionHandler $exceptionHandler} service.
|
||||
* 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
|
||||
* expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
|
||||
* 3. The {@link ng.$rootScope.Scope#methods_$watch watch} listeners are fired immediately after the
|
||||
* expression was executed using the {@link ng.$rootScope.Scope#methods_$digest $digest()} method.
|
||||
*
|
||||
*
|
||||
* @param {(string|function())=} exp An angular expression to be executed.
|
||||
@@ -836,7 +858,7 @@ function $RootScopeProvider(){
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
|
||||
* Listens on events of a given type. See {@link ng.$rootScope.Scope#methods_$emit $emit} for
|
||||
* discussion of event life cycle.
|
||||
*
|
||||
* The event listener function format is: `function(event, args...)`. The `event` object
|
||||
@@ -877,20 +899,20 @@ function $RootScopeProvider(){
|
||||
*
|
||||
* @description
|
||||
* Dispatches an event `name` upwards through the scope hierarchy notifying the
|
||||
* registered {@link ng.$rootScope.Scope#$on} listeners.
|
||||
* registered {@link ng.$rootScope.Scope#methods_$on} listeners.
|
||||
*
|
||||
* The event life cycle starts at the scope on which `$emit` was called. All
|
||||
* {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
|
||||
* {@link ng.$rootScope.Scope#methods_$on listeners} listening for `name` event on this scope get
|
||||
* notified. Afterwards, the event traverses upwards toward the root scope and calls all
|
||||
* registered listeners along the way. The event will stop propagating if one of the listeners
|
||||
* cancels it.
|
||||
*
|
||||
* Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
|
||||
* Any exception emitted from the {@link ng.$rootScope.Scope#methods_$on listeners} will be passed
|
||||
* onto the {@link ng.$exceptionHandler $exceptionHandler} service.
|
||||
*
|
||||
* @param {string} name Event name to emit.
|
||||
* @param {...*} args Optional set of arguments which will be passed onto the event listeners.
|
||||
* @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
|
||||
* @return {Object} Event object (see {@link ng.$rootScope.Scope#methods_$on}).
|
||||
*/
|
||||
$emit: function(name, args) {
|
||||
var empty = [],
|
||||
@@ -946,19 +968,19 @@ function $RootScopeProvider(){
|
||||
*
|
||||
* @description
|
||||
* Dispatches an event `name` downwards to all child scopes (and their children) notifying the
|
||||
* registered {@link ng.$rootScope.Scope#$on} listeners.
|
||||
* registered {@link ng.$rootScope.Scope#methods_$on} listeners.
|
||||
*
|
||||
* The event life cycle starts at the scope on which `$broadcast` was called. All
|
||||
* {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
|
||||
* {@link ng.$rootScope.Scope#methods_$on listeners} listening for `name` event on this scope get
|
||||
* notified. Afterwards, the event propagates to all direct and indirect scopes of the current
|
||||
* scope and calls all registered listeners along the way. The event cannot be canceled.
|
||||
*
|
||||
* Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
|
||||
* Any exception emitted from the {@link ng.$rootScope.Scope#methods_$on listeners} will be passed
|
||||
* onto the {@link ng.$exceptionHandler $exceptionHandler} service.
|
||||
*
|
||||
* @param {string} name Event name to broadcast.
|
||||
* @param {...*} args Optional set of arguments which will be passed onto the event listeners.
|
||||
* @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
|
||||
* @return {Object} Event object, see {@link ng.$rootScope.Scope#methods_$on}
|
||||
*/
|
||||
$broadcast: function(name, args) {
|
||||
var target = this,
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Private service to sanitize uris for links and images. Used by $compile and $sanitize.
|
||||
*/
|
||||
function $$SanitizeUriProvider() {
|
||||
var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
|
||||
imgSrcSanitizationWhitelist = /^\s*(https?|ftp|file):|data:image\//;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
|
||||
* urls during a[href] sanitization.
|
||||
*
|
||||
* The sanitization is a security measure aimed at prevent XSS attacks via html links.
|
||||
*
|
||||
* Any url about to be assigned to a[href] via data-binding is first normalized and turned into
|
||||
* an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
|
||||
* regular expression. If a match is found, the original url is written into the dom. Otherwise,
|
||||
* the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
|
||||
*
|
||||
* @param {RegExp=} regexp New regexp to whitelist urls with.
|
||||
* @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
|
||||
* chaining otherwise.
|
||||
*/
|
||||
this.aHrefSanitizationWhitelist = function(regexp) {
|
||||
if (isDefined(regexp)) {
|
||||
aHrefSanitizationWhitelist = regexp;
|
||||
return this;
|
||||
}
|
||||
return aHrefSanitizationWhitelist;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
|
||||
* urls during img[src] sanitization.
|
||||
*
|
||||
* The sanitization is a security measure aimed at prevent XSS attacks via html links.
|
||||
*
|
||||
* Any url about to be assigned to img[src] via data-binding is first normalized and turned into
|
||||
* an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
|
||||
* regular expression. If a match is found, the original url is written into the dom. Otherwise,
|
||||
* the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
|
||||
*
|
||||
* @param {RegExp=} regexp New regexp to whitelist urls with.
|
||||
* @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
|
||||
* chaining otherwise.
|
||||
*/
|
||||
this.imgSrcSanitizationWhitelist = function(regexp) {
|
||||
if (isDefined(regexp)) {
|
||||
imgSrcSanitizationWhitelist = regexp;
|
||||
return this;
|
||||
}
|
||||
return imgSrcSanitizationWhitelist;
|
||||
};
|
||||
|
||||
this.$get = function() {
|
||||
return function sanitizeUri(uri, isImage) {
|
||||
var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
|
||||
var normalizedVal;
|
||||
// NOTE: urlResolve() doesn't support IE < 8 so we don't sanitize for that case.
|
||||
if (!msie || msie >= 8 ) {
|
||||
normalizedVal = urlResolve(uri).href;
|
||||
if (normalizedVal !== '' && !normalizedVal.match(regex)) {
|
||||
return 'unsafe:'+normalizedVal;
|
||||
}
|
||||
}
|
||||
return uri;
|
||||
};
|
||||
};
|
||||
}
|
||||
+8
-12
@@ -199,8 +199,7 @@ function $SceDelegateProvider() {
|
||||
return resourceUrlBlacklist;
|
||||
};
|
||||
|
||||
this.$get = ['$log', '$document', '$injector', function(
|
||||
$log, $document, $injector) {
|
||||
this.$get = ['$injector', function($injector) {
|
||||
|
||||
var htmlSanitizer = function htmlSanitizer(html) {
|
||||
throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
|
||||
@@ -731,18 +730,15 @@ function $SceProvider() {
|
||||
* sce.js and sceSpecs.js would need to be aware of this detail.
|
||||
*/
|
||||
|
||||
this.$get = ['$parse', '$document', '$sceDelegate', function(
|
||||
$parse, $document, $sceDelegate) {
|
||||
this.$get = ['$parse', '$sniffer', '$sceDelegate', function(
|
||||
$parse, $sniffer, $sceDelegate) {
|
||||
// Prereq: Ensure that we're not running in IE8 quirks mode. In that mode, IE allows
|
||||
// the "expression(javascript expression)" syntax which is insecure.
|
||||
if (enabled && msie) {
|
||||
var documentMode = $document[0].documentMode;
|
||||
if (documentMode !== undefined && documentMode < 8) {
|
||||
throw $sceMinErr('iequirks',
|
||||
'Strict Contextual Escaping does not support Internet Explorer version < 9 in quirks ' +
|
||||
'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' +
|
||||
'document. See http://docs.angularjs.org/api/ng.$sce for more information.');
|
||||
}
|
||||
if (enabled && $sniffer.msie && $sniffer.msieDocumentMode < 8) {
|
||||
throw $sceMinErr('iequirks',
|
||||
'Strict Contextual Escaping does not support Internet Explorer version < 9 in quirks ' +
|
||||
'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' +
|
||||
'document. See http://docs.angularjs.org/api/ng.$sce for more information.');
|
||||
}
|
||||
|
||||
var sce = copy(SCE_CONTEXTS);
|
||||
|
||||
+4
-2
@@ -22,6 +22,7 @@ function $SnifferProvider() {
|
||||
int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
|
||||
boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
|
||||
document = $document[0] || {},
|
||||
documentMode = document.documentMode,
|
||||
vendorPrefix,
|
||||
vendorRegex = /^(Moz|webkit|O|ms)(?=[A-Z])/,
|
||||
bodyStyle = document.body && document.body.style,
|
||||
@@ -66,7 +67,7 @@ function $SnifferProvider() {
|
||||
// jshint +W018
|
||||
hashchange: 'onhashchange' in $window &&
|
||||
// IE8 compatible mode lies
|
||||
(!document.documentMode || document.documentMode > 7),
|
||||
(!documentMode || documentMode > 7),
|
||||
hasEvent: function(event) {
|
||||
// IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
|
||||
// it. In particular the event is not fired when backspace or delete key are pressed or
|
||||
@@ -84,7 +85,8 @@ function $SnifferProvider() {
|
||||
vendorPrefix: vendorPrefix,
|
||||
transitions : transitions,
|
||||
animations : animations,
|
||||
msie : msie
|
||||
msie : msie,
|
||||
msieDocumentMode: documentMode
|
||||
};
|
||||
}];
|
||||
}
|
||||
|
||||
+4
-44
@@ -7,11 +7,6 @@
|
||||
// exactly the behavior needed here. There is little value is mocking these out for this
|
||||
// service.
|
||||
var urlParsingNode = document.createElement("a");
|
||||
/*
|
||||
Matches paths for file protocol on windows,
|
||||
such as /C:/foo/bar, and captures only /foo/bar.
|
||||
*/
|
||||
var windowsFilePathExp = /^\/?.*?:(\/.*)/;
|
||||
var originUrl = urlResolve(window.location.href, true);
|
||||
|
||||
|
||||
@@ -68,8 +63,7 @@ var originUrl = urlResolve(window.location.href, true);
|
||||
*
|
||||
*/
|
||||
function urlResolve(url, base) {
|
||||
var href = url,
|
||||
pathname;
|
||||
var href = url;
|
||||
|
||||
if (msie) {
|
||||
// Normalize before parse. Refer Implementation Notes on why this is
|
||||
@@ -80,21 +74,6 @@ function urlResolve(url, base) {
|
||||
|
||||
urlParsingNode.setAttribute('href', href);
|
||||
|
||||
/*
|
||||
* In Windows, on an anchor node on documents loaded from
|
||||
* the filesystem, the browser will return a pathname
|
||||
* prefixed with the drive name ('/C:/path') when a
|
||||
* pathname without a drive is set:
|
||||
* * a.setAttribute('href', '/foo')
|
||||
* * a.pathname === '/C:/foo' //true
|
||||
*
|
||||
* Inside of Angular, we're always using pathnames that
|
||||
* do not include drive names for routing.
|
||||
*/
|
||||
|
||||
pathname = removeWindowsDriveName(urlParsingNode.pathname, url, base);
|
||||
pathname = (pathname.charAt(0) === '/') ? pathname : '/' + pathname;
|
||||
|
||||
// urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
|
||||
return {
|
||||
href: urlParsingNode.href,
|
||||
@@ -104,11 +83,12 @@ function urlResolve(url, base) {
|
||||
hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
|
||||
hostname: urlParsingNode.hostname,
|
||||
port: urlParsingNode.port,
|
||||
pathname: pathname
|
||||
pathname: (urlParsingNode.pathname.charAt(0) === '/')
|
||||
? urlParsingNode.pathname
|
||||
: '/' + urlParsingNode.pathname
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse a request URL and determine whether this is a same-origin request as the application document.
|
||||
*
|
||||
@@ -121,23 +101,3 @@ function urlIsSameOrigin(requestUrl) {
|
||||
return (parsed.protocol === originUrl.protocol &&
|
||||
parsed.host === originUrl.host);
|
||||
}
|
||||
|
||||
function removeWindowsDriveName (path, url, base) {
|
||||
var firstPathSegmentMatch;
|
||||
|
||||
//Get the relative path from the input URL.
|
||||
if (url.indexOf(base) === 0) {
|
||||
url = url.replace(base, '');
|
||||
}
|
||||
|
||||
/*
|
||||
* The input URL intentionally contains a
|
||||
* first path segment that ends with a colon.
|
||||
*/
|
||||
if (windowsFilePathExp.exec(url)) {
|
||||
return path;
|
||||
}
|
||||
|
||||
firstPathSegmentMatch = windowsFilePathExp.exec(path);
|
||||
return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
|
||||
}
|
||||
+4
-2
@@ -20,13 +20,15 @@
|
||||
<doc:source>
|
||||
<script>
|
||||
function Ctrl($scope, $window) {
|
||||
$scope.$window = $window;
|
||||
$scope.greeting = 'Hello, World!';
|
||||
$scope.doGreeting = function(greeting) {
|
||||
$window.alert(greeting);
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="Ctrl">
|
||||
<input type="text" ng-model="greeting" />
|
||||
<button ng-click="$window.alert(greeting)">ALERT</button>
|
||||
<button ng-click="doGreeting(greeting)">ALERT</button>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
|
||||
+152
-55
@@ -159,7 +159,7 @@
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Staggering animations work by default in ngRepeat (so long as the CSS class is defiend). Outside of ngRepeat, to use staggering animations
|
||||
* Staggering animations work by default in ngRepeat (so long as the CSS class is defined). Outside of ngRepeat, to use staggering animations
|
||||
* on your own, they can be triggered by firing multiple calls to the same event on $animate. However, the restrictions surrounding this
|
||||
* are that each of the elements must have the same CSS className value as well as the same parent element. A stagger operation
|
||||
* will also be reset if more than 10ms has passed after the last animation has been fired.
|
||||
@@ -190,7 +190,7 @@
|
||||
*
|
||||
* <pre>
|
||||
* //!annotate="YourApp" Your AngularJS Module|Replace this or ngModule with the module that you used to define your application.
|
||||
* var ngModule = angular.module('YourApp', []);
|
||||
* var ngModule = angular.module('YourApp', ['ngAnimate']);
|
||||
* ngModule.animation('.my-crazy-animation', function() {
|
||||
* return {
|
||||
* enter: function(element, done) {
|
||||
@@ -199,8 +199,8 @@
|
||||
* //this (optional) function will be called when the animation
|
||||
* //completes or when the animation is cancelled (the cancelled
|
||||
* //flag will be set to true if cancelled).
|
||||
* }
|
||||
* }
|
||||
* };
|
||||
* },
|
||||
* leave: function(element, done) { },
|
||||
* move: function(element, done) { },
|
||||
*
|
||||
@@ -215,14 +215,14 @@
|
||||
*
|
||||
* //animation that can be triggered after the class is removed
|
||||
* removeClass: function(element, className, done) { }
|
||||
* }
|
||||
* };
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* JavaScript-defined animations are created with a CSS-like class selector and a collection of events which are set to run
|
||||
* a javascript callback function. When an animation is triggered, $animate will look for a matching animation which fits
|
||||
* the element's CSS class attribute value and then run the matching animation event function (if found).
|
||||
* In other words, if the CSS classes present on the animated element match any of the JavaScript animations then the callback function
|
||||
* In other words, if the CSS classes present on the animated element match any of the JavaScript animations then the callback function will
|
||||
* be executed. It should be also noted that only simple, single class selectors are allowed (compound class selectors are not supported).
|
||||
*
|
||||
* Within a JavaScript animation, an object containing various event callback animation functions is expected to be returned.
|
||||
@@ -258,14 +258,34 @@ angular.module('ngAnimate', ['ng'])
|
||||
var NG_ANIMATE_CLASS_NAME = 'ng-animate';
|
||||
var rootAnimateState = {running: true};
|
||||
|
||||
function extractElementNode(element) {
|
||||
for(var i = 0; i < element.length; i++) {
|
||||
var elm = element[i];
|
||||
if(elm.nodeType == ELEMENT_NODE) {
|
||||
return elm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isMatchingElement(elm1, elm2) {
|
||||
return extractElementNode(elm1) == extractElementNode(elm2);
|
||||
}
|
||||
|
||||
$provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$timeout', '$rootScope', '$document',
|
||||
function($delegate, $injector, $sniffer, $rootElement, $timeout, $rootScope, $document) {
|
||||
|
||||
$rootElement.data(NG_ANIMATE_STATE, rootAnimateState);
|
||||
|
||||
// disable animations during bootstrap, but once we bootstrapped, enable animations
|
||||
// disable animations during bootstrap, but once we bootstrapped, wait again
|
||||
// for another digest until enabling animations. The reason why we digest twice
|
||||
// is because all structural animations (enter, leave and move) all perform a
|
||||
// post digest operation before animating. If we only wait for a single digest
|
||||
// to pass then the structural animation would render its animation on page load.
|
||||
// (which is what we're trying to avoid when the application first boots up.)
|
||||
$rootScope.$$postDigest(function() {
|
||||
rootAnimateState.running = false;
|
||||
$rootScope.$$postDigest(function() {
|
||||
rootAnimateState.running = false;
|
||||
});
|
||||
});
|
||||
|
||||
function lookup(name) {
|
||||
@@ -363,7 +383,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
* Runs the leave animation operation and, upon completion, removes the element from the DOM. Once
|
||||
* the animation is started, the following CSS classes will be added for the duration of the animation:
|
||||
*
|
||||
* Below is a breakdown of each step that occurs during enter animation:
|
||||
* Below is a breakdown of each step that occurs during leave animation:
|
||||
*
|
||||
* | Animation Step | What the element class attribute looks like |
|
||||
* |----------------------------------------------------------------------------------------------|---------------------------------------------|
|
||||
@@ -510,6 +530,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
* @function
|
||||
*
|
||||
* @param {boolean=} value If provided then set the animation on or off.
|
||||
* @param {jQuery/jqLite element=} element If provided then the element will be used to represent the enable/disable operation
|
||||
* @return {boolean} Current animation state.
|
||||
*
|
||||
* @description
|
||||
@@ -548,7 +569,17 @@ angular.module('ngAnimate', ['ng'])
|
||||
and the onComplete callback will be fired once the animation is fully complete.
|
||||
*/
|
||||
function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, doneCallback) {
|
||||
var classes = (element.attr('class') || '') + ' ' + className;
|
||||
var node = extractElementNode(element);
|
||||
//transcluded directives may sometimes fire an animation using only comment nodes
|
||||
//best to catch this early on to prevent any animation operations from occurring
|
||||
if(!node) {
|
||||
fireDOMOperation();
|
||||
closeAnimation();
|
||||
return;
|
||||
}
|
||||
|
||||
var currentClassName = node.className;
|
||||
var classes = currentClassName + ' ' + className;
|
||||
var animationLookup = (' ' + classes).replace(/\s+/g,'.');
|
||||
if (!parentElement) {
|
||||
parentElement = afterElement ? afterElement.parent() : element.parent();
|
||||
@@ -563,7 +594,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
//the animation if any matching animations are not found at all.
|
||||
//NOTE: IE8 + IE9 should close properly (run closeAnimation()) in case a NO animation is not found.
|
||||
if (animationsDisabled(element, parentElement) || matches.length === 0) {
|
||||
domOperation();
|
||||
fireDOMOperation();
|
||||
closeAnimation();
|
||||
return;
|
||||
}
|
||||
@@ -596,27 +627,48 @@ angular.module('ngAnimate', ['ng'])
|
||||
//this would mean that an animation was not allowed so let the existing
|
||||
//animation do it's thing and close this one early
|
||||
if(animations.length === 0) {
|
||||
domOperation();
|
||||
fireDOMOperation();
|
||||
fireDoneCallbackAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
//this value will be searched for class-based CSS className lookup. Therefore,
|
||||
//we prefix and suffix the current className value with spaces to avoid substring
|
||||
//lookups of className tokens
|
||||
var futureClassName = ' ' + currentClassName + ' ';
|
||||
if(ngAnimateState.running) {
|
||||
//if an animation is currently running on the element then lets take the steps
|
||||
//to cancel that animation and fire any required callbacks
|
||||
$timeout.cancel(ngAnimateState.closeAnimationTimeout);
|
||||
cleanup(element);
|
||||
cancelAnimations(ngAnimateState.animations);
|
||||
(ngAnimateState.done || noop)(true);
|
||||
|
||||
//if the class is removed during the reflow then it will revert the styles temporarily
|
||||
//back to the base class CSS styling causing a jump-like effect to occur. This check
|
||||
//here ensures that the domOperation is only performed after the reflow has commenced
|
||||
if(ngAnimateState.beforeComplete) {
|
||||
(ngAnimateState.done || noop)(true);
|
||||
} else if(isClassBased && !ngAnimateState.structural) {
|
||||
//class-based animations will compare element className values after cancelling the
|
||||
//previous animation to see if the element properties already contain the final CSS
|
||||
//class and if so then the animation will be skipped. Since the domOperation will
|
||||
//be performed only after the reflow is complete then our element's className value
|
||||
//will be invalid. Therefore the same string manipulation that would occur within the
|
||||
//DOM operation will be performed below so that the class comparison is valid...
|
||||
futureClassName = ngAnimateState.event == 'removeClass' ?
|
||||
futureClassName.replace(ngAnimateState.className, '') :
|
||||
futureClassName + ngAnimateState.className + ' ';
|
||||
}
|
||||
}
|
||||
|
||||
//There is no point in perform a class-based animation if the element already contains
|
||||
//(on addClass) or doesn't contain (on removeClass) the className being animated.
|
||||
//The reason why this is being called after the previous animations are cancelled
|
||||
//is so that the CSS classes present on the element can be properly examined.
|
||||
if((animationEvent == 'addClass' && element.hasClass(className)) ||
|
||||
(animationEvent == 'removeClass' && !element.hasClass(className))) {
|
||||
domOperation();
|
||||
var classNameToken = ' ' + className + ' ';
|
||||
if((animationEvent == 'addClass' && futureClassName.indexOf(classNameToken) >= 0) ||
|
||||
(animationEvent == 'removeClass' && futureClassName.indexOf(classNameToken) == -1)) {
|
||||
fireDOMOperation();
|
||||
fireDoneCallbackAsync();
|
||||
return;
|
||||
}
|
||||
@@ -627,6 +679,8 @@ angular.module('ngAnimate', ['ng'])
|
||||
|
||||
element.data(NG_ANIMATE_STATE, {
|
||||
running:true,
|
||||
event:animationEvent,
|
||||
className:className,
|
||||
structural:!isClassBased,
|
||||
animations:animations,
|
||||
done:onBeforeAnimationsComplete
|
||||
@@ -637,7 +691,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
invokeRegisteredAnimationFns(animations, 'before', onBeforeAnimationsComplete);
|
||||
|
||||
function onBeforeAnimationsComplete(cancelled) {
|
||||
domOperation();
|
||||
fireDOMOperation();
|
||||
if(cancelled === true) {
|
||||
closeAnimation();
|
||||
return;
|
||||
@@ -695,6 +749,15 @@ angular.module('ngAnimate', ['ng'])
|
||||
doneCallback && $timeout(doneCallback, 0, false);
|
||||
}
|
||||
|
||||
//it is less complicated to use a flag than managing and cancelling
|
||||
//timeouts containing multiple callbacks.
|
||||
function fireDOMOperation() {
|
||||
if(!fireDOMOperation.hasBeenRun) {
|
||||
fireDOMOperation.hasBeenRun = true;
|
||||
domOperation();
|
||||
}
|
||||
}
|
||||
|
||||
function closeAnimation() {
|
||||
if(!closeAnimation.hasBeenRun) {
|
||||
closeAnimation.hasBeenRun = true;
|
||||
@@ -719,11 +782,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
}
|
||||
|
||||
function cancelChildAnimations(element) {
|
||||
var node = element[0];
|
||||
if(node.nodeType != ELEMENT_NODE) {
|
||||
return;
|
||||
}
|
||||
|
||||
var node = extractElementNode(element);
|
||||
forEach(node.querySelectorAll('.' + NG_ANIMATE_CLASS_NAME), function(element) {
|
||||
element = angular.element(element);
|
||||
var data = element.data(NG_ANIMATE_STATE);
|
||||
@@ -737,17 +796,17 @@ angular.module('ngAnimate', ['ng'])
|
||||
function cancelAnimations(animations) {
|
||||
var isCancelledFlag = true;
|
||||
forEach(animations, function(animation) {
|
||||
if(!animations['beforeComplete']) {
|
||||
if(!animations.beforeComplete) {
|
||||
(animation.beforeEnd || noop)(isCancelledFlag);
|
||||
}
|
||||
if(!animations['afterComplete']) {
|
||||
if(!animations.afterComplete) {
|
||||
(animation.afterEnd || noop)(isCancelledFlag);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function cleanup(element) {
|
||||
if(element[0] == $rootElement[0]) {
|
||||
if(isMatchingElement(element, $rootElement)) {
|
||||
if(!rootAnimateState.disabled) {
|
||||
rootAnimateState.running = false;
|
||||
rootAnimateState.structural = false;
|
||||
@@ -761,7 +820,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
function animationsDisabled(element, parentElement) {
|
||||
if (rootAnimateState.disabled) return true;
|
||||
|
||||
if(element[0] == $rootElement[0]) {
|
||||
if(isMatchingElement(element, $rootElement)) {
|
||||
return rootAnimateState.disabled || rootAnimateState.running;
|
||||
}
|
||||
|
||||
@@ -771,7 +830,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
//any animations on it
|
||||
if(parentElement.length === 0) break;
|
||||
|
||||
var isRoot = parentElement[0] == $rootElement[0];
|
||||
var isRoot = isMatchingElement(parentElement, $rootElement);
|
||||
var state = isRoot ? rootAnimateState : parentElement.data(NG_ANIMATE_STATE);
|
||||
var result = state && (!!state.disabled || !!state.running);
|
||||
if(isRoot || result) {
|
||||
@@ -824,6 +883,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
var NG_ANIMATE_CSS_DATA_KEY = '$$ngAnimateCSS3Data';
|
||||
var NG_ANIMATE_FALLBACK_CLASS_NAME = 'ng-animate-start';
|
||||
var NG_ANIMATE_FALLBACK_ACTIVE_CLASS_NAME = 'ng-animate-active';
|
||||
var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3;
|
||||
|
||||
var lookupCache = {};
|
||||
var parentCounter = 0;
|
||||
@@ -842,13 +902,6 @@ angular.module('ngAnimate', ['ng'])
|
||||
}, 10, false);
|
||||
}
|
||||
|
||||
function applyStyle(node, style) {
|
||||
var oldStyle = node.getAttribute('style') || '';
|
||||
var newStyle = (oldStyle.length > 0 ? '; ' : '') + style;
|
||||
node.setAttribute('style', newStyle);
|
||||
return oldStyle;
|
||||
}
|
||||
|
||||
function getElementAnimationDetails(element, cacheKey) {
|
||||
var data = cacheKey ? lookupCache[cacheKey] : null;
|
||||
if(!data) {
|
||||
@@ -925,7 +978,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
parentElement.data(NG_ANIMATE_PARENT_KEY, ++parentCounter);
|
||||
parentID = parentCounter;
|
||||
}
|
||||
return parentID + '-' + element[0].className;
|
||||
return parentID + '-' + extractElementNode(element).className;
|
||||
}
|
||||
|
||||
function animateSetup(element, className) {
|
||||
@@ -960,14 +1013,15 @@ angular.module('ngAnimate', ['ng'])
|
||||
return false;
|
||||
}
|
||||
|
||||
var node = element[0];
|
||||
//temporarily disable the transition so that the enter styles
|
||||
//don't animate twice (this is here to avoid a bug in Chrome/FF).
|
||||
var activeClassName = '';
|
||||
if(timings.transitionDuration > 0) {
|
||||
element.addClass(NG_ANIMATE_FALLBACK_CLASS_NAME);
|
||||
activeClassName += NG_ANIMATE_FALLBACK_ACTIVE_CLASS_NAME + ' ';
|
||||
node.style[TRANSITION_PROP + PROPERTY_KEY] = 'none';
|
||||
blockTransitions(element);
|
||||
} else {
|
||||
blockKeyframeAnimations(element);
|
||||
}
|
||||
|
||||
forEach(className.split(' '), function(klass, i) {
|
||||
@@ -987,14 +1041,38 @@ angular.module('ngAnimate', ['ng'])
|
||||
return true;
|
||||
}
|
||||
|
||||
function blockTransitions(element) {
|
||||
extractElementNode(element).style[TRANSITION_PROP + PROPERTY_KEY] = 'none';
|
||||
}
|
||||
|
||||
function blockKeyframeAnimations(element) {
|
||||
extractElementNode(element).style[ANIMATION_PROP] = 'none 0s';
|
||||
}
|
||||
|
||||
function unblockTransitions(element) {
|
||||
var prop = TRANSITION_PROP + PROPERTY_KEY;
|
||||
var node = extractElementNode(element);
|
||||
if(node.style[prop] && node.style[prop].length > 0) {
|
||||
node.style[prop] = '';
|
||||
}
|
||||
}
|
||||
|
||||
function unblockKeyframeAnimations(element) {
|
||||
var prop = ANIMATION_PROP;
|
||||
var node = extractElementNode(element);
|
||||
if(node.style[prop] && node.style[prop].length > 0) {
|
||||
node.style[prop] = '';
|
||||
}
|
||||
}
|
||||
|
||||
function animateRun(element, className, activeAnimationComplete) {
|
||||
var data = element.data(NG_ANIMATE_CSS_DATA_KEY);
|
||||
if(!element.hasClass(className) || !data) {
|
||||
var node = extractElementNode(element);
|
||||
if(node.className.indexOf(className) == -1 || !data) {
|
||||
activeAnimationComplete();
|
||||
return;
|
||||
}
|
||||
|
||||
var node = element[0];
|
||||
var timings = data.timings;
|
||||
var stagger = data.stagger;
|
||||
var maxDuration = data.maxDuration;
|
||||
@@ -1002,19 +1080,18 @@ angular.module('ngAnimate', ['ng'])
|
||||
var maxDelayTime = Math.max(timings.transitionDelay, timings.animationDelay) * 1000;
|
||||
var startTime = Date.now();
|
||||
var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT;
|
||||
var formerStyle;
|
||||
var ii = data.ii;
|
||||
|
||||
var applyFallbackStyle, style = '';
|
||||
var applyFallbackStyle, style = '', appliedStyles = [];
|
||||
if(timings.transitionDuration > 0) {
|
||||
node.style[TRANSITION_PROP + PROPERTY_KEY] = '';
|
||||
|
||||
var propertyStyle = timings.transitionPropertyStyle;
|
||||
if(propertyStyle.indexOf('all') == -1) {
|
||||
applyFallbackStyle = true;
|
||||
var fallbackProperty = $sniffer.msie ? '-ms-zoom' : 'clip';
|
||||
var fallbackProperty = $sniffer.msie ? '-ms-zoom' : 'border-spacing';
|
||||
style += CSS_PREFIX + 'transition-property: ' + propertyStyle + ', ' + fallbackProperty + '; ';
|
||||
style += CSS_PREFIX + 'transition-duration: ' + timings.transitionDurationStyle + ', ' + timings.transitionDuration + 's; ';
|
||||
appliedStyles.push(CSS_PREFIX + 'transition-property');
|
||||
appliedStyles.push(CSS_PREFIX + 'transition-duration');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1027,16 +1104,22 @@ angular.module('ngAnimate', ['ng'])
|
||||
|
||||
style += CSS_PREFIX + 'transition-delay: ' +
|
||||
prepareStaggerDelay(delayStyle, stagger.transitionDelay, ii) + '; ';
|
||||
appliedStyles.push(CSS_PREFIX + 'transition-delay');
|
||||
}
|
||||
|
||||
if(stagger.animationDelay > 0 && stagger.animationDuration === 0) {
|
||||
style += CSS_PREFIX + 'animation-delay: ' +
|
||||
prepareStaggerDelay(timings.animationDelayStyle, stagger.animationDelay, ii) + '; ';
|
||||
appliedStyles.push(CSS_PREFIX + 'animation-delay');
|
||||
}
|
||||
}
|
||||
|
||||
if(style.length > 0) {
|
||||
formerStyle = applyStyle(node, style);
|
||||
if(appliedStyles.length > 0) {
|
||||
//the element being animated may sometimes contain comment nodes in
|
||||
//the jqLite object, so we're safe to use a single variable to house
|
||||
//the styles since there is always only one element being animated
|
||||
var oldStyle = node.getAttribute('style') || '';
|
||||
node.setAttribute('style', oldStyle + ' ' + style);
|
||||
}
|
||||
|
||||
element.on(css3AnimationEvents, onAnimationProgress);
|
||||
@@ -1049,10 +1132,9 @@ angular.module('ngAnimate', ['ng'])
|
||||
element.off(css3AnimationEvents, onAnimationProgress);
|
||||
element.removeClass(activeClassName);
|
||||
animateClose(element, className);
|
||||
if(formerStyle != null) {
|
||||
formerStyle.length > 0 ?
|
||||
node.setAttribute('style', formerStyle) :
|
||||
node.removeAttribute('style');
|
||||
var node = extractElementNode(element);
|
||||
for (var i in appliedStyles) {
|
||||
node.style.removeProperty(appliedStyles[i]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1060,6 +1142,11 @@ angular.module('ngAnimate', ['ng'])
|
||||
event.stopPropagation();
|
||||
var ev = event.originalEvent || event;
|
||||
var timeStamp = ev.$manualTimeStamp || ev.timeStamp || Date.now();
|
||||
|
||||
/* Firefox (or possibly just Gecko) likes to not round values up
|
||||
* when a ms measurement is used for the animation */
|
||||
var elapsedTime = parseFloat(ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES));
|
||||
|
||||
/* $manualTimeStamp is a mocked timeStamp value which is set
|
||||
* within browserTrigger(). This is only here so that tests can
|
||||
* mock animations properly. Real events fallback to event.timeStamp,
|
||||
@@ -1067,7 +1154,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
* We're checking to see if the timeStamp surpasses the expected delay,
|
||||
* but we're using elapsedTime instead of the timeStamp on the 2nd
|
||||
* pre-condition since animations sometimes close off early */
|
||||
if(Math.max(timeStamp - startTime, 0) >= maxDelayTime && ev.elapsedTime >= maxDuration) {
|
||||
if(Math.max(timeStamp - startTime, 0) >= maxDelayTime && elapsedTime >= maxDuration) {
|
||||
activeAnimationComplete();
|
||||
}
|
||||
}
|
||||
@@ -1116,6 +1203,8 @@ angular.module('ngAnimate', ['ng'])
|
||||
//happen in the first place
|
||||
var cancel = preReflowCancellation;
|
||||
afterReflow(function() {
|
||||
unblockTransitions(element);
|
||||
unblockKeyframeAnimations(element);
|
||||
//once the reflow is complete then we point cancel to
|
||||
//the new cancellation function which will remove all of the
|
||||
//animation properties from the active animation
|
||||
@@ -1143,12 +1232,12 @@ angular.module('ngAnimate', ['ng'])
|
||||
}
|
||||
|
||||
var parentElement = element.parent();
|
||||
var clone = angular.element(element[0].cloneNode());
|
||||
var clone = angular.element(extractElementNode(element).cloneNode());
|
||||
|
||||
//make the element super hidden and override any CSS style values
|
||||
clone.attr('style','position:absolute; top:-9999px; left:-9999px');
|
||||
clone.removeAttr('id');
|
||||
clone.html('');
|
||||
clone.empty();
|
||||
|
||||
forEach(oldClasses.split(' '), function(klass) {
|
||||
clone.removeClass(klass);
|
||||
@@ -1179,7 +1268,11 @@ angular.module('ngAnimate', ['ng'])
|
||||
beforeAddClass : function(element, className, animationCompleted) {
|
||||
var cancellationMethod = animateBefore(element, suffixClasses(className, '-add'));
|
||||
if(cancellationMethod) {
|
||||
afterReflow(animationCompleted);
|
||||
afterReflow(function() {
|
||||
unblockTransitions(element);
|
||||
unblockKeyframeAnimations(element);
|
||||
animationCompleted();
|
||||
});
|
||||
return cancellationMethod;
|
||||
}
|
||||
animationCompleted();
|
||||
@@ -1192,7 +1285,11 @@ angular.module('ngAnimate', ['ng'])
|
||||
beforeRemoveClass : function(element, className, animationCompleted) {
|
||||
var cancellationMethod = animateBefore(element, suffixClasses(className, '-remove'));
|
||||
if(cancellationMethod) {
|
||||
afterReflow(animationCompleted);
|
||||
afterReflow(function() {
|
||||
unblockTransitions(element);
|
||||
unblockKeyframeAnimations(element);
|
||||
animationCompleted();
|
||||
});
|
||||
return cancellationMethod;
|
||||
}
|
||||
animationCompleted();
|
||||
|
||||
Vendored
+199
-208
@@ -1,13 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*
|
||||
* TODO(vojta): wrap whole file into closure during build
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name angular.mock
|
||||
@@ -560,210 +552,208 @@ angular.mock.$IntervalProvider = function() {
|
||||
* This directive should go inside the anonymous function but a bug in JSHint means that it would
|
||||
* not be enacted early enough to prevent the warning.
|
||||
*/
|
||||
(function() {
|
||||
var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
|
||||
var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
|
||||
|
||||
function jsonStringToDate(string) {
|
||||
var match;
|
||||
if (match = string.match(R_ISO8061_STR)) {
|
||||
var date = new Date(0),
|
||||
tzHour = 0,
|
||||
tzMin = 0;
|
||||
if (match[9]) {
|
||||
tzHour = int(match[9] + match[10]);
|
||||
tzMin = int(match[9] + match[11]);
|
||||
}
|
||||
date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
|
||||
date.setUTCHours(int(match[4]||0) - tzHour,
|
||||
int(match[5]||0) - tzMin,
|
||||
int(match[6]||0),
|
||||
int(match[7]||0));
|
||||
return date;
|
||||
function jsonStringToDate(string) {
|
||||
var match;
|
||||
if (match = string.match(R_ISO8061_STR)) {
|
||||
var date = new Date(0),
|
||||
tzHour = 0,
|
||||
tzMin = 0;
|
||||
if (match[9]) {
|
||||
tzHour = int(match[9] + match[10]);
|
||||
tzMin = int(match[9] + match[11]);
|
||||
}
|
||||
return string;
|
||||
date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
|
||||
date.setUTCHours(int(match[4]||0) - tzHour,
|
||||
int(match[5]||0) - tzMin,
|
||||
int(match[6]||0),
|
||||
int(match[7]||0));
|
||||
return date;
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
function int(str) {
|
||||
return parseInt(str, 10);
|
||||
}
|
||||
|
||||
function padNumber(num, digits, trim) {
|
||||
var neg = '';
|
||||
if (num < 0) {
|
||||
neg = '-';
|
||||
num = -num;
|
||||
}
|
||||
num = '' + num;
|
||||
while(num.length < digits) num = '0' + num;
|
||||
if (trim)
|
||||
num = num.substr(num.length - digits);
|
||||
return neg + num;
|
||||
function int(str) {
|
||||
return parseInt(str, 10);
|
||||
}
|
||||
|
||||
function padNumber(num, digits, trim) {
|
||||
var neg = '';
|
||||
if (num < 0) {
|
||||
neg = '-';
|
||||
num = -num;
|
||||
}
|
||||
num = '' + num;
|
||||
while(num.length < digits) num = '0' + num;
|
||||
if (trim)
|
||||
num = num.substr(num.length - digits);
|
||||
return neg + num;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name angular.mock.TzDate
|
||||
* @description
|
||||
*
|
||||
* *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`.
|
||||
*
|
||||
* Mock of the Date type which has its timezone specified via constructor arg.
|
||||
*
|
||||
* The main purpose is to create Date-like instances with timezone fixed to the specified timezone
|
||||
* offset, so that we can test code that depends on local timezone settings without dependency on
|
||||
* the time zone settings of the machine where the code is running.
|
||||
*
|
||||
* @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored)
|
||||
* @param {(number|string)} timestamp Timestamp representing the desired time in *UTC*
|
||||
*
|
||||
* @example
|
||||
* !!!! WARNING !!!!!
|
||||
* This is not a complete Date object so only methods that were implemented can be called safely.
|
||||
* To make matters worse, TzDate instances inherit stuff from Date via a prototype.
|
||||
*
|
||||
* We do our best to intercept calls to "unimplemented" methods, but since the list of methods is
|
||||
* incomplete we might be missing some non-standard methods. This can result in errors like:
|
||||
* "Date.prototype.foo called on incompatible Object".
|
||||
*
|
||||
* <pre>
|
||||
* var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
|
||||
* newYearInBratislava.getTimezoneOffset() => -60;
|
||||
* newYearInBratislava.getFullYear() => 2010;
|
||||
* newYearInBratislava.getMonth() => 0;
|
||||
* newYearInBratislava.getDate() => 1;
|
||||
* newYearInBratislava.getHours() => 0;
|
||||
* newYearInBratislava.getMinutes() => 0;
|
||||
* newYearInBratislava.getSeconds() => 0;
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
angular.mock.TzDate = function (offset, timestamp) {
|
||||
var self = new Date(0);
|
||||
if (angular.isString(timestamp)) {
|
||||
var tsStr = timestamp;
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name angular.mock.TzDate
|
||||
* @description
|
||||
*
|
||||
* *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`.
|
||||
*
|
||||
* Mock of the Date type which has its timezone specified via constructor arg.
|
||||
*
|
||||
* The main purpose is to create Date-like instances with timezone fixed to the specified timezone
|
||||
* offset, so that we can test code that depends on local timezone settings without dependency on
|
||||
* the time zone settings of the machine where the code is running.
|
||||
*
|
||||
* @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored)
|
||||
* @param {(number|string)} timestamp Timestamp representing the desired time in *UTC*
|
||||
*
|
||||
* @example
|
||||
* !!!! WARNING !!!!!
|
||||
* This is not a complete Date object so only methods that were implemented can be called safely.
|
||||
* To make matters worse, TzDate instances inherit stuff from Date via a prototype.
|
||||
*
|
||||
* We do our best to intercept calls to "unimplemented" methods, but since the list of methods is
|
||||
* incomplete we might be missing some non-standard methods. This can result in errors like:
|
||||
* "Date.prototype.foo called on incompatible Object".
|
||||
*
|
||||
* <pre>
|
||||
* var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
|
||||
* newYearInBratislava.getTimezoneOffset() => -60;
|
||||
* newYearInBratislava.getFullYear() => 2010;
|
||||
* newYearInBratislava.getMonth() => 0;
|
||||
* newYearInBratislava.getDate() => 1;
|
||||
* newYearInBratislava.getHours() => 0;
|
||||
* newYearInBratislava.getMinutes() => 0;
|
||||
* newYearInBratislava.getSeconds() => 0;
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
angular.mock.TzDate = function (offset, timestamp) {
|
||||
var self = new Date(0);
|
||||
if (angular.isString(timestamp)) {
|
||||
var tsStr = timestamp;
|
||||
|
||||
self.origDate = jsonStringToDate(timestamp);
|
||||
self.origDate = jsonStringToDate(timestamp);
|
||||
|
||||
timestamp = self.origDate.getTime();
|
||||
if (isNaN(timestamp))
|
||||
throw {
|
||||
name: "Illegal Argument",
|
||||
message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string"
|
||||
};
|
||||
} else {
|
||||
self.origDate = new Date(timestamp);
|
||||
}
|
||||
|
||||
var localOffset = new Date(timestamp).getTimezoneOffset();
|
||||
self.offsetDiff = localOffset*60*1000 - offset*1000*60*60;
|
||||
self.date = new Date(timestamp + self.offsetDiff);
|
||||
|
||||
self.getTime = function() {
|
||||
return self.date.getTime() - self.offsetDiff;
|
||||
};
|
||||
|
||||
self.toLocaleDateString = function() {
|
||||
return self.date.toLocaleDateString();
|
||||
};
|
||||
|
||||
self.getFullYear = function() {
|
||||
return self.date.getFullYear();
|
||||
};
|
||||
|
||||
self.getMonth = function() {
|
||||
return self.date.getMonth();
|
||||
};
|
||||
|
||||
self.getDate = function() {
|
||||
return self.date.getDate();
|
||||
};
|
||||
|
||||
self.getHours = function() {
|
||||
return self.date.getHours();
|
||||
};
|
||||
|
||||
self.getMinutes = function() {
|
||||
return self.date.getMinutes();
|
||||
};
|
||||
|
||||
self.getSeconds = function() {
|
||||
return self.date.getSeconds();
|
||||
};
|
||||
|
||||
self.getMilliseconds = function() {
|
||||
return self.date.getMilliseconds();
|
||||
};
|
||||
|
||||
self.getTimezoneOffset = function() {
|
||||
return offset * 60;
|
||||
};
|
||||
|
||||
self.getUTCFullYear = function() {
|
||||
return self.origDate.getUTCFullYear();
|
||||
};
|
||||
|
||||
self.getUTCMonth = function() {
|
||||
return self.origDate.getUTCMonth();
|
||||
};
|
||||
|
||||
self.getUTCDate = function() {
|
||||
return self.origDate.getUTCDate();
|
||||
};
|
||||
|
||||
self.getUTCHours = function() {
|
||||
return self.origDate.getUTCHours();
|
||||
};
|
||||
|
||||
self.getUTCMinutes = function() {
|
||||
return self.origDate.getUTCMinutes();
|
||||
};
|
||||
|
||||
self.getUTCSeconds = function() {
|
||||
return self.origDate.getUTCSeconds();
|
||||
};
|
||||
|
||||
self.getUTCMilliseconds = function() {
|
||||
return self.origDate.getUTCMilliseconds();
|
||||
};
|
||||
|
||||
self.getDay = function() {
|
||||
return self.date.getDay();
|
||||
};
|
||||
|
||||
// provide this method only on browsers that already have it
|
||||
if (self.toISOString) {
|
||||
self.toISOString = function() {
|
||||
return padNumber(self.origDate.getUTCFullYear(), 4) + '-' +
|
||||
padNumber(self.origDate.getUTCMonth() + 1, 2) + '-' +
|
||||
padNumber(self.origDate.getUTCDate(), 2) + 'T' +
|
||||
padNumber(self.origDate.getUTCHours(), 2) + ':' +
|
||||
padNumber(self.origDate.getUTCMinutes(), 2) + ':' +
|
||||
padNumber(self.origDate.getUTCSeconds(), 2) + '.' +
|
||||
padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z';
|
||||
timestamp = self.origDate.getTime();
|
||||
if (isNaN(timestamp))
|
||||
throw {
|
||||
name: "Illegal Argument",
|
||||
message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string"
|
||||
};
|
||||
}
|
||||
} else {
|
||||
self.origDate = new Date(timestamp);
|
||||
}
|
||||
|
||||
//hide all methods not implemented in this mock that the Date prototype exposes
|
||||
var unimplementedMethods = ['getUTCDay',
|
||||
'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
|
||||
'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
|
||||
'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
|
||||
'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString',
|
||||
'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
|
||||
var localOffset = new Date(timestamp).getTimezoneOffset();
|
||||
self.offsetDiff = localOffset*60*1000 - offset*1000*60*60;
|
||||
self.date = new Date(timestamp + self.offsetDiff);
|
||||
|
||||
angular.forEach(unimplementedMethods, function(methodName) {
|
||||
self[methodName] = function() {
|
||||
throw new Error("Method '" + methodName + "' is not implemented in the TzDate mock");
|
||||
};
|
||||
});
|
||||
|
||||
return self;
|
||||
self.getTime = function() {
|
||||
return self.date.getTime() - self.offsetDiff;
|
||||
};
|
||||
|
||||
//make "tzDateInstance instanceof Date" return true
|
||||
angular.mock.TzDate.prototype = Date.prototype;
|
||||
})();
|
||||
self.toLocaleDateString = function() {
|
||||
return self.date.toLocaleDateString();
|
||||
};
|
||||
|
||||
self.getFullYear = function() {
|
||||
return self.date.getFullYear();
|
||||
};
|
||||
|
||||
self.getMonth = function() {
|
||||
return self.date.getMonth();
|
||||
};
|
||||
|
||||
self.getDate = function() {
|
||||
return self.date.getDate();
|
||||
};
|
||||
|
||||
self.getHours = function() {
|
||||
return self.date.getHours();
|
||||
};
|
||||
|
||||
self.getMinutes = function() {
|
||||
return self.date.getMinutes();
|
||||
};
|
||||
|
||||
self.getSeconds = function() {
|
||||
return self.date.getSeconds();
|
||||
};
|
||||
|
||||
self.getMilliseconds = function() {
|
||||
return self.date.getMilliseconds();
|
||||
};
|
||||
|
||||
self.getTimezoneOffset = function() {
|
||||
return offset * 60;
|
||||
};
|
||||
|
||||
self.getUTCFullYear = function() {
|
||||
return self.origDate.getUTCFullYear();
|
||||
};
|
||||
|
||||
self.getUTCMonth = function() {
|
||||
return self.origDate.getUTCMonth();
|
||||
};
|
||||
|
||||
self.getUTCDate = function() {
|
||||
return self.origDate.getUTCDate();
|
||||
};
|
||||
|
||||
self.getUTCHours = function() {
|
||||
return self.origDate.getUTCHours();
|
||||
};
|
||||
|
||||
self.getUTCMinutes = function() {
|
||||
return self.origDate.getUTCMinutes();
|
||||
};
|
||||
|
||||
self.getUTCSeconds = function() {
|
||||
return self.origDate.getUTCSeconds();
|
||||
};
|
||||
|
||||
self.getUTCMilliseconds = function() {
|
||||
return self.origDate.getUTCMilliseconds();
|
||||
};
|
||||
|
||||
self.getDay = function() {
|
||||
return self.date.getDay();
|
||||
};
|
||||
|
||||
// provide this method only on browsers that already have it
|
||||
if (self.toISOString) {
|
||||
self.toISOString = function() {
|
||||
return padNumber(self.origDate.getUTCFullYear(), 4) + '-' +
|
||||
padNumber(self.origDate.getUTCMonth() + 1, 2) + '-' +
|
||||
padNumber(self.origDate.getUTCDate(), 2) + 'T' +
|
||||
padNumber(self.origDate.getUTCHours(), 2) + ':' +
|
||||
padNumber(self.origDate.getUTCMinutes(), 2) + ':' +
|
||||
padNumber(self.origDate.getUTCSeconds(), 2) + '.' +
|
||||
padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z';
|
||||
};
|
||||
}
|
||||
|
||||
//hide all methods not implemented in this mock that the Date prototype exposes
|
||||
var unimplementedMethods = ['getUTCDay',
|
||||
'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
|
||||
'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
|
||||
'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
|
||||
'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString',
|
||||
'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
|
||||
|
||||
angular.forEach(unimplementedMethods, function(methodName) {
|
||||
self[methodName] = function() {
|
||||
throw new Error("Method '" + methodName + "' is not implemented in the TzDate mock");
|
||||
};
|
||||
});
|
||||
|
||||
return self;
|
||||
};
|
||||
|
||||
//make "tzDateInstance instanceof Date" return true
|
||||
angular.mock.TzDate.prototype = Date.prototype;
|
||||
/* jshint +W101 */
|
||||
|
||||
angular.mock.animate = angular.module('mock.animate', ['ng'])
|
||||
@@ -1097,7 +1087,8 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
|
||||
var definitions = [],
|
||||
expectations = [],
|
||||
responses = [],
|
||||
responsesPush = angular.bind(responses, responses.push);
|
||||
responsesPush = angular.bind(responses, responses.push),
|
||||
copy = angular.copy;
|
||||
|
||||
function createResponse(status, data, headers) {
|
||||
if (angular.isFunction(status)) return status;
|
||||
@@ -1129,7 +1120,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
|
||||
function handleResponse() {
|
||||
var response = wrapped.response(method, url, data, headers);
|
||||
xhr.$$respHeaders = response[2];
|
||||
callback(response[0], response[1], xhr.getAllResponseHeaders());
|
||||
callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders());
|
||||
}
|
||||
|
||||
function handleTimeout() {
|
||||
@@ -1572,7 +1563,7 @@ function MockHttpExpectation(method, url, data, headers) {
|
||||
if (angular.isUndefined(data)) return true;
|
||||
if (data && angular.isFunction(data.test)) return data.test(d);
|
||||
if (data && angular.isFunction(data)) return data(d);
|
||||
if (data && !angular.isString(data)) return angular.toJson(data) == d;
|
||||
if (data && !angular.isString(data)) return angular.equals(data, angular.fromJson(d));
|
||||
return data == d;
|
||||
};
|
||||
|
||||
@@ -1919,9 +1910,13 @@ angular.mock.clearDataCache = function() {
|
||||
|
||||
|
||||
|
||||
(window.jasmine || window.mocha) && (function(window) {
|
||||
if(window.jasmine || window.mocha) {
|
||||
|
||||
var currentSpec = null,
|
||||
isSpecRunning = function() {
|
||||
return currentSpec && (window.mocha || currentSpec.queue.running);
|
||||
};
|
||||
|
||||
var currentSpec = null;
|
||||
|
||||
beforeEach(function() {
|
||||
currentSpec = this;
|
||||
@@ -1954,10 +1949,6 @@ angular.mock.clearDataCache = function() {
|
||||
angular.callbacks.counter = 0;
|
||||
});
|
||||
|
||||
function isSpecRunning() {
|
||||
return currentSpec && (window.mocha || currentSpec.queue.running);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.mock.module
|
||||
@@ -2112,4 +2103,4 @@ angular.mock.clearDataCache = function() {
|
||||
}
|
||||
}
|
||||
};
|
||||
})(window);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,25 @@ function lookupDottedPath(obj, path) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a shallow copy of an object and clear other fields from the destination
|
||||
*/
|
||||
function shallowClearAndCopy(src, dst) {
|
||||
dst = dst || {};
|
||||
|
||||
angular.forEach(dst, function(value, key){
|
||||
delete dst[key];
|
||||
});
|
||||
|
||||
for (var key in src) {
|
||||
if (src.hasOwnProperty(key) && key.substr(0, 2) !== '$$') {
|
||||
dst[key] = src[key];
|
||||
}
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name ngResource
|
||||
@@ -393,7 +412,7 @@ angular.module('ngResource', ['ng']).
|
||||
}
|
||||
|
||||
function Resource(value){
|
||||
copy(value || {}, this);
|
||||
shallowClearAndCopy(value || {}, this);
|
||||
}
|
||||
|
||||
forEach(actions, function(action, name) {
|
||||
@@ -439,7 +458,7 @@ angular.module('ngResource', ['ng']).
|
||||
}
|
||||
/* jshint +W086 */ /* (purposefully fall through case statements) */
|
||||
|
||||
var isInstanceCall = data instanceof Resource;
|
||||
var isInstanceCall = this instanceof Resource;
|
||||
var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data));
|
||||
var httpConfig = {};
|
||||
var responseInterceptor = action.interceptor && action.interceptor.response ||
|
||||
@@ -465,7 +484,7 @@ angular.module('ngResource', ['ng']).
|
||||
if (data) {
|
||||
// Need to convert action.isArray to boolean in case it is undefined
|
||||
// jshint -W018
|
||||
if ( angular.isArray(data) !== (!!action.isArray) ) {
|
||||
if (angular.isArray(data) !== (!!action.isArray)) {
|
||||
throw $resourceMinErr('badcfg', 'Error in resource configuration. Expected ' +
|
||||
'response to contain an {0} but got an {1}',
|
||||
action.isArray?'array':'object', angular.isArray(data)?'array':'object');
|
||||
@@ -477,7 +496,7 @@ angular.module('ngResource', ['ng']).
|
||||
value.push(new Resource(item));
|
||||
});
|
||||
} else {
|
||||
copy(data, value);
|
||||
shallowClearAndCopy(data, value);
|
||||
value.$promise = promise;
|
||||
}
|
||||
}
|
||||
@@ -522,7 +541,7 @@ angular.module('ngResource', ['ng']).
|
||||
if (isFunction(params)) {
|
||||
error = success; success = params; params = {};
|
||||
}
|
||||
var result = Resource[name](params, this, success, error);
|
||||
var result = Resource[name].call(this, params, this, success, error);
|
||||
return result.$promise || result;
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
ngRouteModule.directive('ngView', ngViewFactory);
|
||||
ngRouteModule.directive('ngView', ngViewFillContentFactory);
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
@@ -166,8 +168,8 @@ ngRouteModule.directive('ngView', ngViewFactory);
|
||||
* @description
|
||||
* Emitted every time the ngView content is reloaded.
|
||||
*/
|
||||
ngViewFactory.$inject = ['$route', '$anchorScroll', '$compile', '$controller', '$animate'];
|
||||
function ngViewFactory( $route, $anchorScroll, $compile, $controller, $animate) {
|
||||
ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate'];
|
||||
function ngViewFactory( $route, $anchorScroll, $animate) {
|
||||
return {
|
||||
restrict: 'ECA',
|
||||
terminal: true,
|
||||
@@ -199,37 +201,28 @@ function ngViewFactory( $route, $anchorScroll, $compile, $controller,
|
||||
|
||||
if (template) {
|
||||
var newScope = scope.$new();
|
||||
$transclude(newScope, function(clone) {
|
||||
clone.html(template);
|
||||
var current = $route.current;
|
||||
|
||||
// Note: This will also link all children of ng-view that were contained in the original
|
||||
// html. If that content contains controllers, ... they could pollute/change the scope.
|
||||
// However, using ng-view on an element with additional content does not make sense...
|
||||
// Note: We can't remove them in the cloneAttchFn of $transclude as that
|
||||
// function is called before linking the content, which would apply child
|
||||
// directives to non existing elements.
|
||||
var clone = $transclude(newScope, function(clone) {
|
||||
$animate.enter(clone, null, currentElement || $element, function onNgViewEnter () {
|
||||
if (angular.isDefined(autoScrollExp)
|
||||
&& (!autoScrollExp || scope.$eval(autoScrollExp))) {
|
||||
$anchorScroll();
|
||||
}
|
||||
});
|
||||
|
||||
cleanupLastView();
|
||||
|
||||
var link = $compile(clone.contents()),
|
||||
current = $route.current;
|
||||
|
||||
currentScope = current.scope = newScope;
|
||||
currentElement = clone;
|
||||
|
||||
if (current.controller) {
|
||||
locals.$scope = currentScope;
|
||||
var controller = $controller(current.controller, locals);
|
||||
if (current.controllerAs) {
|
||||
currentScope[current.controllerAs] = controller;
|
||||
}
|
||||
clone.data('$ngControllerController', controller);
|
||||
clone.children().data('$ngControllerController', controller);
|
||||
}
|
||||
|
||||
link(currentScope);
|
||||
currentScope.$emit('$viewContentLoaded');
|
||||
currentScope.$eval(onloadExp);
|
||||
});
|
||||
|
||||
currentElement = clone;
|
||||
currentScope = current.scope = newScope;
|
||||
currentScope.$emit('$viewContentLoaded');
|
||||
currentScope.$eval(onloadExp);
|
||||
} else {
|
||||
cleanupLastView();
|
||||
}
|
||||
@@ -237,3 +230,36 @@ function ngViewFactory( $route, $anchorScroll, $compile, $controller,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// This directive is called during the $transclude call of the first `ngView` directive.
|
||||
// It will replace and compile the content of the element with the loaded template.
|
||||
// We need this directive so that the element content is already filled when
|
||||
// the link function of another directive on the same element as ngView
|
||||
// is called.
|
||||
ngViewFillContentFactory.$inject = ['$compile', '$controller', '$route'];
|
||||
function ngViewFillContentFactory($compile, $controller, $route) {
|
||||
return {
|
||||
restrict: 'ECA',
|
||||
priority: -400,
|
||||
link: function(scope, $element) {
|
||||
var current = $route.current,
|
||||
locals = current.locals;
|
||||
|
||||
$element.html(locals.$template);
|
||||
|
||||
var link = $compile($element.contents());
|
||||
|
||||
if (current.controller) {
|
||||
locals.$scope = scope;
|
||||
var controller = $controller(current.controller, locals);
|
||||
if (current.controllerAs) {
|
||||
scope[current.controllerAs] = controller;
|
||||
}
|
||||
$element.data('$ngControllerController', controller);
|
||||
$element.children().data('$ngControllerController', controller);
|
||||
}
|
||||
|
||||
link(scope);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+12
-5
@@ -9,6 +9,9 @@
|
||||
*
|
||||
* The `ngRoute` module provides routing and deeplinking services and directives for angular apps.
|
||||
*
|
||||
* ## Example
|
||||
* See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
|
||||
*
|
||||
* {@installModule route}
|
||||
*
|
||||
* <div doc-module-components="ngRoute"></div>
|
||||
@@ -24,8 +27,12 @@ var ngRouteModule = angular.module('ngRoute', ['ng']).
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Used for configuring routes. See {@link ngRoute.$route $route} for an example.
|
||||
* Used for configuring routes.
|
||||
*
|
||||
* ## Example
|
||||
* See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
|
||||
*
|
||||
* ## Dependencies
|
||||
* Requires the {@link ngRoute `ngRoute`} module to be installed.
|
||||
*/
|
||||
function $RouteProvider(){
|
||||
@@ -45,13 +52,13 @@ function $RouteProvider(){
|
||||
* `$location.path` will be updated to add or drop the trailing slash to exactly match the
|
||||
* route definition.
|
||||
*
|
||||
* * `path` can contain named groups starting with a colon (`:name`). All characters up
|
||||
* * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up
|
||||
* to the next slash are matched and stored in `$routeParams` under the given `name`
|
||||
* when the route matches.
|
||||
* * `path` can contain named groups starting with a colon and ending with a star (`:name*`).
|
||||
* All characters are eagerly stored in `$routeParams` under the given `name`
|
||||
* * `path` can contain named groups starting with a colon and ending with a star:
|
||||
* e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name`
|
||||
* when the route matches.
|
||||
* * `path` can contain optional named groups with a question mark (`:name?`).
|
||||
* * `path` can contain optional named groups with a question mark: e.g.`:name?`.
|
||||
*
|
||||
* For example, routes like `/color/:color/largecode/:largecode*\/edit` will match
|
||||
* `/color/brown/largecode/code/with/slashs/edit` and extract:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
/* global htmlSanitizeWriter: false */
|
||||
/* global sanitizeText: false */
|
||||
|
||||
/**
|
||||
* @ngdoc filter
|
||||
@@ -100,7 +100,7 @@
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angular.module('ngSanitize').filter('linky', function() {
|
||||
angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
|
||||
var LINKY_URL_REGEXP =
|
||||
/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>]/,
|
||||
MAILTO_REGEXP = /^mailto:/;
|
||||
@@ -110,28 +110,40 @@ angular.module('ngSanitize').filter('linky', function() {
|
||||
var match;
|
||||
var raw = text;
|
||||
var html = [];
|
||||
// TODO(vojta): use $sanitize instead
|
||||
var writer = htmlSanitizeWriter(html);
|
||||
var url;
|
||||
var i;
|
||||
var properties = {};
|
||||
if (angular.isDefined(target)) {
|
||||
properties.target = target;
|
||||
}
|
||||
while ((match = raw.match(LINKY_URL_REGEXP))) {
|
||||
// We can not end in these as they are sometimes found at the end of the sentence
|
||||
url = match[0];
|
||||
// if we did not match ftp/http/mailto then assume mailto
|
||||
if (match[2] == match[3]) url = 'mailto:' + url;
|
||||
i = match.index;
|
||||
writer.chars(raw.substr(0, i));
|
||||
properties.href = url;
|
||||
writer.start('a', properties);
|
||||
writer.chars(match[0].replace(MAILTO_REGEXP, ''));
|
||||
writer.end('a');
|
||||
addText(raw.substr(0, i));
|
||||
addLink(url, match[0].replace(MAILTO_REGEXP, ''));
|
||||
raw = raw.substring(i + match[0].length);
|
||||
}
|
||||
writer.chars(raw);
|
||||
return html.join('');
|
||||
addText(raw);
|
||||
return $sanitize(html.join(''));
|
||||
|
||||
function addText(text) {
|
||||
if (!text) {
|
||||
return;
|
||||
}
|
||||
html.push(sanitizeText(text));
|
||||
}
|
||||
|
||||
function addLink(url, text) {
|
||||
html.push('<a ');
|
||||
if (angular.isDefined(target)) {
|
||||
html.push('target="');
|
||||
html.push(target);
|
||||
html.push('" ');
|
||||
}
|
||||
html.push('href="');
|
||||
html.push(url);
|
||||
html.push('">');
|
||||
addText(text);
|
||||
html.push('</a>');
|
||||
}
|
||||
};
|
||||
});
|
||||
}]);
|
||||
|
||||
+44
-11
@@ -46,6 +46,8 @@ var $sanitizeMinErr = angular.$$minErr('$sanitize');
|
||||
* it into the returned string, however, since our parser is more strict than a typical browser
|
||||
* parser, it's possible that some obscure input, which would be recognized as valid HTML by a
|
||||
* browser, won't make it through the sanitizer.
|
||||
* The whitelist is configured using the functions `aHrefSanitizationWhitelist` and
|
||||
* `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider `$compileProvider`}.
|
||||
*
|
||||
* @param {string} html Html input.
|
||||
* @returns {string} Sanitized html.
|
||||
@@ -128,11 +130,24 @@ var $sanitizeMinErr = angular.$$minErr('$sanitize');
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
var $sanitize = function(html) {
|
||||
function $SanitizeProvider() {
|
||||
this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
|
||||
return function(html) {
|
||||
var buf = [];
|
||||
htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) {
|
||||
return !/^unsafe/.test($$sanitizeUri(uri, isImage));
|
||||
}));
|
||||
return buf.join('');
|
||||
};
|
||||
}];
|
||||
}
|
||||
|
||||
function sanitizeText(chars) {
|
||||
var buf = [];
|
||||
htmlParser(html, htmlSanitizeWriter(buf));
|
||||
return buf.join('');
|
||||
};
|
||||
var writer = htmlSanitizeWriter(buf, angular.noop);
|
||||
writer.chars(chars);
|
||||
return buf.join('');
|
||||
}
|
||||
|
||||
|
||||
// Regular Expressions for parsing tags and attributes
|
||||
@@ -145,7 +160,6 @@ var START_TAG_REGEXP =
|
||||
COMMENT_REGEXP = /<!--(.*?)-->/g,
|
||||
DOCTYPE_REGEXP = /<!DOCTYPE([^>]*?)>/i,
|
||||
CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
|
||||
URI_REGEXP = /^((ftp|https?):\/\/|mailto:|tel:|#)/i,
|
||||
// Match everything outside of normal chars and " (quote character)
|
||||
NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;
|
||||
|
||||
@@ -346,15 +360,32 @@ function htmlParser( html, handler ) {
|
||||
}
|
||||
}
|
||||
|
||||
var hiddenPre=document.createElement("pre");
|
||||
var spaceRe = /^(\s*)([\s\S]*?)(\s*)$/;
|
||||
/**
|
||||
* decodes all entities into regular string
|
||||
* @param value
|
||||
* @returns {string} A string with decoded entities.
|
||||
*/
|
||||
var hiddenPre=document.createElement("pre");
|
||||
function decodeEntities(value) {
|
||||
hiddenPre.innerHTML=value.replace(/</g,"<");
|
||||
return hiddenPre.innerText || hiddenPre.textContent || '';
|
||||
if (!value) { return ''; }
|
||||
|
||||
// Note: IE8 does not preserve spaces at the start/end of innerHTML
|
||||
// so we must capture them and reattach them afterward
|
||||
var parts = spaceRe.exec(value);
|
||||
var spaceBefore = parts[1];
|
||||
var spaceAfter = parts[3];
|
||||
var content = parts[2];
|
||||
if (content) {
|
||||
hiddenPre.innerHTML=content.replace(/</g,"<");
|
||||
// innerText depends on styling as it doesn't display hidden elements.
|
||||
// Therefore, it's better to use textContent not to cause unnecessary
|
||||
// reflows. However, IE<9 don't support textContent so the innerText
|
||||
// fallback is necessary.
|
||||
content = 'textContent' in hiddenPre ?
|
||||
hiddenPre.textContent : hiddenPre.innerText;
|
||||
}
|
||||
return spaceBefore + content + spaceAfter;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -384,7 +415,7 @@ function encodeEntities(value) {
|
||||
* comment: function(text) {}
|
||||
* }
|
||||
*/
|
||||
function htmlSanitizeWriter(buf){
|
||||
function htmlSanitizeWriter(buf, uriValidator){
|
||||
var ignore = false;
|
||||
var out = angular.bind(buf, buf.push);
|
||||
return {
|
||||
@@ -398,7 +429,9 @@ function htmlSanitizeWriter(buf){
|
||||
out(tag);
|
||||
angular.forEach(attrs, function(value, key){
|
||||
var lkey=angular.lowercase(key);
|
||||
if (validAttrs[lkey]===true && (uriAttrs[lkey]!==true || value.match(URI_REGEXP))) {
|
||||
var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
|
||||
if (validAttrs[lkey] === true &&
|
||||
(uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
|
||||
out(' ');
|
||||
out(key);
|
||||
out('="');
|
||||
@@ -430,4 +463,4 @@ function htmlSanitizeWriter(buf){
|
||||
|
||||
|
||||
// define ngSanitize module and register $sanitize service
|
||||
angular.module('ngSanitize', []).value('$sanitize', $sanitize);
|
||||
angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user