Compare commits
219 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dc6f149973 | |||
| 420f6bfccb | |||
| ae33e11694 | |||
| 4d8b0282b4 | |||
| 00845fca88 | |||
| 84fe86c7fd | |||
| 5e1ed9d5d2 | |||
| 2ba668732a | |||
| 2cb73b71d1 | |||
| 16a2ce2b13 | |||
| 18e87a7544 | |||
| f81431dd72 | |||
| 04bdb9f813 | |||
| 1cfe281a76 | |||
| 6165bd7d1e | |||
| 0135564ad9 | |||
| 6847cbeff8 | |||
| 84daf33c03 | |||
| 86cbdb893a | |||
| 11ee680d38 | |||
| 4c5b382b69 | |||
| 39f4a776d6 | |||
| a250116694 | |||
| 9a73d71f47 | |||
| 73aaca05f8 | |||
| a993112098 | |||
| 9145d5ec3e | |||
| efc863844c | |||
| cc260e7864 | |||
| 42ce8f7f55 | |||
| 661390aef3 | |||
| ed4a70e765 | |||
| 1866968310 | |||
| 5fbf98ec23 | |||
| 5d2bb2c1b9 | |||
| 9039ddbb56 | |||
| 8f8510fc22 | |||
| 1e99ea6a51 | |||
| 016e1e675e | |||
| 1240641f76 | |||
| f1a34f0908 | |||
| 01bda54e05 | |||
| bc04afe183 | |||
| ac086ae616 | |||
| f75a2b093f | |||
| 4dba7b0203 | |||
| d3cd3c0a9b | |||
| e351874a0a | |||
| dcdf4fc78b | |||
| cf38d8c55b | |||
| b0233a33a1 | |||
| c3235db9ee | |||
| 738113bac8 | |||
| 6613ee40e6 | |||
| ce6035d953 | |||
| 2bc4793ee8 | |||
| 9c6e34bfc1 | |||
| 4e65ff31a8 | |||
| 15461c7883 | |||
| de5352cfcf | |||
| ae26ed994e | |||
| 94745f6274 | |||
| dbfa0d88bb | |||
| cf3b5cb2fc | |||
| 192a225854 | |||
| 3508f76719 | |||
| a7d081fac0 | |||
| ee774f6e5b | |||
| 80f34598f8 | |||
| 7e168c8ad2 | |||
| 3ebc2c2442 | |||
| 631d86f723 | |||
| 3ac97f2b3d | |||
| ed55346be7 | |||
| e31ec1f7ed | |||
| 70e4fd2865 | |||
| 7898490779 | |||
| 0893e83c92 | |||
| ef334d0070 | |||
| 9f08d03978 | |||
| 3ca6c4bfb9 | |||
| cfea2095ca | |||
| 171bec3b0e | |||
| 93891ad2e9 | |||
| eece726651 | |||
| dc6665caed | |||
| 5b592cbaf4 | |||
| 348138d7cd | |||
| 6a34a4ebeb | |||
| 5ca247c749 | |||
| b16bffbf76 | |||
| b2aec3706d | |||
| d577c5def1 | |||
| a4dd14952e | |||
| 8879b3733e | |||
| 35b02226ca | |||
| 814feaa2ab | |||
| aa2e66dcaf | |||
| 7b7b12e477 | |||
| 187cd0a058 | |||
| 0c690af2fe | |||
| 40ecd2d8e5 | |||
| 484286d536 | |||
| d56b62dcda | |||
| b94125ac14 | |||
| 3f34319398 | |||
| ba076a29b9 | |||
| 13000c7350 | |||
| fd2649fc65 | |||
| 437b09c155 | |||
| b2b015a53b | |||
| 5bea4c5692 | |||
| 1b527b7acf | |||
| 4b8629b6b8 | |||
| ed81d19ce9 | |||
| 3497bf2d82 | |||
| afcf03fd2c | |||
| 582612b000 | |||
| 7bf32ccadb | |||
| f5c18861b6 | |||
| 4acd75b904 | |||
| cd6dd22b19 | |||
| 139c532019 | |||
| e5b57bf01c | |||
| 5ecd6d4e0a | |||
| 03bbe9aab1 | |||
| 0ef9d54ccd | |||
| 6ade77efa2 | |||
| 91fa865bf2 | |||
| 10ae76673c | |||
| 9497757842 | |||
| bb5abe0e9c | |||
| 76c0ddfc0b | |||
| 36b888e781 | |||
| a476972e2e | |||
| 866d3fb573 | |||
| 70cf0a389f | |||
| 7d4ccea579 | |||
| 654dd1d5e8 | |||
| bf114f6ee3 | |||
| 5727eaf767 | |||
| 346e98330c | |||
| 2b1f10266a | |||
| fc7970fdf0 | |||
| 65957e99ba | |||
| 7f9a94f8bc | |||
| cb560e2441 | |||
| 55856565c2 | |||
| 13968343d4 | |||
| 4c428121b9 | |||
| 4816f7ee5c | |||
| ce53fbde50 | |||
| 1516a69cd2 | |||
| 7a77fdae4f | |||
| b13da18e11 | |||
| f98f8a3892 | |||
| 77c4a7fd66 | |||
| e281413919 | |||
| 2007ddd3f8 | |||
| d8922fe3e9 | |||
| 6c611df8f0 | |||
| 6be24df5bc | |||
| 4759aacba9 | |||
| 802bfc259c | |||
| 64db8d166e | |||
| d2a769e196 | |||
| 68a8c8907d | |||
| 701d61080a | |||
| a8cc449706 | |||
| 2aa212b19c | |||
| 1f23cfe9c7 | |||
| 0fa8e47fb5 | |||
| 8043784fd7 | |||
| 526a6b31e5 | |||
| 14fd064a62 | |||
| 3178afbf0c | |||
| ce3b616432 | |||
| 54a761905d | |||
| aa531d7bd1 | |||
| d7e9ae1215 | |||
| 6cf9ede88e | |||
| 6092291bd7 | |||
| 3d0f11212f | |||
| 6194e002e2 | |||
| 75545d4d1c | |||
| d67eb2f2db | |||
| 6b8153ff0f | |||
| fb132732f1 | |||
| 336b157497 | |||
| d16975a9de | |||
| 87f6b36bab | |||
| 43fccf5617 | |||
| a5b3bcf41c | |||
| 8801d9c286 | |||
| a8e114f351 | |||
| 9a3a9b46e5 | |||
| 934204ec18 | |||
| 7cb8f8fb44 | |||
| 8d34bf2fea | |||
| 8801e69dba | |||
| f4afa398a1 | |||
| 32063278bd | |||
| 92208d2f85 | |||
| ab7c74b4b9 | |||
| e283abe171 | |||
| d7620f68bb | |||
| 971d97e2ec | |||
| 559d5efc04 | |||
| 85042820fb | |||
| 24a2eec815 | |||
| d987a79ab1 | |||
| eba09353e6 | |||
| 297660c9a3 | |||
| 8343c05fd8 | |||
| 7c3d064786 | |||
| c2ccc1cbdf | |||
| 04e080660a | |||
| f3cca88384 | |||
| 978bbd2d49 |
+4
-4
@@ -5,9 +5,9 @@ node_js:
|
||||
before_script:
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
- npm install -g testacular@canary
|
||||
- rake package
|
||||
- ./nodeserver.sh > /dev/null &
|
||||
- npm install -g grunt-cli
|
||||
- grunt package
|
||||
- grunt webserver > /dev/null &
|
||||
|
||||
script:
|
||||
- rake test[Firefox,"--reporters=dots"]
|
||||
- grunt test --browsers Firefox --reporters=dots
|
||||
|
||||
+464
-2
@@ -1,3 +1,465 @@
|
||||
<a name="1.1.5"></a>
|
||||
# 1.1.5 triangle-squarification (2013-05-22)
|
||||
|
||||
_Note: 1.1.x releases are [considered unstable](http://blog.angularjs.org/2012/07/angularjs-10-12-roadmap.html).
|
||||
They pass all tests but we reserve the right to change new features/apis in between minor releases. Check them
|
||||
out and please give us feedback._
|
||||
|
||||
_Note: This release also contains all bug fixes available in [1.0.7](#1.0.7)._
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$animator:**
|
||||
- provide support for custom animation events
|
||||
([c53d4c94](https://github.com/angular/angular.js/commit/c53d4c94300c97dd005f9a0cbdbfa387294b9026))
|
||||
- allow to globally disable and enable animations
|
||||
([5476cb6e](https://github.com/angular/angular.js/commit/5476cb6e9b6d7a16e3a86585bc2db5e63b16cd4d))
|
||||
- **$http:**
|
||||
- add support for aborting via timeout promises
|
||||
([9f4f5937](https://github.com/angular/angular.js/commit/9f4f5937112655a9881d3281da8e72035bc8b180),
|
||||
[#1159](https://github.com/angular/angular.js/issues/1159))
|
||||
- add a default content type header for PATCH requests
|
||||
([f9b897de](https://github.com/angular/angular.js/commit/f9b897de4b5cc438515cbb54519fbdf6242f5858))
|
||||
- add timeout support for JSONP requests
|
||||
([cda7b711](https://github.com/angular/angular.js/commit/cda7b71146f6748116ad5bbc9050ee7e79a9ce2b))
|
||||
|
||||
- **$parse:** add support for ternary operators to parser
|
||||
([6798fec4](https://github.com/angular/angular.js/commit/6798fec4390a72b7943a49505f8a245b6016c84b))
|
||||
|
||||
- **$q:** add $q.always() method
|
||||
([6605adf6](https://github.com/angular/angular.js/commit/6605adf6d96cee2ef53dfad24e99d325df732cab))
|
||||
|
||||
- **$controller:** support "Controller as" syntax
|
||||
([cd38cbf9](https://github.com/angular/angular.js/commit/cd38cbf975b501d846e6149d1d993972a1af0053),
|
||||
[400f9360](https://github.com/angular/angular.js/commit/400f9360bb2f7553c5bd3b1f256a5f3db175b7bc))
|
||||
|
||||
- **$injector:** add `has` method for querying
|
||||
([80341cb9](https://github.com/angular/angular.js/commit/80341cb9badd952fdc80094df4123629313b4cc4),
|
||||
[#2556](https://github.com/angular/angular.js/issues/2556))
|
||||
|
||||
- **Directives:**
|
||||
- **ngAnimate:**
|
||||
- add support for CSS3 Animations with working delays and multiple durations
|
||||
([14757874](https://github.com/angular/angular.js/commit/14757874a7cea7961f31211b245c417bd4b20512))
|
||||
- cancel previous incomplete animations when new animations take place
|
||||
([4acc28a3](https://github.com/angular/angular.js/commit/4acc28a310d006c62afe0de8ec82fed21c98c2d6))
|
||||
- **ngSrcset:** add new ngSrcset directive
|
||||
([d551d729](https://github.com/angular/angular.js/commit/d551d72924f7c43a043e4760ff05d7389e310f99),
|
||||
[#2601](https://github.com/angular/angular.js/issues/2601))
|
||||
- **ngIf:** add directive to remove and recreate DOM elements
|
||||
([2f96fbd1](https://github.com/angular/angular.js/commit/2f96fbd17577685bc013a4f7ced06664af253944))
|
||||
- **select:** match options by expression other than object identity
|
||||
([c32a859b](https://github.com/angular/angular.js/commit/c32a859bdb93699cc080f9affed4bcff63005a64))
|
||||
- **ngInclude:** $includeContentRequested event
|
||||
([af0eaa30](https://github.com/angular/angular.js/commit/af0eaa304748f330739a4b0aadb13201126c5407))
|
||||
|
||||
- **Mobile:**
|
||||
- **ngClick:** Add a CSS class while the element is held down via a tap
|
||||
([52a55ec6](https://github.com/angular/angular.js/commit/52a55ec61895951999cb0d74e706725b965e9c9f))
|
||||
- **ngSwipe:** Add ngSwipeRight/Left directives to ngMobile
|
||||
([5e0f876c](https://github.com/angular/angular.js/commit/5e0f876c39099adb6a0300c429b8df1f6b544846))
|
||||
|
||||
- **docs:**
|
||||
- Add FullText search to replace Google search in docs
|
||||
([3a49b7ee](https://github.com/angular/angular.js/commit/3a49b7eec4836ec9dc1588e6cedda942755dc7bf))
|
||||
- external links to github, plunkr and jsfiddle available for code examples
|
||||
([c8197b44](https://github.com/angular/angular.js/commit/c8197b44eb0b4d49acda142f4179876732e1c751))
|
||||
- add variable type hinting with colors
|
||||
([404c9a65](https://github.com/angular/angular.js/commit/404c9a653a1e28de1c6dda996875d6616812313a))
|
||||
- support for HTML table generation from docs code
|
||||
([b3a62b2e](https://github.com/angular/angular.js/commit/b3a62b2e19b1743df52034d4d7a0405e6a65f925))
|
||||
|
||||
- **scenario runner:** adds mousedown and mouseup event triggers to scenario
|
||||
([629fb373](https://github.com/angular/angular.js/commit/629fb37351ce5778a40a8bc8cd7c1385b382ce75))
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$animator:** remove dependency on window.setTimeout
|
||||
([021bdf39](https://github.com/angular/angular.js/commit/021bdf3922b6525bd117e59fb4945b30a5a55341))
|
||||
|
||||
- **$controller:** allow dots in a controller name
|
||||
([de2cdb06](https://github.com/angular/angular.js/commit/de2cdb0658b8b8cff5a59e26c5ec1c9b470efb9b))
|
||||
|
||||
- **$location:**
|
||||
- prevent navigation when event isDefaultPrevented
|
||||
([2c69a673](https://github.com/angular/angular.js/commit/2c69a6735e8af5d1b9b73fd221274d374e8efdea))
|
||||
- compare against actual instead of current URL
|
||||
([a348e90a](https://github.com/angular/angular.js/commit/a348e90aa141921b914f87ec930cd6ebf481a446))
|
||||
- prevent navigation if already on the URL
|
||||
([4bd7bedf](https://github.com/angular/angular.js/commit/4bd7bedf48c0c1ebb62f6bd8c85e8ea00f94502b))
|
||||
- fix URL interception in hash-bang mode
|
||||
([58ef3230](https://github.com/angular/angular.js/commit/58ef32308f45141c8f7f7cc32a6156cd328ba692),
|
||||
[#1051](https://github.com/angular/angular.js/issues/1051))
|
||||
- correctly rewrite Html5 urls
|
||||
([77ff1085](https://github.com/angular/angular.js/commit/77ff1085554675f1a8375642996e5b1e51f9ed2d))
|
||||
|
||||
- **$resource:**
|
||||
- null default param results in TypeError
|
||||
([cefbcd47](https://github.com/angular/angular.js/commit/cefbcd470d4c9020cc3487b2326d45058ef831e2))
|
||||
- collapse empty suffix parameters correctly
|
||||
([53061363](https://github.com/angular/angular.js/commit/53061363c7aa1ab9085273d269c6f04ac2162336))
|
||||
|
||||
- **$rootScope:**
|
||||
- ensure $watchCollection correctly handles arrayLike objects
|
||||
([6452707d](https://github.com/angular/angular.js/commit/6452707d4098235bdbde34e790aee05a1b091218))
|
||||
|
||||
- **date filter:** correctly format dates with more than 3 sub-second digits
|
||||
([4f2e3606](https://github.com/angular/angular.js/commit/4f2e36068502f18814fee0abd26951124881f951))
|
||||
|
||||
- **jqLite:**
|
||||
- pass a dummy event into triggerHandler
|
||||
([0401a7f5](https://github.com/angular/angular.js/commit/0401a7f598ef9a36ffe1f217e1a98961046fa551))
|
||||
|
||||
- **Directives:**
|
||||
- **ngAnimate:**
|
||||
- eval ng-animate expression on each animation
|
||||
([fd21c750](https://github.com/angular/angular.js/commit/fd21c7502f0a25364a810c26ebeecb678e5783c5))
|
||||
- prevent animation on initial page load
|
||||
([570463a4](https://github.com/angular/angular.js/commit/570463a465fae02efc33e5a1fa963437cdc275dd))
|
||||
- skip animation on first render
|
||||
([1351ba26](https://github.com/angular/angular.js/commit/1351ba2632b5011ad6eaddf004a7f0411bea8453))
|
||||
- **ngPattern:** allow modifiers on inline ng-pattern
|
||||
([12b6deb1](https://github.com/angular/angular.js/commit/12b6deb1ce99df64e2fc91a06bf05cd7f4a3a475),
|
||||
[#1437](https://github.com/angular/angular.js/issues/1437))
|
||||
- **ngRepeat:**
|
||||
- correctly iterate over array-like objects
|
||||
([1d8e11dd](https://github.com/angular/angular.js/commit/1d8e11ddfbd6b08ff02df4331f6df125f49da3dc),
|
||||
[#2546](https://github.com/angular/angular.js/issues/2546))
|
||||
- prevent initial duplicates
|
||||
([a0bc71e2](https://github.com/angular/angular.js/commit/a0bc71e27107c58282e71415c4e8d89e916ae99c))
|
||||
- **ngView:** accidentally compiling leaving content
|
||||
([9956baed](https://github.com/angular/angular.js/commit/9956baedd73d5e8d0edd04c9eed368bd3988444b))
|
||||
|
||||
- **scenario runner:** correct bootstrap issue on IE
|
||||
([ab755a25](https://github.com/angular/angular.js/commit/ab755a25f9ca3f3f000623071d8de3ddc4b1d78e))
|
||||
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **$animator/ngAnimate:**
|
||||
|
||||
|
||||
- **$resource:** due to [53061363](https://github.com/angular/angular.js/commit/53061363c7aa1ab9085273d269c6f04ac2162336),
|
||||
A `/` followed by a `.`, in the last segment of the URL template is now collapsed into a single `.` delimiter.
|
||||
|
||||
For example: `users/.json` will become `users.json`. If your server relied upon this sequence then it will no longer
|
||||
work. In this case you can now escape the `/.` sequence with `/\.`
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.7"></a>
|
||||
# 1.0.7 monochromatic-rainbow (2013-05-22)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$browser:** should use first value for a cookie.
|
||||
([3952d35a](https://github.com/angular/angular.js/commit/3952d35abe334a0e6afd1f6e34a74d984d1e9d24),
|
||||
[#2635](https://github.com/angular/angular.js/issues/2635))
|
||||
|
||||
- **$cookieStore:** $cookieStore.get now parses blank string as blank string
|
||||
([cf4729fa](https://github.com/angular/angular.js/commit/cf4729faa3e6e0a5178e2064a6f3cfd345686554))
|
||||
|
||||
- **$location:** back-button should fire $locationChangeStart
|
||||
([dc9a5806](https://github.com/angular/angular.js/commit/dc9a580617a838b63cbf5feae362b6f9cf5ed986),
|
||||
[#2109](https://github.com/angular/angular.js/issues/2109))
|
||||
|
||||
- **$parse:** Fix context access and double function call
|
||||
([7812ae75](https://github.com/angular/angular.js/commit/7812ae75d578314c1a285e9644fc75812940eb1d),
|
||||
[#2496](https://github.com/angular/angular.js/issues/2496))
|
||||
|
||||
- **dateFilter:** correctly format ISODates on Android<=2.1
|
||||
([f046f6f7](https://github.com/angular/angular.js/commit/f046f6f73c910998a94f30a4cb4ed087b6325485),
|
||||
[#2277](https://github.com/angular/angular.js/issues/2277))
|
||||
|
||||
- **jqLite:** correct implementation of mouseenter/mouseleave event
|
||||
([06f2b2a8](https://github.com/angular/angular.js/commit/06f2b2a8cf7e8216ad9ef05f73426271c2d97faa),
|
||||
[#2131](https://github.com/angular/angular.js/issues/2131))
|
||||
|
||||
- **angular.copy/angular.extend:** do not copy $$hashKey in copy/extend functions.
|
||||
([6d0b325f](https://github.com/angular/angular.js/commit/6d0b325f7f5b9c1f3cfac9b73c6cd5fc3d1e2af0),
|
||||
[#1875](https://github.com/angular/angular.js/issues/1875))
|
||||
|
||||
- **i18n:** escape all chars above \u007f in locale files
|
||||
([695c54c1](https://github.com/angular/angular.js/commit/695c54c17b3299cd6170c45878b41cb46a577cd2),
|
||||
[#2417](https://github.com/angular/angular.js/issues/2417))
|
||||
|
||||
- **Directives:**
|
||||
- **ngPluralize:** handle the empty string as a valid override
|
||||
([67a4a25b](https://github.com/angular/angular.js/commit/67a4a25b890fada0043c1ff98e5437d793f44d0c),
|
||||
[#2575](https://github.com/angular/angular.js/issues/2575))
|
||||
- **select:** ensure empty option is not lost in IE9
|
||||
([4622af3f](https://github.com/angular/angular.js/commit/4622af3f075204e2d5ab33d5bd002074f2d940c9),
|
||||
[#2150](https://github.com/angular/angular.js/issues/2150))
|
||||
- **ngModel:** use paste/cut events in IE to support context menu
|
||||
([363e4cbf](https://github.com/angular/angular.js/commit/363e4cbf649de4c5206f1904ee76f89301ceaab0),
|
||||
[#1462](https://github.com/angular/angular.js/issues/1462))
|
||||
- **ngClass:** should remove classes when object is the same but property has changed
|
||||
([0ac969a5](https://github.com/angular/angular.js/commit/0ac969a5ee1687cfd4517821943f34fe948bb3fc))
|
||||
|
||||
- **PhoneCat Tutorial:** renamed Testacular to Karma
|
||||
([angular-phonecat](https://github.com/angular/angular-phonecat))
|
||||
|
||||
|
||||
|
||||
<a name="1.1.4"></a>
|
||||
# 1.1.4 quantum-manipulation (2013-04-03)
|
||||
|
||||
_Note: 1.1.x releases are [considered unstable](http://blog.angularjs.org/2012/07/angularjs-10-12-roadmap.html).
|
||||
They pass all tests but we reserve the right to change new features/apis in between minor releases. Check them
|
||||
out and please give us feedback._
|
||||
|
||||
_Note: This release also contains all bug fixes available in [1.0.6](#1.0.6)._
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$compile:**
|
||||
- allow directives to modify interpolated attributes
|
||||
([fe8d893b](https://github.com/angular/angular.js/commit/fe8d893b839e9b14e3e55a3a0523cc1e6355bdd5))
|
||||
- support for dynamic template generation
|
||||
([eb53423a](https://github.com/angular/angular.js/commit/eb53423a41136fcda0c5e711f2d104952080354b))
|
||||
- add attribute binding support via ngAttr*
|
||||
([cf17c6af](https://github.com/angular/angular.js/commit/cf17c6af475eace31cf52944afd8e10d3afcf6c0),
|
||||
[#1050](https://github.com/angular/angular.js/issues/1050), [#1925](https://github.com/angular/angular.js/issues/1925))
|
||||
- `'=?'` makes `'='` binding optional
|
||||
([ac899d0d](https://github.com/angular/angular.js/commit/ac899d0da59157fa1c6429510791b6c3103d9401),
|
||||
[#909](https://github.com/angular/angular.js/issues/909), [#1435](https://github.com/angular/angular.js/issues/1435))
|
||||
|
||||
- **$q:** `$q.all()` now accepts hash
|
||||
([e27bb6eb](https://github.com/angular/angular.js/commit/e27bb6eb132a68665c8fca3f5a216b19b1129ba6))
|
||||
|
||||
- **$resource:** ability to override url in resource actions
|
||||
([60f1f099](https://github.com/angular/angular.js/commit/60f1f099fc7e5197808cd6acb7407cdc40f50a3f))
|
||||
|
||||
- **$route:** add `caseInsensitiveMatch` option for url matching
|
||||
([5e18a15f](https://github.com/angular/angular.js/commit/5e18a15fb01d2e81adda68503754289fa9655082))
|
||||
|
||||
- **http:**
|
||||
- support request/response promise chaining
|
||||
([4ae46814](https://github.com/angular/angular.js/commit/4ae46814ff4e7c0bbcdbbefc0a97277283a84065))
|
||||
- set custom default cache in $http.defaults.cache
|
||||
([99f3b70b](https://github.com/angular/angular.js/commit/99f3b70b2d316f5bb39e21249e752c29f49c90ab))
|
||||
|
||||
|
||||
- **JQLite:** `ready()` now supports `document.readyState=='complete'`
|
||||
([753fc9e5](https://github.com/angular/angular.js/commit/753fc9e58d5e554d4930548558efecc283557eeb))
|
||||
|
||||
- **Scenario:** autodisable animations when running e2e tests
|
||||
([fec4ef38](https://github.com/angular/angular.js/commit/fec4ef38815340e8e5a6b65fd6c08f5c74e701d8))
|
||||
|
||||
- **Scope:** add `$watchCollection` method for observing collections
|
||||
([5eb96855](https://github.com/angular/angular.js/commit/5eb968553a1130461ab8704535691e00eb154ac2))
|
||||
|
||||
- **angular.bootstrap:** support deferred bootstrap (mainly useful for tools like test runners and Batarang)
|
||||
([603fe0d1](https://github.com/angular/angular.js/commit/603fe0d19608ffe1915d8bc23bf412912e7ee1ac))
|
||||
|
||||
- **ngMobile:** add ngMobile module with mobile-specific ngClick
|
||||
([707c65d5](https://github.com/angular/angular.js/commit/707c65d5a228b44ab3aea2fad95516fe6c57169a))
|
||||
|
||||
- **Directives:**
|
||||
- **ngKeypress:** add ngKeypress directive for handling keypress event
|
||||
([f20646bc](https://github.com/angular/angular.js/commit/f20646bce5f0c914992a78fc2556bda136c27ac9))
|
||||
- **ngSwitch:** Preserve the order of the elements not in the ng-switch
|
||||
([e88d6179](https://github.com/angular/angular.js/commit/e88d6179c3a6a137e75fa09de906fc83c6515db2),
|
||||
[#1074](https://github.com/angular/angular.js/issues/1074))
|
||||
- **ngAnimate:** add support for animation
|
||||
([0b6f1ce5](https://github.com/angular/angular.js/commit/0b6f1ce5f89f47f9302ff1e8cd8f4b92f837c413))
|
||||
- **ngRepeat:** add support for custom tracking of items
|
||||
([61f2767c](https://github.com/angular/angular.js/commit/61f2767ce65562257599649d9eaf9da08f321655))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **$route:** due to [6f71e809](https://github.com/angular/angular.js/commit/6f71e809141bf89501e55c378921d6e7ec9512bc),
|
||||
in $routeChangeStart event, nextRoute.$route property is gone. Use the nextRoute object itself instead of nextRoute.$route.
|
||||
|
||||
- **ngRepeat:** due to [61f2767c](https://github.com/angular/angular.js/commit/61f2767ce65562257599649d9eaf9da08f321655), it is now considered an error to have two identical items (identified by the new "track by" expression) in a collection that is fed into the repeater. This behavior was previously tolerated.
|
||||
|
||||
- **ngSwitch:** due to [e88d6179](https://github.com/angular/angular.js/commit/e88d6179c3a6a137e75fa09de906fc83c6515db2),
|
||||
elements not in the ng-switch were rendered after the ng-switch elements. Now they are rendered in-place.
|
||||
|
||||
Templates with ngSwitch directives and nested non-ngSwitchWhen elements should be updated to preserve render order.
|
||||
|
||||
For example: The following was previously rendered with `<li>1</li>` after `<li>2</li>`:
|
||||
|
||||
<ul ng-switch="select">
|
||||
<li>1</li>
|
||||
<li ng-switch-when="option">2</li>
|
||||
</ul>
|
||||
|
||||
To keep the old behaviour, use:
|
||||
|
||||
<ul ng-switch="select">
|
||||
<li ng-switch-when="1">2</li>
|
||||
<li>1</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<a name="1.0.6"></a>
|
||||
# 1.0.6 universal-irreversibility (2013-04-04)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- compile replace directives in external template
|
||||
([398691be](https://github.com/angular/angular.js/commit/398691beb3fc40a481afa258d181de06ec0d153c),
|
||||
[#1859](https://github.com/angular/angular.js/issues/1859))
|
||||
- whitelist file:// in url sanitization
|
||||
([7b236b29](https://github.com/angular/angular.js/commit/7b236b29aa3a6f6dfe722815e0a2667d9b7f0899))
|
||||
- handle elements with no childNodes property
|
||||
([bec614fd](https://github.com/angular/angular.js/commit/bec614fd90c48c3921a4b659912008574e553b40))
|
||||
- **$http:** don't encode URL query substring "null" to "+"
|
||||
([86d191ed](https://github.com/angular/angular.js/commit/86d191ed4aea9015adc71b852223475c5c762c34))
|
||||
- **$httpBackend:** prevent DOM err due to dereferencing .responseText
|
||||
([509ec745](https://github.com/angular/angular.js/commit/509ec745fdbb54b54672fbf8595a4958c16f2b53),
|
||||
[#1922](https://github.com/angular/angular.js/issues/1922))
|
||||
- **$location:**
|
||||
- parse FirefoxOS packaged app urls
|
||||
([3a81dd8b](https://github.com/angular/angular.js/commit/3a81dd8bddbade81c4c9f734813458d0d969a4bf),
|
||||
[#2112](https://github.com/angular/angular.js/issues/2112))
|
||||
- correctly rewrite html5 url to hashbang url
|
||||
([9befe370](https://github.com/angular/angular.js/commit/9befe37014141fbfdf0cded318d28322fc058c13))
|
||||
- **$route:** make nextRoute.$route private
|
||||
([6f71e809](https://github.com/angular/angular.js/commit/6f71e809141bf89501e55c378921d6e7ec9512bc),
|
||||
[#1907](https://github.com/angular/angular.js/issues/1907))
|
||||
- **mocks:** prevent NPE when module definition outside of it.
|
||||
([5c735eb4](https://github.com/angular/angular.js/commit/5c735eb4ab07144a62949472ed388cb185099201))
|
||||
- **dateFilter:** correct timezone date filter for 1/2 hour offsets
|
||||
([1c1cd4fd](https://github.com/angular/angular.js/commit/1c1cd4fdf6b6d7511c7b8dc61b8042011dc54830))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.1.3"></a>
|
||||
# 1.1.3 radioactive-gargle (2013-02-20)
|
||||
|
||||
_Note: 1.1.x releases are [considered unstable](http://blog.angularjs.org/2012/07/angularjs-10-12-roadmap.html).
|
||||
They pass all tests but we reserve the right to change new features/apis in between minor releases. Check them
|
||||
out and please give us feedback._
|
||||
|
||||
_Note: This release also contains all bug fixes available in [1.0.5](#1.0.5)._
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- initialize interpolated attributes before directive linking
|
||||
([bb8448c0](https://github.com/angular/angular.js/commit/bb8448c011127306df08c7479b66e5afe7a0fa94))
|
||||
- interpolate @ locals before the link function runs
|
||||
([2ed53087](https://github.com/angular/angular.js/commit/2ed53087d7dd06d728e333a449265f7685275548))
|
||||
- **$http:**
|
||||
- do not encode special characters `@$:,` in params
|
||||
([288b69a3](https://github.com/angular/angular.js/commit/288b69a314e9bd14458b6647532eb62aad5c5cdf))
|
||||
- **$resource:**
|
||||
- params should expand array values properly
|
||||
([2a212344](https://github.com/angular/angular.js/commit/2a2123441c2b749b8f316a24c3ca3f77a9132a01))
|
||||
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$http:** allow overriding the XSRF header and cookie name
|
||||
([8155c3a2](https://github.com/angular/angular.js/commit/8155c3a29ea0eb14806913b8ac08ba7727e1969c))
|
||||
- **$parse:** added `constant` and `literal` properties
|
||||
([1ed63858](https://github.com/angular/angular.js/commit/1ed638582d2f2c7f89384d9712f4cfac52cc5b70))
|
||||
- **$resource:** expose promise based api via $then and $resolved
|
||||
([dba6bc73](https://github.com/angular/angular.js/commit/dba6bc73e802fdae685a9f351d3e23c7efa8568a))
|
||||
- **$routeProvider:** add support to catch-all parameters in routes
|
||||
([7eafbb98](https://github.com/angular/angular.js/commit/7eafbb98c64c0dc079d7d3ec589f1270b7f6fea5))
|
||||
- **Scope:**
|
||||
- expose transcluded and isolate scope info for batarang
|
||||
([649b8922](https://github.com/angular/angular.js/commit/649b892205615a144dafff9984c0e6ab10ed341d))
|
||||
- only evaluate constant $watch expressions once
|
||||
([1d7a95df](https://github.com/angular/angular.js/commit/1d7a95df565192fc02a18b0b297b39dd615eaeb5))
|
||||
- **angular.noConflict:** added api to restore previous angular namespace reference
|
||||
([12ba6cec](https://github.com/angular/angular.js/commit/12ba6cec4fb79521101744e02a7e09f9fbb591c4))
|
||||
- **Directives:**
|
||||
- **ngSwitch:** support multiple matches on ngSwitchWhen and ngSwitchDefault
|
||||
([0af17204](https://github.com/angular/angular.js/commit/0af172040e03811c59d01682968241e3df226774),
|
||||
[#1074](https://github.com/angular/angular.js/issues/1074))
|
||||
- **Filters:**
|
||||
- **date:** add `[.,]sss` formatter for milliseconds
|
||||
([df744f3a](https://github.com/angular/angular.js/commit/df744f3af46fc227a934f16cb63c7a6038e7133b))
|
||||
- **filter:** add comparison function to filter
|
||||
([ace54ff0](https://github.com/angular/angular.js/commit/ace54ff08c4593195b49eadb04d258e6409d969e))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **$http:** due to [288b69a3](https://github.com/angular/angular.js/commit/288b69a314e9bd14458b6647532eb62aad5c5cdf),
|
||||
$http now follows RFC3986 and does not encode special characters like `$@,:` in params.
|
||||
If your application needs to encode these characters, encode them manually, before sending the request.
|
||||
- **$resource:** due to [2a212344](https://github.com/angular/angular.js/commit/2a2123441c2b749b8f316a24c3ca3f77a9132a01),
|
||||
if the server relied on the buggy behavior of serializing arrays as http query arguments then
|
||||
either the backend should be fixed or a simple serialization of the array should be done
|
||||
on the client before calling the resource service.
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.5"></a>
|
||||
# 1.0.5 flatulent-propulsion (2013-02-20)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- sanitize values bound to `a[href]`
|
||||
([9532234b](https://github.com/angular/angular.js/commit/9532234bf1c408af9a6fd2c4743fdb585b920531))
|
||||
- rename $compileNote to compileNode
|
||||
([92ca7efa](https://github.com/angular/angular.js/commit/92ca7efaa4bc4f37da3008b234e19343a1fa4207),
|
||||
[#1941](https://github.com/angular/angular.js/issues/1941))
|
||||
- should not leak memory when there are top level empty text nodes
|
||||
([791804bd](https://github.com/angular/angular.js/commit/791804bdbfa6da7a39283623bd05628a01cd8720))
|
||||
- allow startingTag method to handle text / comment nodes
|
||||
([755beb2b](https://github.com/angular/angular.js/commit/755beb2b66ce9f9f9a218f2355bbaf96d94fbc15))
|
||||
- **$cookies:** set cookies on Safari&IE when `base[href]` is undefined
|
||||
([70909245](https://github.com/angular/angular.js/commit/7090924515214752b919b0c5630b3ea5e7c77223),
|
||||
[#1190](https://github.com/angular/angular.js/issues/1190))
|
||||
- **$http:**
|
||||
- patch for Firefox bug w/ CORS and response headers
|
||||
([e19b04c9](https://github.com/angular/angular.js/commit/e19b04c9ec985821edf1269c628cfa261f81d631),
|
||||
[#1468](https://github.com/angular/angular.js/issues/1468))
|
||||
- **$resource:**
|
||||
- update RegExp to allow urlParams with out leading slash
|
||||
([b7e1fb05](https://github.com/angular/angular.js/commit/b7e1fb0515798e1b4f3f2426f6b050951bee2617))
|
||||
- **Directives:**
|
||||
- **a:** workaround IE bug affecting mailto urls
|
||||
([37e8b122](https://github.com/angular/angular.js/commit/37e8b12265291918396bfee65d444a8f63697b73),
|
||||
[#1949](https://github.com/angular/angular.js/issues/1949))
|
||||
- **ngClass:** keep track of old ngClass value manually
|
||||
([5f5d4fea](https://github.com/angular/angular.js/commit/5f5d4feadbfa9d8ecc8150041dfd2bca2b2e9fea),
|
||||
[#1637](https://github.com/angular/angular.js/issues/1637))
|
||||
- **ngSwitch:** make ngSwitch compatible with controller backwards-compatiblity module
|
||||
([9b7c1d0f](https://github.com/angular/angular.js/commit/9b7c1d0f7ce442d4ad2ec587e66d2d335e64fa4e))
|
||||
- **Filters:**
|
||||
- **date:** invert timezone sign and always display sign
|
||||
([b001c8ec](https://github.com/angular/angular.js/commit/b001c8ece5472626bf49cf82753e8ac1aafd2513),
|
||||
[#1261](https://github.com/angular/angular.js/issues/1261))
|
||||
- **number:** fix formatting when "0" passed as fractionSize
|
||||
([f5835963](https://github.com/angular/angular.js/commit/f5835963d5982003a713dd354eefd376ed39ac02))
|
||||
- **scenario runner:** include error messages in XML output
|
||||
([d46fe3c2](https://github.com/angular/angular.js/commit/d46fe3c23fa269dcc10249148f2af14f3db6b066))
|
||||
- **Misc:**
|
||||
- don't use instanceof to detect arrays
|
||||
([3c2aee01](https://github.com/angular/angular.js/commit/3c2aee01b0b299995eb92f4255159585b0f53c10),
|
||||
[#1966](https://github.com/angular/angular.js/issues/1966))
|
||||
- angular.forEach should correctly iterate over objects with length prop
|
||||
([ec54712f](https://github.com/angular/angular.js/commit/ec54712ff3dab1ade44f94fa82d67edeffa79a1d),
|
||||
[#1840](https://github.com/angular/angular.js/issues/1840))
|
||||
|
||||
|
||||
|
||||
<a name="1.1.2"></a>
|
||||
# 1.1.2 tofu-animation (2013-01-22)
|
||||
|
||||
@@ -73,6 +535,8 @@ _Note: This release also contains all bug fixes available in [1.0.4](#1.0.4)._
|
||||
- HTTP method should be case-insensitive
|
||||
([8991680d](https://github.com/angular/angular.js/commit/8991680d8ab632dda60cd70c780868c803c74509),
|
||||
[#1403](https://github.com/angular/angular.js/issues/1403))
|
||||
- correct leading slash removal in resource URLs
|
||||
([b2f46251](https://github.com/angular/angular.js/commit/b2f46251aca76c8568ee7d4bab54edbc9d7a186a))
|
||||
- **$route:**
|
||||
- support route params not separated with slashes.
|
||||
([c6392616](https://github.com/angular/angular.js/commit/c6392616ea5245bd0d2f77dded0b948d9e2637c8))
|
||||
@@ -100,8 +564,6 @@ _Note: This release also contains all bug fixes available in [1.0.4](#1.0.4)._
|
||||
- **ngRepeat:** correctly apply $last if repeating over object
|
||||
([7e746015](https://github.com/angular/angular.js/commit/7e746015ea7dec3e9eb81bc4678fa9b6a83bc47c),
|
||||
[#1789](https://github.com/angular/angular.js/issues/1789))
|
||||
- **ngResource:** correct leading slash removal.
|
||||
([b2f46251](https://github.com/angular/angular.js/commit/b2f46251aca76c8568ee7d4bab54edbc9d7a186a))
|
||||
- **ngSwitch:** don't leak when destroyed while not attached
|
||||
([a26234f7](https://github.com/angular/angular.js/commit/a26234f7183013e2fcc9b35377e181ad96dc9917),
|
||||
[#1621](https://github.com/angular/angular.js/issues/1621))
|
||||
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
var files = require('./angularFiles').files;
|
||||
var util = require('./lib/grunt/utils.js');
|
||||
|
||||
module.exports = function(grunt) {
|
||||
//grunt plugins
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-contrib-connect');
|
||||
grunt.loadNpmTasks('grunt-contrib-compress');
|
||||
grunt.loadTasks('lib/grunt');
|
||||
|
||||
var NG_VERSION = util.getVersion();
|
||||
var dist = 'angular-'+ NG_VERSION.full;
|
||||
|
||||
|
||||
//global beforeEach
|
||||
util.init();
|
||||
|
||||
|
||||
//config
|
||||
grunt.initConfig({
|
||||
NG_VERSION: NG_VERSION,
|
||||
|
||||
connect: {
|
||||
devserver: {
|
||||
options: {
|
||||
port: 8000,
|
||||
hostname: '0.0.0.0',
|
||||
base: '.',
|
||||
keepalive: true,
|
||||
middleware: function(connect, options){
|
||||
return [
|
||||
//uncomment to enable CSP
|
||||
// util.csp(),
|
||||
util.rewrite(),
|
||||
connect.favicon('images/favicon.ico'),
|
||||
connect.static(options.base),
|
||||
connect.directory(options.base)
|
||||
];
|
||||
}
|
||||
}
|
||||
},
|
||||
testserver: {}
|
||||
},
|
||||
|
||||
|
||||
test: {
|
||||
jqlite: 'karma-jqlite.conf.js',
|
||||
jquery: 'karma-jquery.conf.js',
|
||||
modules: 'karma-modules.conf.js',
|
||||
//NOTE run grunt test:e2e instead and it will start a webserver for you
|
||||
end2end: 'karma-e2e.conf.js'
|
||||
},
|
||||
|
||||
|
||||
autotest: {
|
||||
jqlite: 'karma-jqlite.conf.js',
|
||||
jquery: 'karma-jquery.conf.js'
|
||||
},
|
||||
|
||||
|
||||
clean: {build: ['build']},
|
||||
|
||||
|
||||
build: {
|
||||
scenario: {
|
||||
dest: 'build/angular-scenario.js',
|
||||
src: [
|
||||
'lib/jquery/jquery.js',
|
||||
util.wrap([files['angularSrc'], files['angularScenario']], 'ngScenario/angular')
|
||||
],
|
||||
styles: {
|
||||
css: ['css/angular.css', 'css/angular-scenario.css']
|
||||
}
|
||||
},
|
||||
angular: {
|
||||
dest: 'build/angular.js',
|
||||
src: util.wrap([files['angularSrc']], 'angular'),
|
||||
styles: {
|
||||
css: ['css/angular.css'],
|
||||
minify: true
|
||||
}
|
||||
},
|
||||
loader: {
|
||||
dest: 'build/angular-loader.js',
|
||||
src: util.wrap(['src/loader.js'], 'loader')
|
||||
},
|
||||
mocks: {
|
||||
dest: 'build/angular-mocks.js',
|
||||
src: ['src/ngMock/angular-mocks.js'],
|
||||
strict: false
|
||||
},
|
||||
sanitize: {
|
||||
dest: 'build/angular-sanitize.js',
|
||||
src: util.wrap([
|
||||
'src/ngSanitize/sanitize.js',
|
||||
'src/ngSanitize/directive/ngBindHtml.js',
|
||||
'src/ngSanitize/filter/linky.js',
|
||||
], 'module')
|
||||
},
|
||||
resource: {
|
||||
dest: 'build/angular-resource.js',
|
||||
src: util.wrap(['src/ngResource/resource.js'], 'module')
|
||||
},
|
||||
cookies: {
|
||||
dest: 'build/angular-cookies.js',
|
||||
src: util.wrap(['src/ngCookies/cookies.js'], 'module')
|
||||
},
|
||||
bootstrap: {
|
||||
dest: 'build/angular-bootstrap.js',
|
||||
src: util.wrap(['src/bootstrap/bootstrap.js'], 'module')
|
||||
},
|
||||
bootstrapPrettify: {
|
||||
dest: 'build/angular-bootstrap-prettify.js',
|
||||
src: util.wrap(['src/bootstrap/bootstrap-prettify.js', 'src/bootstrap/google-prettify/prettify.js'], 'module'),
|
||||
styles: {
|
||||
css: ['src/bootstrap/google-prettify/prettify.css'],
|
||||
minify: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
min: {
|
||||
angular: 'build/angular.js',
|
||||
cookies: 'build/angular-cookies.js',
|
||||
loader: 'build/angular-loader.js',
|
||||
resource: 'build/angular-resource.js',
|
||||
sanitize: 'build/angular-sanitize.js',
|
||||
bootstrap: 'build/angular-bootstrap.js',
|
||||
bootstrapPrettify: 'build/angular-bootstrap-prettify.js',
|
||||
},
|
||||
|
||||
|
||||
docs: {
|
||||
process: ['build/docs/*.html', 'build/docs/.htaccess']
|
||||
},
|
||||
|
||||
|
||||
copy: {
|
||||
i18n: {
|
||||
files: [
|
||||
{ src: 'src/ngLocale/**', dest: 'build/i18n/', expand: true, flatten: true }
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
compress: {
|
||||
build: {
|
||||
options: {archive: 'build/' + dist +'.zip'},
|
||||
src: ['**'], cwd: 'build', expand: true, dot: true, dest: dist + '/'
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
write: {
|
||||
versionTXT: {file: 'build/version.txt', val: NG_VERSION.full},
|
||||
versionJSON: {file: 'build/version.json', val: JSON.stringify(NG_VERSION)}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
//alias tasks
|
||||
grunt.registerTask('test:unit', ['test:jqlite', 'test:jquery', 'test:modules']);
|
||||
grunt.registerTask('minify', ['clean', 'build', 'minall']);
|
||||
grunt.registerTask('test:e2e', ['connect:testserver', 'test:end2end']);
|
||||
grunt.registerTask('webserver', ['connect:devserver']);
|
||||
grunt.registerTask('package', ['clean', 'buildall', 'minall', 'docs', 'copy', 'write', 'compress']);
|
||||
grunt.registerTask('default', ['package']);
|
||||
};
|
||||
@@ -21,25 +21,19 @@ Building AngularJS
|
||||
---------
|
||||
[Once you have your environment setup](http://docs.angularjs.org/misc/contribute) just run:
|
||||
|
||||
rake package
|
||||
grunt package
|
||||
|
||||
|
||||
Running Tests
|
||||
-------------
|
||||
Running tests requires installation of [Testacular](http://vojtajina.github.com/testacular):
|
||||
|
||||
sudo npm install -g testacular
|
||||
|
||||
To execute all unit tests, use:
|
||||
|
||||
rake test:unit
|
||||
grunt test:unit
|
||||
|
||||
To execute end-to-end (e2e) tests, use:
|
||||
|
||||
rake package
|
||||
rake webserver &
|
||||
rake test:e2e
|
||||
grunt package
|
||||
grunt test:e2e
|
||||
|
||||
To learn more about the rake tasks, run `rake -T` and also read our
|
||||
[contribution guidelines](http://docs.angularjs.org/misc/contribute) and instructions in this
|
||||
[commit message](https://github.com/angular/angular.js/commit/9d168f058f9c6d7eeae0daa7cb72ea4e02a0003a).
|
||||
To learn more about the grunt tasks, run `grunt --help` and also read our
|
||||
[contribution guidelines](http://docs.angularjs.org/misc/contribute).
|
||||
|
||||
@@ -1,374 +0,0 @@
|
||||
require 'yaml'
|
||||
include FileUtils
|
||||
|
||||
|
||||
## High level flow of the build:
|
||||
##
|
||||
## clean -> init -> concat -> minify -> package
|
||||
##
|
||||
|
||||
|
||||
content = File.open('angularFiles.js', 'r') {|f| f.read }
|
||||
files = eval(content.gsub(/\};(\s|\S)*/, '}').
|
||||
gsub(/angularFiles = /, '').
|
||||
gsub(/:/, '=>').
|
||||
gsub(/\/\//, '#'));
|
||||
|
||||
BUILD_DIR = 'build'
|
||||
|
||||
task :default => [:package]
|
||||
|
||||
|
||||
desc 'Init the build workspace'
|
||||
task :init do
|
||||
FileUtils.mkdir(BUILD_DIR) unless File.directory?(BUILD_DIR)
|
||||
|
||||
v = YAML::load( File.open( 'version.yaml' ) )
|
||||
match = v['version'].match(/^([^-]*)(-snapshot)?$/)
|
||||
|
||||
NG_VERSION = Struct.new(:full, :major, :minor, :dot, :codename, :stable).
|
||||
new(match[1] + (match[2] ? ('-' + %x(git rev-parse HEAD)[0..7]) : ''),
|
||||
match[1].split('.')[0],
|
||||
match[1].split('.')[1],
|
||||
match[1].split('.')[2].sub(/\D+.*$/, ''),
|
||||
v['codename'],
|
||||
v['stable'])
|
||||
end
|
||||
|
||||
|
||||
desc 'Clean Generated Files'
|
||||
task :clean do
|
||||
FileUtils.rm_r(BUILD_DIR, :force => true)
|
||||
FileUtils.mkdir(BUILD_DIR)
|
||||
FileUtils.rm_r('test_out', :force => true)
|
||||
end
|
||||
|
||||
|
||||
desc 'Concat Scenario'
|
||||
task :concat_scenario => :init do
|
||||
|
||||
concat_file('angular-scenario.js', [
|
||||
'lib/jquery/jquery.js',
|
||||
'src/ngScenario/angular.prefix',
|
||||
files['angularSrc'],
|
||||
files['angularScenario'],
|
||||
'src/ngScenario/angular.suffix',
|
||||
], gen_css('css/angular.css') + "\n" + gen_css('css/angular-scenario.css'))
|
||||
end
|
||||
|
||||
|
||||
desc 'Concat JSTD Scenario Adapter'
|
||||
task :concat_jstd_scenario_adapter => :init do
|
||||
|
||||
concat_file('jstd-scenario-adapter.js', [
|
||||
'src/ngScenario/jstd-scenario-adapter/angular.prefix',
|
||||
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
|
||||
'src/ngScenario/jstd-scenario-adapter/angular.suffix',
|
||||
])
|
||||
|
||||
# TODO(vojta) use jstd configuration when implemented
|
||||
# (instead of including jstd-adapter-config.js)
|
||||
File.open(path_to('jstd-scenario-adapter-config.js'), 'w') do |f|
|
||||
f.write("/**\r\n" +
|
||||
" * Configuration for jstd scenario adapter \n */\n" +
|
||||
"var jstdScenarioAdapter = {\n relativeUrlPrefix: '/build/docs/'\n};\n")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
desc 'Concat AngularJS files'
|
||||
task :concat => :init do
|
||||
concat_file('angular.js', [
|
||||
'src/angular.prefix',
|
||||
files['angularSrc'],
|
||||
'src/angular.suffix',
|
||||
], gen_css('css/angular.css', true))
|
||||
|
||||
FileUtils.cp_r 'src/ngLocale', path_to('i18n')
|
||||
|
||||
concat_file('angular-loader.js', [
|
||||
'src/loader.prefix',
|
||||
'src/loader.js',
|
||||
'src/loader.suffix'])
|
||||
|
||||
|
||||
concat_module('sanitize', [
|
||||
'src/ngSanitize/sanitize.js',
|
||||
'src/ngSanitize/directive/ngBindHtml.js',
|
||||
'src/ngSanitize/filter/linky.js'])
|
||||
|
||||
concat_module('resource', ['src/ngResource/resource.js'])
|
||||
concat_module('cookies', ['src/ngCookies/cookies.js'])
|
||||
concat_module('bootstrap', ['src/bootstrap/bootstrap.js'])
|
||||
concat_module('bootstrap-prettify', ['src/bootstrap/bootstrap-prettify.js',
|
||||
'src/bootstrap/google-prettify/prettify.js'],
|
||||
gen_css('src/bootstrap/google-prettify/prettify.css', true))
|
||||
|
||||
|
||||
FileUtils.cp 'src/ngMock/angular-mocks.js', path_to('angular-mocks.js')
|
||||
|
||||
rewrite_file(path_to('angular-mocks.js')) do |content|
|
||||
content.sub!('"NG_VERSION_FULL"', NG_VERSION.full)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
desc 'Minify JavaScript'
|
||||
task :minify => [:init, :concat, :concat_scenario, :concat_jstd_scenario_adapter] do
|
||||
[ 'angular.js',
|
||||
'angular-cookies.js',
|
||||
'angular-loader.js',
|
||||
'angular-resource.js',
|
||||
'angular-sanitize.js',
|
||||
'angular-bootstrap.js',
|
||||
'angular-bootstrap-prettify.js'
|
||||
].each do |file|
|
||||
unless ENV['TRAVIS']
|
||||
fork { closure_compile(file) }
|
||||
else
|
||||
closure_compile(file)
|
||||
end
|
||||
end
|
||||
|
||||
Process.waitall
|
||||
end
|
||||
|
||||
|
||||
desc 'Generate version.txt and version.json files'
|
||||
task :version => [:init] do
|
||||
`echo #{NG_VERSION.full} > #{path_to('version.txt')}`
|
||||
`echo '{
|
||||
"full": "#{NG_VERSION.full}",
|
||||
"major": "#{NG_VERSION.major}",
|
||||
"minor": "#{NG_VERSION.minor}",
|
||||
"dot": "#{NG_VERSION.dot}",
|
||||
"codename": "#{NG_VERSION.codename}"\n}' > #{path_to('version.json')}`
|
||||
end
|
||||
|
||||
|
||||
desc 'Generate docs'
|
||||
task :docs => [:init] do
|
||||
`node docs/src/gen-docs.js`
|
||||
|
||||
[ path_to('docs/.htaccess'),
|
||||
path_to('docs/index.html'),
|
||||
path_to('docs/index-debug.html'),
|
||||
path_to('docs/index-nocache.html'),
|
||||
path_to('docs/index-jq.html'),
|
||||
path_to('docs/index-jq-debug.html'),
|
||||
path_to('docs/index-jq-nocache.html'),
|
||||
path_to('docs/docs-scenario.html')
|
||||
].each do |src|
|
||||
rewrite_file(src) do |content|
|
||||
content.sub!('"NG_VERSION_FULL"', NG_VERSION.full).
|
||||
sub('"NG_VERSION_STABLE"', NG_VERSION.stable)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
desc 'Create angular distribution'
|
||||
task :package => [:clean, :minify, :version, :docs] do
|
||||
zip_dir = "angular-#{NG_VERSION.full}"
|
||||
zip_file = "#{zip_dir}.zip"
|
||||
|
||||
FileUtils.ln_s BUILD_DIR, zip_dir
|
||||
%x(zip -r #{zip_file} #{zip_dir})
|
||||
FileUtils.rm zip_dir
|
||||
|
||||
FileUtils.mv zip_file, path_to(zip_file)
|
||||
|
||||
puts "Package created: #{path_to(zip_file)}"
|
||||
end
|
||||
|
||||
|
||||
desc 'Start development webserver'
|
||||
task :webserver, :port do |t, args|
|
||||
exec "node lib/nodeserver/server.js #{args[:port]}"
|
||||
end
|
||||
|
||||
|
||||
desc 'Run all AngularJS tests'
|
||||
task :test, :browsers, :misc_options do |t, args|
|
||||
[ 'test:jqlite',
|
||||
'test:jquery',
|
||||
'test:modules',
|
||||
'test:e2e'
|
||||
].each do |task|
|
||||
Rake::Task[task].invoke(args[:browsers], args[:misc_options])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
namespace :test do
|
||||
|
||||
desc 'Run all unit tests (single run)'
|
||||
task :unit, :browsers, :misc_options do |t, args|
|
||||
[ 'test:jqlite',
|
||||
'test:jquery',
|
||||
'test:modules'
|
||||
].each do |task|
|
||||
Rake::Task[task].invoke(args[:browsers], args[:misc_options])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
desc 'Run jqLite-based unit test suite (single run)'
|
||||
task :jqlite, :browsers, :misc_options do |t, args|
|
||||
start_testacular('testacular-jqlite.conf.js', true, args[:browsers], args[:misc_options])
|
||||
end
|
||||
|
||||
|
||||
desc 'Run jQuery-based unit test suite (single run)'
|
||||
task :jquery, :browsers, :misc_options do |t, args|
|
||||
start_testacular('testacular-jquery.conf.js', true, args[:browsers], args[:misc_options])
|
||||
end
|
||||
|
||||
|
||||
desc 'Run bundled modules unit test suite (single run)'
|
||||
task :modules, :browsers, :misc_options do |t, args|
|
||||
start_testacular('testacular-modules.conf.js', true, args[:browsers], args[:misc_options])
|
||||
end
|
||||
|
||||
|
||||
desc 'Run e2e test suite (single run)'
|
||||
task :e2e, :browsers, :misc_options do |t, args|
|
||||
start_testacular('testacular-e2e.conf.js', true, args[:browsers], args[:misc_options])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
namespace :autotest do
|
||||
|
||||
desc 'Run jqLite-based unit test suite (autowatch)'
|
||||
task :jqlite, :browsers, :misc_options do |t, args|
|
||||
start_testacular('testacular-jqlite.conf.js', false, args[:browsers], args[:misc_options])
|
||||
end
|
||||
|
||||
|
||||
desc 'Run jQuery-based unit test suite (autowatch)'
|
||||
task :jquery, :browsers, :misc_options do |t, args|
|
||||
start_testacular('testacular-jquery.conf.js', false, args[:browsers], args[:misc_options])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
###################
|
||||
# utility methods #
|
||||
###################
|
||||
|
||||
|
||||
##
|
||||
# generates css snippet from a given files and optionally applies simple minification rules
|
||||
#
|
||||
def gen_css(cssFile, minify = false)
|
||||
css = ''
|
||||
File.open(cssFile, 'r') do |f|
|
||||
css = f.read
|
||||
end
|
||||
|
||||
if minify
|
||||
css.gsub! /\n/, ''
|
||||
css.gsub! /\/\*.*?\*\//, ''
|
||||
css.gsub! /:\s+/, ':'
|
||||
css.gsub! /\s*\{\s*/, '{'
|
||||
css.gsub! /\s*\}\s*/, '}'
|
||||
css.gsub! /\s*\,\s*/, ','
|
||||
css.gsub! /\s*\;\s*/, ';'
|
||||
end
|
||||
|
||||
#escape for js
|
||||
css.gsub! /\\/, "\\\\\\"
|
||||
css.gsub! /'/, "\\\\'"
|
||||
css.gsub! /\n/, "\\n"
|
||||
|
||||
return %Q{angular.element(document).find('head').append('<style type="text/css">#{css}</style>');}
|
||||
end
|
||||
|
||||
|
||||
##
|
||||
# returns path to the file in the build directory
|
||||
#
|
||||
def path_to(filename)
|
||||
return File.join(BUILD_DIR, *filename)
|
||||
end
|
||||
|
||||
|
||||
##
|
||||
# returns the 32-bit mode force flags for java compiler if supported, this makes the build much
|
||||
# faster
|
||||
#
|
||||
def java32flags
|
||||
return '-d32 -client' unless Rake::Win32.windows? || `java -version -d32 2>&1`.match(/Error/i)
|
||||
end
|
||||
|
||||
|
||||
def closure_compile(filename)
|
||||
puts "Minifying #{filename} ..."
|
||||
|
||||
min_path = path_to(filename.gsub(/\.js$/, '.min.js'))
|
||||
|
||||
%x(java \
|
||||
#{java32flags()} \
|
||||
-jar lib/closure-compiler/compiler.jar \
|
||||
--compilation_level SIMPLE_OPTIMIZATIONS \
|
||||
--language_in ECMASCRIPT5_STRICT \
|
||||
--js #{path_to(filename)} \
|
||||
--js_output_file #{min_path})
|
||||
|
||||
rewrite_file(min_path) do |content|
|
||||
content.sub!("'use strict';", "").
|
||||
sub!(/\(function\([^)]*\)\{/, "\\0'use strict';")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def concat_file(filename, deps, footer='')
|
||||
puts "Creating #{filename} ..."
|
||||
File.open(path_to(filename), 'w') do |f|
|
||||
concat = 'cat ' + deps.flatten.join(' ')
|
||||
|
||||
content = %x{#{concat}}.
|
||||
gsub('"NG_VERSION_FULL"', NG_VERSION.full).
|
||||
gsub('"NG_VERSION_MAJOR"', NG_VERSION.major).
|
||||
gsub('"NG_VERSION_MINOR"', NG_VERSION.minor).
|
||||
gsub('"NG_VERSION_DOT"', NG_VERSION.dot).
|
||||
gsub('"NG_VERSION_CODENAME"', NG_VERSION.codename).
|
||||
gsub(/^\s*['"]use strict['"];?\s*$/, ''). # remove all file-specific strict mode flags
|
||||
sub(/\(function\([^)]*\)\s*\{/, "\\0\n'use strict';") # add single strict mode flag
|
||||
|
||||
f.write(content)
|
||||
f.write(footer)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def concat_module(name, files, footer='')
|
||||
concat_file('angular-' + name + '.js', ['src/module.prefix'] + files + ['src/module.suffix'], footer)
|
||||
end
|
||||
|
||||
|
||||
def rewrite_file(filename)
|
||||
File.open(filename, File::RDWR) do |f|
|
||||
content = f.read
|
||||
|
||||
content = yield content
|
||||
|
||||
raise "File rewrite failed - No content!" unless content
|
||||
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write content
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def start_testacular(config, singleRun, browsers, misc_options)
|
||||
sh "./node_modules/testacular/bin/testacular start " +
|
||||
"#{config} " +
|
||||
"#{'--single-run=true' if singleRun} " +
|
||||
"#{'--browsers=' + browsers.gsub('+', ',') if browsers} " +
|
||||
"#{(misc_options || '').gsub('+', ',')}"
|
||||
end
|
||||
Vendored
+1
-1
@@ -201,7 +201,7 @@ if (exports) {
|
||||
var files = [];
|
||||
|
||||
[].splice.call(arguments, 0).forEach(function(file) {
|
||||
if (file.match(/testacular/)) {
|
||||
if (file.match(/karma/)) {
|
||||
files.push(file);
|
||||
} else {
|
||||
angularFiles[file].forEach(function(f) {
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
rake minify
|
||||
grunt minify
|
||||
gzip -c < build/angular.min.js > build/angular.min.js.gzip
|
||||
ls -l build/angular.min.*
|
||||
|
||||
@@ -38,6 +38,10 @@ initialization.
|
||||
|
||||
<html ng-app>
|
||||
|
||||
* If IE7 support is required add `id="ng-app"`
|
||||
|
||||
<html ng-app id="ng-app">
|
||||
|
||||
* If you choose to use the old style directive syntax `ng:` then include xml-namespace in `html`
|
||||
to make IE happy. (This is here for historical reasons, and we no longer recommend use of
|
||||
`ng:`.)
|
||||
|
||||
@@ -30,7 +30,7 @@ involved.
|
||||
# Compiler
|
||||
|
||||
Compiler is an angular service which traverses the DOM looking for attributes. The compilation
|
||||
process happens into two phases.
|
||||
process happens in two phases.
|
||||
|
||||
1. **Compile:** traverse the DOM and collect all of the directives. The result is a linking
|
||||
function.
|
||||
@@ -70,8 +70,8 @@ Here is a directive which makes any element draggable. Notice the `draggable` at
|
||||
<file name="script.js">
|
||||
angular.module('drag', []).
|
||||
directive('draggable', function($document) {
|
||||
var startX=0, startY=0, x = 0, y = 0;
|
||||
return function(scope, element, attr) {
|
||||
var startX = 0, startY = 0, x = 0, y = 0;
|
||||
element.css({
|
||||
position: 'relative',
|
||||
border: '1px solid red',
|
||||
@@ -79,6 +79,8 @@ Here is a directive which makes any element draggable. Notice the `draggable` at
|
||||
cursor: 'pointer'
|
||||
});
|
||||
element.bind('mousedown', function(event) {
|
||||
// Prevent default dragging of selected content
|
||||
event.preventDefault();
|
||||
startX = event.screenX - x;
|
||||
startY = event.screenY - y;
|
||||
$document.bind('mousemove', mousemove);
|
||||
|
||||
@@ -61,7 +61,7 @@ This is how we get the ball rolling (refer to the diagram and example below):
|
||||
|
||||
The diagram and the example below describe how Angular interacts with the browser's event loop.
|
||||
|
||||
1. The browser's event-loop waits for an event to arrive. An event is a user interactions, timer event,
|
||||
1. The browser's event-loop waits for an event to arrive. An event is a user interaction, timer event,
|
||||
or network event (response from a server).
|
||||
2. The event's callback gets executed. This enters the JavaScript context. The callback can
|
||||
modify the DOM structure.
|
||||
@@ -74,7 +74,7 @@ applied in Angular execution context will benefit from Angular data-binding, exc
|
||||
property watching, etc... You can also use $apply() to enter Angular execution context from JavaScript. Keep in
|
||||
mind that in most places (controllers, services) $apply has already been called for you by the
|
||||
directive which is handling the event. An explicit call to $apply is needed only when
|
||||
implementing custom event callbacks, or when working with a third-party library callbacks.
|
||||
implementing custom event callbacks, or when working with third-party library callbacks.
|
||||
|
||||
1. Enter Angular execution context by calling {@link guide/scope scope}`.`{@link
|
||||
api/ng.$rootScope.Scope#$apply $apply}`(stimulusFn)`. Where `stimulusFn` is
|
||||
@@ -101,7 +101,7 @@ implementing custom event callbacks, or when working with a third-party library
|
||||
re-rendering the DOM to reflect any changes.
|
||||
|
||||
|
||||
Here is the explanation of how the `Hello wold` example achieves the data-binding effect when the
|
||||
Here is the explanation of how the `Hello world` example achieves the data-binding effect when the
|
||||
user enters text into the text field.
|
||||
|
||||
1. During the compilation phase:
|
||||
@@ -143,8 +143,8 @@ provides the execution context for expressions. The scopes are nested in a hiera
|
||||
which closely follow the DOM structure. (See individual directive documentation to see which
|
||||
directives cause a creation of new scopes.)
|
||||
|
||||
The following example demonstrates how `name` {@link guide/expression expression} will evaluate
|
||||
into different value depending on which scope it is evaluated in. The example is followed by
|
||||
The following example demonstrates how the `name` {@link guide/expression expression} will evaluate
|
||||
into a different value depending on which scope it is evaluated in. The example is followed by
|
||||
a diagram depicting the scope boundaries.
|
||||
|
||||
<div class="clear">
|
||||
@@ -232,9 +232,9 @@ The separation of the controller and the view is important because:
|
||||
|
||||
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-model.png">
|
||||
|
||||
The model is the data which is used merged with the template to produce the view. To be able to
|
||||
The model is the data which is merged with the template to produce the view. To be able to
|
||||
render the model into the view, the model has to be able to be referenced from the scope. Unlike many
|
||||
other frameworks Angular makes no restrictions or requirements an the model. There are no classes
|
||||
other frameworks Angular makes no restrictions or requirements on the model. There are no classes
|
||||
to inherit from or special accessor methods for accessing or changing the model. The model can be
|
||||
primitive, object hash, or a full object Type. In short the model is a plain JavaScript object.
|
||||
|
||||
@@ -248,9 +248,9 @@ primitive, object hash, or a full object Type. In short the model is a plain Jav
|
||||
|
||||
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-view.png">
|
||||
|
||||
The view is what the users sees. The view begins its life as a template, it is merged with the
|
||||
The view is what the user sees. The view begins its life as a template, is merged with the
|
||||
model and finally rendered into the browser DOM. Angular takes a very different approach to
|
||||
rendering the view, compared to most other templating systems.
|
||||
rendering the view compared to most other templating systems.
|
||||
|
||||
* **Others** - Most templating systems begin as an HTML string with special templating markup.
|
||||
Often the template markup breaks the HTML syntax which means that the template can not be
|
||||
@@ -425,7 +425,7 @@ function arguments. When angular calls the function, it will use the {@link
|
||||
api/AUTO.$injector#invoke call} which will automatically fill the function
|
||||
arguments.
|
||||
|
||||
Examine the `ClockCtrl` bellow, and notice how it lists the dependencies in the constructor. When the
|
||||
Examine the `ClockCtrl` below, and notice how it lists the dependencies in the constructor. When the
|
||||
{@link api/ng.directive:ngController ng-controller} instantiates
|
||||
the controller it automatically provides the dependencies. There is no need to create
|
||||
dependencies, look for dependencies, or even get a reference to the injector.
|
||||
|
||||
@@ -97,7 +97,8 @@ the test frame.
|
||||
Asserts the value of the given `future` satisfies the `matcher`. All API statements return a
|
||||
`future` object, which get a `value` assigned after they are executed. Matchers are defined using
|
||||
`angular.scenario.matcher`, and they use the value of futures to run the expectation. For example:
|
||||
`expect(browser().location().href()).toEqual('http://www.google.com')`
|
||||
`expect(browser().location().href()).toEqual('http://www.google.com')`. Available matchers
|
||||
are presented further down this document.
|
||||
|
||||
## expect(future).not().{matcher}
|
||||
Asserts the value of the given `future` satisfies the negation of the `matcher`.
|
||||
@@ -176,3 +177,128 @@ JavaScript is a dynamically typed language which comes with great power of expre
|
||||
come with almost no-help from the compiler. For this reason we feel very strongly that any code
|
||||
written in JavaScript needs to come with a strong set of tests. We have built many features into
|
||||
angular which makes testing your angular applications easy. So there is no excuse for not testing.
|
||||
|
||||
# Matchers
|
||||
|
||||
Matchers are used in combination with the `expect(...)` function as described above and can
|
||||
be negated with `not()`. For instance: `expect(element('h1').text()).not().toEqual('Error')`.
|
||||
|
||||
Source: {@link https://github.com/angular/angular.js/blob/master/src/ngScenario/matchers.js}
|
||||
|
||||
<pre>
|
||||
// value and Object comparison following the rules of angular.equals().
|
||||
expect(value).toEqual(value)
|
||||
|
||||
// a simpler value comparison using ===
|
||||
expect(value).toBe(value)
|
||||
|
||||
// checks that the value is defined by checking its type.
|
||||
expect(value).toBeDefined()
|
||||
|
||||
// the following two matchers are using JavaScript's standard truthiness rules
|
||||
expect(value).toBeTruthy()
|
||||
expect(value).toBeFalsy()
|
||||
|
||||
// verify that the value matches the given regular expression. The regular
|
||||
// expression may be passed in form of a string or a regular expression
|
||||
// object.
|
||||
expect(value).toMatch(expectedRegExp)
|
||||
|
||||
// a check for null using ===
|
||||
expect(value).toBeNull()
|
||||
|
||||
// Array.indexOf(...) is used internally to check whether the element is
|
||||
// contained within the array.
|
||||
expect(value).toContain(expected)
|
||||
|
||||
// number comparison using < and >
|
||||
expect(value).toBeLessThan(expected)
|
||||
expect(value).toBeGreaterThan(expected)
|
||||
</pre>
|
||||
|
||||
# Example
|
||||
See the {@link https://github.com/angular/angular-seed angular-seed} project for more examples.
|
||||
|
||||
## Conditional actions with element(...).query(fn)
|
||||
|
||||
E2E testing with angular scenario is highly asynchronous and hides a lot of complexity by
|
||||
queueing actions and expectations that can handle futures. From time to time, you might need
|
||||
conditional assertions or element selection. Even though you should generally try to avoid this
|
||||
(as it is can be sign for unstable tests), you can add conditional behavior with
|
||||
`element(...).query(fn)`. The following code listing shows how this function can be used to delete
|
||||
added entries (where an entry is some domain object) using the application's web interface.
|
||||
|
||||
Imagine the application to be structure into two views:
|
||||
|
||||
1. *Overview view* which lists all the added entries in a table and
|
||||
2. a *detail view* which shows the entries' details and contains a delete button. When clicking the
|
||||
delete button, the user is redirected back to the *overview page*.
|
||||
|
||||
<pre>
|
||||
beforeEach(function () {
|
||||
var deleteEntry = function () {
|
||||
browser().navigateTo('/entries');
|
||||
|
||||
// we need to select the <tbody> element as it might be the case that there
|
||||
// are no entries (and therefore no rows). When the selector does not
|
||||
// result in a match, the test would be marked as a failure.
|
||||
element('table tbody').query(function (tbody, done) {
|
||||
// ngScenario gives us a jQuery lite wrapped element. We call the
|
||||
// `children()` function to retrieve the table body's rows
|
||||
var children = tbody.children();
|
||||
|
||||
if (children.length > 0) {
|
||||
// if there is at least one entry in the table, click on the link to
|
||||
// the entry's detail view
|
||||
element('table tbody a').click();
|
||||
// and, after a route change, click the delete button
|
||||
element('.btn-danger').click();
|
||||
}
|
||||
|
||||
// if there is more than one entry shown in the table, queue another
|
||||
// delete action.
|
||||
if (children.length > 1) {
|
||||
deleteEntry();
|
||||
}
|
||||
|
||||
// remember to call `done()` so that ngScenario can continue
|
||||
// test execution.
|
||||
done();
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
// start deleting entries
|
||||
deleteEntry();
|
||||
});
|
||||
</pre>
|
||||
|
||||
In order to understand what is happening, we should emphasize that ngScenario calls are not
|
||||
immediately executed, but queued (in ngScenario terms, we would be talking about adding
|
||||
future actions). If we had only one entry in our table, than the following future actions
|
||||
would be queued:
|
||||
|
||||
<pre>
|
||||
// delete entry 1
|
||||
browser().navigateTo('/entries');
|
||||
element('table tbody').query(function (tbody, done) { ... });
|
||||
element('table tbody a');
|
||||
element('.btn-danger').click();
|
||||
</pre>
|
||||
|
||||
For two entries, ngScenario would have to work on the following queue:
|
||||
|
||||
<pre>
|
||||
// delete entry 1
|
||||
browser().navigateTo('/entries');
|
||||
element('table tbody').query(function (tbody, done) { ... });
|
||||
element('table tbody a');
|
||||
element('.btn-danger').click();
|
||||
|
||||
// delete entry 2
|
||||
// indented to represent "recursion depth"
|
||||
browser().navigateTo('/entries');
|
||||
element('table tbody').query(function (tbody, done) { ... });
|
||||
element('table tbody a');
|
||||
element('.btn-danger').click();
|
||||
</pre>
|
||||
@@ -3,10 +3,7 @@
|
||||
@description
|
||||
|
||||
In Angular, a controller is a JavaScript function(type/class) that is used to augment instances of
|
||||
angular {@link scope Scope}, excluding the root scope. When you or Angular create a new
|
||||
child scope object via the {@link api/ng.$rootScope.Scope#$new scope.$new} API , there is an
|
||||
option to pass in a controller as a method argument. This will tell Angular to associate the
|
||||
controller with the new scope and to augment its behavior.
|
||||
angular {@link scope Scope}, excluding the root scope.
|
||||
|
||||
Use controllers to:
|
||||
|
||||
@@ -37,8 +34,8 @@ your application as follows:
|
||||
|
||||
var myApp = angular.module('myApp',[]);
|
||||
|
||||
myApp.controller('GreetingCtrl', ['$scope', function(scope) {
|
||||
scope.greeting = 'Hola!';
|
||||
myApp.controller('GreetingCtrl', ['$scope', function($scope) {
|
||||
$scope.greeting = 'Hola!';
|
||||
}]);
|
||||
|
||||
Note also that we use the array notation to explicitly specify the dependency
|
||||
@@ -82,8 +79,7 @@ instances).
|
||||
|
||||
# Associating Controllers with Angular Scope Objects
|
||||
|
||||
You can associate controllers with scope objects explicitly via the {@link api/ng.$rootScope.Scope#$new
|
||||
scope.$new} api or implicitly via the {@link api/ng.directive:ngController ngController
|
||||
You can associate controllers with scope objects implicitly via the {@link api/ng.directive:ngController ngController
|
||||
directive} or {@link api/ng.$route $route service}.
|
||||
|
||||
|
||||
@@ -178,8 +174,9 @@ have a look at an example:
|
||||
<body ng-controller="MainCtrl">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
<div ng-controller="ChildCtrl">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
<p ng-controller="BabyCtrl">Good {{timeOfDay}}, {{name}}!</p>
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
<p ng-controller="BabyCtrl">Good {{timeOfDay}}, {{name}}!</p>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
function MainCtrl($scope) {
|
||||
|
||||
@@ -28,7 +28,7 @@ occurs in controllers:
|
||||
|
||||
* Use an {@link expression angular expression} with an assignment operator in templates:
|
||||
|
||||
<button ng-click="{{foos='ball'}}">Click me</button>
|
||||
<button ng-click="{{foo='ball'}}">Click me</button>
|
||||
|
||||
* Use {@link api/ng.directive:ngInit ngInit directive} in templates (for toy/example apps
|
||||
only, not recommended for real applications):
|
||||
|
||||
@@ -98,7 +98,7 @@ To configure the `$location` service, retrieve the
|
||||
|
||||
- **hashPrefix(prefix)**: {string}<br />
|
||||
prefix used for Hashbang URLs (used in Hashbang mode or in legacy browser in Html5 mode)<br />
|
||||
default: `'!'`
|
||||
default: `""`
|
||||
|
||||
### Example configuration
|
||||
<pre>
|
||||
@@ -619,21 +619,25 @@ to the $location object (using {@link api/ng.directive:input.text
|
||||
ngModel} directive on an input field), you will need to specify an extra model property
|
||||
(e.g. `locationPath`) with two watchers which push $location updates in both directions. For
|
||||
example:
|
||||
<pre>
|
||||
<!-- html -->
|
||||
<input type="text" ng-model="locationPath" />
|
||||
</pre>
|
||||
<pre>
|
||||
// js - controller
|
||||
$scope.$watch('locationPath', function(path) {
|
||||
$location.path(path);
|
||||
});
|
||||
|
||||
$scope.$watch('$location.path()', function(path) {
|
||||
scope.locationPath = path;
|
||||
});
|
||||
</pre>
|
||||
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<div ng-controller="LocationController">
|
||||
<input type="text" ng-model="locationPath" />
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
function LocationController($scope, $location) {
|
||||
$scope.$watch('locationPath', function(path) {
|
||||
$location.path(path);
|
||||
});
|
||||
$scope.$watch(function() {
|
||||
return $location.path();
|
||||
}, function(path) {
|
||||
$scope.locationPath = path;
|
||||
});
|
||||
}
|
||||
</file>
|
||||
</example>
|
||||
|
||||
# Related API
|
||||
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
@name Developer Guide: Templates: Understanding Angular Filters
|
||||
@description
|
||||
|
||||
Angular filters format data for display to the user. In addition to formatting data, filters can
|
||||
also modify the DOM. This allows filters to handle tasks such as conditionally applying CSS styles
|
||||
to filtered output.
|
||||
Angular filters format data for display to the user.
|
||||
|
||||
For example, you might have a data object that needs to be formatted according to the locale before
|
||||
displaying it to the user. You can pass expressions through a chain of filters like this:
|
||||
|
||||
@@ -19,6 +19,10 @@ You can also pass colon-delimited arguments to filters, for example, to display
|
||||
|
||||
123 | number:2
|
||||
|
||||
Use the same syntax for multiple arguments:
|
||||
|
||||
myArray | orderBy:'timestamp':true
|
||||
|
||||
Here are some examples that show values before and after applying different filters to an
|
||||
expression in a binding:
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
@description
|
||||
|
||||
JavaScript is a dynamically typed language which comes with great power of expression, but it also
|
||||
come with almost no-help from the compiler. For this reason we feel very strongly that any code
|
||||
comes with almost no-help from the compiler. For this reason we feel very strongly that any code
|
||||
written in JavaScript needs to come with a strong set of tests. We have built many features into
|
||||
Angular which makes testing your Angular applications easy. So there is no excuse for not testing.
|
||||
|
||||
@@ -97,7 +97,7 @@ function MyClass() {
|
||||
|
||||
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
|
||||
through monkey patching. The basic issue for testing is that global variable needs to be mutated in
|
||||
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
|
||||
http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/ Brittle Global
|
||||
State & Singletons}
|
||||
@@ -275,7 +275,62 @@ expect(length('abc')).toEqual(3);
|
||||
</pre>
|
||||
|
||||
## Directives
|
||||
Directives in angular are responsible for updating the DOM when the state of the model changes.
|
||||
Directives in angular are responsible for encapsulating complex functionality within custom HTML tags,
|
||||
attributes, classes or comments. Unit tests are very important for directives because the components
|
||||
you create with directives may be used throughout your application and in many different contexts.
|
||||
|
||||
### Simple HTML Element Directive
|
||||
|
||||
Lets start with an angular app with no dependencies.
|
||||
|
||||
<pre>
|
||||
var app = angular.module('myApp', []);
|
||||
</pre>
|
||||
|
||||
Now we can add a directive to our app.
|
||||
|
||||
<pre>
|
||||
app.directive('aGreatEye', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
template: '<h1>lidless, wreathed in flame</h1>'
|
||||
};
|
||||
});
|
||||
</pre>
|
||||
|
||||
This directive is used as a tag `<a-great-eye></a-great-eye>`. It replaces the entire tag with the
|
||||
template `<h1>lidless, wreathed in flame</h1>`. Now we are going to write a jasmine unit test to
|
||||
verify this functionality.
|
||||
|
||||
<pre>
|
||||
describe('Unit testing great quotes', function() {
|
||||
var $compile;
|
||||
var $rootScope;
|
||||
|
||||
// Load the myApp module, which contains the directive
|
||||
beforeEach(module('myApp'));
|
||||
|
||||
// Store references to $rootScope and $compile
|
||||
// so they are available to all tests in this describe block
|
||||
beforeEach(inject(function(_$compile_, _$rootScope_){
|
||||
// The injector unwraps the underscores (_) from around the parameter names when matching
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
}));
|
||||
|
||||
it('Replaces the element with the appropriate content', function() {
|
||||
// Compile a piece of HTML containing the directive
|
||||
var element = $compile("<a-great-eye></a-great-eye>")($rootScope);
|
||||
// Check that the compiled element contains the templated content
|
||||
expect(element.html()).toContain("lidless, wreathed in flame");
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
We inject the $compile service and $rootScope before each jasmine test. The $compile service is used
|
||||
to render the aGreatEye directive. After rendering the directive we ensure that the directive has
|
||||
replaced the content and "lidless, wreathed in flame" is present.
|
||||
|
||||
## Mocks
|
||||
oue
|
||||
@@ -293,4 +348,5 @@ ou
|
||||
ou
|
||||
|
||||
## Sample project
|
||||
uoe
|
||||
See the {@link https://github.com/angular/angular-seed angular-seed} project for an example.
|
||||
|
||||
|
||||
+17
-14
@@ -14,7 +14,7 @@ book.
|
||||
|
||||
## DI in a nutshell
|
||||
|
||||
There are only three ways how an object or a function can get a hold of its dependencies:
|
||||
There are only three ways an object or a function can get a hold of its dependencies:
|
||||
|
||||
1. The dependency can be created, typically using the `new` operator.
|
||||
|
||||
@@ -23,8 +23,8 @@ There are only three ways how an object or a function can get a hold of its depe
|
||||
3. The dependency can be passed in to where it is needed.
|
||||
|
||||
|
||||
The first two option of creating or looking up dependencies are not optimal, because they hard
|
||||
code the dependency, making it difficult, if not impossible, to modify the dependencies.
|
||||
The first two options of creating or looking up dependencies are not optimal because they hard
|
||||
code the dependency. This make it difficult, if not impossible, to modify the dependencies.
|
||||
This is especially problematic in tests, where it is often desirable to provide mock dependencies
|
||||
for test isolation.
|
||||
|
||||
@@ -33,7 +33,7 @@ dependency from the component. The dependency is simply handed to the component.
|
||||
|
||||
<pre>
|
||||
function SomeClass(greeter) {
|
||||
this.greeter = greeter
|
||||
this.greeter = greeter;
|
||||
}
|
||||
|
||||
SomeClass.prototype.doSomething = function(name) {
|
||||
@@ -41,18 +41,18 @@ dependency from the component. The dependency is simply handed to the component.
|
||||
}
|
||||
</pre>
|
||||
|
||||
In the above example the `SomeClass` is not concerned with locating the `greeter` dependency, it
|
||||
In the above example `SomeClass` is not concerned with locating the `greeter` dependency, it
|
||||
is simply handed the `greeter` at runtime.
|
||||
|
||||
This is desirable, but it puts the responsibility of getting hold of the dependency onto the
|
||||
code responsible for the construction of `SomeClass`.
|
||||
This is desirable, but it puts the responsibility of getting hold of the dependency on the
|
||||
code that constructs `SomeClass`.
|
||||
|
||||
To manage the responsibility of dependency creation, each Angular application has an {@link
|
||||
api/angular.injector injector}. The injector is a service locator that is responsible for
|
||||
construction and lookup of dependencies.
|
||||
|
||||
Here is an example of using the injector service:
|
||||
|
||||
Here is an example of using the injector service.
|
||||
<pre>
|
||||
// Provide the wiring information in a module
|
||||
angular.module('myModule', []).
|
||||
@@ -101,7 +101,7 @@ dependency lookup responsibility to the injector by declaring the dependencies a
|
||||
</pre>
|
||||
|
||||
Notice that by having the `ng-controller` instantiate the class, it can satisfy all of the
|
||||
dependencies of the `MyController` without the controller ever knowing about the injector. This is
|
||||
dependencies of `MyController` without the controller ever knowing about the injector. This is
|
||||
the best outcome. The application code simply ask for the dependencies it needs, without having to
|
||||
deal with the injector. This setup does not break the Law of Demeter.
|
||||
|
||||
@@ -159,16 +159,18 @@ Sometimes using the `$inject` annotation style is not convenient such as when an
|
||||
directives.
|
||||
|
||||
For example:
|
||||
|
||||
<pre>
|
||||
someModule.factory('greeter', function($window) {
|
||||
...;
|
||||
...
|
||||
});
|
||||
</pre>
|
||||
|
||||
Results in code bloat due to the need of temporary variable:
|
||||
Results in code bloat due to needing a temporary variable:
|
||||
|
||||
<pre>
|
||||
var greeterFactory = function(renamed$window) {
|
||||
...;
|
||||
...
|
||||
};
|
||||
|
||||
greeterFactory.$inject = ['$window'];
|
||||
@@ -177,9 +179,10 @@ Results in code bloat due to the need of temporary variable:
|
||||
</pre>
|
||||
|
||||
For this reason the third annotation style is provided as well.
|
||||
|
||||
<pre>
|
||||
someModule.factory('greeter', ['$window', function(renamed$window) {
|
||||
...;
|
||||
...
|
||||
}]);
|
||||
</pre>
|
||||
|
||||
@@ -213,7 +216,7 @@ services, and filters. The factory methods are registered with the module, and t
|
||||
of declaring factories is:
|
||||
|
||||
<pre>
|
||||
angualar.module('myModule', []).
|
||||
angular.module('myModule', []).
|
||||
config(['depProvider', function(depProvider){
|
||||
...
|
||||
}]).
|
||||
|
||||
@@ -39,11 +39,11 @@ the following example.
|
||||
</script>
|
||||
<div ng-controller="Ctrl1">
|
||||
Hello <input ng-model='name'> <hr/>
|
||||
<span ng:bind="name"> <span ng:bind="name"></span> <br/>
|
||||
<span ng_bind="name"> <span ng_bind="name"></span> <br/>
|
||||
<span ng-bind="name"> <span ng-bind="name"></span> <br/>
|
||||
<span data-ng-bind="name"> <span data-ng-bind="name"></span> <br/>
|
||||
<span x-ng-bind="name"> <span x-ng-bind="name"></span> <br/>
|
||||
<span ng:bind="name"> <span ng:bind="name"></span> <br/>
|
||||
<span ng_bind="name"> <span ng_bind="name"></span> <br/>
|
||||
<span ng-bind="name"> <span ng-bind="name"></span> <br/>
|
||||
<span data-ng-bind="name"> <span data-ng-bind="name"></span> <br/>
|
||||
<span x-ng-bind="name"> <span x-ng-bind="name"></span> <br/>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
@@ -95,7 +95,7 @@ Compilation of HTML happens in three phases:
|
||||
var $compile = ...; // injected into your code
|
||||
var scope = ...;
|
||||
|
||||
var html = '<div ng-bind='exp'></div>';
|
||||
var html = '<div ng-bind="exp"></div>';
|
||||
|
||||
// Step 1: parse HTML into DOM element
|
||||
var template = angular.element(html);
|
||||
@@ -132,7 +132,7 @@ able to quickly stamp out new `li`s for every `action` in `user.actions`. This m
|
||||
to save a clean copy of the `li` element for cloning purposes and as new `action`s are inserted,
|
||||
the template `li` element needs to be cloned and inserted into `ul`. But cloning the `li` element
|
||||
is not enough. It also needs to compile the `li` so that its directives such as
|
||||
`{{action.descriptions}}` evaluate against the right {@link api/ng.$rootScope.Scope
|
||||
`{{action.description}}` evaluate against the right {@link api/ng.$rootScope.Scope
|
||||
scope}. A naive method would be to simply insert a copy of the `li` element and then compile it.
|
||||
But compiling on every `li` element clone would be slow, since the compilation requires that we
|
||||
traverse the DOM tree and look for directives and execute them. If we put the compilation inside a
|
||||
@@ -206,7 +206,7 @@ In this example we will build a directive that displays the current time.
|
||||
}
|
||||
|
||||
// listen on DOM destroy (removal) event, and cancel the next UI update
|
||||
// to prevent updating time ofter the DOM element was removed.
|
||||
// to prevent updating time after the DOM element was removed.
|
||||
element.bind('$destroy', function() {
|
||||
$timeout.cancel(timeoutId);
|
||||
});
|
||||
@@ -225,7 +225,12 @@ In this example we will build a directive that displays the current time.
|
||||
|
||||
# Writing directives (long version)
|
||||
|
||||
An example skeleton of the directive is shown here, for the complete list see below.
|
||||
There are different ways to declare a directive. The difference resides in the return
|
||||
value of the factory function. You can either return a Directive Definition Object
|
||||
(see below) that defines the directive properties, or just the postLink function
|
||||
of such an object (all other properties will have the default values).
|
||||
|
||||
Here's an example directive declared with a Directive Definition Object:
|
||||
|
||||
<pre>
|
||||
var myModule = angular.module(...);
|
||||
@@ -239,6 +244,7 @@ An example skeleton of the directive is shown here, for the complete list see be
|
||||
transclude: false,
|
||||
restrict: 'A',
|
||||
scope: false,
|
||||
controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
|
||||
compile: function compile(tElement, tAttrs, transclude) {
|
||||
return {
|
||||
pre: function preLink(scope, iElement, iAttrs, controller) { ... },
|
||||
@@ -251,12 +257,11 @@ An example skeleton of the directive is shown here, for the complete list see be
|
||||
});
|
||||
</pre>
|
||||
|
||||
In most cases you will not need such fine control and so the above can be simplified. All of the
|
||||
different parts of this skeleton are explained in following sections. In this section we are
|
||||
interested only in some of this skeleton.
|
||||
In most cases you will not need such fine control and so the above can be simplified. You can still
|
||||
return a Directive Definition Object, but only setting the 'compile' function property of the Object,
|
||||
and rely on the default values for other properties.
|
||||
|
||||
The first step in simplyfing the code is to rely on the default values. Therefore the above can be
|
||||
simplified as:
|
||||
Therefore the above can be simplified as:
|
||||
|
||||
<pre>
|
||||
var myModule = angular.module(...);
|
||||
@@ -271,8 +276,10 @@ simplified as:
|
||||
});
|
||||
</pre>
|
||||
|
||||
Most directives concern themselves only with instances, not with template transformations, allowing
|
||||
further simplification:
|
||||
Finally, most directives concern themselves only with instances, not with template transformations, allowing
|
||||
further simplification.
|
||||
|
||||
Here we only define the postLink function:
|
||||
|
||||
<pre>
|
||||
var myModule = angular.module(...);
|
||||
@@ -355,10 +362,16 @@ compiler}. The attributes are:
|
||||
|
||||
* `$scope` - Current scope associated with the element
|
||||
* `$element` - Current element
|
||||
* `$attrs` - Current attributes obeject for the element
|
||||
* `$attrs` - Current attributes object for the element
|
||||
* `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
|
||||
`function(cloneLinkingFn)`.
|
||||
|
||||
To avoid errors after minification the bracket notation should be used:
|
||||
|
||||
<pre>
|
||||
controller: ['$scope', '$element', '$attrs', '$transclude', function($scope, $element, $attrs, $transclude) { ... }]
|
||||
</pre>
|
||||
|
||||
* `require` - Require another controller be passed into current directive linking function. The
|
||||
`require` takes a name of the directive controller to pass in. If no such controller can be
|
||||
found an error is raised. The name can be prefixed with:
|
||||
@@ -376,8 +389,8 @@ compiler}. The attributes are:
|
||||
* `M` - Comment: `<!-- directive: my-directive exp -->`
|
||||
|
||||
* `template` - replace the current element with the contents of the HTML. The replacement process
|
||||
migrates all of the attributes / classes from the old element to the new one. See Creating
|
||||
Widgets section below for more information.
|
||||
migrates all of the attributes / classes from the old element to the new one. See the
|
||||
{@link guide/directive#Components Creating Components} section below for more information.
|
||||
|
||||
* `templateUrl` - Same as `template` but the template is loaded from the specified URL. Because
|
||||
the template loading is asynchronous the compilation/linking is suspended until the template
|
||||
@@ -432,8 +445,8 @@ done in a linking function rather than in a compile function.
|
||||
|
||||
A compile function can have a return value which can be either a function or an object.
|
||||
|
||||
* returning a function - is equivalent to registering the linking function via the `link` property
|
||||
of the config object when the compile function is empty.
|
||||
* returning a (post-link) function - is equivalent to registering the linking function via the
|
||||
`link` property of the config object when the compile function is empty.
|
||||
|
||||
* returning an object with function(s) registered via `pre` and `post` properties - allows you to
|
||||
control when a linking function should be called during the linking phase. See info about
|
||||
@@ -601,6 +614,7 @@ restrict: 'E',
|
||||
replace: true
|
||||
</pre>
|
||||
|
||||
<a name="Components"></a>
|
||||
# Creating Components
|
||||
|
||||
It is often desirable to replace a single directive with a more complex DOM structure. This
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
@description
|
||||
|
||||
Expressions are JavaScript-like code snippets that are usually placed in bindings such as `{{
|
||||
expression }}`. Expressions are processed by {@link api/ng.$parse $parse}
|
||||
expression }}`. Expressions are processed by the {@link api/ng.$parse $parse}
|
||||
service.
|
||||
|
||||
For example, these are all valid expressions in angular:
|
||||
@@ -179,7 +179,7 @@ angular uses, to differentiate its API names from others. If angular didn't use
|
||||
`a.length()` would return undefined because neither a nor angular define such a property.
|
||||
|
||||
Consider that in a future version of Angular we might choose to add a length method, in which case
|
||||
the behavior of the expression would change. Worse yet, you the developer could create a length
|
||||
the behavior of the expression would change. Worse yet, you, the developer, could create a length
|
||||
property and then we would have a collision. This problem exists because Angular augments existing
|
||||
objects with additional behavior. By prefixing its additions with $ we are reserving our namespace
|
||||
so that angular developers and developers who use Angular can develop in harmony without collisions.
|
||||
|
||||
@@ -93,10 +93,10 @@ This ensures that the user is not distracted with an error until after interacti
|
||||
|
||||
<script>
|
||||
function Controller($scope) {
|
||||
$scope.master= {};
|
||||
$scope.master = {};
|
||||
|
||||
$scope.update = function(user) {
|
||||
$scope.master= angular.copy(user);
|
||||
$scope.master = angular.copy(user);
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
@@ -113,7 +113,7 @@ This ensures that the user is not distracted with an error until after interacti
|
||||
|
||||
# Binding to form and control state
|
||||
|
||||
A form is in instance of {@link api/ng.directive:form.FormController FormController}.
|
||||
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.
|
||||
@@ -278,7 +278,7 @@ However, if you need more flexibility, you can write your own form control as a
|
||||
|
||||
In order for custom control to work with `ngModel` and to achieve two-way data-binding it needs to:
|
||||
|
||||
- implement `render` method, which is responsible for rendering the data after it passed the {@link api/ng.directive:ngModel.NgModelController#$formatters NgModelController#$formatters},
|
||||
- implement `$render` method, which is responsible for rendering the data after it passed the {@link api/ng.directive:ngModel.NgModelController#$formatters NgModelController#$formatters},
|
||||
- call `$setViewValue` method, whenever the user interacts with the control and model needs to be updated. This is usually done inside a DOM Event listener.
|
||||
|
||||
See {@link guide/directive $compileProvider.directive} for more info.
|
||||
|
||||
@@ -53,7 +53,7 @@ There are two approaches to providing locale rules to Angular:
|
||||
You can pre-bundle the desired locale file with Angular by concatenating the content of the
|
||||
locale-specific file to the end of `angular.js` or `angular.min.js` file.
|
||||
|
||||
For example on *nix, to create a an angular.js file that contains localization rules for german
|
||||
For example on *nix, to create an angular.js file that contains localization rules for german
|
||||
locale, you can do the following:
|
||||
|
||||
`cat angular.js i18n/angular-locale_de-ge.js > angular_de-ge.js`
|
||||
|
||||
+45
-24
@@ -15,33 +15,54 @@ To make your Angular application work on IE please make sure that:
|
||||
1. You polyfill JSON.stringify if necessary (IE7 will need this). You can use
|
||||
[JSON2](https://github.com/douglascrockford/JSON-js) or
|
||||
[JSON3](http://bestiejs.github.com/json3/) polyfills for this.
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<head>
|
||||
<!--[if lte IE 8]>
|
||||
<script src="/path/to/json2.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
...
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
2. you **do not** use custom element tags such as `<ng:view>` (use the attribute version
|
||||
2. add `id="ng-app"` to the root element in conjunction with `ng-app` attribute
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="optionalModuleName">
|
||||
...
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
3. you **do not** use custom element tags such as `<ng:view>` (use the attribute version
|
||||
`<div ng-view>` instead), or
|
||||
|
||||
3. if you **do use** custom element tags, then you must take these steps to make IE happy:
|
||||
|
||||
<pre>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<head>
|
||||
<!--[if lte IE 8]>
|
||||
<script>
|
||||
document.createElement('ng-include');
|
||||
document.createElement('ng-pluralize');
|
||||
document.createElement('ng-view');
|
||||
|
||||
// Optionally these for CSS
|
||||
document.createElement('ng:include');
|
||||
document.createElement('ng:pluralize');
|
||||
document.createElement('ng:view');
|
||||
</script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
...
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
4. if you **do use** custom element tags, then you must take these steps to make IE happy:
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="optionalModuleName">
|
||||
<head>
|
||||
<!--[if lte IE 8]>
|
||||
<script>
|
||||
document.createElement('ng-include');
|
||||
document.createElement('ng-pluralize');
|
||||
document.createElement('ng-view');
|
||||
|
||||
// Optionally these for CSS
|
||||
document.createElement('ng:include');
|
||||
document.createElement('ng:pluralize');
|
||||
document.createElement('ng:view');
|
||||
</script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
...
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
The **important** parts are:
|
||||
|
||||
|
||||
@@ -158,9 +158,9 @@ angular.module('myModule', []).
|
||||
|
||||
angular.module('myModule', []).
|
||||
config(function($provide, $compileProvider, $filterProvider) {
|
||||
$provide.value('a', 123)
|
||||
$provide.factory('a', function() { return 123; })
|
||||
$compileProvider.directive('directiveName', ...).
|
||||
$provide.value('a', 123);
|
||||
$provide.factory('a', function() { return 123; });
|
||||
$compileProvider.directive('directiveName', ...);
|
||||
$filterProvider.register('filterName', ...);
|
||||
});
|
||||
</pre>
|
||||
@@ -180,7 +180,7 @@ ignored in the unit-tests.
|
||||
|
||||
Modules can list other modules as their dependencies. Depending on a module implies that required
|
||||
module needs to be loaded before the requiring module is loaded. In other words the configuration
|
||||
blocks of the required modules execute before the configuration blocks or the requiring module.
|
||||
blocks of the required modules execute before the configuration blocks of the requiring module.
|
||||
The same is true for the run blocks. Each module can only be loaded once, even if multiple other
|
||||
modules require it.
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ concepts which the application developer may face:
|
||||
<table>
|
||||
<tr><td>Quantity</td><td>Cost</td></tr>
|
||||
<tr>
|
||||
<td><input type="integer" min="0" ng-model="qty" required ></td>
|
||||
<td><input type="number" ng-pattern="/\d+/" step="1" min="0" ng-model="qty" required ></td>
|
||||
<td><input type="number" ng-model="cost" required ></td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -124,7 +124,7 @@ We load Angular using the `<script>` tag:
|
||||
From the `ng-model` attribute of the `<input>` tags, Angular automatically sets up two-way data
|
||||
binding, and we also demonstrate some easy input validation:
|
||||
|
||||
Quantity: <input type="integer" min="0" ng-model="qty" required >
|
||||
Quantity: <input type="number" ng-pattern="/\d+/" step="1" min="0" ng-model="qty" required >
|
||||
Cost: <input type="number" ng-model="cost" required >
|
||||
|
||||
These input widgets look normal enough, but consider these points:
|
||||
@@ -202,6 +202,6 @@ Angular frees you from the following pain:
|
||||
|
||||
# Watch a Presentation About Angular
|
||||
|
||||
Here is a presentation on Angular from May 2012.
|
||||
Here is a presentation on Angular from May 2012. The {@link http://mhevery.github.io/angular-demo-slides/index.html#/list corresponding slides} are also available.
|
||||
|
||||
<iframe width="560" height="315" src="http://www.youtube.com/embed/bfrn5VNpwsg" frameborder="0" allowfullscreen></iframe>
|
||||
|
||||
@@ -165,7 +165,7 @@ Scopes are attached to the DOM as `$scope` data property, and can be retrieved f
|
||||
purposes. (It is unlikely that one would need to retrieve scopes in this way inside the
|
||||
application.) The location where the root scope is attached to the DOM is defined by the location
|
||||
of {@link api/ng.directive:ngApp `ng-app`} directive. Typically
|
||||
`ng-app` is placed an the `<html>` element, but it can be placed on other elements as well, if,
|
||||
`ng-app` is placed on the `<html>` element, but it can be placed on other elements as well, if,
|
||||
for example, only a portion of the view needs to be controlled by Angular.
|
||||
|
||||
To examine the scope in the debugger:
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Type
|
||||
@description
|
||||
@@ -81,23 +81,27 @@ Several steps are needed to check out and build AngularJS:
|
||||
Before you can build AngularJS, you must install or configure the following dependencies on your
|
||||
machine:
|
||||
|
||||
* {@link http://rake.rubyforge.org Rake}: We use Rake as our build system, which is pre-installed
|
||||
on most Macintosh and Linux machines. If that is not true in your case, you can grab it from the
|
||||
Rake website.
|
||||
|
||||
* Git: The {@link http://help.github.com/mac-git-installation Github Guide to Installing Git} is
|
||||
quite a good source for information on Git.
|
||||
|
||||
* {@link http://nodejs.org Node.js}: We use Node to generate the documentation and to run a
|
||||
development web server. Depending on your system, you can install Node either from source or as a
|
||||
* {@link http://nodejs.org Node.js}: We use Node to generate the documentation, run a
|
||||
development web server, run tests, and generate a build. Depending on your system, you can install Node either from source or as a
|
||||
pre-packaged bundle.
|
||||
|
||||
* {@link http://www.java.com Java}: JavaScript is minified using
|
||||
{@link https://developers.google.com/closure/ Closure Tools} jar. Make sure you have Java (version 6 or higher) installed
|
||||
and included in your {@link http://docs.oracle.com/javase/tutorial/essential/environment/paths.html PATH} variable.
|
||||
|
||||
Once installed, you'll also need several npms (node packages), which you can install once you checked out a local copy
|
||||
of the Angular repository (see below) with:
|
||||
|
||||
* `cd angular.js`
|
||||
* `npm install`
|
||||
|
||||
* {@link http://gruntjs.com Grunt}: We use Grunt as our build system. Install the grunt command-line tool globally with:
|
||||
|
||||
* `sudo npm install -g grunt-cli`
|
||||
|
||||
|
||||
## Creating a Github Account and Forking Angular
|
||||
|
||||
@@ -108,7 +112,7 @@ https://github.com/angular/angular.js main angular repository}.
|
||||
|
||||
## Building AngularJS
|
||||
|
||||
To build AngularJS, you check out the source code and use Rake to generate the non-minified and
|
||||
To build AngularJS, you check out the source code and use Grunt to generate the non-minified and
|
||||
minified AngularJS files:
|
||||
|
||||
1. To clone your Github repository, run:
|
||||
@@ -129,7 +133,11 @@ minified AngularJS files:
|
||||
|
||||
5. To build AngularJS, run:
|
||||
|
||||
rake package
|
||||
grunt package
|
||||
|
||||
NOTE: If you're using Windows you must run your command line with administrative privileges (right click, run as
|
||||
Administrator).
|
||||
|
||||
|
||||
The build output can be located under the `build` directory. It consists of the following files and
|
||||
directories:
|
||||
@@ -158,7 +166,7 @@ made available a local web server based on Node.js.
|
||||
|
||||
1. To start the web server, run:
|
||||
|
||||
rake webserver
|
||||
grunt webserver
|
||||
|
||||
2. To access the local server, go to this website:
|
||||
|
||||
@@ -170,33 +178,35 @@ made available a local web server based on Node.js.
|
||||
<a name="unit-tests"></a>
|
||||
## Running the Unit Test Suite
|
||||
|
||||
Our unit and integration tests are written with Jasmine and executed with Testacular. To run all of the
|
||||
Our unit and integration tests are written with Jasmine and executed with Karma. To run all of the
|
||||
tests once on Chrome run:
|
||||
|
||||
rake test:unit
|
||||
grunt test:unit
|
||||
|
||||
To run the tests on other browsers (Chrome, ChromeCanary, Firefox, Opera and Safari are pre-configured) use:
|
||||
|
||||
rake test:unit[Opera+Firefox]
|
||||
grunt test:unit --browsers Opera,Firefox
|
||||
|
||||
Note there should be _no spaces between browsers_. `Opera, Firefox` is INVALID.
|
||||
|
||||
During development it's however more productive to continuously run unit tests every time the source or test files
|
||||
change. To execute tests in this mode run:
|
||||
|
||||
1. To start the Testacular server, capture Chrome browser and run unit tests, run:
|
||||
1. To start the Karma server, capture Chrome browser and run unit tests, run:
|
||||
|
||||
rake autotest:jqlite
|
||||
grunt autotest:jqlite
|
||||
|
||||
2. To capture more browsers, open this url in the desired browser (url might be different if you have multiple instance
|
||||
of Testacular running, read Testacular's console output for the correct url):
|
||||
of Karma running, read Karma's console output for the correct url):
|
||||
|
||||
http://localhost:9876/
|
||||
|
||||
3. To re-run tests just change any source or test file.
|
||||
|
||||
|
||||
To learn more about all of the preconfigured Rake tasks run:
|
||||
To learn more about all of the preconfigured Grunt tasks run:
|
||||
|
||||
rake -T
|
||||
grunt --help
|
||||
|
||||
|
||||
## Running the end-to-end Test Suite
|
||||
@@ -205,7 +215,7 @@ To run the E2E test suite:
|
||||
|
||||
1. Start the local web server if it's not running already.
|
||||
|
||||
rake webserver
|
||||
grunt webserver
|
||||
|
||||
2. In a browser, go to:
|
||||
|
||||
@@ -213,7 +223,13 @@ To run the E2E test suite:
|
||||
|
||||
or in terminal run:
|
||||
|
||||
rake test:e2e
|
||||
grunt test:end2end
|
||||
|
||||
For convenience you can also simply run:
|
||||
|
||||
grunt test:e2e
|
||||
|
||||
This will start the webserver for you and run the tests.
|
||||
|
||||
|
||||
|
||||
@@ -222,7 +238,8 @@ To run the E2E test suite:
|
||||
|
||||
To create and submit a change:
|
||||
|
||||
1. Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code changes to be
|
||||
1. <a name="CLA"></a>
|
||||
Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code changes to be
|
||||
accepted, the CLA must be signed. It's a quick process, we promise!
|
||||
|
||||
For individuals we have a [simple click-through form](http://code.google.com/legal/individual-cla-v1.0.html). For
|
||||
|
||||
@@ -51,7 +51,8 @@ Yes. See instructions in {@link downloading}.
|
||||
### What browsers does Angular work with?
|
||||
|
||||
We run our extensive test suite against the following browsers: Safari, Chrome, Firefox, Opera,
|
||||
IE8, IE9 and mobile browsers (Android, Chrome Mobile, iOS Safari).
|
||||
IE8, IE9 and mobile browsers (Android, Chrome Mobile, iOS Safari). See {@link guide/ie Internet
|
||||
Explorer Compatibility} for more details in supporting legacy IE browsers.
|
||||
|
||||
|
||||
### What's Angular's performance like?
|
||||
|
||||
@@ -2,41 +2,42 @@
|
||||
@name Tutorial
|
||||
@description
|
||||
|
||||
A great way to get introduced to AngularJS is to work through this tutorial, which walks you through
|
||||
the construction of an AngularJS web app. The app you will build is a catalog that displays a list
|
||||
of Android devices, lets you filter the list to see only devices that interest you, and then view
|
||||
details for any device.
|
||||
<div class="tutorial-page tutorial-page-no-nav">
|
||||
A great way to get introduced to AngularJS is to work through this tutorial, which walks you through
|
||||
the construction of an AngularJS web app. The app you will build is a catalog that displays a list
|
||||
of Android devices, lets you filter the list to see only devices that interest you, and then view
|
||||
details for any device.
|
||||
|
||||
<img class="diagram" src="img/tutorial/catalog_screen.png" width="488" height="413">
|
||||
<img class="diagram" src="img/tutorial/catalog_screen.png" width="488" height="413">
|
||||
|
||||
Work through the tutorial to see how Angular makes browsers smarter — without the use of extensions
|
||||
or plug-ins. As you work through the tutorial, you will:
|
||||
Work through the tutorial to see how Angular makes browsers smarter — without the use of extensions
|
||||
or plug-ins. As you work through the tutorial, you will:
|
||||
|
||||
* See examples of how to use client-side data binding and dependency injection to build dynamic
|
||||
views of data that change immediately in response to user actions.
|
||||
* See how Angular creates listeners on your data without the need for DOM manipulation.
|
||||
* Learn a better, easier way to test your web apps.
|
||||
* Learn how to use Angular services to make common web tasks, such as getting data into your app,
|
||||
easier.
|
||||
* See examples of how to use client-side data binding and dependency injection to build dynamic
|
||||
views of data that change immediately in response to user actions.
|
||||
* See how Angular creates listeners on your data without the need for DOM manipulation.
|
||||
* Learn a better, easier way to test your web apps.
|
||||
* Learn how to use Angular services to make common web tasks, such as getting data into your app,
|
||||
easier.
|
||||
|
||||
And all of this works in any browser without modification to the browser!
|
||||
And all of this works in any browser without modification to the browser!
|
||||
|
||||
When you finish the tutorial you will be able to:
|
||||
When you finish the tutorial you will be able to:
|
||||
|
||||
* Create a dynamic application that works in any browser.
|
||||
* Define the differences between Angular and common JavaScript frameworks.
|
||||
* Understand how data binding works in AngularJS.
|
||||
* Use the angular-seed project to quickly boot-strap your own projects.
|
||||
* Create and run tests.
|
||||
* Identify resources for learning more about AngularJS.
|
||||
* Create a dynamic application that works in any browser.
|
||||
* Define the differences between Angular and common JavaScript frameworks.
|
||||
* Understand how data binding works in AngularJS.
|
||||
* Use the angular-seed project to quickly boot-strap your own projects.
|
||||
* Create and run tests.
|
||||
* Identify resources for learning more about AngularJS.
|
||||
|
||||
The tutorial guides you through the entire process of building a simple application, including
|
||||
writing and running unit and end-to-end tests. Experiments at the end of each step provide
|
||||
suggestions for you to learn more about AngularJS and the application you are building.
|
||||
The tutorial guides you through the entire process of building a simple application, including
|
||||
writing and running unit and end-to-end tests. Experiments at the end of each step provide
|
||||
suggestions for you to learn more about AngularJS and the application you are building.
|
||||
|
||||
You can go through the whole tutorial in a couple of hours or you may want to spend a pleasant day
|
||||
really digging into it. If you're looking for a shorter introduction to AngularJS, check out the
|
||||
{@link misc/started Getting Started} document.
|
||||
You can go through the whole tutorial in a couple of hours or you may want to spend a pleasant day
|
||||
really digging into it. If you're looking for a shorter introduction to AngularJS, check out the
|
||||
{@link misc/started Getting Started} document.
|
||||
|
||||
|
||||
|
||||
@@ -44,72 +45,73 @@ really digging into it. If you're looking for a shorter introduction to AngularJ
|
||||
|
||||
|
||||
|
||||
# Working with the code
|
||||
# Working with the code
|
||||
|
||||
You can follow this tutorial and hack on the code in either the Mac/Linux or the Windows
|
||||
environment. The tutorial relies on the use of Git versioning system for source code management.
|
||||
You don't need to know anything about Git to follow the tutorial. Select one of the tabs below
|
||||
and follow the instructions for setting up your computer.
|
||||
You can follow this tutorial and hack on the code in either the Mac/Linux or the Windows
|
||||
environment. The tutorial relies on the use of Git versioning system for source code management.
|
||||
You don't need to know anything about Git to follow the tutorial. Select one of the tabs below
|
||||
and follow the instructions for setting up your computer.
|
||||
|
||||
<div class="tabbable" show="true">
|
||||
<div class="tab-pane well" id="git-mac" title="Git on Mac/Linux">
|
||||
<ol>
|
||||
<li><p>You will need Node.js and Testacular to run unit tests, so please verify that you have
|
||||
<a href="http://nodejs.org/">Node.js</a> v0.8 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>
|
||||
<p>Additionally install <a href="http://vojtajina.github.com/testacular">Testacular</a> if you
|
||||
don't have it already:</p>
|
||||
<pre>npm install -g testacular</pre>
|
||||
<li><p>You'll also need Git, which you can get from
|
||||
<a href="http://git-scm.com/download">the Git site</a>.</p></li>
|
||||
<li><p>Clone the angular-phonecat repository located at <a
|
||||
href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
|
||||
<pre>git clone git://github.com/angular/angular-phonecat.git</pre>
|
||||
<p>This command creates the <code>angular-phonecat</code> directory in your current
|
||||
directory.</p></li>
|
||||
<li><p>Change your current directory to <code>angular-phonecat</code>:</p>
|
||||
<pre>cd angular-phonecat</pre>
|
||||
<p>The tutorial instructions assume you are running all commands from the angular-phonecat
|
||||
directory.</p></li>
|
||||
<li><p>You will need an http server running on your system. Mac and Linux machines typically
|
||||
have Apache pre-installed, but If you don't already have one installed, you can use <code>node</code>
|
||||
to run <code>scripts/web-server.js</code>, a simple bundled http server.</p></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane well" id="git-win" title="Git on Windows">
|
||||
<ol>
|
||||
<li><p>You will need Node.js and Testacular to run unit tests, so please verify that you have
|
||||
<div class="tabbable" show="true">
|
||||
<div class="tab-pane well" id="git-mac" title="Git on Mac/Linux">
|
||||
<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
|
||||
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>
|
||||
<p>Additionally install <a href="http://vojtajina.github.com/testacular">Testacular</a> if you
|
||||
don't have it already:</p>
|
||||
<pre>npm install -g testacular</pre>
|
||||
</li>
|
||||
<li><p>You'll also need Git, which you can get from
|
||||
<a href="http://git-scm.com/download">the Git site</a>.</p></li>
|
||||
<li><p>Clone the angular-phonecat repository located at <a
|
||||
href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
|
||||
<pre>git clone git://github.com/angular/angular-phonecat.git</pre>
|
||||
<p>This command creates the angular-phonecat directory in your current directory.</p></li>
|
||||
<li><p>Change your current directory to angular-phonecat.</p>
|
||||
<pre>cd angular-phonecat</pre>
|
||||
<p>The tutorial instructions assume you are running all commands from the angular-phonecat
|
||||
directory.</p>
|
||||
<p>You should run all <code>git</code> commands from Git bash.</p>
|
||||
<p>Other commands like <code>test.bat</code> or <code>e2e-test.bat</code> should be
|
||||
executed from the Windows command line.</li>
|
||||
<li><p>You need an http server running on your system, but if you don't already have one
|
||||
already installed, you can use <code>node</code> to run <code>scripts\web-server.js</code>, a simple
|
||||
bundled http server.</p></li>
|
||||
</ol>
|
||||
</div>
|
||||
<p>Additionally install <a href="http://karma-runner.github.io/">Karma</a> if you
|
||||
don't have it already:</p>
|
||||
<pre>npm install -g karma</pre>
|
||||
<li><p>You'll also need Git, which you can get from
|
||||
<a href="http://git-scm.com/download">the Git site</a>.</p></li>
|
||||
<li><p>Clone the angular-phonecat repository located at <a
|
||||
href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
|
||||
<pre>git clone git://github.com/angular/angular-phonecat.git</pre>
|
||||
<p>This command creates the <code>angular-phonecat</code> directory in your current
|
||||
directory.</p></li>
|
||||
<li><p>Change your current directory to <code>angular-phonecat</code>:</p>
|
||||
<pre>cd angular-phonecat</pre>
|
||||
<p>The tutorial instructions assume you are running all commands from the angular-phonecat
|
||||
directory.</p></li>
|
||||
<li><p>You will need an http server running on your system. Mac and Linux machines typically
|
||||
have Apache pre-installed, but If you don't already have one installed, you can use <code>node</code>
|
||||
to run <code>scripts/web-server.js</code>, a simple bundled http server.</p></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
The last thing to do is to make sure your computer has a web browser and a good text editor
|
||||
installed. Now, let's get some cool stuff done!
|
||||
<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
|
||||
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>
|
||||
<p>Additionally install <a href="http://karma-runner.github.io/">Karma</a> if you
|
||||
don't have it already:</p>
|
||||
<pre>npm install -g karma</pre>
|
||||
</li>
|
||||
<li><p>You'll also need Git, which you can get from
|
||||
<a href="http://git-scm.com/download">the Git site</a>.</p></li>
|
||||
<li><p>Clone the angular-phonecat repository located at <a
|
||||
href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
|
||||
<pre>git clone git://github.com/angular/angular-phonecat.git</pre>
|
||||
<p>This command creates the angular-phonecat directory in your current directory.</p></li>
|
||||
<li><p>Change your current directory to angular-phonecat.</p>
|
||||
<pre>cd angular-phonecat</pre>
|
||||
<p>The tutorial instructions assume you are running all commands from the angular-phonecat
|
||||
directory.</p>
|
||||
<p>You should run all <code>git</code> commands from Git bash.</p>
|
||||
<p>Other commands like <code>test.bat</code> or <code>e2e-test.bat</code> should be
|
||||
executed from the Windows command line.</li>
|
||||
<li><p>You need an http server running on your system, but if you don't already have one
|
||||
already installed, you can use <code>node</code> to run <code>scripts\web-server.js</code>, a simple
|
||||
bundled http server.</p></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
{@link step_00 <span class="btn btn-primary">Get Started!</span>}
|
||||
The last thing to do is to make sure your computer has a web browser and a good text editor
|
||||
installed. Now, let's get some cool stuff done!
|
||||
|
||||
{@link step_00 <span class="btn btn-primary">Get Started!</span>}
|
||||
</div>
|
||||
|
||||
@@ -109,7 +109,7 @@ __`app/index.html`:__
|
||||
|
||||
<html ng-app>
|
||||
|
||||
The `ng-app` attribute is represents an Angular directive (named `ngApp`; Angular uses
|
||||
The `ng-app` attribute represents an Angular directive (named `ngApp`; Angular uses
|
||||
`name-with-dashes` for attribute names and `camelCase` for the corresponding directive name)
|
||||
used to flag an element which Angular should consider to be the root element of our application.
|
||||
This gives application developers the freedom to tell Angular if the entire html page or only a
|
||||
@@ -127,7 +127,7 @@ being the element on which the `ngApp` directive was defined.
|
||||
|
||||
* Double-curly binding with an expression:
|
||||
|
||||
Nothing here {{'yet' + '!'}}`
|
||||
Nothing here {{'yet' + '!'}}
|
||||
|
||||
This line demonstrates the core feature of Angular's templating capabilities – a binding, denoted
|
||||
by double-curlies `{{ }}` as well as a simple expression `'yet' + '!'` used in this binding.
|
||||
|
||||
@@ -146,24 +146,25 @@ http://pivotal.github.com/jasmine/ Jasmine home page} and on the {@link
|
||||
https://github.com/pivotal/jasmine/wiki Jasmine wiki}.
|
||||
|
||||
The angular-seed project is pre-configured to run all unit tests using {@link
|
||||
http://vojtajina.github.com/testacular/ Testacular}. To run the test, do the following:
|
||||
http://karma-runner.github.io/ Karma}. 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 Testacular server.
|
||||
`./scripts/test.sh` to start the Karma server (the config file necessary to start the server
|
||||
is located at `./config/karma.conf.js`).
|
||||
|
||||
2. Testacular will start a new instance of Chrome browser automatically. Just ignore it and let it run in
|
||||
the background. Testacular will use this browser for test execution.
|
||||
2. Karma will start a new instance of Chrome browser automatically. Just ignore it and let it run in
|
||||
the background. Karma will use this browser for test execution.
|
||||
|
||||
3. You should see the following or similar output in the terminal:
|
||||
|
||||
info: Testacular server started at http://localhost:9876/
|
||||
info: Karma server started at http://localhost:9876/
|
||||
info (launcher): Starting browser "Chrome"
|
||||
info (Chrome 22.0): Connected on socket id tPUm9DXcLHtZTKbAEO-n
|
||||
Chrome 22.0: Executed 1 of 1 SUCCESS (0.093 secs / 0.004 secs)
|
||||
|
||||
Yay! The test passed! Or not...
|
||||
|
||||
4. To rerun the tests, just change any of the source or test files. Testacular will notice the change
|
||||
4. To rerun the tests, just change any of the source or test files. Karma will notice the change
|
||||
and will rerun the tests for you. Now isn't that sweet?
|
||||
|
||||
# Experiments
|
||||
|
||||
@@ -122,9 +122,9 @@ To run the end-to-end test, open one of the following in a new browser tab:
|
||||
`http://localhost:[port-number]/[context-path]/test/e2e/runner.html`
|
||||
* casual reader: {@link http://angular.github.com/angular-phonecat/step-3/test/e2e/runner.html}
|
||||
|
||||
Previously we've seen how Testacular can be used to execute unit tests. Well, it can also run the
|
||||
Previously we've seen how Karma can be used to execute unit tests. Well, it can also run the
|
||||
end-to-end tests! Use `./scripts/e2e-test.sh` script for that. End-to-end tests are slow, so unlike
|
||||
with unit tests, Testacular will exit after the test run and will not automatically rerun the test
|
||||
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.
|
||||
|
||||
This test verifies that the search box and the repeater are correctly wired together. Notice how
|
||||
|
||||
@@ -134,7 +134,7 @@ The unit test now verifies that the default ordering property is set.
|
||||
We used Jasmine's API to extract the controller construction into a `beforeEach` block, which is
|
||||
shared by all tests in the parent `describe` block.
|
||||
|
||||
You should now see the following output in the Testacular tab:
|
||||
You should now see the following output in the Karma tab:
|
||||
|
||||
Chrome 22.0: Executed 2 of 2 SUCCESS (0.021 secs / 0.001 secs)
|
||||
|
||||
|
||||
@@ -138,6 +138,9 @@ describe('PhoneCat controllers', function() {
|
||||
describe('PhoneListCtrl', function(){
|
||||
var scope, ctrl, $httpBackend;
|
||||
|
||||
// The injector ignores leading and trailing underscores here (i.e. _$httpBackend_).
|
||||
// This allows us to inject a service but then attach it to a variable
|
||||
// with the same name as the service.
|
||||
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
|
||||
$httpBackend = _$httpBackend_;
|
||||
$httpBackend.expectGET('phones/phones.json').
|
||||
@@ -208,7 +211,7 @@ Finally, we verify that the default value of `orderProp` is set correctly:
|
||||
});
|
||||
</pre>
|
||||
|
||||
You should now see the following output in the Testacular tab:
|
||||
You should now see the following output in the Karma tab:
|
||||
|
||||
Chrome 22.0: Executed 2 of 2 SUCCESS (0.028 secs / 0.007 secs)
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ __`test/unit/controllersSpec.js`:__
|
||||
...
|
||||
</pre>
|
||||
|
||||
You should now see the following output in the Testacular tab:
|
||||
You should now see the following output in the Karma tab:
|
||||
|
||||
Chrome 22.0: Executed 3 of 3 SUCCESS (0.039 secs / 0.012 secs)
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ describe('filter', function() {
|
||||
Note that you need to configure our test injector with the `phonecatFilters` module before any of
|
||||
our filter tests execute.
|
||||
|
||||
You should now see the following output in the Testacular tab:
|
||||
You should now see the following output in the Karma tab:
|
||||
|
||||
Chrome 22.0: Executed 4 of 4 SUCCESS (0.034 secs / 0.012 secs)
|
||||
|
||||
|
||||
@@ -214,7 +214,7 @@ describe('PhoneCat controllers', function() {
|
||||
});
|
||||
</pre>
|
||||
|
||||
You should now see the following output in the Testacular tab:
|
||||
You should now see the following output in the Karma tab:
|
||||
|
||||
Chrome 22.0: Executed 4 of 4 SUCCESS (0.038 secs / 0.01 secs)
|
||||
|
||||
|
||||
@@ -2,20 +2,22 @@
|
||||
@name Tutorial: The End
|
||||
@description
|
||||
|
||||
Our application is now complete. Feel free to experiment with the code further, and jump back to
|
||||
previous steps using the `git checkout` command.
|
||||
<div class="tutorial-page tutorial-page-no-nav">
|
||||
Our application is now complete. Feel free to experiment with the code further, and jump back to
|
||||
previous steps using the `git checkout` command.
|
||||
|
||||
For more details and examples of the Angular concepts we touched on in this tutorial, see the
|
||||
{@link guide/ Developer Guide}.
|
||||
For more details and examples of the Angular concepts we touched on in this tutorial, see the
|
||||
{@link guide/ Developer Guide}.
|
||||
|
||||
For several more examples of code, see the {@link cookbook/ Cookbook}.
|
||||
For several more examples of code, see the {@link cookbook/ Cookbook}.
|
||||
|
||||
When you are ready to start developing a project using Angular, we recommend that you bootstrap
|
||||
your development with the {@link https://github.com/angular/angular-seed angular-seed} project.
|
||||
When you are ready to start developing a project using Angular, we recommend that you bootstrap
|
||||
your development with the {@link https://github.com/angular/angular-seed angular-seed} project.
|
||||
|
||||
We hope this tutorial was useful to you and that you learned enough about Angular to make you want
|
||||
to learn more. We especially hope you are inspired to go out and develop Angular web apps of your
|
||||
own, and that you might be interested in {@link misc/contribute contributing} to Angular.
|
||||
We hope this tutorial was useful to you and that you learned enough about Angular to make you want
|
||||
to learn more. We especially hope you are inspired to go out and develop Angular web apps of your
|
||||
own, and that you might be interested in {@link misc/contribute contributing} to Angular.
|
||||
|
||||
If you have questions or feedback or just want to say "hi", please post a message at {@link
|
||||
https://groups.google.com/forum/#!forum/angular}.
|
||||
If you have questions or feedback or just want to say "hi", please post a message at {@link
|
||||
https://groups.google.com/forum/#!forum/angular}.
|
||||
</div>
|
||||
|
||||
+23
-5
@@ -55,12 +55,15 @@ describe('ngdoc', function() {
|
||||
'@name a\n' +
|
||||
'@param {*} a short\n' +
|
||||
'@param {Type} b med\n' +
|
||||
'@param {Class=} [c=2] long\nline');
|
||||
'@param {Class=} [c=2] long\nline\n' +
|
||||
'@param {function(number, string=)} d fn with optional arguments');
|
||||
doc.parse();
|
||||
expect(doc.param).toEqual([
|
||||
{name:'a', description:'<p>short</p>', type:'*', optional:false, 'default':undefined},
|
||||
{name:'b', description:'<p>med</p>', type:'Type', optional:false, 'default':undefined},
|
||||
{name:'c', description:'<p>long\nline</p>', type:'Class', optional:true, 'default':'2'}
|
||||
{name:'c', description:'<p>long\nline</p>', type:'Class', optional:true, 'default':'2'},
|
||||
{name:'d', description:'<p>fn with optional arguments</p>',
|
||||
type: 'function(number, string=)', optional: false, 'default':undefined}
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -318,9 +321,9 @@ describe('ngdoc', function() {
|
||||
});
|
||||
|
||||
it('should not parse @property without a type', function() {
|
||||
var doc = new Doc("@property fake");
|
||||
var doc = new Doc("@property fake", 'test.js', '44');
|
||||
expect(function() { doc.parse(); }).
|
||||
toThrow(new Error("Not a valid 'property' format: fake"));
|
||||
toThrow(new Error("Not a valid 'property' format: fake (found in: test.js:44)"));
|
||||
});
|
||||
|
||||
it('should parse @property with type', function() {
|
||||
@@ -350,15 +353,30 @@ describe('ngdoc', function() {
|
||||
describe('@returns', function() {
|
||||
it('should not parse @returns without type', function() {
|
||||
var doc = new Doc("@returns lala");
|
||||
expect(doc.parse).toThrow();
|
||||
expect(function() { doc.parse(); }).
|
||||
toThrow();
|
||||
});
|
||||
|
||||
|
||||
it('should not parse @returns with invalid type', function() {
|
||||
var doc = new Doc("@returns {xx}x} lala", 'test.js', 34);
|
||||
expect(function() { doc.parse(); }).
|
||||
toThrow(new Error("Not a valid 'returns' format: {xx}x} lala (found in: test.js:34)"));
|
||||
});
|
||||
|
||||
|
||||
it('should parse @returns with type and description', function() {
|
||||
var doc = new Doc("@name a\n@returns {string} descrip tion");
|
||||
doc.parse();
|
||||
expect(doc.returns).toEqual({type: 'string', description: '<p>descrip tion</p>'});
|
||||
});
|
||||
|
||||
it('should parse @returns with complex type and description', function() {
|
||||
var doc = new Doc("@name a\n@returns {function(string, number=)} description");
|
||||
doc.parse();
|
||||
expect(doc.returns).toEqual({type: 'function(string, number=)', description: '<p>description</p>'});
|
||||
});
|
||||
|
||||
it('should transform description of @returns with markdown', function() {
|
||||
var doc = new Doc("@name a\n@returns {string} descrip *tion*");
|
||||
doc.parse();
|
||||
|
||||
@@ -5,10 +5,6 @@ var reader = require('./reader.js'),
|
||||
appCache = require('./appCache.js').appCache,
|
||||
Q = require('qq');
|
||||
|
||||
process.on('uncaughtException', function(err) {
|
||||
console.error(err.stack || err);
|
||||
});
|
||||
|
||||
var start = now();
|
||||
var docs;
|
||||
|
||||
@@ -42,10 +38,10 @@ writer.makeDir('build/docs/', true).then(function() {
|
||||
function writeTheRest(writesFuture) {
|
||||
var metadata = ngdoc.metadata(docs);
|
||||
|
||||
writesFuture.push(writer.symlinkTemplate('css'));
|
||||
writesFuture.push(writer.symlinkTemplate('font'));
|
||||
writesFuture.push(writer.symlink('../../docs/img', 'build/docs/img'));
|
||||
writesFuture.push(writer.symlinkTemplate('js'));
|
||||
writesFuture.push(writer.symlinkTemplate('css', 'dir'));
|
||||
writesFuture.push(writer.symlinkTemplate('font', 'dir'));
|
||||
writesFuture.push(writer.symlink('../../docs/img', 'build/docs/img', 'dir'));
|
||||
writesFuture.push(writer.symlinkTemplate('js', 'dir'));
|
||||
|
||||
var manifest = 'manifest="/build/docs/appcache.manifest"';
|
||||
|
||||
|
||||
+61
-45
@@ -2,7 +2,7 @@
|
||||
* All parsing/transformation code goes here. All code here should be sync to ease testing.
|
||||
*/
|
||||
|
||||
var Showdown = require('../../lib/showdown').Showdown;
|
||||
var Showdown = require('showdown');
|
||||
var DOM = require('./dom.js').DOM;
|
||||
var htmlEscape = require('./dom.js').htmlEscape;
|
||||
var Example = require('./example.js').Example;
|
||||
@@ -174,7 +174,7 @@ Doc.prototype = {
|
||||
});
|
||||
});
|
||||
text = parts.join('');
|
||||
text = new Showdown.converter().makeHtml(text);
|
||||
text = new Showdown.converter({ extensions : ['table'] }).makeHtml(text);
|
||||
text = text.replace(/(?:<p>)?(REPLACEME\d+)(?:<\/p>)?/g, function(_, id) {
|
||||
return placeholderMap[id];
|
||||
});
|
||||
@@ -203,7 +203,7 @@ Doc.prototype = {
|
||||
flush();
|
||||
this.shortName = this.name.split(/[\.:#]/).pop().trim();
|
||||
this.id = this.id || // if we have an id just use it
|
||||
(((this.file||'').match(/.*\/([^\/]*)\.ngdoc/)||{})[1]) || // try to extract it from file name
|
||||
(((this.file||'').match(/.*(\/|\\)([^(\/|\\)]*)\.ngdoc/)||{})[2]) || // try to extract it from file name
|
||||
this.name; // default to name
|
||||
this.description = this.markdown(this.description);
|
||||
this.example = this.markdown(this.example);
|
||||
@@ -214,23 +214,25 @@ Doc.prototype = {
|
||||
if (atName) {
|
||||
var text = trim(atText.join('\n')), match;
|
||||
if (atName == 'param') {
|
||||
match = text.match(/^\{([^}=]+)(=)?\}\s+(([^\s=]+)|\[(\S+)=([^\]]+)\])\s+(.*)/);
|
||||
// 1 12 2 34 4 5 5 6 6 3 7 7
|
||||
match = text.match(/^\{([^}]+)\}\s+(([^\s=]+)|\[(\S+)=([^\]]+)\])\s+(.*)/);
|
||||
// 1 1 23 3 4 4 5 5 2 6 6
|
||||
if (!match) {
|
||||
throw new Error("Not a valid 'param' format: " + text);
|
||||
throw new Error("Not a valid 'param' format: " + text + ' (found in: ' + self.file + ':' + self.line + ')');
|
||||
}
|
||||
|
||||
var optional = (match[1].slice(-1) === '=');
|
||||
var param = {
|
||||
name: match[5] || match[4],
|
||||
description:self.markdown(text.replace(match[0], match[7])),
|
||||
type: match[1],
|
||||
optional: !!match[2],
|
||||
'default':match[6]
|
||||
name: match[4] || match[3],
|
||||
description:self.markdown(text.replace(match[0], match[6])),
|
||||
type: optional ? match[1].substring(0, match[1].length-1) : match[1],
|
||||
optional: optional,
|
||||
'default':match[5]
|
||||
};
|
||||
self.param.push(param);
|
||||
} else if (atName == 'returns' || atName == 'return') {
|
||||
match = text.match(/^\{([^}=]+)\}\s+(.*)/);
|
||||
match = text.match(/^\{([^}]+)\}\s+(.*)/);
|
||||
if (!match) {
|
||||
throw new Error("Not a valid 'returns' format: " + text + ' in ' + self.file + ':' + self.line);
|
||||
throw new Error("Not a valid 'returns' format: " + text + ' (found in: ' + self.file + ':' + self.line + ')');
|
||||
}
|
||||
self.returns = {
|
||||
type: match[1],
|
||||
@@ -245,7 +247,7 @@ Doc.prototype = {
|
||||
} else if(atName == 'property') {
|
||||
match = text.match(/^\{(\S+)\}\s+(\S+)(\s+(.*))?/);
|
||||
if (!match) {
|
||||
throw new Error("Not a valid 'property' format: " + text);
|
||||
throw new Error("Not a valid 'property' format: " + text + ' (found in: ' + self.file + ':' + self.line + ')');
|
||||
}
|
||||
var property = new Doc({
|
||||
type: match[1],
|
||||
@@ -270,8 +272,9 @@ Doc.prototype = {
|
||||
self = this;
|
||||
|
||||
dom.h(title(this.name), function() {
|
||||
notice('deprecated', 'Deprecated API', self.deprecated);
|
||||
|
||||
notice('deprecated', 'Deprecated API', self.deprecated);
|
||||
dom.tag('a', {href: 'http://github.com/angular/angular.js/edit/master/' + self.file, class: 'improve-docs btn btn-primary'}, 'Improve this doc');
|
||||
if (self.ngdoc != 'overview') {
|
||||
dom.h('Description', self.description, dom.html);
|
||||
}
|
||||
@@ -383,40 +386,53 @@ Doc.prototype = {
|
||||
var self = this;
|
||||
dom.h('Usage', function() {
|
||||
var restrict = self.restrict || 'AC';
|
||||
|
||||
if (restrict.match(/E/)) {
|
||||
dom.text('as element (see ');
|
||||
dom.text('This directive can be used as custom element, but be aware of ');
|
||||
dom.tag('a', {href:'guide/ie'}, 'IE restrictions');
|
||||
dom.text(')');
|
||||
dom.code(function() {
|
||||
dom.text('<');
|
||||
dom.text(dashCase(self.shortName));
|
||||
renderParams('\n ', '="', '"');
|
||||
dom.text('>\n</');
|
||||
dom.text(dashCase(self.shortName));
|
||||
dom.text('>');
|
||||
});
|
||||
dom.text('.');
|
||||
}
|
||||
if (restrict.match(/A/)) {
|
||||
var element = self.element || 'ANY';
|
||||
dom.text('as attribute');
|
||||
dom.code(function() {
|
||||
dom.text('<' + element + ' ');
|
||||
dom.text(dashCase(self.shortName));
|
||||
renderParams('\n ', '="', '"', true);
|
||||
dom.text('>\n ...\n');
|
||||
dom.text('</' + element + '>');
|
||||
});
|
||||
}
|
||||
if (restrict.match(/C/)) {
|
||||
dom.text('as class');
|
||||
var element = self.element || 'ANY';
|
||||
dom.code(function() {
|
||||
dom.text('<' + element + ' class="');
|
||||
dom.text(dashCase(self.shortName));
|
||||
renderParams(' ', ': ', ';', true);
|
||||
dom.text('">\n ...\n');
|
||||
dom.text('</' + element + '>');
|
||||
|
||||
if (self.usage) {
|
||||
dom.tag('pre', function() {
|
||||
dom.tag('code', function() {
|
||||
dom.text(self.usage);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
if (restrict.match(/E/)) {
|
||||
dom.text('as element:');
|
||||
dom.code(function() {
|
||||
dom.text('<');
|
||||
dom.text(dashCase(self.shortName));
|
||||
renderParams('\n ', '="', '"');
|
||||
dom.text('>\n</');
|
||||
dom.text(dashCase(self.shortName));
|
||||
dom.text('>');
|
||||
});
|
||||
}
|
||||
if (restrict.match(/A/)) {
|
||||
var element = self.element || 'ANY';
|
||||
dom.text('as attribute');
|
||||
dom.code(function() {
|
||||
dom.text('<' + element + ' ');
|
||||
dom.text(dashCase(self.shortName));
|
||||
renderParams('\n ', '="', '"', true);
|
||||
dom.text('>\n ...\n');
|
||||
dom.text('</' + element + '>');
|
||||
});
|
||||
}
|
||||
if (restrict.match(/C/)) {
|
||||
dom.text('as class');
|
||||
var element = self.element || 'ANY';
|
||||
dom.code(function() {
|
||||
dom.text('<' + element + ' class="');
|
||||
dom.text(dashCase(self.shortName));
|
||||
renderParams(' ', ': ', ';', true);
|
||||
dom.text('">\n ...\n');
|
||||
dom.text('</' + element + '>');
|
||||
});
|
||||
}
|
||||
}
|
||||
self.html_usage_directiveInfo(dom);
|
||||
self.html_usage_parameters(dom);
|
||||
|
||||
+3
-2
@@ -7,7 +7,8 @@ exports.collect = collect;
|
||||
|
||||
var ngdoc = require('./ngdoc.js'),
|
||||
Q = require('qq'),
|
||||
qfs = require('q-fs');
|
||||
qfs = require('q-fs'),
|
||||
PATH = require('path');
|
||||
|
||||
var NEW_LINE = /\n\r?/;
|
||||
|
||||
@@ -43,7 +44,7 @@ function collect() {
|
||||
var work2;
|
||||
if (file.match(/\.ngdoc$/)) {
|
||||
work2 = Q.when(qfs.read(file, 'b'), function(content){
|
||||
var section = '@section ' + file.split('/')[2] + '\n';
|
||||
var section = '@section ' + file.split(PATH.sep)[2] + '\n';
|
||||
allDocs.push(new ngdoc.Doc(section + content.toString(),file, 1).parse());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# current angular version. If this rule matches the appcache-offline.manifest will be served for
|
||||
# requests to appcache.manifest
|
||||
#
|
||||
# This file must be processed by Rake in order to replace %ANGULAR_VERSION% with the actual version.
|
||||
# This file must be processed by Grunt in order to replace %ANGULAR_VERSION% with the actual version.
|
||||
|
||||
Options -Indexes
|
||||
RewriteEngine on
|
||||
|
||||
@@ -86,6 +86,10 @@ img.AngularJS-small {
|
||||
/* Content */
|
||||
/* =============================== */
|
||||
|
||||
.improve-docs {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.hint {
|
||||
font-size: .7em;
|
||||
color: #c0c0c0;
|
||||
@@ -184,3 +188,15 @@ ul.events > li > h3 {
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.tutorial-page {
|
||||
position:relative;
|
||||
}
|
||||
.tutorial-page-no-nav {
|
||||
padding-top:50px;
|
||||
}
|
||||
.tutorial-page-no-nav .improve-docs {
|
||||
position:absolute;
|
||||
top:0;
|
||||
right:0;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
headEl = document.head,
|
||||
angularVersion = {
|
||||
current: '"NG_VERSION_FULL"', // rewrite during build
|
||||
stable: '"NG_VERSION_STABLE"'
|
||||
cdn: '"NG_VERSION_CDN"'
|
||||
};
|
||||
|
||||
addTag('script', {src: path('angular-scenario.js')}, function() {
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
function path(name) {
|
||||
return production
|
||||
? 'http://code.angularjs.org/' + angularVersion.stable + '/' + name
|
||||
? 'http://code.angularjs.org/' + angularVersion.cdn + '/' + name
|
||||
: '../' + name;
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
sync = true,
|
||||
angularVersion = {
|
||||
current: '"NG_VERSION_FULL"', // rewrite during build
|
||||
stable: '"NG_VERSION_STABLE"'
|
||||
cdn: '"NG_VERSION_CDN"'
|
||||
};
|
||||
|
||||
addTag('base', {href: baseUrl});
|
||||
@@ -48,12 +48,12 @@
|
||||
if (production) {
|
||||
if (name.match(/^angular(-\w+)?\.js/) && !name.match(/bootstrap/)) {
|
||||
name = '//ajax.googleapis.com/ajax/libs/angularjs/' +
|
||||
angularVersion.stable +
|
||||
angularVersion.cdn +
|
||||
'/' +
|
||||
name.replace(/\.js$/, '.min.js');
|
||||
} else {
|
||||
name = 'http://code.angularjs.org/' +
|
||||
angularVersion.stable +
|
||||
angularVersion.cdn +
|
||||
'/' +
|
||||
name.replace(/\.js$/, '.min.js');
|
||||
}
|
||||
|
||||
+4
-5
@@ -61,22 +61,21 @@ exports.copy = function(from, to, transform) {
|
||||
|
||||
|
||||
exports.symlink = symlink;
|
||||
function symlink(from, to) {
|
||||
function symlink(from, to, type) {
|
||||
return qfs.exists(to).then(function(exists) {
|
||||
if (!exists) {
|
||||
return qfs.symbolicLink(to, from);
|
||||
return qfs.symbolicLink(to, from, type);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
exports.symlinkTemplate = symlinkTemplate;
|
||||
function symlinkTemplate(filename) {
|
||||
function symlinkTemplate(filename, type) {
|
||||
var dest = OUTPUT_DIR + filename,
|
||||
dirDepth = dest.split('/').length,
|
||||
src = Array(dirDepth).join('../') + 'docs/src/templates/' + filename;
|
||||
|
||||
return symlink(src, dest);
|
||||
return symlink(src, dest, type);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!doctype html>
|
||||
<html ng-app>
|
||||
<html ng-app="personalLog">
|
||||
<head>
|
||||
<title>Personal Log</title>
|
||||
<script src="../../src/loader.js"></script>
|
||||
|
||||
+158
-121
@@ -16,14 +16,14 @@
|
||||
/**
|
||||
* @fileoverview A utility to get better currency format pattern.
|
||||
*
|
||||
* This module implement a new currency format representation model. It
|
||||
* This module implements a new currency format representation model. It
|
||||
* provides 3 currency representation forms: global, portable and local. Local
|
||||
* format is the most popular format people use to represent currency in its
|
||||
* circulating country without worrying about how it should be distinguished
|
||||
* from other currencies. Global format is a formal representation in context
|
||||
* of multiple currencies in same page, it is ISO 4217 currency code. Portable
|
||||
* format is a compromise between global and local. It looks similar to how
|
||||
* people would like to see how their currencies is being represented in other
|
||||
* people would like to see how their currency is being represented in other
|
||||
* media. While at the same time, it should be distinguishable to world's
|
||||
* popular currencies (like USD, EUR) and currencies somewhat relevant in the
|
||||
* area (like CNY in HK, though native currency is HKD). There is no guarantee
|
||||
@@ -43,15 +43,14 @@ goog.i18n.currency.PRECISION_MASK_ = 0x07;
|
||||
|
||||
|
||||
/**
|
||||
* If this flag is set, it means the currency sign should position before
|
||||
* number.
|
||||
* Whether the currency sign should be positioned after the number.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.currency.POSITION_FLAG_ = 0x08;
|
||||
|
||||
|
||||
/**
|
||||
* Should a space to inserted between number and currency sign.
|
||||
* Whether a space should be inserted between the number and currency sign.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.currency.SPACE_FLAG_ = 0x20;
|
||||
@@ -59,8 +58,8 @@ goog.i18n.currency.SPACE_FLAG_ = 0x20;
|
||||
|
||||
/**
|
||||
* This function will add tier2 currency support. Be default, only tier1
|
||||
* (most popular currencies) are supportted. If an application really need
|
||||
* to support some of the rarely used currency, it should call this function
|
||||
* (most popular currencies) are supported. If an application really needs
|
||||
* to support some of the rarely used currencies, it should call this function
|
||||
* before any other functions in this namespace.
|
||||
*/
|
||||
goog.i18n.currency.addTier2Support = function() {
|
||||
@@ -75,8 +74,8 @@ goog.i18n.currency.addTier2Support = function() {
|
||||
* Global currency pattern always uses ISO-4217 currency code as prefix. Local
|
||||
* currency sign is added if it is different from currency code. Each currency
|
||||
* is unique in this form. The negative side is that ISO code looks weird in
|
||||
* some countries as poeple normally do not use it. Local currency sign
|
||||
* alleviate the problem, but also make it a little verbose.
|
||||
* some countries as people normally do not use it. Local currency sign
|
||||
* alleviates the problem, but also makes it a little verbose.
|
||||
*
|
||||
* @param {string} currencyCode ISO-4217 3-letter currency code.
|
||||
* @return {string} Global currency pattern string for given currency.
|
||||
@@ -85,9 +84,6 @@ goog.i18n.currency.getGlobalCurrencyPattern = function(currencyCode) {
|
||||
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
|
||||
var patternNum = info[0];
|
||||
if (currencyCode == info[1]) {
|
||||
if ((patternNum & goog.i18n.currency.POSITION_FLAG_) == 0) {
|
||||
patternNum |= goog.i18n.currency.SPACE_FLAG_;
|
||||
}
|
||||
return goog.i18n.currency.getCurrencyPattern_(patternNum, info[1]);
|
||||
}
|
||||
return currencyCode + ' ' +
|
||||
@@ -104,10 +100,8 @@ goog.i18n.currency.getGlobalCurrencyPattern = function(currencyCode) {
|
||||
*/
|
||||
goog.i18n.currency.getGlobalCurrencySign = function(currencyCode) {
|
||||
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
|
||||
if (currencyCode == info[1]) {
|
||||
return currencyCode;
|
||||
}
|
||||
return currencyCode + ' ' + info[1];
|
||||
return (currencyCode == info[1]) ? currencyCode :
|
||||
currencyCode + ' ' + info[1];
|
||||
};
|
||||
|
||||
|
||||
@@ -128,6 +122,7 @@ goog.i18n.currency.getLocalCurrencyPattern = function(currencyCode) {
|
||||
/**
|
||||
* Returns local currency sign string for those applications that need to
|
||||
* handle currency sign separately.
|
||||
*
|
||||
* @param {string} currencyCode ISO-4217 3-letter currency code.
|
||||
* @return {string} Local currency sign for given currency.
|
||||
*/
|
||||
@@ -156,6 +151,7 @@ goog.i18n.currency.getPortableCurrencyPattern = function(currencyCode) {
|
||||
/**
|
||||
* Return portable currency sign string for those applications that need to
|
||||
* handle currency sign themselves.
|
||||
*
|
||||
* @param {string} currencyCode ISO-4217 3-letter currency code.
|
||||
* @return {string} Portable currency sign for given currency.
|
||||
*/
|
||||
@@ -165,10 +161,13 @@ goog.i18n.currency.getPortableCurrencySign = function(currencyCode) {
|
||||
|
||||
|
||||
/**
|
||||
* This function returns the default currency sign position. Some application
|
||||
* This function returns the default currency sign position. Some applications
|
||||
* may want to handle currency sign and currency amount separately. This
|
||||
* function can be used in such situation to position the currency sign
|
||||
* relative to amount field correctly.
|
||||
* function can be used in such situations to correctly position the currency
|
||||
* sign relative to the amount.
|
||||
*
|
||||
* To match the behavior of ICU, position is not determined by display locale.
|
||||
*
|
||||
* @param {string} currencyCode ISO-4217 3-letter currency code.
|
||||
* @return {boolean} true if currency should be positioned before amount field.
|
||||
*/
|
||||
@@ -179,13 +178,12 @@ goog.i18n.currency.isPrefixSignPosition = function(currencyCode) {
|
||||
|
||||
|
||||
/**
|
||||
* This function construct the currency pattern. Currency sign is provided. The
|
||||
* This function constructs the currency pattern. Currency sign is provided. The
|
||||
* pattern information is encoded in patternNum.
|
||||
*
|
||||
* @param {number} patternNum Encoded pattern number that has
|
||||
* currency pattern information.
|
||||
* @param {string} sign the currency sign that will be used in pattern.
|
||||
*
|
||||
* @param {string} sign The currency sign that will be used in pattern.
|
||||
* @return {string} currency pattern string.
|
||||
* @private
|
||||
*/
|
||||
@@ -211,56 +209,97 @@ goog.i18n.currency.getCurrencyPattern_ = function(patternNum, sign) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Modify currency pattern string by adjusting precision for given currency.
|
||||
* Standard currency pattern will have 2 digit after decimal point.
|
||||
* Examples:
|
||||
* $#,##0.00 -> $#,##0 (precision == 0)
|
||||
* $#,##0.00 -> $#,##0.0 (precision == 1)
|
||||
* $#,##0.00 -> $#,##0.000 (precision == 3)
|
||||
*
|
||||
* @param {string} pattern currency pattern string.
|
||||
* @param {string} currencyCode 3-letter currency code.
|
||||
* @return {string} modified currency pattern string.
|
||||
*/
|
||||
goog.i18n.currency.adjustPrecision = function(pattern, currencyCode) {
|
||||
var strParts = ['0'];
|
||||
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
|
||||
var precision = info[0] & goog.i18n.currency.PRECISION_MASK_;
|
||||
if (precision > 0) {
|
||||
strParts.push('.');
|
||||
for (var i = 0; i < precision; i++) {
|
||||
strParts.push('0');
|
||||
}
|
||||
}
|
||||
return pattern.replace(/0.00/g, strParts.join(''));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Tier 1 currency information.
|
||||
*
|
||||
* The first number in the array is a combination of the precision mask and
|
||||
* other flags. The precision mask indicates how many decimal places to show for
|
||||
* the currency. Valid values are [0..7]. The position flag indicates whether
|
||||
* the currency sign should be positioned after the number. Valid values are 0
|
||||
* (before the number) or 16 (after the number). The space flag indicates
|
||||
* whether a space should be inserted between the currency sign and number.
|
||||
* Valid values are 0 (no space) and 24 (space).
|
||||
*
|
||||
* The number in the array is calculated by adding together the mask and flag
|
||||
* values. For example:
|
||||
*
|
||||
* 0: no precision (0), currency sign first (0), no space (0)
|
||||
* 2: two decimals precision (2), currency sign first (0), no space (0)
|
||||
* 18: two decimals precision (2), currency sign last (16), no space (0)
|
||||
* 42: two decimals precision (2), currency sign last (16), space (24)
|
||||
*
|
||||
* @type {!Object.<!Array>}
|
||||
*/
|
||||
goog.i18n.currency.CurrencyInfo = {
|
||||
'AED': [2, '\u062F\u002e\u0625', 'DH'],
|
||||
'ARS': [2, '$', 'AR$'],
|
||||
'AED': [2, 'dh', '\u062f.\u0625.', 'DH'],
|
||||
'AUD': [2, '$', 'AU$'],
|
||||
'BDT': [2, '\u09F3', 'Tk'],
|
||||
'BRL': [2, 'R$', 'R$'],
|
||||
'CAD': [2, '$', 'C$'],
|
||||
'CHF': [2, 'Fr.', 'CHF'],
|
||||
'CHF': [2, 'CHF', 'CHF'],
|
||||
'CLP': [0, '$', 'CL$'],
|
||||
'CNY': [2, '¥', 'RMB¥'],
|
||||
'COP': [2, '$', 'COL$'],
|
||||
'CRC': [2, '\u20a1', 'CR₡'],
|
||||
'CUP': [2, '$', '$MN'],
|
||||
'CZK': [10, 'Kč', 'Kč'],
|
||||
'DKK': [26, 'kr', 'kr'],
|
||||
'COP': [0, '$', 'COL$'],
|
||||
'CRC': [0, '\u20a1', 'CR\u20a1'],
|
||||
'CZK': [2, 'K\u010d', 'K\u010d'],
|
||||
'DKK': [18, 'kr', 'kr'],
|
||||
'DOP': [2, '$', 'RD$'],
|
||||
'EGP': [2, '£', 'LE'],
|
||||
'EUR': [26, '€', '€'],
|
||||
'EUR': [18, '€', '€'],
|
||||
'GBP': [2, '£', 'GB£'],
|
||||
'HKD': [2, '$', 'HK$'],
|
||||
'ILS': [10, '\u20AA', 'IL₪'],
|
||||
'INR': [2, 'Rs', 'Rs'],
|
||||
'ISK': [10, 'kr', 'kr'],
|
||||
'ILS': [2, '\u20AA', 'IL\u20AA'],
|
||||
'INR': [2, '\u20B9', 'Rs'],
|
||||
'ISK': [0, 'kr', 'kr'],
|
||||
'JMD': [2, '$', 'JA$'],
|
||||
'JPY': [0, '¥', 'JP¥'],
|
||||
'KRW': [0, '\u20A9', 'KR₩'],
|
||||
'LKR': [2, 'Rs', 'SLRs'],
|
||||
'MNT': [2, '\u20AE', 'MN₮'],
|
||||
'MNT': [0, '\u20AE', 'MN₮'],
|
||||
'MXN': [2, '$', 'Mex$'],
|
||||
'MYR': [2, 'RM', 'RM'],
|
||||
'NOK': [26, 'kr', 'NOkr'],
|
||||
'NOK': [18, 'kr', 'NOkr'],
|
||||
'PAB': [2, 'B/.', 'B/.'],
|
||||
'PEN': [2, 'S/.', 'S/.'],
|
||||
'PHP': [2, 'P', 'PHP'],
|
||||
'PKR': [2, 'Rs.', 'PKRs.'],
|
||||
'RUB': [10, 'руб', 'руб'],
|
||||
'SAR': [2, '\u0633\u002E\u0631', 'SR'],
|
||||
'SEK': [10, 'kr', 'kr'],
|
||||
'PHP': [2, '\u20B1', 'Php'],
|
||||
'PKR': [0, 'Rs', 'PKRs.'],
|
||||
'RUB': [42, 'руб.', 'руб.'],
|
||||
'SAR': [2, 'Rial', 'Rial'],
|
||||
'SEK': [2, 'kr', 'kr'],
|
||||
'SGD': [2, '$', 'S$'],
|
||||
'THB': [2, '\u0e3f', 'THB'],
|
||||
'TRY': [2, 'YTL', 'YTL'],
|
||||
'TRY': [2, 'TL', 'YTL'],
|
||||
'TWD': [2, 'NT$', 'NT$'],
|
||||
'USD': [2, '$', 'US$'],
|
||||
'UYU': [2, '$', 'UY$'],
|
||||
'VND': [10, '\u20AB', 'VN₫'],
|
||||
'YER': [2, 'YER', 'YER'],
|
||||
'VND': [0, '\u20AB', 'VN\u20AB'],
|
||||
'YER': [0, 'Rial', 'Rial'],
|
||||
'ZAR': [2, 'R', 'ZAR']
|
||||
};
|
||||
|
||||
@@ -270,116 +309,114 @@ goog.i18n.currency.CurrencyInfo = {
|
||||
* @type {!Object.<!Array>}
|
||||
*/
|
||||
goog.i18n.currency.CurrencyInfoTier2 = {
|
||||
'AFN': [18, '\u060b', 'AFN'],
|
||||
'ALL': [2, 'Lek', 'Lek'],
|
||||
'AMD': [10, '\u0564\u0580\u002e', 'dram'],
|
||||
'ANG': [2, '\u0083', 'NAƒ'],
|
||||
'AFN': [16, 'Af.', 'AFN'],
|
||||
'ALL': [0, 'Lek', 'Lek'],
|
||||
'AMD': [0, 'Dram', 'dram'],
|
||||
'AOA': [2, 'Kz', 'Kz'],
|
||||
'AWG': [2, 'ƒ', 'Afl.'],
|
||||
'AZN': [2, 'm', 'man'],
|
||||
'BAM': [18, 'КМ', 'KM'],
|
||||
'ARS': [2, '$', 'AR$'],
|
||||
'AWG': [2, 'Afl.', 'Afl.'],
|
||||
'AZN': [2, 'man.', 'man.'],
|
||||
'BAM': [18, 'KM', 'KM'],
|
||||
'BBD': [2, '$', 'Bds$'],
|
||||
'BGN': [10, '\u043b\u0432', 'лв'],
|
||||
'BHD': [3, '\u0628\u002e\u062f\u002e', 'BD'],
|
||||
'BGN': [2, 'lev', 'lev'],
|
||||
'BHD': [3, 'din', 'din'],
|
||||
'BIF': [0, 'FBu', 'FBu'],
|
||||
'BMD': [2, '$', 'BD$'],
|
||||
'BND': [2, '$', 'B$'],
|
||||
'BOB': [2, 'B$', 'B$'],
|
||||
'BSD': [2, '$', 'B$'],
|
||||
'BOB': [2, 'Bs', 'Bs'],
|
||||
'BSD': [2, '$', 'BS$'],
|
||||
'BTN': [2, 'Nu.', 'Nu.'],
|
||||
'BWP': [2, 'P', 'pula'],
|
||||
'BYR': [0, 'Br', 'Br'],
|
||||
'BYR': [0, 'BYR', 'BYR'],
|
||||
'BZD': [2, '$', 'BZ$'],
|
||||
'CDF': [2, 'F', 'CDF'],
|
||||
'CVE': [2, '$', 'Esc'],
|
||||
'CDF': [2, 'FrCD', 'CDF'],
|
||||
'CUC': [1, '$', 'CUC$'],
|
||||
'CUP': [2, '$', 'CU$'],
|
||||
'CVE': [2, 'CVE', 'Esc'],
|
||||
'DJF': [0, 'Fdj', 'Fdj'],
|
||||
'DZD': [2, '\u062f\u062C', 'DA'],
|
||||
'EEK': [10, 'EEK', 'EEK'],
|
||||
'DZD': [2, 'din', 'din'],
|
||||
'ERN': [2, 'Nfk', 'Nfk'],
|
||||
'ETB': [2, 'Br', 'Br'],
|
||||
'ETB': [2, 'Birr', 'Birr'],
|
||||
'FJD': [2, '$', 'FJ$'],
|
||||
'FKP': [2, '£', 'FK£'],
|
||||
'GEL': [2, 'GEL', 'GEL'],
|
||||
'GHS': [2, '\u20B5', 'GHS¢'],
|
||||
'GHS': [2, 'GHS', 'GHS'],
|
||||
'GIP': [2, '£', 'GI£'],
|
||||
'GMD': [2, 'D', 'GMD'],
|
||||
'GMD': [2, 'GMD', 'GMD'],
|
||||
'GNF': [0, 'FG', 'FG'],
|
||||
'GTQ': [2, 'Q', 'GTQ'],
|
||||
'GYD': [2, '$', 'GY$'],
|
||||
'GYD': [0, '$', 'GY$'],
|
||||
'HNL': [2, 'L', 'HNL'],
|
||||
'HRK': [2, 'kn', 'kn'],
|
||||
'HTG': [2, 'G', 'HTG'],
|
||||
'HUF': [10, 'Ft', 'Ft'],
|
||||
'IDR': [2, 'Rp', 'Rp'],
|
||||
'IQD': [3, '\u0639\u062F', 'IQD'],
|
||||
'IRR': [2, '\ufdfc', 'IRR'],
|
||||
'JOD': [3, 'JOD', 'JOD'],
|
||||
'KES': [2, 'KSh', 'KSh'],
|
||||
'KGS': [2, 'som', 'som'],
|
||||
'KHR': [10, '\u17DB', 'KHR'],
|
||||
'KMF': [0, 'KMF', 'KMF'],
|
||||
'KPW': [2, '\u20A9', 'KPW'],
|
||||
'KWD': [3, '\u062F\u002e\u0643', 'KWD'],
|
||||
'KYD': [2, '$', 'CI$'],
|
||||
'KZT': [10, 'KZT', 'KZT'],
|
||||
'LAK': [2, '\u20AD', 'LA₭'],
|
||||
'LBP': [2, '\u0644\u002e\u0644', 'LBP'],
|
||||
'HTG': [2, 'HTG', 'HTG'],
|
||||
'HUF': [0, 'Ft', 'Ft'],
|
||||
'IDR': [0, 'Rp', 'Rp'],
|
||||
'IQD': [0, 'din', 'IQD'],
|
||||
'IRR': [0, 'Rial', 'IRR'],
|
||||
'JOD': [3, 'din', 'JOD'],
|
||||
'KES': [2, 'Ksh', 'Ksh'],
|
||||
'KGS': [2, 'KGS', 'KGS'],
|
||||
'KHR': [2, 'Riel', 'KHR'],
|
||||
'KMF': [0, 'CF', 'KMF'],
|
||||
'KPW': [0, '\u20A9KP', 'KPW'],
|
||||
'KWD': [3, 'din', 'KWD'],
|
||||
'KYD': [2, '$', 'KY$'],
|
||||
'KZT': [2, '\u20B8', 'KZT'],
|
||||
'LAK': [0, '\u20AD', '\u20AD'],
|
||||
'LBP': [0, 'L£', 'LBP'],
|
||||
'LRD': [2, '$', 'L$'],
|
||||
'LSL': [2, 'L', 'LSL'],
|
||||
'LTL': [10, 'Lt', 'Lt'],
|
||||
'LVL': [10, 'Ls', 'Ls'],
|
||||
'LYD': [3, '\u0644\u002e\u062F', 'LD'],
|
||||
'MAD': [2, '\u0645\u002E\u062F\u002E', 'MAD'],
|
||||
'LSL': [2, 'LSL', 'LSL'],
|
||||
'LTL': [2, 'Lt', 'Lt'],
|
||||
'LVL': [2, 'Ls', 'Ls'],
|
||||
'LYD': [3, 'din', 'LD'],
|
||||
'MAD': [2, 'dh', 'MAD'],
|
||||
'MDL': [2, 'MDL', 'MDL'],
|
||||
'MGA': [1, 'MGA', 'MGA'],
|
||||
'MKD': [2, 'MKD', 'MKD'],
|
||||
'MMK': [2, 'K', 'MMK'],
|
||||
'MOP': [2, 'MOP$', 'MOP$'],
|
||||
'MRO': [1, 'UM', 'UM'],
|
||||
'MUR': [2, 'Rs', 'MURs'],
|
||||
'MVR': [2, 'Rf', 'MRF'],
|
||||
'MWK': [2, 'MK', 'MK'],
|
||||
'MGA': [0, 'Ar', 'MGA'],
|
||||
'MKD': [2, 'din', 'MKD'],
|
||||
'MMK': [0, 'K', 'MMK'],
|
||||
'MOP': [2, 'MOP', 'MOP$'],
|
||||
'MRO': [0, 'MRO', 'MRO'],
|
||||
'MUR': [0, 'MURs', 'MURs'],
|
||||
'MWK': [2, 'MWK', 'MWK'],
|
||||
'MZN': [2, 'MTn', 'MTn'],
|
||||
'NAD': [2, '$', 'N$'],
|
||||
'NGN': [2, '\u20A6', 'NG₦'],
|
||||
'NGN': [2, '\u20A6', 'NG\u20A6'],
|
||||
'NIO': [2, 'C$', 'C$'],
|
||||
'NPR': [2, 'Rs', 'NPRs'],
|
||||
'NZD': [2, '$', 'NZ$'],
|
||||
'OMR': [3, '\u0639\u002E\u062F\u002E', 'OMR'],
|
||||
'PGK': [2, 'K', 'PGK'],
|
||||
'PLN': [10, 'zł', 'zł'],
|
||||
'PYG': [0, '\u20b2', 'PYG'],
|
||||
'QAR': [2, '\u0642\u002E\u0631', 'QR'],
|
||||
'RON': [2, 'L', 'RON'],
|
||||
'RSD': [2, 'РС\u0414', 'RSD'],
|
||||
'OMR': [3, 'Rial', 'OMR'],
|
||||
'PGK': [2, 'PGK', 'PGK'],
|
||||
'PLN': [2, 'z\u0142', 'z\u0142'],
|
||||
'PYG': [0, 'Gs', 'PYG'],
|
||||
'QAR': [2, 'Rial', 'QR'],
|
||||
'RON': [2, 'RON', 'RON'],
|
||||
'RSD': [0, 'din', 'RSD'],
|
||||
'RWF': [0, 'RF', 'RF'],
|
||||
'SBD': [2, '$', 'SI$'],
|
||||
'SCR': [2, 'SR', 'SCR'],
|
||||
'SCR': [2, 'SCR', 'SCR'],
|
||||
'SDG': [2, 'SDG', 'SDG'],
|
||||
'SHP': [2, '£', 'SH£'],
|
||||
'SKK': [10, 'Sk', 'Sk'],
|
||||
'SLL': [2, 'Le', 'Le'],
|
||||
'SOS': [2, 'So. Sh.', 'So. Sh.'],
|
||||
'SLL': [0, 'SLL', 'SLL'],
|
||||
'SOS': [0, 'SOS', 'SOS'],
|
||||
'SRD': [2, '$', 'SR$'],
|
||||
'STD': [2, 'Db', 'Db'],
|
||||
'SYP': [18, 'SYP', 'SYP'],
|
||||
'SZL': [2, 'L', 'SZL'],
|
||||
'TJS': [2, 'TJS', 'TJS'],
|
||||
'TMM': [2, 'm', 'TMM'],
|
||||
'TND': [3, '\u062F\u002e\u062A ', 'DT'],
|
||||
'STD': [0, 'Db', 'Db'],
|
||||
'SYP': [16, '£', 'SY£'],
|
||||
'SZL': [2, 'SZL', 'SZL'],
|
||||
'TJS': [2, 'Som', 'TJS'],
|
||||
'TND': [3, 'din', 'DT'],
|
||||
'TOP': [2, 'T$', 'T$'],
|
||||
'TTD': [2, '$', 'TT$'],
|
||||
'TZS': [10, 'TZS', 'TZS'],
|
||||
'UAH': [10, '\u20B4', 'грн'],
|
||||
'UGX': [2, 'USh', 'USh'],
|
||||
'UZS': [2, 'UZS', 'UZS'],
|
||||
'VEF': [2, 'Bs.F', 'Bs.F'],
|
||||
'VUV': [0, 'Vt', 'Vt'],
|
||||
'WST': [2, 'WS$', 'WS$'],
|
||||
'TZS': [0, 'TSh', 'TSh'],
|
||||
'UAH': [2, '\u20B4', 'UAH'],
|
||||
'UGX': [0, 'UGX', 'UGX'],
|
||||
'UYU': [1, '$', '$U'],
|
||||
'UZS': [0, 'so\u02bcm', 'UZS'],
|
||||
'VEF': [2, 'Bs', 'Bs'],
|
||||
'VUV': [0, 'VUV', 'VUV'],
|
||||
'WST': [2, 'WST', 'WST'],
|
||||
'XAF': [0, 'FCFA', 'FCFA'],
|
||||
'XCD': [2, '$', 'EC$'],
|
||||
'XOF': [0, 'CFA', 'CFA'],
|
||||
'XPF': [0, 'F', 'XPF'],
|
||||
'ZMK': [2, 'ZK', 'ZK'],
|
||||
'ZWL': [2, '$', 'ZW$']
|
||||
'XPF': [0, 'FCFP', 'FCFP'],
|
||||
'ZMK': [0, 'ZMK', 'ZMK']
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+497
-296
File diff suppressed because it is too large
Load Diff
+358
-181
File diff suppressed because it is too large
Load Diff
+213
-146
@@ -1,4 +1,4 @@
|
||||
// Copyright 2011 The Closure Library Authors. All Rights Reserved
|
||||
// Copyright 2012 The Closure Library Authors. All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -7,35 +7,24 @@
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations under
|
||||
// the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Plural rules.
|
||||
*
|
||||
* This file is autogenerated by script:
|
||||
* http://go/generate_pluralrules.py
|
||||
* using the --for_closure flag.
|
||||
* http://go/generate_pluralrules.py
|
||||
*
|
||||
* To reduce the file size (which may cause issues in some JS
|
||||
* developing environments), this file will only contain locales
|
||||
* that are usually supported by google products. This is defined as
|
||||
* closure_tier1_locales and will change (most likely addition)
|
||||
* over time. Rest of the data can be found in another file named
|
||||
* "pluralrulesext.js", which will be generated at the
|
||||
* same time together with this file.
|
||||
*
|
||||
* Before checkin, this file could have been manually edited. This is
|
||||
* to incorporate changes before we could fix CLDR. All manual
|
||||
* modification must be documented in this section, and should be
|
||||
* removed after those changes land to CLDR.
|
||||
* Before check in, this file could have been manually edited. This is to
|
||||
* incorporate changes before we could fix CLDR. All manual modification must be
|
||||
* documented in this section, and should be removed after those changes land to
|
||||
* CLDR.
|
||||
*/
|
||||
|
||||
goog.provide('goog.i18n.pluralRules');
|
||||
|
||||
|
||||
/**
|
||||
* Plural pattern keyword
|
||||
* @enum {string}
|
||||
@@ -53,7 +42,7 @@ goog.i18n.pluralRules.Keyword = {
|
||||
/**
|
||||
* Default plural select rule.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Default plural value.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Default value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.defaultSelect_ = function(n) {
|
||||
@@ -64,25 +53,25 @@ goog.i18n.pluralRules.defaultSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for ar locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.arSelect_ = function(n) {
|
||||
if (n == 0) {
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
}
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 2) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
}
|
||||
if ((n % 100) >= 3 && (n % 100) <= 10 && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == (n | 0) && n % 100 >= 3 && n % 100 <= 10) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
if ((n % 100) >= 11 && (n % 100) <= 99 && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
if (n == (n | 0) && n % 100 >= 11 && n % 100 <= 99) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -91,13 +80,13 @@ goog.i18n.pluralRules.arSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for en locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.enSelect_ = function(n) {
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -106,13 +95,13 @@ goog.i18n.pluralRules.enSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for fil locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.filSelect_ = function(n) {
|
||||
if (n == 0 || n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -121,13 +110,13 @@ goog.i18n.pluralRules.filSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for fr locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.frSelect_ = function(n) {
|
||||
if (n >= 0 && n < 2) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
if (n >= 0 && n <= 2 && n != 2) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -136,16 +125,34 @@ goog.i18n.pluralRules.frSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for lv locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.lvSelect_ = function(n) {
|
||||
if (n == 0) {
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
}
|
||||
if ((n % 10) == 1 && (n % 100) != 11) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
if (n % 10 == 1 && n % 100 != 11) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Plural select rules for iu locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.iuSelect_ = function(n) {
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 2) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -154,16 +161,22 @@ goog.i18n.pluralRules.lvSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for ga locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.gaSelect_ = function(n) {
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 2) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
}
|
||||
if (n == (n | 0) && n >= 3 && n <= 6) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
if (n == (n | 0) && n >= 7 && n <= 10) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -172,17 +185,16 @@ goog.i18n.pluralRules.gaSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for ro locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.roSelect_ = function(n) {
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 0 || n != 1 && (n % 100) >= 1 &&
|
||||
(n % 100) <= 19 && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == 0 || n != 1 && n == (n | 0) && n % 100 >= 1 && n % 100 <= 19) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -191,40 +203,37 @@ goog.i18n.pluralRules.roSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for lt locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.ltSelect_ = function(n) {
|
||||
if ((n % 10) == 1 && ((n % 100) < 11 || (n % 100) > 19)) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
if (n % 10 == 1 && (n % 100 < 11 || n % 100 > 19)) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if ((n % 10) >= 2 && (n % 10) <= 9 &&
|
||||
((n % 100) < 11 || (n % 100) > 19) && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == (n | 0) && n % 10 >= 2 && n % 10 <= 9 && (n % 100 < 11 || n % 100 > 19)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Plural select rules for hr locale
|
||||
* Plural select rules for be locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.hrSelect_ = function(n) {
|
||||
if ((n % 10) == 1 && (n % 100) != 11) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
goog.i18n.pluralRules.beSelect_ = function(n) {
|
||||
if (n % 10 == 1 && n % 100 != 11) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if ((n % 10) >= 2 && (n % 10) <= 4 &&
|
||||
((n % 100) < 12 || (n % 100) > 14) && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == (n | 0) && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
if ((n % 10) == 0 || ((n % 10) >= 5 && (n % 10) <= 9) ||
|
||||
((n % 100) >= 11 && (n % 100) <= 14) && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
if (n % 10 == 0 || n == (n | 0) && n % 10 >= 5 && n % 10 <= 9 || n == (n | 0) && n % 100 >= 11 && n % 100 <= 14) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -233,16 +242,16 @@ goog.i18n.pluralRules.hrSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for cs locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.csSelect_ = function(n) {
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 2 || n == 3 || n == 4) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == (n | 0) && n >= 2 && n <= 4) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -251,22 +260,19 @@ goog.i18n.pluralRules.csSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for pl locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.plSelect_ = function(n) {
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if ((n % 10) >= 2 && (n % 10) <= 4 &&
|
||||
((n % 100) < 12 || (n % 100) > 14) && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == (n | 0) && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
if ((n % 10) == 0 || n != 1 && (n % 10) == 1 ||
|
||||
((n % 10) >= 5 && (n % 10) <= 9 || (n % 100) >= 12 && (n % 100) <= 14) &&
|
||||
n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
if (n != 1 && (n % 10 == 0 || n % 10 == 1) || n == (n | 0) && n % 10 >= 5 && n % 10 <= 9 || n == (n | 0) && n % 100 >= 12 && n % 100 <= 14) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -275,19 +281,19 @@ goog.i18n.pluralRules.plSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for sl locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.slSelect_ = function(n) {
|
||||
if ((n % 100) == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
if (n % 100 == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if ((n % 100) == 2) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
if (n % 100 == 2) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
}
|
||||
if ((n % 100) == 3 || (n % 100) == 4) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n % 100 == 3 || n % 100 == 4) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -296,19 +302,19 @@ goog.i18n.pluralRules.slSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for mt locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.mtSelect_ = function(n) {
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 0 || ((n % 100) >= 2 && (n % 100) <= 4 && n == Math.floor(n))) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == 0 || n == (n | 0) && n % 100 >= 2 && n % 100 <= 10) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
if ((n % 100) >= 11 && (n % 100) <= 19 && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
if (n == (n | 0) && n % 100 >= 11 && n % 100 <= 19) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -317,13 +323,13 @@ goog.i18n.pluralRules.mtSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for mk locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.mkSelect_ = function(n) {
|
||||
if ((n % 10) == 1 && n != 11) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
if (n % 10 == 1 && n != 11) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -332,25 +338,25 @@ goog.i18n.pluralRules.mkSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for cy locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.cySelect_ = function(n) {
|
||||
if (n == 0) {
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
}
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 2) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
}
|
||||
if (n == 3) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
if (n == 6) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -359,16 +365,16 @@ goog.i18n.pluralRules.cySelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for lag locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.lagSelect_ = function(n) {
|
||||
if (n == 0) {
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
}
|
||||
if (n > 0 && n < 2) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
if (n >= 0 && n <= 2 && n != 0 && n != 2) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -377,16 +383,16 @@ goog.i18n.pluralRules.lagSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for shi locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.shiSelect_ = function(n) {
|
||||
if (n >= 0 && n <= 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n >= 2 && n <= 10 && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == (n | 0) && n >= 2 && n <= 10) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -395,25 +401,91 @@ goog.i18n.pluralRules.shiSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for br locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.brSelect_ = function(n) {
|
||||
if (n % 10 == 1 && n % 100 != 11 && n % 100 != 71 && n % 100 != 91) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n % 10 == 2 && n % 100 != 12 && n % 100 != 72 && n % 100 != 92) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
}
|
||||
if ((n % 10 == 3 || n % 10 == 4 || n % 10 == 9) && ((n % 100 < 10 || n % 100 > 19) && (n % 100 < 70 || n % 100 > 79) && (n % 100 < 90 || n % 100 > 99))) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
if (n % 1000000 == 0 && n != 0) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Plural select rules for ksh locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.kshSelect_ = function(n) {
|
||||
if (n == 0) {
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
}
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 2) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Plural select rules for tzm locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.tzmSelect_ = function(n) {
|
||||
if (n == 0 || n == 1 || n == (n | 0) && n >= 11 && n <= 99) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 3) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Plural select rules for gv locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.gvSelect_ = function(n) {
|
||||
if (n % 10 == 1 || n % 10 == 2 || n % 20 == 0) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 6) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Plural select rules for gd locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.gdSelect_ = function(n) {
|
||||
if (n == 1 || n == 11) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 2 || n == 12) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
}
|
||||
if (n == (n | 0) && (n >= 3 && n <= 10 || n >= 13 && n <= 19)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -423,7 +495,6 @@ goog.i18n.pluralRules.brSelect_ = function(n) {
|
||||
* Selected plural rules by locale.
|
||||
*/
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
|
||||
|
||||
if (goog.LOCALE == 'am') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.filSelect_;
|
||||
}
|
||||
@@ -557,7 +628,7 @@ if (goog.LOCALE == 'hi') {
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'hr') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.hrSelect_;
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.beSelect_;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'hu') {
|
||||
@@ -581,7 +652,7 @@ if (goog.LOCALE == 'it') {
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'iw') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'ja') {
|
||||
@@ -612,10 +683,6 @@ if (goog.LOCALE == 'ml') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'mo') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.roSelect_;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'mr') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
|
||||
}
|
||||
@@ -661,7 +728,7 @@ if (goog.LOCALE == 'ro') {
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'ru') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.hrSelect_;
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.beSelect_;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'sk') {
|
||||
@@ -677,7 +744,7 @@ if (goog.LOCALE == 'sq') {
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'sr') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.hrSelect_;
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.beSelect_;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'sv') {
|
||||
@@ -709,7 +776,7 @@ if (goog.LOCALE == 'tr') {
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'uk') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.hrSelect_;
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.beSelect_;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'ur') {
|
||||
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
PARENT_DIR="$(dirname "$0")"
|
||||
jasmine-node "$PARENT_DIR"/spec/
|
||||
@@ -0,0 +1,261 @@
|
||||
var closureI18nExtractor = require('../src/closureI18nExtractor.js');
|
||||
var converter = require('../src/converter.js');
|
||||
findLocaleId = closureI18nExtractor.findLocaleId;
|
||||
extractNumberSymbols = closureI18nExtractor.extractNumberSymbols;
|
||||
extractCurrencySymbols = closureI18nExtractor.extractCurrencySymbols;
|
||||
extractDateTimeSymbols = closureI18nExtractor.extractDateTimeSymbols;
|
||||
|
||||
|
||||
function newTestLocaleInfo() {
|
||||
return { fr_CA: {
|
||||
DATETIME_FORMATS: {
|
||||
MONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre',
|
||||
'octobre', 'novembre', 'décembre'],
|
||||
SHORTMONTH: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.',
|
||||
'nov.', 'déc.'],
|
||||
DAY: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
|
||||
SHORTDAY: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
|
||||
AMPMS: ['AM', 'PM'],
|
||||
medium: 'yyyy-MM-dd HH:mm:ss',
|
||||
short: 'yy-MM-dd HH:mm',
|
||||
fullDate: 'EEEE d MMMM y',
|
||||
longDate: 'd MMMM y',
|
||||
mediumDate: 'yyyy-MM-dd',
|
||||
shortDate: 'yy-MM-dd',
|
||||
mediumTime: 'HH:mm:ss',
|
||||
shortTime: 'HH:mm'
|
||||
},
|
||||
NUMBER_FORMATS: {
|
||||
"DECIMAL_SEP": ".",
|
||||
"GROUP_SEP": ",",
|
||||
"PATTERNS": [{
|
||||
"minInt": 1,
|
||||
"minFrac": 0,
|
||||
"macFrac": 0,
|
||||
"posPre": "",
|
||||
"posSuf": "",
|
||||
"negPre": "-",
|
||||
"negSuf": "",
|
||||
"gSize": 3,
|
||||
"lgSize": 3,
|
||||
"maxFrac": 3
|
||||
}, {
|
||||
"minInt": 1,
|
||||
"minFrac": 2,
|
||||
"macFrac": 0,
|
||||
"posPre": "¤",
|
||||
"posSuf": "",
|
||||
"negPre": "¤-",
|
||||
"negSuf": "",
|
||||
"gSize": 3,
|
||||
"lgSize": 3,
|
||||
"maxFrac": 2
|
||||
}],
|
||||
"CURRENCY_SYM": "£"
|
||||
}}};
|
||||
}
|
||||
|
||||
|
||||
describe("findLocaleId", function () {
|
||||
it("should find the id from numbers", function() {
|
||||
expect(findLocaleId("NumberFormatSymbols_en_GB", "num")).toEqual("en_GB");
|
||||
});
|
||||
|
||||
|
||||
it("should find the id from datetime", function() {
|
||||
expect(findLocaleId("DateTimeSymbols_en_ISO", "datetime")).toEqual("en_ISO");
|
||||
});
|
||||
|
||||
|
||||
it("should throw an error otherwise", function() {
|
||||
expect(function() {
|
||||
findLocaleId("str", "otherwise")
|
||||
}).toThrow("unknown type in findLocaleId: otherwise");
|
||||
});
|
||||
});
|
||||
|
||||
describe("extractNumberSymbols", function () {
|
||||
it("should extract number data", function() {
|
||||
var CONTENT = [
|
||||
"goog.provide('goog.i18n.NumberFormatSymbols_en_GB');",
|
||||
"goog.i18n.NumberFormatSymbols_en_GB = {",
|
||||
"DECIMAL_SEP: '.',",
|
||||
"GROUP_SEP: ',',",
|
||||
"PERCENT: '%',",
|
||||
"ZERO_DIGIT: '0',",
|
||||
"PLUS_SIGN: '+',",
|
||||
"MINUS_SIGN: '-',",
|
||||
"EXP_SYMBOL: 'E',",
|
||||
"PERMILL: '\u2030',",
|
||||
"INFINITY: '\u221E',",
|
||||
"NAN: 'NaN',",
|
||||
"DECIMAL_PATTERN: '#,##0.###',",
|
||||
"SCIENTIFIC_PATTERN: '#E0',",
|
||||
"PERCENT_PATTERN: '#,##0%',",
|
||||
"CURRENCY_PATTERN: '\u00A4#,##0.00',",
|
||||
"DEF_CURRENCY_CODE: 'GBP' };"
|
||||
].join('\n');
|
||||
|
||||
var currencySymbols = {'GBP':[2, '£', 'GB£']};
|
||||
|
||||
var expectedNumberFormats = converter.convertNumberData(
|
||||
{
|
||||
DECIMAL_SEP:'.',
|
||||
GROUP_SEP:',',
|
||||
DECIMAL_PATTERN:'#,##0.###',
|
||||
CURRENCY_PATTERN:'\u00A4#,##0.00',
|
||||
DEF_CURRENCY_CODE: 'GBP'
|
||||
}, currencySymbols
|
||||
);
|
||||
|
||||
var localeInfo = {};
|
||||
extractNumberSymbols(CONTENT, localeInfo, currencySymbols);
|
||||
|
||||
expect(localeInfo).toEqual({
|
||||
'en_GB': { NUMBER_FORMATS: expectedNumberFormats }
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
describe("extractCurrencySymbols", function () {
|
||||
it("should extract currency data", function() {
|
||||
var CONTENT = [
|
||||
"goog.i18n.currency.CurrencyInfo = {",
|
||||
" 'GBP':[2, '£', 'GB£'],",
|
||||
"};",
|
||||
"goog.i18n.currency.CurrencyInfoTier2 = {",
|
||||
" 'AOA':[2, 'Kz', 'Kz'],",
|
||||
"};"
|
||||
].join('\n');
|
||||
|
||||
var localeInfo = {};
|
||||
expect(extractCurrencySymbols(CONTENT)).toEqual({
|
||||
'GBP':[2, '£', 'GB£'],
|
||||
'AOA':[2, 'Kz', 'Kz']
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("extractDateTimeSymbols", function () {
|
||||
it("should extract date time data", function() {
|
||||
var CONTENT = [
|
||||
"goog.i18n.DateTimeSymbols_fr_CA = {",
|
||||
" ERAS: ['av. J.-C.', 'ap. J.-C.'],",
|
||||
" ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],",
|
||||
" NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],",
|
||||
" STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',",
|
||||
" 'N', 'D'],",
|
||||
" MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',",
|
||||
" 'août', 'septembre', 'octobre', 'novembre', 'décembre'],",
|
||||
" STANDALONEMONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin',",
|
||||
" 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],",
|
||||
" SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',",
|
||||
" 'août', 'sept.', 'oct.', 'nov.', 'déc.'],",
|
||||
" STANDALONESHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin',",
|
||||
" 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'],",
|
||||
" WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',",
|
||||
" 'samedi'],",
|
||||
" STANDALONEWEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi',",
|
||||
" 'vendredi', 'samedi'],",
|
||||
" SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],",
|
||||
" STANDALONESHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.',",
|
||||
" 'sam.'],",
|
||||
" NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],",
|
||||
" STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],",
|
||||
" SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],",
|
||||
" QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],",
|
||||
" AMPMS: ['AM', 'PM'],",
|
||||
" DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'yyyy-MM-dd', 'yy-MM-dd'],",
|
||||
" TIMEFORMATS: ['HH \\'h\\' mm \\'min\\' ss \\'s\\' zzzz', 'HH:mm:ss z',",
|
||||
" 'HH:mm:ss', 'HH:mm'],",
|
||||
" FIRSTDAYOFWEEK: 6,",
|
||||
" WEEKENDRANGE: [5, 6],",
|
||||
" FIRSTWEEKCUTOFFDAY: 2",
|
||||
"};"
|
||||
].join('\n');
|
||||
var localeInfo = {};
|
||||
var expectedLocaleInfo = {
|
||||
fr_CA: {
|
||||
DATETIME_FORMATS: {
|
||||
MONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre',
|
||||
'octobre', 'novembre', 'décembre'],
|
||||
SHORTMONTH: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.',
|
||||
'nov.', 'déc.'],
|
||||
DAY: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
|
||||
SHORTDAY: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
|
||||
AMPMS: ['AM', 'PM'],
|
||||
medium: 'yyyy-MM-dd HH:mm:ss',
|
||||
short: 'yy-MM-dd HH:mm',
|
||||
fullDate: 'EEEE d MMMM y',
|
||||
longDate: 'd MMMM y',
|
||||
mediumDate: 'yyyy-MM-dd',
|
||||
shortDate: 'yy-MM-dd',
|
||||
mediumTime: 'HH:mm:ss',
|
||||
shortTime: 'HH:mm'
|
||||
}
|
||||
}
|
||||
};
|
||||
extractDateTimeSymbols(CONTENT, localeInfo);
|
||||
expect(localeInfo).toEqual(expectedLocaleInfo);
|
||||
})
|
||||
});
|
||||
|
||||
describe("pluralExtractor", function() {
|
||||
it("should output PLURAL_CAT in the output string code", function() {
|
||||
var localeIds = ["fr_CA"];
|
||||
var content = (
|
||||
"goog.provide('goog.i18n.pluralRules');\n" +
|
||||
"\n" +
|
||||
"goog.i18n.pluralRules.Keyword = {\n" +
|
||||
" ZERO: 'zero',\n" +
|
||||
" ONE: 'one',\n" +
|
||||
" TWO: 'two',\n" +
|
||||
" FEW: 'few',\n" +
|
||||
" MANY: 'many',\n" +
|
||||
" OTHER: 'other'\n" +
|
||||
"};\n" +
|
||||
"\n" +
|
||||
"goog.i18n.pluralRules.frSelect_ = function(n) {\n" +
|
||||
" if (n >= 0 && n < 2) {\n" +
|
||||
" return goog.i18n.pluralRules.Keyword.ONE;\n" +
|
||||
" }\n" +
|
||||
" return goog.i18n.pluralRules.Keyword.OTHER;\n" +
|
||||
"};\n" +
|
||||
"\n" +
|
||||
"if (goog.LOCALE == 'fr') {\n" +
|
||||
" goog.i18n.pluralRules.select = goog.i18n.pluralRules.frSelect_;\n" +
|
||||
"}"
|
||||
);
|
||||
var localeInfo = newTestLocaleInfo();
|
||||
closureI18nExtractor.pluralExtractor(content, localeInfo);
|
||||
var pluralCat = localeInfo["fr_CA"].pluralCat;
|
||||
expect(pluralCat).toBeDefined();
|
||||
// pluralCat is the source text for the pluralCat and contains @@
|
||||
// placeholders that need to be stripped before evaluation.
|
||||
// Ref: closureI18nExtractor.pluralExtractor.
|
||||
pluralCat = pluralCat.replace(/^@@|@@$/g, '');
|
||||
// pluralCat requires these constants to exist.
|
||||
var PLURAL_CATEGORY = {
|
||||
ZERO: "zero", ONE: "one", TWO: "two",
|
||||
FEW: "few", MANY: "many", OTHER: "other"
|
||||
};
|
||||
// Obtain the function by evaluating the source text.
|
||||
pluralCat = eval("(" + pluralCat + ")");
|
||||
// Confirm some expectations for pluralCat in fr_CA.
|
||||
expect(pluralCat(0)).toEqual("one");
|
||||
expect(pluralCat(3)).toEqual("other");
|
||||
})
|
||||
});
|
||||
|
||||
describe("serializeContent", function() {
|
||||
it("should not make any modifications to the content of the locale", function() {
|
||||
var serializedContent = closureI18nExtractor.serializeContent(newTestLocaleInfo());
|
||||
expect(eval("(" + serializedContent + ")")).toEqual(newTestLocaleInfo());
|
||||
});
|
||||
it("should only have ascii characters", function() {
|
||||
var serializedContent = closureI18nExtractor.serializeContent(newTestLocaleInfo());
|
||||
expect((/[^\u0001-\u007f]/).test(serializedContent)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,6 +24,8 @@ describe('parsePattern', function() {
|
||||
parseAndExpect('#,##,##0.###', '', '-', '', '', 1, 0, 3, 2, 3);
|
||||
parseAndExpect("#,##0.###;\'\u202A\'-#,##0.###\'\u202C\'",
|
||||
'', '\u202A-', '', '\u202C', 1, 0, 3, 3, 3);
|
||||
parseAndExpect('#0.###;#0.###-', '', '', '', '-', 1, 0, 3, 0, 0);
|
||||
|
||||
});
|
||||
|
||||
it('should parse CURRENCY patterns', function() {
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
'use strict';
|
||||
|
||||
var converter = require('./converter.js');
|
||||
|
||||
exports.extractNumberSymbols = extractNumberSymbols;
|
||||
exports.extractCurrencySymbols = extractCurrencySymbols;
|
||||
exports.extractDateTimeSymbols = extractDateTimeSymbols;
|
||||
exports.pluralExtractor = pluralExtractor;
|
||||
exports.outputLocale = outputLocale;
|
||||
exports.correctedLocaleId = correctedLocaleId;
|
||||
exports.findLocaleId = findLocaleId;
|
||||
exports.serializeContent = serializeContent;
|
||||
|
||||
var goog = { provide: function() {},
|
||||
require: function() {},
|
||||
i18n: {currency: {}, pluralRules: {}} };
|
||||
|
||||
function findLocaleId(str, type) {
|
||||
if (type === 'num') {
|
||||
return (str.match(/^NumberFormatSymbols_(.+)$/) || [])[1];
|
||||
}
|
||||
|
||||
if (type != 'datetime') { throw new Error('unknown type in findLocaleId: ' + type); }
|
||||
|
||||
return (str.match(/^DateTimeSymbols_(.+)$/) || [])[1];
|
||||
}
|
||||
|
||||
|
||||
function getInfoForLocale(localeInfo, localeID) {
|
||||
if (!localeInfo[localeID]) {
|
||||
localeInfo[localeID] = {};
|
||||
//localeIds.push(localeID);
|
||||
}
|
||||
return localeInfo[localeID];
|
||||
}
|
||||
|
||||
function extractNumberSymbols(content, localeInfo, currencySymbols) {
|
||||
//eval script in the current context so that we get access to all the symbols
|
||||
eval(content.toString());
|
||||
for (var propName in goog.i18n) {
|
||||
var localeID = findLocaleId(propName, 'num');
|
||||
if (localeID) {
|
||||
var info = getInfoForLocale(localeInfo, localeID);
|
||||
info.NUMBER_FORMATS =
|
||||
converter.convertNumberData(goog.i18n[propName], currencySymbols);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function extractCurrencySymbols(content) {
|
||||
//eval script in the current context so that we get access to all the symbols
|
||||
eval(content.toString());
|
||||
var currencySymbols = goog.i18n.currency.CurrencyInfo;
|
||||
currencySymbols.__proto__ = goog.i18n.currency.CurrencyInfoTier2;
|
||||
|
||||
return currencySymbols;
|
||||
}
|
||||
|
||||
function extractDateTimeSymbols(content, localeInfo) {
|
||||
//eval script in the current context so that we get access to all the symbols
|
||||
eval(content.toString());
|
||||
for (var propName in goog.i18n) {
|
||||
var localeID = findLocaleId(propName, 'datetime');
|
||||
if (localeID) {
|
||||
var info = getInfoForLocale(localeInfo, localeID);
|
||||
localeInfo[localeID].DATETIME_FORMATS =
|
||||
converter.convertDatetimeData(goog.i18n[propName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pluralExtractor(content, localeInfo) {
|
||||
var contentText = content.toString();
|
||||
var localeIds = Object.keys(localeInfo);
|
||||
for (var i = 0; i < localeIds.length; i++) {
|
||||
//We don't need to care about country ID because the plural rules in more specific id are
|
||||
//always the same as those in its language ID.
|
||||
// e.g. plural rules for en_SG is the same as those for en.
|
||||
goog.LOCALE = localeIds[i].match(/[^_]+/)[0];
|
||||
try {
|
||||
eval(contentText);
|
||||
} catch(e) {
|
||||
console.log("Error in eval(contentText): " + e.stack);
|
||||
}
|
||||
if (!goog.i18n.pluralRules.select) {
|
||||
console.log('No select for lang [' + goog.LOCALE + ']');
|
||||
continue;
|
||||
}
|
||||
var temp = goog.i18n.pluralRules.select.toString().
|
||||
replace(/goog.i18n.pluralRules.Keyword/g, 'PLURAL_CATEGORY').replace(/\n/g, '');
|
||||
|
||||
///@@ is a crazy place holder to be replaced before writing to file
|
||||
localeInfo[localeIds[i]].pluralCat = "@@" + temp + "@@";
|
||||
}
|
||||
}
|
||||
|
||||
function correctedLocaleId(localeID) {
|
||||
// e.g. from zh_CN to zh-CN, from en_US to en-US
|
||||
return localeID.replace(/_/g, '-').toLowerCase();
|
||||
}
|
||||
|
||||
function canonicalizeForJsonStringify(unused_key, object) {
|
||||
// This function is intended to be called as the 2nd argument to
|
||||
// JSON.stringify. The goal here is to ensure that the generated JSON has
|
||||
// objects with their keys in ascending order. Without this, it's much
|
||||
// harder to diff the generated files in src/ngLocale as the order isn't
|
||||
// exactly consistent. We've gotten lucky in the past.
|
||||
//
|
||||
// Iteration order, for string keys, ends up being the same as insertion
|
||||
// order. Refer :-
|
||||
// 1. http://ejohn.org/blog/javascript-in-chrome/
|
||||
// (search for "for loop order").
|
||||
// Currently all major browsers loop over the properties of an object
|
||||
// in the order in which they were defined.
|
||||
// - John Resig
|
||||
// 2. https://code.google.com/p/v8/issues/detail?id=164
|
||||
// ECMA-262 does not specify enumeration order. The de facto standard
|
||||
// is to match insertion order, which V8 also does ...
|
||||
if (typeof object != "object") {
|
||||
return object;
|
||||
}
|
||||
var result = {};
|
||||
Object.keys(object).sort().forEach(function(key) {
|
||||
result[key] = object[key];
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function serializeContent(localeObj) {
|
||||
return JSON.stringify(localeObj, canonicalizeForJsonStringify, ' ')
|
||||
.replace(new RegExp('[\\u007f-\\uffff]', 'g'), function(c) { return '\\u'+('0000'+c.charCodeAt(0).toString(16)).slice(-4); })
|
||||
.replace(/"@@|@@"/g, '');
|
||||
}
|
||||
|
||||
function outputLocale(localeInfo, localeID) {
|
||||
var fallBackID = localeID.match(/[A-Za-z]+/)[0],
|
||||
localeObj = localeInfo[localeID],
|
||||
fallBackObj = localeInfo[fallBackID];
|
||||
|
||||
// fallBack to language formats when country format is missing
|
||||
// e.g. if NUMBER_FORMATS of en_xyz is not present, use the NUMBER_FORMATS of en instead
|
||||
if (!localeObj.NUMBER_FORMATS) {
|
||||
localeObj.NUMBER_FORMATS = fallBackObj.NUMBER_FORMATS;
|
||||
}
|
||||
|
||||
// datetimesymbolsext.js provides more top level locales than the other
|
||||
// files. We process datetimesymbolsext.js because we want the country
|
||||
// specific formats that are missing from datetimesymbols.js. However, we
|
||||
// don't want to write locale files that only have dateformat (i.e. missing
|
||||
// number formats.) So we skip them.
|
||||
if (!localeObj.NUMBER_FORMATS) {
|
||||
console.log("Skipping locale %j: Don't have any number formats", localeID);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!localeObj.DATETIME_FORMATS) {
|
||||
localeObj.DATETIME_FORMATS = fallBackObj.DATETIME_FORMATS;
|
||||
}
|
||||
localeObj.id = correctedLocaleId(localeID);
|
||||
|
||||
var prefix =
|
||||
'angular.module("ngLocale", [], ["$provide", function($provide) {\n' +
|
||||
'var PLURAL_CATEGORY = {' +
|
||||
'ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"' +
|
||||
'};\n' +
|
||||
'$provide.value("$locale", ';
|
||||
|
||||
var suffix = ');\n}]);';
|
||||
|
||||
localeObj = {
|
||||
DATETIME_FORMATS: localeObj.DATETIME_FORMATS,
|
||||
NUMBER_FORMATS: localeObj.NUMBER_FORMATS,
|
||||
pluralCat: localeObj.pluralCat,
|
||||
id: localeObj.id
|
||||
};
|
||||
|
||||
var content = serializeContent(localeInfo[localeID]);
|
||||
|
||||
return prefix + content + suffix;
|
||||
}
|
||||
+57
-102
@@ -1,130 +1,85 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
var Q = require('qq'),
|
||||
var Q = require('q'),
|
||||
qfs = require('q-fs'),
|
||||
converter = require('./converter.js'),
|
||||
util = require('./util.js'),
|
||||
closureI18nExtractor = require('./closureI18nExtractor.js'),
|
||||
localeInfo = {},
|
||||
localeIds = [],
|
||||
currencySymbols,
|
||||
goog = { provide: function() {},
|
||||
require: function() {},
|
||||
i18n: {currency: {}, pluralRules: {}} };
|
||||
|
||||
createFolder('../../src/ngLocale/').then(function() {
|
||||
var promiseA = Q.defer(),
|
||||
promiseB = Q.defer();
|
||||
|
||||
qfs.read(__dirname + '/../closure/currencySymbols.js', 'b').then(function(content) {
|
||||
eval(content.toString());
|
||||
currencySymbols = goog.i18n.currency.CurrencyInfo;
|
||||
currencySymbols.__proto__ = goog.i18n.currency.CurrencyInfoTier2;
|
||||
var NG_LOCALE_DIR = '../src/ngLocale/';
|
||||
|
||||
qfs.read(__dirname + '/../closure/numberSymbols.js', 'b').then(function(content) {
|
||||
//eval script in the current context so that we get access to all the symbols
|
||||
eval(content.toString());
|
||||
for (var propName in goog.i18n) {
|
||||
var localeID = util.findLocaleId(propName, 'num');
|
||||
if (localeID) {
|
||||
if (!localeInfo[localeID]) {
|
||||
localeInfo[localeID] = {};
|
||||
localeIds.push(localeID);
|
||||
}
|
||||
var convertedData = converter.convertNumberData(goog.i18n[propName], currencySymbols);
|
||||
localeInfo[localeID].NUMBER_FORMATS = convertedData;
|
||||
}
|
||||
}
|
||||
|
||||
promiseA.resolve();
|
||||
function readSymbols() {
|
||||
console.log("Processing currency and number symbols ...");
|
||||
var numericStagePromise = qfs.read(__dirname + '/../closure/currencySymbols.js', 'b')
|
||||
.then(function(content) {
|
||||
var currencySymbols = closureI18nExtractor.extractCurrencySymbols(content);
|
||||
return qfs.read(__dirname + '/../closure/numberSymbols.js', 'b').then(function(content) {
|
||||
closureI18nExtractor.extractNumberSymbols(content, localeInfo, currencySymbols);
|
||||
});
|
||||
});
|
||||
|
||||
console.log("Processing datetime symbols ...");
|
||||
var datetimeStagePromise = qfs.read(__dirname + '/../closure/datetimeSymbols.js', 'b')
|
||||
.then(function(content) {
|
||||
closureI18nExtractor.extractDateTimeSymbols(content, localeInfo);
|
||||
return qfs.read(__dirname + '/../closure/datetimeSymbolsExt.js', 'b').then(function(content) {
|
||||
closureI18nExtractor.extractDateTimeSymbols(content, localeInfo);
|
||||
});
|
||||
});
|
||||
|
||||
return Q.all([numericStagePromise, datetimeStagePromise]);
|
||||
}
|
||||
|
||||
function extractPlurals() {
|
||||
console.log('Extracting Plurals ...');
|
||||
return qfs.read(__dirname + '/../closure/pluralRules.js').then(function(content) {
|
||||
closureI18nExtractor.pluralExtractor(content, localeInfo);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
qfs.read(__dirname + '/../closure/datetimeSymbols.js', 'b').then(function(content) {
|
||||
eval(content.toString());
|
||||
for (var propName in goog.i18n) {
|
||||
var localeID = util.findLocaleId(propName, 'datetime');
|
||||
if (localeID) {
|
||||
if (!localeInfo[localeID]) {
|
||||
localeInfo[localeID] = {};
|
||||
localeIds.push(localeID);
|
||||
}
|
||||
var convertedData = converter.convertDatetimeData(goog.i18n[propName]);
|
||||
localeInfo[localeID].DATETIME_FORMATS = convertedData;
|
||||
}
|
||||
}
|
||||
|
||||
promiseB.resolve();
|
||||
});
|
||||
|
||||
return Q.join(promiseA.promise, promiseB.promise, noop);
|
||||
}).then(function() {
|
||||
var promise = Q.defer();
|
||||
|
||||
qfs.read(__dirname + '/../closure/pluralRules.js').then(function(content) {
|
||||
for(var i = 0; i < localeIds.length; i++) {
|
||||
//We don't need to care about country ID because the plural rules in more specific id are
|
||||
//always the same as those in its language ID.
|
||||
// e.g. plural rules for en_SG is the same as those for en.
|
||||
goog.LOCALE = localeIds[i].match(/[^_]+/)[0];
|
||||
eval(content);
|
||||
var temp = goog.i18n.pluralRules.select.toString().
|
||||
replace(/goog.i18n.pluralRules.Keyword/g, 'PLURAL_CATEGORY').replace(/\n/g, '');
|
||||
|
||||
///@@ is a crazy place holder to be replaced before writing to file
|
||||
localeInfo[localeIds[i]].pluralCat = "@@" + temp + "@@";
|
||||
}
|
||||
promise.resolve();
|
||||
});
|
||||
|
||||
return promise.promise;
|
||||
}).then(function() {
|
||||
function writeLocaleFiles() {
|
||||
console.log('Final stage: Writing angular locale files to directory: %j', NG_LOCALE_DIR);
|
||||
var writePromises = [];
|
||||
var localeIds = Object.keys(localeInfo);
|
||||
var num_files = 0;
|
||||
localeIds.forEach(function(localeID) {
|
||||
var fallBackID = localeID.match(/[A-Za-z]+/)[0],
|
||||
localeObj = localeInfo[localeID],
|
||||
fallBackObj = localeInfo[fallBackID];
|
||||
|
||||
// fallBack to language formats when country format is missing
|
||||
// e.g. if NUMBER_FORMATS of en_xyz is not present, use the NUMBER_FORMATS of en instead
|
||||
if (!localeObj.NUMBER_FORMATS) {
|
||||
localeObj.NUMBER_FORMATS = fallBackObj.NUMBER_FORMATS;
|
||||
}
|
||||
|
||||
if (!localeObj.DATETIME_FORMATS) {
|
||||
localeObj.DATETIME_FORMATS = fallBackObj.DATETIME_FORMATS;
|
||||
}
|
||||
|
||||
// e.g. from zh_CN to zh-CN, from en_US to en-US
|
||||
var correctedLocaleId = localeID.replace(/_/g, '-').toLowerCase();
|
||||
localeObj.id = correctedLocaleId;
|
||||
|
||||
var prefix =
|
||||
'angular.module("ngLocale", [], ["$provide", function($provide) {\n' +
|
||||
'var PLURAL_CATEGORY = {' +
|
||||
'ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"' +
|
||||
'};\n' +
|
||||
'$provide.value("$locale", ';
|
||||
|
||||
var suffix = ');\n}]);';
|
||||
|
||||
var content = JSON.stringify(localeInfo[localeID]).replace(/\¤/g,'\\u00A4').
|
||||
replace(/"@@|@@"/g, '');
|
||||
|
||||
var toWrite = prefix + content + suffix;
|
||||
qfs.write(__dirname + '/../locale/' + 'angular-locale_' + correctedLocaleId + '.js', toWrite);
|
||||
var content = closureI18nExtractor.outputLocale(localeInfo, localeID);
|
||||
if (!content) return;
|
||||
var correctedLocaleId = closureI18nExtractor.correctedLocaleId(localeID);
|
||||
var filename = NG_LOCALE_DIR + 'angular-locale_' + correctedLocaleId + '.js'
|
||||
writePromises.push(
|
||||
qfs.write(filename, content)
|
||||
.then(function () {
|
||||
console.log('Wrote ' + filename);
|
||||
++num_files;
|
||||
}));
|
||||
console.log('Writing ' + filename);
|
||||
});
|
||||
console.log('Generated ' + localeIds.length + ' locale files!');
|
||||
}).end();
|
||||
|
||||
function noop() {};
|
||||
console.log('Generated %j locale files.', localeIds.length);
|
||||
return Q.all(writePromises).then(function() { return num_files });
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a folder under current directory.
|
||||
* @param folder {string} name of the folder to be made
|
||||
*/
|
||||
function createFolder(folder) {
|
||||
return qfs.isDirectory(__dirname + '/' + folder).then(function(isDir) {
|
||||
if (!isDir) return qfs.makeDirectory(__dirname + '/' + folder);
|
||||
return qfs.isDirectory(folder).then(function(isDir) {
|
||||
if (!isDir) return qfs.makeDirectory(folder).then(function() {
|
||||
console.log('Created directory %j', folder); });
|
||||
});
|
||||
}
|
||||
|
||||
createFolder(NG_LOCALE_DIR)
|
||||
.then(readSymbols)
|
||||
.then(extractPlurals)
|
||||
.then(writeLocaleFiles)
|
||||
.done(function(num_files) { console.log("Wrote %j files.\nAll Done!", num_files); });
|
||||
|
||||
+2
-2
@@ -45,8 +45,8 @@ function parsePattern(pattern) {
|
||||
}
|
||||
|
||||
var groups = integer.split(GROUP_SEP);
|
||||
p.gSize = groups[1].length;
|
||||
p.lgSize = (groups[2] || groups[1]).length;
|
||||
p.gSize = groups[1] ? groups[1].length : 0;
|
||||
p.lgSize = (groups[2] || groups[1]) ? (groups[2] || groups[1]).length : 0;
|
||||
|
||||
if (negative) {
|
||||
var trunkLen = positive.length - p.posPre.length - p.posSuf.length,
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e # Exit on error.
|
||||
|
||||
BASE_DIR=`dirname $0`
|
||||
cd $BASE_DIR
|
||||
|
||||
set -x # Trace commands as they're executed.
|
||||
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/currency.js > closure/currencySymbols.js
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/datetimesymbols.js > closure/datetimeSymbols.js
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/datetimesymbolsext.js > closure/datetimeSymbolsExt.js
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/numberformatsymbols.js > closure/numberSymbols.js
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/pluralrules.js > closure/pluralRules.js
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
+4
-4
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# Script to initialize angular repo
|
||||
# - install required node packages
|
||||
# - install Testacular
|
||||
# - install Karma
|
||||
# - install git hooks
|
||||
|
||||
|
||||
@@ -22,10 +22,10 @@ fi
|
||||
echo "Installing required npm packages..."
|
||||
npm install
|
||||
|
||||
testacular=`which testacular 2>&1`
|
||||
karma=`which karma 2>&1`
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Installing Testacular..."
|
||||
npm install -g testacular
|
||||
echo "Installing Karma..."
|
||||
npm install -g karma
|
||||
fi
|
||||
|
||||
echo "Installing git hooks..."
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
var util = require('./utils.js');
|
||||
var spawn = require('child_process').spawn;
|
||||
|
||||
module.exports = function(grunt) {
|
||||
|
||||
grunt.registerMultiTask('min', 'minify JS files', function(){
|
||||
util.min.call(util, this.data, this.async());
|
||||
});
|
||||
|
||||
|
||||
grunt.registerTask('minall', 'minify all the JS files in parallel', function(){
|
||||
var files = grunt.config('min');
|
||||
files = Object.keys(files).map(function(key){ return files[key]; });
|
||||
grunt.util.async.forEach(files, util.min.bind(util), this.async());
|
||||
});
|
||||
|
||||
|
||||
grunt.registerMultiTask('build', 'build JS files', function(){
|
||||
util.build.call(util, this.data, this.async());
|
||||
});
|
||||
|
||||
|
||||
grunt.registerTask('buildall', 'build all the JS files in parallel', function(){
|
||||
var builds = grunt.config('build');
|
||||
builds = Object.keys(builds).map(function(key){ return builds[key]; });
|
||||
grunt.util.async.forEach(builds, util.build.bind(util), this.async());
|
||||
});
|
||||
|
||||
|
||||
grunt.registerMultiTask('write', 'write content to a file', function(){
|
||||
grunt.file.write(this.data.file, this.data.val);
|
||||
grunt.log.ok('wrote to ' + this.data.file);
|
||||
});
|
||||
|
||||
|
||||
grunt.registerMultiTask('docs', 'create angular docs', function(){
|
||||
var done = this.async();
|
||||
var files = this.data;
|
||||
var docs = spawn('node', ['docs/src/gen-docs.js']);
|
||||
docs.stdout.pipe(process.stdout);
|
||||
docs.stderr.pipe(process.stderr);
|
||||
docs.on('exit', function(code){
|
||||
if(code !== 0) grunt.fail.warn('Error creating docs');
|
||||
grunt.file.expand(files).forEach(function(file){
|
||||
grunt.file.write(file, util.process(grunt.file.read(file), grunt.config('NG_VERSION'), false));
|
||||
});
|
||||
grunt.log.ok('docs created');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
grunt.registerMultiTask('test', 'Run the unit tests with Karma', function(){
|
||||
util.startKarma.call(util, this.data, true, this.async());
|
||||
});
|
||||
|
||||
|
||||
grunt.registerMultiTask('autotest', 'Run and watch the unit tests with Karma', function(){
|
||||
util.startKarma.call(util, this.data, false, this.async());
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,174 @@
|
||||
var fs = require('fs');
|
||||
var shell = require('shelljs');
|
||||
var grunt = require('grunt');
|
||||
var spawn = require('child_process').spawn;
|
||||
|
||||
module.exports = {
|
||||
|
||||
init: function() {
|
||||
shell.exec('npm install');
|
||||
},
|
||||
|
||||
|
||||
getVersion: function(){
|
||||
var package = JSON.parse(fs.readFileSync('package.json', 'UTF-8'));
|
||||
var match = package.version.match(/^([^\-]*)(-snapshot)?$/);
|
||||
var semver = match[1].split('.');
|
||||
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
|
||||
|
||||
var version = {
|
||||
full: (match[1] + (match[2] ? '-' + hash : '')),
|
||||
major: semver[0],
|
||||
minor: semver[1],
|
||||
dot: semver[2],
|
||||
codename: package.codename,
|
||||
cdn: package.cdnVersion
|
||||
};
|
||||
|
||||
return version;
|
||||
},
|
||||
|
||||
|
||||
startKarma: function(config, singleRun, done){
|
||||
var browsers = grunt.option('browsers');
|
||||
var reporters = grunt.option('reporters');
|
||||
var noColor = grunt.option('no-colors');
|
||||
var p = spawn('node', ['node_modules/karma/bin/karma', 'start', config,
|
||||
singleRun ? '--single-run=true' : '',
|
||||
reporters ? '--reporters=' + reporters : '',
|
||||
browsers ? '--browsers=' + browsers : '',
|
||||
noColor ? '--no-colors' : ''
|
||||
]);
|
||||
p.stdout.pipe(process.stdout);
|
||||
p.stderr.pipe(process.stderr);
|
||||
p.on('exit', function(code){
|
||||
if(code !== 0) grunt.fail.warn("Test(s) failed");
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
wrap: function(src, name){
|
||||
src.unshift('src/' + name + '.prefix');
|
||||
src.push('src/' + name + '.suffix');
|
||||
return src;
|
||||
},
|
||||
|
||||
|
||||
addStyle: function(src, styles, minify){
|
||||
styles = styles.map(processCSS.bind(this)).join('\n');
|
||||
src += styles;
|
||||
return src;
|
||||
|
||||
function processCSS(file){
|
||||
var css = fs.readFileSync(file).toString();
|
||||
if(minify){
|
||||
css = css
|
||||
.replace(/\r?\n/g, '')
|
||||
.replace(/\/\*.*?\*\//g, '')
|
||||
.replace(/:\s+/g, ':')
|
||||
.replace(/\s*\{\s*/g, '{')
|
||||
.replace(/\s*\}\s*/g, '}')
|
||||
.replace(/\s*\,\s*/g, ',')
|
||||
.replace(/\s*\;\s*/g, ';');
|
||||
}
|
||||
//escape for js
|
||||
css = css
|
||||
.replace(/\\/g, '\\\\')
|
||||
.replace(/'/g, "\\'")
|
||||
.replace(/\r?\n/g, '\\n');
|
||||
return "angular.element(document).find('head').append('<style type=\"text/css\">" + css + "</style>');";
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
process: function(src, NG_VERSION, strict){
|
||||
var processed = src
|
||||
.replace(/"NG_VERSION_FULL"/g, NG_VERSION.full)
|
||||
.replace(/"NG_VERSION_MAJOR"/, NG_VERSION.major)
|
||||
.replace(/"NG_VERSION_MINOR"/, NG_VERSION.minor)
|
||||
.replace(/"NG_VERSION_DOT"/, NG_VERSION.dot)
|
||||
.replace(/"NG_VERSION_CDN"/, NG_VERSION.cdn)
|
||||
.replace(/"NG_VERSION_CODENAME"/, NG_VERSION.codename);
|
||||
if (strict !== false) processed = this.singleStrict(processed, '\n\n', true);
|
||||
return processed;
|
||||
},
|
||||
|
||||
|
||||
build: function(config, fn){
|
||||
var files = grunt.file.expand(config.src);
|
||||
var styles = config.styles;
|
||||
//concat
|
||||
var src = files.map(function(filepath){
|
||||
return grunt.file.read(filepath);
|
||||
}).join(grunt.util.normalizelf('\n'));
|
||||
//process
|
||||
var processed = this.process(src, grunt.config('NG_VERSION'), config.strict);
|
||||
if (styles) processed = this.addStyle(processed, styles.css, styles.minify);
|
||||
//write
|
||||
grunt.file.write(config.dest, processed);
|
||||
grunt.log.ok('File ' + config.dest + ' created.');
|
||||
fn();
|
||||
},
|
||||
|
||||
|
||||
singleStrict: function(src, insert, newline){
|
||||
var useStrict = newline ? "$1\n'use strict';" : "$1'use strict';";
|
||||
return src
|
||||
.replace(/\s*("|')use strict("|');\s*/g, insert) // remove all file-specific strict mode flags
|
||||
.replace(/(\(function\([^)]*\)\s*\{)/, useStrict); // add single strict mode flag
|
||||
},
|
||||
|
||||
|
||||
min: function(file, done) {
|
||||
var minFile = file.replace(/\.js$/, '.min.js');
|
||||
shell.exec(
|
||||
'java ' +
|
||||
this.java32flags() + ' ' +
|
||||
'-jar lib/closure-compiler/compiler.jar ' +
|
||||
'--compilation_level SIMPLE_OPTIMIZATIONS ' +
|
||||
'--language_in ECMASCRIPT5_STRICT ' +
|
||||
'--js ' + file + ' ' +
|
||||
'--js_output_file ' + minFile,
|
||||
function(code) {
|
||||
if (code !== 0) grunt.fail.warn('Error minifying ' + file);
|
||||
grunt.file.write(minFile, this.singleStrict(grunt.file.read(minFile), '\n'));
|
||||
grunt.log.ok(file + ' minified into ' + minFile);
|
||||
done();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
|
||||
//returns the 32-bit mode force flags for java compiler if supported, this makes the build much faster
|
||||
java32flags: function(){
|
||||
if (process.platform === "win32") return '';
|
||||
if (shell.exec('java -version -d32 2>&1', {silent: true}).code !== 0) return '';
|
||||
return ' -d32 -client';
|
||||
},
|
||||
|
||||
|
||||
//csp connect middleware
|
||||
csp: function(){
|
||||
return function(req, res, next){
|
||||
res.setHeader("X-WebKit-CSP", "default-src 'self';");
|
||||
res.setHeader("X-Content-Security-Policy", "default-src 'self'");
|
||||
next();
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
//rewrite connect middleware
|
||||
rewrite: function(){
|
||||
return function(req, res, next){
|
||||
var REWRITE = /\/(guide|api|cookbook|misc|tutorial).*$/,
|
||||
IGNORED = /(\.(css|js|png|jpg)$|partials\/.*\.html$)/,
|
||||
match;
|
||||
|
||||
if (!IGNORED.test(req.url) && (match = req.url.match(REWRITE))) {
|
||||
console.log('rewriting', req.url);
|
||||
req.url = req.url.replace(match[0], '/index.html');
|
||||
}
|
||||
next();
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -1,273 +0,0 @@
|
||||
var sys = require('sys'),
|
||||
http = require('http'),
|
||||
fs = require('fs'),
|
||||
url = require('url'),
|
||||
events = require('events');
|
||||
|
||||
var DEFAULT_PORT = 8000;
|
||||
|
||||
function main(argv) {
|
||||
new HttpServer({
|
||||
'GET': createServlet(StaticServlet),
|
||||
'HEAD': createServlet(StaticServlet)
|
||||
}).start(Number(argv[2]) || DEFAULT_PORT);
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
return value.toString().
|
||||
replace('<', '<').
|
||||
replace('>', '>').
|
||||
replace('"', '"');
|
||||
}
|
||||
|
||||
function createServlet(Class) {
|
||||
var servlet = new Class();
|
||||
return servlet.handleRequest.bind(servlet);
|
||||
}
|
||||
|
||||
/**
|
||||
* An Http server implementation that uses a map of methods to decide
|
||||
* action routing.
|
||||
*
|
||||
* @param {Object} Map of method => Handler function
|
||||
*/
|
||||
function HttpServer(handlers) {
|
||||
this.handlers = handlers;
|
||||
this.server = http.createServer(this.handleRequest_.bind(this));
|
||||
}
|
||||
|
||||
HttpServer.prototype.start = function(port) {
|
||||
this.port = port;
|
||||
this.server.listen(port);
|
||||
sys.puts('Http Server running at http://127.0.0.1:' + port + '/');
|
||||
};
|
||||
|
||||
HttpServer.prototype.parseUrl_ = function(urlString) {
|
||||
var parsed = url.parse(urlString);
|
||||
parsed.pathname = url.resolve('/', parsed.pathname);
|
||||
return url.parse(url.format(parsed), true);
|
||||
};
|
||||
|
||||
HttpServer.prototype.handleRequest_ = function(req, res) {
|
||||
var logEntry = req.method + ' ' + req.url;
|
||||
if (req.headers['user-agent']) {
|
||||
logEntry += ' ' + req.headers['user-agent'];
|
||||
}
|
||||
sys.puts(logEntry);
|
||||
req.url = this.parseUrl_(req.url);
|
||||
var handler = this.handlers[req.method];
|
||||
if (!handler) {
|
||||
res.writeHead(501);
|
||||
res.end();
|
||||
} else {
|
||||
handler.call(this, req, res);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles static content.
|
||||
*/
|
||||
function StaticServlet() {}
|
||||
|
||||
StaticServlet.MimeMap = {
|
||||
'txt': 'text/plain',
|
||||
'html': 'text/html',
|
||||
'css': 'text/css',
|
||||
'xml': 'application/xml',
|
||||
'json': 'application/json',
|
||||
'js': 'application/javascript',
|
||||
'jpg': 'image/jpeg',
|
||||
'jpeg': 'image/jpeg',
|
||||
'gif': 'image/gif',
|
||||
'png': 'image/png',
|
||||
'manifest': 'text/cache-manifest',
|
||||
// it should be application/font-woff
|
||||
// but only this silences chrome warnings
|
||||
'woff': 'font/opentype'
|
||||
};
|
||||
|
||||
StaticServlet.prototype.handleRequest = function(req, res) {
|
||||
var self = this;
|
||||
var path = ('./' + req.url.pathname).replace('//','/').replace(/%(..)/g, function(match, hex){
|
||||
return String.fromCharCode(parseInt(hex, 16));
|
||||
});
|
||||
var parts = path.split('/');
|
||||
if (parts[parts.length-1].charAt(0) === '.')
|
||||
return self.sendForbidden_(req, res, path);
|
||||
|
||||
// favicon rewriting
|
||||
if (path === './favicon.ico')
|
||||
return self.sendFile_(req, res, './lib/nodeserver/favicon.ico');
|
||||
|
||||
// docs rewriting
|
||||
var REWRITE = /\/(guide|api|cookbook|misc|tutorial).*$/,
|
||||
IGNORED = /(\.(css|js|png|jpg)$|partials\/.*\.html$)/,
|
||||
match;
|
||||
|
||||
if (!IGNORED.test(path) && (match = path.match(REWRITE))) {
|
||||
path = path.replace(match[0], '/index.html');
|
||||
sys.puts('Rewrite to ' + path);
|
||||
}
|
||||
|
||||
// end of docs rewriting
|
||||
|
||||
fs.stat(path, function(err, stat) {
|
||||
if (err)
|
||||
return self.sendMissing_(req, res, path);
|
||||
if (stat.isDirectory())
|
||||
return fs.stat(path + 'index.html', function(err, stat) {
|
||||
// send index.html if exists
|
||||
if (!err)
|
||||
return self.sendFile_(req, res, path + 'index.html');
|
||||
|
||||
// list files otherwise
|
||||
return self.sendDirectory_(req, res, path);
|
||||
});
|
||||
|
||||
return self.sendFile_(req, res, path);
|
||||
});
|
||||
};
|
||||
|
||||
StaticServlet.prototype.sendError_ = function(req, res, error) {
|
||||
res.writeHead(500, {
|
||||
'Content-Type': 'text/html'
|
||||
});
|
||||
res.write('<!doctype html>\n');
|
||||
res.write('<title>Internal Server Error</title>\n');
|
||||
res.write('<h1>Internal Server Error</h1>');
|
||||
res.write('<pre>' + escapeHtml(sys.inspect(error)) + '</pre>');
|
||||
sys.puts('500 Internal Server Error');
|
||||
sys.puts(sys.inspect(error));
|
||||
};
|
||||
|
||||
StaticServlet.prototype.sendMissing_ = function(req, res, path) {
|
||||
path = path.substring(1);
|
||||
res.writeHead(404, {
|
||||
'Content-Type': 'text/html'
|
||||
});
|
||||
res.write('<!doctype html>\n');
|
||||
res.write('<title>404 Not Found</title>\n');
|
||||
res.write('<h1>Not Found</h1>');
|
||||
res.write(
|
||||
'<p>The requested URL ' +
|
||||
escapeHtml(path) +
|
||||
' was not found on this server.</p>'
|
||||
);
|
||||
res.end();
|
||||
sys.puts('404 Not Found: ' + path);
|
||||
};
|
||||
|
||||
StaticServlet.prototype.sendForbidden_ = function(req, res, path) {
|
||||
path = path.substring(1);
|
||||
res.writeHead(403, {
|
||||
'Content-Type': 'text/html'
|
||||
});
|
||||
res.write('<!doctype html>\n');
|
||||
res.write('<title>403 Forbidden</title>\n');
|
||||
res.write('<h1>Forbidden</h1>');
|
||||
res.write(
|
||||
'<p>You do not have permission to access ' +
|
||||
escapeHtml(path) + ' on this server.</p>'
|
||||
);
|
||||
res.end();
|
||||
sys.puts('403 Forbidden: ' + path);
|
||||
};
|
||||
|
||||
StaticServlet.prototype.sendRedirect_ = function(req, res, redirectUrl) {
|
||||
res.writeHead(301, {
|
||||
'Content-Type': 'text/html',
|
||||
'Location': redirectUrl
|
||||
});
|
||||
res.write('<!doctype html>\n');
|
||||
res.write('<title>301 Moved Permanently</title>\n');
|
||||
res.write('<h1>Moved Permanently</h1>');
|
||||
res.write(
|
||||
'<p>The document has moved <a href="' +
|
||||
redirectUrl +
|
||||
'">here</a>.</p>'
|
||||
);
|
||||
res.end();
|
||||
sys.puts('401 Moved Permanently: ' + redirectUrl);
|
||||
};
|
||||
|
||||
StaticServlet.prototype.sendFile_ = function(req, res, path) {
|
||||
var self = this;
|
||||
var file = fs.createReadStream(path);
|
||||
res.writeHead(200, {
|
||||
// CSP headers, uncomment to enable CSP
|
||||
//"X-WebKit-CSP": "default-src 'self';",
|
||||
//"X-Content-Security-Policy": "default-src 'self'",
|
||||
'Content-Type': StaticServlet.
|
||||
MimeMap[path.split('.').pop()] || 'text/plain'
|
||||
});
|
||||
if (req.method === 'HEAD') {
|
||||
res.end();
|
||||
} else {
|
||||
file.on('data', res.write.bind(res));
|
||||
file.on('close', function() {
|
||||
res.end();
|
||||
});
|
||||
file.on('error', function(error) {
|
||||
self.sendError_(req, res, error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
StaticServlet.prototype.sendDirectory_ = function(req, res, path) {
|
||||
var self = this;
|
||||
if (path.match(/[^\/]$/)) {
|
||||
req.url.pathname += '/';
|
||||
var redirectUrl = url.format(url.parse(url.format(req.url)));
|
||||
return self.sendRedirect_(req, res, redirectUrl);
|
||||
}
|
||||
fs.readdir(path, function(err, files) {
|
||||
if (err)
|
||||
return self.sendError_(req, res, error);
|
||||
|
||||
if (!files.length)
|
||||
return self.writeDirectoryIndex_(req, res, path, []);
|
||||
|
||||
var remaining = files.length;
|
||||
files.forEach(function(fileName, index) {
|
||||
fs.stat(path + '/' + fileName, function(err, stat) {
|
||||
if (err)
|
||||
return self.sendError_(req, res, err);
|
||||
if (stat.isDirectory()) {
|
||||
files[index] = fileName + '/';
|
||||
}
|
||||
if (!(--remaining))
|
||||
return self.writeDirectoryIndex_(req, res, path, files);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
StaticServlet.prototype.writeDirectoryIndex_ = function(req, res, path, files) {
|
||||
path = path.substring(1);
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/html'
|
||||
});
|
||||
if (req.method === 'HEAD') {
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
res.write('<!doctype html>\n');
|
||||
res.write('<title>' + escapeHtml(path) + '</title>\n');
|
||||
res.write('<style>\n');
|
||||
res.write(' ol { list-style-type: none; font-size: 1.2em; }\n');
|
||||
res.write('</style>\n');
|
||||
res.write('<h1>Directory: ' + escapeHtml(path) + '</h1>');
|
||||
res.write('<ol>');
|
||||
files.forEach(function(fileName) {
|
||||
if (fileName.charAt(0) !== '.') {
|
||||
res.write('<li><a href="' +
|
||||
escapeHtml(fileName) + '">' +
|
||||
escapeHtml(fileName) + '</a></li>');
|
||||
}
|
||||
});
|
||||
res.write('</ol>');
|
||||
res.end();
|
||||
};
|
||||
|
||||
// Must be last,
|
||||
main(process.argv);
|
||||
@@ -1,7 +0,0 @@
|
||||
var fs = require('fs');
|
||||
var vm = require('vm');
|
||||
|
||||
var filename = __dirname + '/showdown-0.9.js';
|
||||
var src = fs.readFileSync(filename);
|
||||
var Showdown = vm.runInThisContext(src + '\nShowdown;', filename);
|
||||
exports.Showdown = Showdown;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
node lib/nodeserver/server.js $1
|
||||
+24
-7
@@ -1,10 +1,27 @@
|
||||
{
|
||||
"name": "AngularJS",
|
||||
"version": "0.0.0",
|
||||
"dependencies" : {
|
||||
"testacular" : "canary",
|
||||
"jasmine-node" : "*",
|
||||
"q-fs" : "*",
|
||||
"qq" : "*"
|
||||
}
|
||||
"version": "1.0.7",
|
||||
"cdnVersion": "1.0.6",
|
||||
"codename": "monochromatic-rainbow",
|
||||
"devDependencies": {
|
||||
"grunt": "0.4.0",
|
||||
"grunt-contrib-clean": "0.4.0",
|
||||
"grunt-contrib-compress": "0.4.1",
|
||||
"grunt-contrib-connect": "0.1.2",
|
||||
"grunt-contrib-copy": "0.4.1",
|
||||
"jasmine-node": "1.2.3",
|
||||
"q": "~0.9.2",
|
||||
"q-fs": "0.1.36",
|
||||
"qq": "0.3.5",
|
||||
"shelljs": "0.1.2",
|
||||
"karma": "0.8.4",
|
||||
"yaml-js": "0.0.5",
|
||||
"showdown": "0.3.1"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT",
|
||||
"url": "https://github.com/angular/angular.js/blob/master/LICENSE"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+100
-36
@@ -28,12 +28,12 @@ var uppercase = function(string){return isString(string) ? string.toUpperCase()
|
||||
|
||||
var manualLowercase = function(s) {
|
||||
return isString(s)
|
||||
? s.replace(/[A-Z]/g, function(ch) {return fromCharCode(ch.charCodeAt(0) | 32);})
|
||||
? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
|
||||
: s;
|
||||
};
|
||||
var manualUppercase = function(s) {
|
||||
return isString(s)
|
||||
? s.replace(/[a-z]/g, function(ch) {return fromCharCode(ch.charCodeAt(0) & ~32);})
|
||||
? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
|
||||
: s;
|
||||
};
|
||||
|
||||
@@ -46,11 +46,8 @@ if ('i' !== 'I'.toLowerCase()) {
|
||||
uppercase = manualUppercase;
|
||||
}
|
||||
|
||||
function fromCharCode(code) {return String.fromCharCode(code);}
|
||||
|
||||
|
||||
var Error = window.Error,
|
||||
/** holds major version number for IE or NaN for real browsers */
|
||||
var /** holds major version number for IE or NaN for real browsers */
|
||||
msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]),
|
||||
jqLite, // delay binding since jQuery could be loaded after us.
|
||||
jQuery, // delay binding
|
||||
@@ -64,6 +61,29 @@ var Error = window.Error,
|
||||
nodeName_,
|
||||
uid = ['0', '0', '0'];
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {*} obj
|
||||
* @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, ...)
|
||||
*/
|
||||
function isArrayLike(obj) {
|
||||
if (!obj || (typeof obj.length !== 'number')) return false;
|
||||
|
||||
// We have on object which has length property. Should we treat it as array?
|
||||
if (typeof obj.hasOwnProperty != 'function' &&
|
||||
typeof obj.constructor != 'function') {
|
||||
// This is here for IE8: it is a bogus object treat it as array;
|
||||
return true;
|
||||
} else {
|
||||
return obj instanceof JQLite || // JQLite
|
||||
(jQuery && obj instanceof jQuery) || // jQuery
|
||||
toString.call(obj) !== '[object Object]' || // some browser native object
|
||||
typeof obj.callee === 'function'; // arguments (on IE8 looks like regular obj)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.forEach
|
||||
@@ -102,7 +122,7 @@ function forEach(obj, iterator, context) {
|
||||
}
|
||||
} else if (obj.forEach && obj.forEach !== forEach) {
|
||||
obj.forEach(iterator, context);
|
||||
} else if (isObject(obj) && isNumber(obj.length)) {
|
||||
} else if (isArrayLike(obj)) {
|
||||
for (key = 0; key < obj.length; key++)
|
||||
iterator.call(context, obj[key], key);
|
||||
} else {
|
||||
@@ -147,7 +167,7 @@ function reverseParams(iteratorFn) {
|
||||
/**
|
||||
* A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric
|
||||
* characters such as '012ABC'. The reason why we are not using simply a number counter is that
|
||||
* the number string gets longer over time, and it can also overflow, where as the the nextId
|
||||
* the number string gets longer over time, and it can also overflow, where as the nextId
|
||||
* will grow much slower, it is a string, and it will never overflow.
|
||||
*
|
||||
* @returns an unique alpha-numeric string
|
||||
@@ -174,6 +194,21 @@ function nextUid() {
|
||||
return uid.join('');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set or clear the hashkey for an object.
|
||||
* @param obj object
|
||||
* @param h the hashkey (!truthy to delete the hashkey)
|
||||
*/
|
||||
function setHashKey(obj, h) {
|
||||
if (h) {
|
||||
obj.$$hashKey = h;
|
||||
}
|
||||
else {
|
||||
delete obj.$$hashKey;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.extend
|
||||
@@ -185,8 +220,10 @@ function nextUid() {
|
||||
*
|
||||
* @param {Object} dst Destination object.
|
||||
* @param {...Object} src Source object(s).
|
||||
* @returns {Object} Reference to `dst`.
|
||||
*/
|
||||
function extend(dst) {
|
||||
var h = dst.$$hashKey;
|
||||
forEach(arguments, function(obj){
|
||||
if (obj !== dst) {
|
||||
forEach(obj, function(value, key){
|
||||
@@ -194,6 +231,8 @@ function extend(dst) {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
setHashKey(dst,h);
|
||||
return dst;
|
||||
}
|
||||
|
||||
@@ -543,19 +582,19 @@ function copy(source, destination){
|
||||
} else {
|
||||
if (source === destination) throw Error("Can't copy equivalent objects or arrays");
|
||||
if (isArray(source)) {
|
||||
while(destination.length) {
|
||||
destination.pop();
|
||||
}
|
||||
destination.length = 0;
|
||||
for ( var i = 0; i < source.length; i++) {
|
||||
destination.push(copy(source[i]));
|
||||
}
|
||||
} else {
|
||||
var h = destination.$$hashKey;
|
||||
forEach(destination, function(value, key){
|
||||
delete destination[key];
|
||||
});
|
||||
for ( var key in source) {
|
||||
destination[key] = copy(source[key]);
|
||||
}
|
||||
setHashKey(destination,h);
|
||||
}
|
||||
}
|
||||
return destination;
|
||||
@@ -595,7 +634,7 @@ function shallowCopy(src, dst) {
|
||||
* During a property comparision, properties of `function` type and properties with names
|
||||
* that begin with `$` are ignored.
|
||||
*
|
||||
* Scope and DOMWindow objects are being compared only be identify (`===`).
|
||||
* Scope and DOMWindow objects are being compared only by identify (`===`).
|
||||
*
|
||||
* @param {*} o1 Object or value to compare.
|
||||
* @param {*} o2 Object or value to compare.
|
||||
@@ -655,7 +694,7 @@ function sliceArgs(args, startIndex) {
|
||||
*
|
||||
* @description
|
||||
* Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
|
||||
* `fn`). You can supply optional `args` that are are prebound to the function. This feature is also
|
||||
* `fn`). You can supply optional `args` that are prebound to the function. This feature is also
|
||||
* known as [function currying](http://en.wikipedia.org/wiki/Currying).
|
||||
*
|
||||
* @param {Object} self Context which `fn` should be evaluated in.
|
||||
@@ -756,9 +795,18 @@ function startingTag(element) {
|
||||
// are not allowed to have children. So we just ignore it.
|
||||
element.html('');
|
||||
} catch(e) {}
|
||||
return jqLite('<div>').append(element).html().
|
||||
match(/^(<[^>]+>)/)[1].
|
||||
replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
|
||||
// As Per DOM Standards
|
||||
var TEXT_NODE = 3;
|
||||
var elemHtml = jqLite('<div>').append(element).html();
|
||||
try {
|
||||
return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) :
|
||||
elemHtml.
|
||||
match(/^(<[^>]+>)/)[1].
|
||||
replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
|
||||
} catch(e) {
|
||||
return lowercase(elemHtml);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -825,7 +873,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
|
||||
replace(/%3A/gi, ':').
|
||||
replace(/%24/g, '$').
|
||||
replace(/%2C/gi, ',').
|
||||
replace((pctEncodeSpaces ? null : /%20/g), '+');
|
||||
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
|
||||
}
|
||||
|
||||
|
||||
@@ -839,10 +887,10 @@ function encodeUriQuery(val, pctEncodeSpaces) {
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Use this directive to auto-bootstrap on application. Only
|
||||
* Use this directive to auto-bootstrap an application. Only
|
||||
* one directive can be used per HTML document. The directive
|
||||
* designates the root of the application and is typically placed
|
||||
* ot the root of the page.
|
||||
* at the root of the page.
|
||||
*
|
||||
* In the example below if the `ngApp` directive would not be placed
|
||||
* on the `html` element then the document would not be compiled
|
||||
@@ -914,22 +962,38 @@ function angularInit(element, bootstrap) {
|
||||
* @returns {AUTO.$injector} Returns the newly created injector for this app.
|
||||
*/
|
||||
function bootstrap(element, modules) {
|
||||
element = jqLite(element);
|
||||
modules = modules || [];
|
||||
modules.unshift(['$provide', function($provide) {
|
||||
$provide.value('$rootElement', element);
|
||||
}]);
|
||||
modules.unshift('ng');
|
||||
var injector = createInjector(modules);
|
||||
injector.invoke(
|
||||
['$rootScope', '$rootElement', '$compile', '$injector', function(scope, element, compile, injector){
|
||||
scope.$apply(function() {
|
||||
element.data('$injector', injector);
|
||||
compile(element)(scope);
|
||||
});
|
||||
}]
|
||||
);
|
||||
return injector;
|
||||
var resumeBootstrapInternal = function() {
|
||||
element = jqLite(element);
|
||||
modules = modules || [];
|
||||
modules.unshift(['$provide', function($provide) {
|
||||
$provide.value('$rootElement', element);
|
||||
}]);
|
||||
modules.unshift('ng');
|
||||
var injector = createInjector(modules);
|
||||
injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
|
||||
function(scope, element, compile, injector) {
|
||||
scope.$apply(function() {
|
||||
element.data('$injector', injector);
|
||||
compile(element)(scope);
|
||||
});
|
||||
}]
|
||||
);
|
||||
return injector;
|
||||
};
|
||||
|
||||
var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
|
||||
|
||||
if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
|
||||
return resumeBootstrapInternal();
|
||||
}
|
||||
|
||||
window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
|
||||
angular.resumeBootstrap = function(extraModules) {
|
||||
forEach(extraModules, function(module) {
|
||||
modules.push(module);
|
||||
});
|
||||
resumeBootstrapInternal();
|
||||
};
|
||||
}
|
||||
|
||||
var SNAKE_CASE_REGEXP = /[A-Z]/g;
|
||||
@@ -962,7 +1026,7 @@ function bindJQuery() {
|
||||
}
|
||||
|
||||
/**
|
||||
* throw error of the argument is falsy.
|
||||
* throw error if the argument is falsy.
|
||||
*/
|
||||
function assertArg(arg, name, reason) {
|
||||
if (!arg) {
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
|
||||
*/
|
||||
var version = {
|
||||
full: '"NG_VERSION_FULL"', // all of these placeholder strings will be replaced by rake's
|
||||
major: "NG_VERSION_MAJOR", // compile task
|
||||
full: '"NG_VERSION_FULL"', // all of these placeholder strings will be replaced by grunt's
|
||||
major: "NG_VERSION_MAJOR", // package task
|
||||
minor: "NG_VERSION_MINOR",
|
||||
dot: "NG_VERSION_DOT",
|
||||
codeName: '"NG_VERSION_CODENAME"'
|
||||
|
||||
Vendored
+1
-1
@@ -222,6 +222,6 @@
|
||||
function isActuallyNaN(val) {
|
||||
return (typeof val === 'number') && isNaN(val);
|
||||
}
|
||||
};
|
||||
}
|
||||
})(window, document);
|
||||
|
||||
|
||||
+13
-13
@@ -62,7 +62,7 @@ function annotate(fn) {
|
||||
}
|
||||
} else if (isArray(fn)) {
|
||||
last = fn.length - 1;
|
||||
assertArgFn(fn[last], 'fn')
|
||||
assertArgFn(fn[last], 'fn');
|
||||
$inject = fn.slice(0, last);
|
||||
} else {
|
||||
assertArgFn(fn, 'fn', true);
|
||||
@@ -96,19 +96,19 @@ function annotate(fn) {
|
||||
* # Injection Function Annotation
|
||||
*
|
||||
* JavaScript does not have annotations, and annotations are needed for dependency injection. The
|
||||
* following ways are all valid way of annotating function with injection arguments and are equivalent.
|
||||
* following are all valid ways of annotating function with injection arguments and are equivalent.
|
||||
*
|
||||
* <pre>
|
||||
* // inferred (only works if code not minified/obfuscated)
|
||||
* $inject.invoke(function(serviceA){});
|
||||
* $injector.invoke(function(serviceA){});
|
||||
*
|
||||
* // annotated
|
||||
* function explicit(serviceA) {};
|
||||
* explicit.$inject = ['serviceA'];
|
||||
* $inject.invoke(explicit);
|
||||
* $injector.invoke(explicit);
|
||||
*
|
||||
* // inline
|
||||
* $inject.invoke(['serviceA', function(serviceA){}]);
|
||||
* $injector.invoke(['serviceA', function(serviceA){}]);
|
||||
* </pre>
|
||||
*
|
||||
* ## Inference
|
||||
@@ -192,7 +192,7 @@ function annotate(fn) {
|
||||
* This method does not work with code minfication / obfuscation. For this reason the following annotation strategies
|
||||
* are supported.
|
||||
*
|
||||
* # The `$injector` property
|
||||
* # The `$inject` property
|
||||
*
|
||||
* If a function has an `$inject` property and its value is an array of strings, then the strings represent names of
|
||||
* services to be injected into the function.
|
||||
@@ -225,7 +225,7 @@ function annotate(fn) {
|
||||
* // ...
|
||||
* };
|
||||
* tmpFn.$inject = ['$compile', '$rootScope'];
|
||||
* injector.invoke(tempFn);
|
||||
* injector.invoke(tmpFn);
|
||||
*
|
||||
* // To better support inline function the inline annotation is supported
|
||||
* injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
|
||||
@@ -254,7 +254,7 @@ function annotate(fn) {
|
||||
* @description
|
||||
*
|
||||
* Use `$provide` to register new providers with the `$injector`. The providers are the factories for the instance.
|
||||
* The providers share the same name as the instance they create with the `Provider` suffixed to them.
|
||||
* The providers share the same name as the instance they create with `Provider` suffixed to them.
|
||||
*
|
||||
* A provider is an object with a `$get()` method. The injector calls the `$get` method to create a new instance of
|
||||
* a service. The Provider can have additional methods which would allow for configuration of the provider.
|
||||
@@ -278,7 +278,7 @@ function annotate(fn) {
|
||||
*
|
||||
* beforeEach(module(function($provide) {
|
||||
* $provide.provider('greet', GreetProvider);
|
||||
* });
|
||||
* }));
|
||||
*
|
||||
* it('should greet', inject(function(greet) {
|
||||
* expect(greet('angular')).toEqual('Hello angular!');
|
||||
@@ -291,9 +291,7 @@ function annotate(fn) {
|
||||
* inject(function(greet) {
|
||||
* expect(greet('angular')).toEqual('Ahoj angular!');
|
||||
* });
|
||||
* )};
|
||||
*
|
||||
* });
|
||||
* });
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
@@ -387,7 +385,7 @@ function annotate(fn) {
|
||||
*
|
||||
* @param {string} name The name of the service to decorate.
|
||||
* @param {function()} decorator This function will be invoked when the service needs to be
|
||||
* instanciated. The function is called using the {@link AUTO.$injector#invoke
|
||||
* instantiated. The function is called using the {@link AUTO.$injector#invoke
|
||||
* injector.invoke} method and is therefore fully injectable. Local injection arguments:
|
||||
*
|
||||
* * `$delegate` - The original service instance, which can be monkey patched, configured,
|
||||
@@ -587,6 +585,8 @@ function createInjector(modulesToLoad) {
|
||||
var Constructor = function() {},
|
||||
instance, returnedValue;
|
||||
|
||||
// Check if Type is annotated and use just the given function at n-1 as parameter
|
||||
// e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
|
||||
Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;
|
||||
instance = new Constructor();
|
||||
returnedValue = invoke(Type, instance, locals);
|
||||
|
||||
Vendored
+4
-4
@@ -4,10 +4,10 @@ var directive = {};
|
||||
var service = { value: {} };
|
||||
|
||||
var DEPENDENCIES = {
|
||||
'angular.js': 'http://code.angularjs.org/' + angular.version.full + 'angular.min.js',
|
||||
'angular-resource.js': 'http://code.angularjs.org/' + angular.version.full + 'angular-resource.min.js',
|
||||
'angular-sanitize.js': 'http://code.angularjs.org/' + angular.version.full + 'angular-sanitize.min.js',
|
||||
'angular-cookies.js': 'http://code.angularjs.org/' + angular.version.full + 'angular-cookies.min.js'
|
||||
'angular.js': 'http://code.angularjs.org/' + angular.version.full + '/angular.min.js',
|
||||
'angular-resource.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-resource.min.js',
|
||||
'angular-sanitize.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-sanitize.min.js',
|
||||
'angular-cookies.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-cookies.min.js'
|
||||
};
|
||||
|
||||
|
||||
|
||||
Vendored
+8
@@ -143,6 +143,14 @@ directive.tabbable = function() {
|
||||
};
|
||||
};
|
||||
|
||||
directive.table = function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function(scope, element, attrs) {
|
||||
element[0].className = 'table table-bordered table-striped code-table';
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
directive.tabPane = function() {
|
||||
return {
|
||||
|
||||
+40
-20
@@ -32,18 +32,18 @@
|
||||
* - [after()](http://api.jquery.com/after/)
|
||||
* - [append()](http://api.jquery.com/append/)
|
||||
* - [attr()](http://api.jquery.com/attr/)
|
||||
* - [bind()](http://api.jquery.com/bind/)
|
||||
* - [children()](http://api.jquery.com/children/)
|
||||
* - [bind()](http://api.jquery.com/bind/) - Does not support namespaces
|
||||
* - [children()](http://api.jquery.com/children/) - Does not support selectors
|
||||
* - [clone()](http://api.jquery.com/clone/)
|
||||
* - [contents()](http://api.jquery.com/contents/)
|
||||
* - [css()](http://api.jquery.com/css/)
|
||||
* - [data()](http://api.jquery.com/data/)
|
||||
* - [eq()](http://api.jquery.com/eq/)
|
||||
* - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name.
|
||||
* - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name
|
||||
* - [hasClass()](http://api.jquery.com/hasClass/)
|
||||
* - [html()](http://api.jquery.com/html/)
|
||||
* - [next()](http://api.jquery.com/next/)
|
||||
* - [parent()](http://api.jquery.com/parent/)
|
||||
* - [next()](http://api.jquery.com/next/) - Does not support selectors
|
||||
* - [parent()](http://api.jquery.com/parent/) - Does not support selectors
|
||||
* - [prepend()](http://api.jquery.com/prepend/)
|
||||
* - [prop()](http://api.jquery.com/prop/)
|
||||
* - [ready()](http://api.jquery.com/ready/)
|
||||
@@ -55,7 +55,7 @@
|
||||
* - [text()](http://api.jquery.com/text/)
|
||||
* - [toggleClass()](http://api.jquery.com/toggleClass/)
|
||||
* - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Doesn't pass native event objects to handlers.
|
||||
* - [unbind()](http://api.jquery.com/unbind/)
|
||||
* - [unbind()](http://api.jquery.com/unbind/) - Does not support namespaces
|
||||
* - [val()](http://api.jquery.com/val/)
|
||||
* - [wrap()](http://api.jquery.com/wrap/)
|
||||
*
|
||||
@@ -602,23 +602,43 @@ forEach({
|
||||
|
||||
if (!eventFns) {
|
||||
if (type == 'mouseenter' || type == 'mouseleave') {
|
||||
var counter = 0;
|
||||
var contains = document.body.contains || document.body.compareDocumentPosition ?
|
||||
function( a, b ) {
|
||||
var adown = a.nodeType === 9 ? a.documentElement : a,
|
||||
bup = b && b.parentNode;
|
||||
return a === bup || !!( bup && bup.nodeType === 1 && (
|
||||
adown.contains ?
|
||||
adown.contains( bup ) :
|
||||
a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
|
||||
));
|
||||
} :
|
||||
function( a, b ) {
|
||||
if ( b ) {
|
||||
while ( (b = b.parentNode) ) {
|
||||
if ( b === a ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
events.mouseenter = [];
|
||||
events.mouseleave = [];
|
||||
events[type] = [];
|
||||
|
||||
// Refer to jQuery's implementation of mouseenter & mouseleave
|
||||
// Read about mouseenter and mouseleave:
|
||||
// http://www.quirksmode.org/js/events_mouse.html#link8
|
||||
var eventmap = { mouseleave : "mouseout", mouseenter : "mouseover"}
|
||||
bindFn(element, eventmap[type], function(event) {
|
||||
var ret, target = this, related = event.relatedTarget;
|
||||
// For mousenter/leave call the handler if related is outside the target.
|
||||
// NB: No relatedTarget if the mouse left/entered the browser window
|
||||
if ( !related || (related !== target && !contains(target, related)) ){
|
||||
handle(event, type);
|
||||
}
|
||||
|
||||
bindFn(element, 'mouseover', function(event) {
|
||||
counter++;
|
||||
if (counter == 1) {
|
||||
handle(event, 'mouseenter');
|
||||
}
|
||||
});
|
||||
bindFn(element, 'mouseout', function(event) {
|
||||
counter --;
|
||||
if (counter == 0) {
|
||||
handle(event, 'mouseleave');
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
addEventListenerFn(element, type, handle);
|
||||
events[type] = [];
|
||||
|
||||
+8
-2
@@ -237,7 +237,7 @@ function Browser(window, document, $log, $sniffer) {
|
||||
*/
|
||||
self.baseHref = function() {
|
||||
var href = baseElement.attr('href');
|
||||
return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : href;
|
||||
return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : '';
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
@@ -297,7 +297,13 @@ function Browser(window, document, $log, $sniffer) {
|
||||
cookie = cookieArray[i];
|
||||
index = cookie.indexOf('=');
|
||||
if (index > 0) { //ignore nameless cookies
|
||||
lastCookies[unescape(cookie.substring(0, index))] = unescape(cookie.substring(index + 1));
|
||||
var name = unescape(cookie.substring(0, index));
|
||||
// the first value that is seen for a cookie is the most
|
||||
// specific one. values for the same cookie name that
|
||||
// follow are for less specific paths.
|
||||
if (lastCookies[name] === undefined) {
|
||||
lastCookies[name] = unescape(cookie.substring(index + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+77
-21
@@ -155,7 +155,8 @@ function $CompileProvider($provide) {
|
||||
Suffix = 'Directive',
|
||||
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
|
||||
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
|
||||
MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ';
|
||||
MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ',
|
||||
urlSanitizationWhitelist = /^\s*(https?|ftp|mailto|file):/;
|
||||
|
||||
|
||||
/**
|
||||
@@ -209,11 +210,41 @@ function $CompileProvider($provide) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.$compileProvider#urlSanitizationWhitelist
|
||||
* @methodOf ng.$compileProvider
|
||||
* @function
|
||||
*
|
||||
* @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 `urlSanitizationWhitelist` 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 it is 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.urlSanitizationWhitelist = function(regexp) {
|
||||
if (isDefined(regexp)) {
|
||||
urlSanitizationWhitelist = regexp;
|
||||
return this;
|
||||
}
|
||||
return urlSanitizationWhitelist;
|
||||
};
|
||||
|
||||
|
||||
this.$get = [
|
||||
'$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
|
||||
'$controller', '$rootScope',
|
||||
'$controller', '$rootScope', '$document',
|
||||
function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse,
|
||||
$controller, $rootScope) {
|
||||
$controller, $rootScope, $document) {
|
||||
|
||||
var Attributes = function(element, attr) {
|
||||
this.$$element = element;
|
||||
@@ -235,7 +266,8 @@ function $CompileProvider($provide) {
|
||||
*/
|
||||
$set: function(key, value, writeAttr, attrName) {
|
||||
var booleanKey = getBooleanAttrName(this.$$element[0], key),
|
||||
$$observers = this.$$observers;
|
||||
$$observers = this.$$observers,
|
||||
normalizedVal;
|
||||
|
||||
if (booleanKey) {
|
||||
this.$$element.prop(key, value);
|
||||
@@ -254,6 +286,19 @@ function $CompileProvider($provide) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// sanitize a[href] values
|
||||
if (nodeName_(this.$$element[0]) === 'A' && key === 'href') {
|
||||
urlSanitizationNode.setAttribute('href', value);
|
||||
|
||||
// href property always returns normalized absolute url, so we can match against that
|
||||
normalizedVal = urlSanitizationNode.href;
|
||||
if (!normalizedVal.match(urlSanitizationWhitelist)) {
|
||||
this[key] = value = 'unsafe:' + normalizedVal;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (writeAttr !== false) {
|
||||
if (value === null || value === undefined) {
|
||||
this.$$element.removeAttr(attrName);
|
||||
@@ -297,7 +342,8 @@ function $CompileProvider($provide) {
|
||||
}
|
||||
};
|
||||
|
||||
var startSymbol = $interpolate.startSymbol(),
|
||||
var urlSanitizationNode = $document[0].createElement('a'),
|
||||
startSymbol = $interpolate.startSymbol(),
|
||||
endSymbol = $interpolate.endSymbol(),
|
||||
denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
|
||||
? identity
|
||||
@@ -312,7 +358,7 @@ function $CompileProvider($provide) {
|
||||
|
||||
function compile($compileNodes, transcludeFn, maxPriority) {
|
||||
if (!($compileNodes instanceof jqLite)) {
|
||||
// jquery always rewraps, where as we need to preserve the original selector so that we can modify it.
|
||||
// jquery always rewraps, whereas we need to preserve the original selector so that we can modify it.
|
||||
$compileNodes = jqLite($compileNodes);
|
||||
}
|
||||
// We can not compile top level text elements since text nodes can be merged and we will
|
||||
@@ -330,7 +376,14 @@ function $CompileProvider($provide) {
|
||||
var $linkNode = cloneConnectFn
|
||||
? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!!
|
||||
: $compileNodes;
|
||||
$linkNode.data('$scope', scope);
|
||||
|
||||
// Attach scope only to non-text nodes.
|
||||
for(var i = 0, ii = $linkNode.length; i<ii; i++) {
|
||||
var node = $linkNode[i];
|
||||
if (node.nodeType == 1 /* element */ || node.nodeType == 9 /* document */) {
|
||||
$linkNode.eq(i).data('$scope', scope);
|
||||
}
|
||||
}
|
||||
safeAddClass($linkNode, 'ng-scope');
|
||||
if (cloneConnectFn) cloneConnectFn($linkNode, scope);
|
||||
if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode);
|
||||
@@ -357,7 +410,7 @@ function $CompileProvider($provide) {
|
||||
* functions return values - the linking functions - are combined into a composite linking
|
||||
* function, which is the a linking function for the node.
|
||||
*
|
||||
* @param {NodeList} nodeList an array of nodes to compile
|
||||
* @param {NodeList} nodeList an array of nodes or NodeList to compile
|
||||
* @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
|
||||
* scope argument is auto-generated to the new child of the transcluded parent scope.
|
||||
* @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then the
|
||||
@@ -380,7 +433,7 @@ function $CompileProvider($provide) {
|
||||
? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement)
|
||||
: null;
|
||||
|
||||
childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes.length)
|
||||
childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes || !nodeList[i].childNodes.length)
|
||||
? null
|
||||
: compileNodes(nodeList[i].childNodes,
|
||||
nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);
|
||||
@@ -420,6 +473,7 @@ function $CompileProvider($provide) {
|
||||
(function(transcludeFn) {
|
||||
return function(cloneFn) {
|
||||
var transcludeScope = scope.$new();
|
||||
transcludeScope.$$transcluded = true;
|
||||
|
||||
return transcludeFn(transcludeScope, cloneFn).
|
||||
bind('$destroy', bind(transcludeScope, transcludeScope.$destroy));
|
||||
@@ -515,9 +569,9 @@ function $CompileProvider($provide) {
|
||||
|
||||
|
||||
/**
|
||||
* Once the directives have been collected their compile functions is executed. This method
|
||||
* Once the directives have been collected, their compile functions are executed. This method
|
||||
* is responsible for inlining directive templates as well as terminating the application
|
||||
* of the directives if the terminal directive has been reached..
|
||||
* of the directives if the terminal directive has been reached.
|
||||
*
|
||||
* @param {Array} directives Array of collected directives to execute their compile function.
|
||||
* this needs to be pre-sorted by priority order.
|
||||
@@ -525,11 +579,11 @@ function $CompileProvider($provide) {
|
||||
* @param {Object} templateAttrs The shared attribute function
|
||||
* @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
|
||||
* scope argument is auto-generated to the new child of the transcluded parent scope.
|
||||
* @param {DOMElement} $rootElement If we are working on the root of the compile tree then this
|
||||
* argument has the root jqLite array so that we can replace widgets on it.
|
||||
* @param {JQLite} jqCollection If we are working on the root of the compile tree then this
|
||||
* argument has the root jqLite array so that we can replace nodes on it.
|
||||
* @returns linkFn
|
||||
*/
|
||||
function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, $rootElement) {
|
||||
function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, jqCollection) {
|
||||
var terminalPriority = -Number.MAX_VALUE,
|
||||
preLinkFns = [],
|
||||
postLinkFns = [],
|
||||
@@ -583,7 +637,7 @@ function $CompileProvider($provide) {
|
||||
$compileNode = templateAttrs.$$element =
|
||||
jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' '));
|
||||
compileNode = $compileNode[0];
|
||||
replaceWith($rootElement, jqLite($template[0]), compileNode);
|
||||
replaceWith(jqCollection, jqLite($template[0]), compileNode);
|
||||
childTranscludeFn = compile($template, transcludeFn, terminalPriority);
|
||||
} else {
|
||||
$template = jqLite(JQLiteClone(compileNode)).contents();
|
||||
@@ -607,7 +661,7 @@ function $CompileProvider($provide) {
|
||||
throw new Error(MULTI_ROOT_TEMPLATE_ERROR + directiveValue);
|
||||
}
|
||||
|
||||
replaceWith($rootElement, $compileNode, compileNode);
|
||||
replaceWith(jqCollection, $compileNode, compileNode);
|
||||
|
||||
var newTemplateAttrs = {$attr: {}};
|
||||
|
||||
@@ -635,7 +689,7 @@ function $CompileProvider($provide) {
|
||||
assertNoDuplicate('template', templateDirective, directive, $compileNode);
|
||||
templateDirective = directive;
|
||||
nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i),
|
||||
nodeLinkFn, $compileNode, templateAttrs, $rootElement, directive.replace,
|
||||
nodeLinkFn, $compileNode, templateAttrs, jqCollection, directive.replace,
|
||||
childTranscludeFn);
|
||||
ii = directives.length;
|
||||
} else if (directive.compile) {
|
||||
@@ -725,6 +779,8 @@ function $CompileProvider($provide) {
|
||||
lastValue,
|
||||
parentGet, parentSet;
|
||||
|
||||
scope.$$isolateBindings[scopeName] = mode + attrName;
|
||||
|
||||
switch (mode) {
|
||||
|
||||
case '@': {
|
||||
@@ -766,7 +822,7 @@ function $CompileProvider($provide) {
|
||||
parentGet = $parse(attrs[attrName]);
|
||||
scope[scopeName] = function(locals) {
|
||||
return parentGet(parentScope, locals);
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -935,8 +991,8 @@ function $CompileProvider($provide) {
|
||||
}
|
||||
|
||||
directives.unshift(derivedSyncDirective);
|
||||
afterTemplateNodeLinkFn = applyDirectivesToNode(directives, $compileNode, tAttrs, childTranscludeFn);
|
||||
afterTemplateChildLinkFn = compileNodes($compileNode.contents(), childTranscludeFn);
|
||||
afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn);
|
||||
afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
|
||||
|
||||
|
||||
while(linkQueue.length) {
|
||||
@@ -1015,10 +1071,10 @@ function $CompileProvider($provide) {
|
||||
function addAttrInterpolateDirective(node, directives, value, name) {
|
||||
var interpolateFn = $interpolate(value, true);
|
||||
|
||||
|
||||
// no interpolation found -> ignore
|
||||
if (!interpolateFn) return;
|
||||
|
||||
|
||||
directives.push({
|
||||
priority: 100,
|
||||
compile: valueFn(function attrInterpolateLinkFn(scope, element, attr) {
|
||||
|
||||
@@ -52,7 +52,7 @@ function $ControllerProvider() {
|
||||
* @description
|
||||
* `$controller` service is responsible for instantiating controllers.
|
||||
*
|
||||
* It's just simple call to {@link AUTO.$injector $injector}, but extracted into
|
||||
* It's just a simple call to {@link AUTO.$injector $injector}, but extracted into
|
||||
* a service, so that one can override this service with {@link https://gist.github.com/1649788
|
||||
* BC version}.
|
||||
*/
|
||||
|
||||
+15
-5
@@ -11,15 +11,25 @@
|
||||
*
|
||||
* The reasoning for this change is to allow easy creation of action links with `ngClick` directive
|
||||
* without changing the location or causing page reloads, e.g.:
|
||||
* <a href="" ng-click="model.$save()">Save</a>
|
||||
* `<a href="" ng-click="model.$save()">Save</a>`
|
||||
*/
|
||||
var htmlAnchorDirective = valueFn({
|
||||
restrict: 'E',
|
||||
compile: function(element, attr) {
|
||||
// turn <a href ng-click="..">link</a> into a link in IE
|
||||
// but only if it doesn't have name attribute, in which case it's an anchor
|
||||
if (!attr.href) {
|
||||
attr.$set('href', '');
|
||||
|
||||
if (msie <= 8) {
|
||||
|
||||
// turn <a href ng-click="..">link</a> into a stylable link in IE
|
||||
// but only if it doesn't have name attribute, in which case it's an anchor
|
||||
if (!attr.href && !attr.name) {
|
||||
attr.$set('href', '');
|
||||
}
|
||||
|
||||
// add a comment node to anchors to workaround IE bug that causes element content to be reset
|
||||
// to new attribute content if attribute is updated with value containing @ and element also
|
||||
// contains value with @
|
||||
// see issue #1949
|
||||
element.append(document.createComment('IE fix'));
|
||||
}
|
||||
|
||||
return function(scope, element) {
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
it('should execute ng-click but not reload when no href but name specified', function() {
|
||||
element('#link-5').click();
|
||||
expect(input('value').val()).toEqual('5');
|
||||
expect(element('#link-5').attr('href')).toBe('');
|
||||
expect(element('#link-5').attr('href')).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should only change url when only ng-href', function() {
|
||||
@@ -309,8 +309,9 @@ forEach(['src', 'href'], function(attrName) {
|
||||
|
||||
// on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
|
||||
// then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
|
||||
// to set the property as well to achieve the desired effect
|
||||
if (msie) element.prop(attrName, value);
|
||||
// to set the property as well to achieve the desired effect.
|
||||
// we use attr[attrName] value since $set can sanitize the url.
|
||||
if (msie) element.prop(attrName, attr[attrName]);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
+19
-10
@@ -85,8 +85,8 @@ var inputType = {
|
||||
*
|
||||
* @param {string} ngModel Assignable angular expression to data-bind to.
|
||||
* @param {string=} name Property name of the form under which the control is published.
|
||||
* @param {string=} min Sets the `min` validation error key if the value entered is less then `min`.
|
||||
* @param {string=} max Sets the `max` validation error key if the value entered is greater then `min`.
|
||||
* @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
|
||||
* @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
|
||||
* @param {string=} required Sets `required` validation error key if the value is not entered.
|
||||
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
|
||||
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
|
||||
@@ -398,6 +398,15 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
} else {
|
||||
var timeout;
|
||||
|
||||
var deferListener = function() {
|
||||
if (!timeout) {
|
||||
timeout = $browser.defer(function() {
|
||||
listener();
|
||||
timeout = null;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
element.bind('keydown', function(event) {
|
||||
var key = event.keyCode;
|
||||
|
||||
@@ -405,16 +414,16 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
// command modifiers arrows
|
||||
if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
|
||||
|
||||
if (!timeout) {
|
||||
timeout = $browser.defer(function() {
|
||||
listener();
|
||||
timeout = null;
|
||||
});
|
||||
}
|
||||
deferListener();
|
||||
});
|
||||
|
||||
// if user paste into input using mouse, we need "change" event to catch it
|
||||
element.bind('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.bind('paste cut', deferListener);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -713,7 +722,7 @@ function checkboxInputType(scope, element, attr, ctrl) {
|
||||
<tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br>
|
||||
<tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br>
|
||||
<tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br>
|
||||
<tt>myForm.userName.$error = {{myForm.lastName.$error}}</tt><br>
|
||||
<tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br>
|
||||
<tt>myForm.$valid = {{myForm.$valid}}</tt><br>
|
||||
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
|
||||
<tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br>
|
||||
@@ -976,7 +985,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
* For example {@link ng.directive:input input} or
|
||||
* {@link ng.directive:select select} directives call it.
|
||||
*
|
||||
* It internally calls all `formatters` and if resulted value is valid, updates the model and
|
||||
* It internally calls all `parsers` and if resulted value is valid, updates the model and
|
||||
* calls all registered change listeners.
|
||||
*
|
||||
* @param {string} value Value from the view.
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
|
||||
* `{{ expression }}` which is similar but less verbose.
|
||||
*
|
||||
* Once scenario in which the use of `ngBind` is prefered over `{{ expression }}` binding is when
|
||||
* One scenario in which the use of `ngBind` is preferred over `{{ expression }}` binding is when
|
||||
* it's desirable to put bindings into template that is momentarily displayed by the browser in its
|
||||
* raw state before Angular compiles it. Since `ngBind` is an element attribute, it makes the
|
||||
* bindings invisible to the user while the page is loading.
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
function classDirective(name, selector) {
|
||||
name = 'ngClass' + name;
|
||||
return ngDirective(function(scope, element, attr) {
|
||||
var oldVal = undefined;
|
||||
|
||||
scope.$watch(attr[name], ngClassWatchAction, true);
|
||||
|
||||
@@ -14,9 +15,9 @@ function classDirective(name, selector) {
|
||||
|
||||
if (name !== 'ngClass') {
|
||||
scope.$watch('$index', function($index, old$index) {
|
||||
var mod = $index % 2;
|
||||
if (mod !== old$index % 2) {
|
||||
if (mod == selector) {
|
||||
var mod = $index & 1;
|
||||
if (mod !== old$index & 1) {
|
||||
if (mod === selector) {
|
||||
addClass(scope.$eval(attr[name]));
|
||||
} else {
|
||||
removeClass(scope.$eval(attr[name]));
|
||||
@@ -26,13 +27,14 @@ function classDirective(name, selector) {
|
||||
}
|
||||
|
||||
|
||||
function ngClassWatchAction(newVal, oldVal) {
|
||||
function ngClassWatchAction(newVal) {
|
||||
if (selector === true || scope.$index % 2 === selector) {
|
||||
if (oldVal && (newVal !== oldVal)) {
|
||||
if (oldVal && !equals(newVal,oldVal)) {
|
||||
removeClass(oldVal);
|
||||
}
|
||||
addClass(newVal);
|
||||
}
|
||||
oldVal = copy(newVal);
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +67,7 @@ function classDirective(name, selector) {
|
||||
*
|
||||
* The directive won't add duplicate classes if a particular class was already set.
|
||||
*
|
||||
* When the expression changes, the previously added classes are removed and only then the classes
|
||||
* When the expression changes, the previously added classes are removed and only then the
|
||||
* new classes are added.
|
||||
*
|
||||
* @element ANY
|
||||
@@ -158,7 +160,7 @@ var ngClassOddDirective = classDirective('Odd', 0);
|
||||
* @name ng.directive:ngClassEven
|
||||
*
|
||||
* @description
|
||||
* The `ngClassOdd` and `ngClassEven` works exactly as
|
||||
* The `ngClassOdd` and `ngClassEven` directives work exactly as
|
||||
* {@link ng.directive:ngClass ngClass}, except it works in
|
||||
* conjunction with `ngRepeat` and takes affect only on odd (even) rows.
|
||||
*
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* `angular.min.js` files. Following is the css rule:
|
||||
*
|
||||
* <pre>
|
||||
* [ng\:cloak], [ng-cloak], .ng-cloak {
|
||||
* [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
|
||||
* display: none;
|
||||
* }
|
||||
* </pre>
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
* * Controller — The `ngController` directive specifies a Controller class; the class has
|
||||
* methods that typically express the business logic behind the application.
|
||||
*
|
||||
* Note that an alternative way to define controllers is via the `{@link ng.$route}`
|
||||
* service.
|
||||
* Note that an alternative way to define controllers is via the {@link ng.$route $route} service.
|
||||
*
|
||||
* @element ANY
|
||||
* @scope
|
||||
|
||||
@@ -5,16 +5,32 @@
|
||||
* @name ng.directive:ngCsp
|
||||
* @priority 1000
|
||||
*
|
||||
* @element html
|
||||
* @description
|
||||
* Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support.
|
||||
* This directive should be used on the root element of the application (typically the `<html>`
|
||||
* element or other element with the {@link ng.directive:ngApp ngApp}
|
||||
* directive).
|
||||
*
|
||||
* If enabled the performance of template expression evaluator will suffer slightly, so don't enable
|
||||
* this mode unless you need it.
|
||||
*
|
||||
* @element html
|
||||
*
|
||||
* This is necessary when developing things like Google Chrome Extensions.
|
||||
*
|
||||
* CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
|
||||
* For us to be compatible, we just need to implement the "getterFn" in $parse without violating
|
||||
* any of these restrictions.
|
||||
*
|
||||
* AngularJS uses `Function(string)` generated functions as a speed optimization. By applying `ngCsp`
|
||||
* it is be possible to opt into the CSP compatible mode. When this mode is on AngularJS will
|
||||
* evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will
|
||||
* be raised.
|
||||
*
|
||||
* In order to use this feature put `ngCsp` directive on the root element of the application.
|
||||
*
|
||||
* @example
|
||||
* This example shows how to apply the `ngCsp` directive to the `html` tag.
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html ng-app ng-csp>
|
||||
...
|
||||
...
|
||||
</html>
|
||||
</pre>
|
||||
*/
|
||||
|
||||
var ngCspDirective = ['$sniffer', function($sniffer) {
|
||||
|
||||
@@ -194,7 +194,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
|
||||
if (!isNaN(value)) {
|
||||
//if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise,
|
||||
//check it against pluralization rules in $locale service
|
||||
if (!whens[value]) value = $locale.pluralCat(value - offset);
|
||||
if (!(value in whens)) value = $locale.pluralCat(value - offset);
|
||||
return whensExpFns[value](scope, element, true);
|
||||
} else {
|
||||
return '';
|
||||
|
||||
@@ -96,7 +96,7 @@ var ngRepeatDirective = ngDirective({
|
||||
// Same as lastOrder but it has the current state. It will become the
|
||||
// lastOrder on the next iteration.
|
||||
nextOrder = new HashQueueMap(),
|
||||
arrayLength,
|
||||
arrayBound,
|
||||
childScope,
|
||||
key, value, // key/value of iteration
|
||||
array,
|
||||
@@ -117,7 +117,7 @@ var ngRepeatDirective = ngDirective({
|
||||
array = collection || [];
|
||||
}
|
||||
|
||||
arrayLength = array.length;
|
||||
arrayBound = array.length-1;
|
||||
|
||||
// we are not using forEach for perf reasons (trying to avoid #call)
|
||||
for (index = 0, length = array.length; index < length; index++) {
|
||||
@@ -154,7 +154,7 @@ var ngRepeatDirective = ngDirective({
|
||||
childScope.$index = index;
|
||||
|
||||
childScope.$first = (index === 0);
|
||||
childScope.$last = (index === (arrayLength - 1));
|
||||
childScope.$last = (index === arrayBound);
|
||||
childScope.$middle = !(childScope.$first || childScope.$last);
|
||||
|
||||
if (!last) {
|
||||
|
||||
@@ -8,11 +8,13 @@
|
||||
* @description
|
||||
* Conditionally change the DOM structure.
|
||||
*
|
||||
* @usageContent
|
||||
* <ANY ng-switch-when="matchValue1">...</ANY>
|
||||
* @usage
|
||||
* <ANY ng-switch="expression">
|
||||
* <ANY ng-switch-when="matchValue1">...</ANY>
|
||||
* <ANY ng-switch-when="matchValue2">...</ANY>
|
||||
* ...
|
||||
* <ANY ng-switch-default>...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
* @scope
|
||||
* @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.
|
||||
@@ -63,9 +65,10 @@ var NG_SWITCH = 'ng-switch';
|
||||
var ngSwitchDirective = valueFn({
|
||||
restrict: 'EA',
|
||||
require: 'ngSwitch',
|
||||
controller: function ngSwitchController() {
|
||||
// asks for $scope to fool the BC controller module
|
||||
controller: ['$scope', function ngSwitchController() {
|
||||
this.cases = {};
|
||||
},
|
||||
}],
|
||||
link: function(scope, element, attr, ctrl) {
|
||||
var watchExpr = attr.ngSwitch || attr.on,
|
||||
selectedTransclude,
|
||||
|
||||
@@ -147,7 +147,7 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c
|
||||
if (current.controller) {
|
||||
locals.$scope = lastScope;
|
||||
controller = $controller(current.controller, locals);
|
||||
element.contents().data('$ngControllerController', controller);
|
||||
element.children().data('$ngControllerController', controller);
|
||||
}
|
||||
|
||||
link(lastScope);
|
||||
|
||||
+13
-10
@@ -27,7 +27,8 @@
|
||||
* `select` model to be bound to a non-string value. This is because an option element can currently
|
||||
* be bound to string values only.
|
||||
*
|
||||
* @param {string} name assignable expression to data-bind to.
|
||||
* @param {string} ngModel Assignable angular expression to data-bind to.
|
||||
* @param {string=} name Property name of the form under which the control is published.
|
||||
* @param {string=} required The control is considered valid only if value is entered.
|
||||
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
|
||||
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
|
||||
@@ -122,7 +123,7 @@
|
||||
|
||||
var ngOptionsDirective = valueFn({ terminal: true });
|
||||
var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
//00001111100000000000222200000000000000000000003333000000000000044444444444444444000000000555555555555555550000000666666666666666660000000000000007777
|
||||
//0000111110000000000022220000000000000000000000333300000000000000444444444444444440000000005555555555555555500000006666666666666666600000000000000077770
|
||||
var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/,
|
||||
nullModelCtrl = {$setViewValue: noop};
|
||||
|
||||
@@ -394,10 +395,6 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
|
||||
if (multiple) {
|
||||
selectedSet = new HashMap(modelValue);
|
||||
} else if (modelValue === null || nullOption) {
|
||||
// if we are not multiselect, and we are null then we have to add the nullOption
|
||||
optionGroups[''].push({selected:modelValue === null, id:'', label:''});
|
||||
selectedSet = true;
|
||||
}
|
||||
|
||||
// We now build up the list of options we need (we merge later)
|
||||
@@ -422,9 +419,14 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
selected: selected // determine if we should be selected
|
||||
});
|
||||
}
|
||||
if (!multiple && !selectedSet) {
|
||||
// nothing was selected, we have to insert the undefined item
|
||||
optionGroups[''].unshift({id:'?', label:'', selected:true});
|
||||
if (!multiple) {
|
||||
if (nullOption || modelValue === null) {
|
||||
// insert null option if we have a placeholder, or the model is null
|
||||
optionGroups[''].unshift({id:'', label:'', selected:!selectedSet});
|
||||
} else if (!selectedSet) {
|
||||
// option could not be found, we have to insert the undefined item
|
||||
optionGroups[''].unshift({id:'?', label:'', selected:true});
|
||||
}
|
||||
}
|
||||
|
||||
// Now we need to update the list of DOM nodes to match the optionGroups we computed above
|
||||
@@ -468,7 +470,8 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
if (existingOption.id !== option.id) {
|
||||
lastElement.val(existingOption.id = option.id);
|
||||
}
|
||||
if (existingOption.element.selected !== option.selected) {
|
||||
// lastElement.prop('selected') provided by jQuery has side-effects
|
||||
if (lastElement[0].selected !== option.selected) {
|
||||
lastElement.prop('selected', (existingOption.selected = option.selected));
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
*
|
||||
*/
|
||||
function $ExceptionHandlerProvider() {
|
||||
this.$get = ['$log', function($log){
|
||||
this.$get = ['$log', function($log) {
|
||||
return function(exception, cause) {
|
||||
$log.error.apply($log, arguments);
|
||||
};
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@
|
||||
*
|
||||
* Filters are just functions which transform input to an output. However filters need to be Dependency Injected. To
|
||||
* achieve this a filter definition consists of a factory function which is annotated with dependencies and is
|
||||
* responsible for creating a the filter function.
|
||||
* responsible for creating a filter function.
|
||||
*
|
||||
* <pre>
|
||||
* // Filter registration
|
||||
@@ -69,7 +69,7 @@
|
||||
*
|
||||
* The general syntax in templates is as follows:
|
||||
*
|
||||
* {{ expression | [ filter_name ] }}
|
||||
* {{ expression [| filter_name[:parameter_value] ... ] }}
|
||||
*
|
||||
* @param {String} name Name of the filter function to retrieve
|
||||
* @return {Function} the filter function
|
||||
|
||||
@@ -43,22 +43,22 @@
|
||||
|
||||
Search: <input ng-model="searchText">
|
||||
<table id="searchTextResults">
|
||||
<tr><th>Name</th><th>Phone</th><tr>
|
||||
<tr><th>Name</th><th>Phone</th></tr>
|
||||
<tr ng-repeat="friend in friends | filter:searchText">
|
||||
<td>{{friend.name}}</td>
|
||||
<td>{{friend.phone}}</td>
|
||||
<tr>
|
||||
</tr>
|
||||
</table>
|
||||
<hr>
|
||||
Any: <input ng-model="search.$"> <br>
|
||||
Name only <input ng-model="search.name"><br>
|
||||
Phone only <input ng-model="search.phone"å><br>
|
||||
Phone only <input ng-model="search.phone"><br>
|
||||
<table id="searchObjResults">
|
||||
<tr><th>Name</th><th>Phone</th><tr>
|
||||
<tr><th>Name</th><th>Phone</th></tr>
|
||||
<tr ng-repeat="friend in friends | filter:search">
|
||||
<td>{{friend.name}}</td>
|
||||
<td>{{friend.phone}}</td>
|
||||
<tr>
|
||||
</tr>
|
||||
</table>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
@@ -82,7 +82,7 @@
|
||||
*/
|
||||
function filterFilter() {
|
||||
return function(array, expression) {
|
||||
if (!(array instanceof Array)) return array;
|
||||
if (!isArray(array)) return array;
|
||||
var predicates = [];
|
||||
predicates.check = function(value) {
|
||||
for (var j = 0; j < predicates.length; j++) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user