Compare commits
214 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5fdf42ce39 | |||
| bf6a0b7289 | |||
| 989446ecee | |||
| 5214c1d0cb | |||
| 4511d39cc7 | |||
| 15b8f205bb | |||
| 1d388676e3 | |||
| 84542d2431 | |||
| 91db99208e | |||
| acf095d178 | |||
| 301d8f233b | |||
| d70223e53e | |||
| 8ad02bb5a8 | |||
| ec1c5dfaee | |||
| 24e7da4f19 | |||
| 7b739c9702 | |||
| c1533ef576 | |||
| 679cb8a74a | |||
| 4e65635f85 | |||
| aa02534865 | |||
| b99f65f64d | |||
| f76474823a | |||
| 8ba1fd87e1 | |||
| 4d2dd46483 | |||
| b24cc63bcb | |||
| 49dfdf8f02 | |||
| 5bcb749abb | |||
| 499a76a08c | |||
| 8e2675029f | |||
| d0159454df | |||
| 7f0eb15161 | |||
| c4fa487250 | |||
| cef3535c16 | |||
| fbb499e0a8 | |||
| e40f8d829f | |||
| 9c0418cf1a | |||
| 1564b82b49 | |||
| b431ee3850 | |||
| a44d3dcd6a | |||
| ee579a071a | |||
| 5df7e6fae5 | |||
| fff31d8d61 | |||
| 9cba23a588 | |||
| 705f4bbf11 | |||
| bd530e2257 | |||
| 843f762c57 | |||
| beea3a4bed | |||
| 3bd3cc571d | |||
| c7f1101520 | |||
| 76afa406b1 | |||
| f3c77858be | |||
| 96758c1c52 | |||
| 006fb4fbeb | |||
| 075c089b5c | |||
| 2b87c814ab | |||
| 2b1b257034 | |||
| 73caf76225 | |||
| dbb92efd13 | |||
| 1214084e9d | |||
| a18926f986 | |||
| b806b30861 | |||
| 43d15f830f | |||
| 1d26acb874 | |||
| 983c309542 | |||
| 904b69c745 | |||
| c65c34ebfe | |||
| 8ebe5ccd9a | |||
| e61fd1b43a | |||
| ce15a3e049 | |||
| 46bb08a9d0 | |||
| 94dd685709 | |||
| dc32ea627e | |||
| eafe15f54c | |||
| 666f326c5d | |||
| 908785960d | |||
| 5cc245dd80 | |||
| 0bd0ef7813 | |||
| 0c7252f929 | |||
| b94fb5c8c1 | |||
| c322735f83 | |||
| 9260b4937d | |||
| e9ccec76a6 | |||
| 2037facc99 | |||
| b2d0a386f6 | |||
| 6d7e7fdea6 | |||
| df72852f34 | |||
| c4f6ccb065 | |||
| 0c49bbdc38 | |||
| 7d074a3775 | |||
| dceafd32ee | |||
| 0a5050eb3c | |||
| 7c430c5ed0 | |||
| 93d62860e9 | |||
| 5bcd719866 | |||
| e1743cc837 | |||
| 52ee1ab5eb | |||
| fcc556df37 | |||
| 5c0ec9d06d | |||
| ac2f0cece6 | |||
| fbaa1968b7 | |||
| 13d5528a5f | |||
| b5406d276d | |||
| 0f89383d98 | |||
| 10daefc6f4 | |||
| dc7b764d4d | |||
| 82d90a4096 | |||
| 7468bcb80b | |||
| bd4a4d390c | |||
| 94fca76a08 | |||
| 1c8c083404 | |||
| 0f2de12273 | |||
| 1bbc67ef6c | |||
| 637817e3ba | |||
| 86182a9415 | |||
| 15ecc6f366 | |||
| 53b2254ea7 | |||
| 6336b6e89e | |||
| fdf17d729f | |||
| 85776c0d37 | |||
| 02cf958a07 | |||
| 8fe4295a06 | |||
| dcb8e0767f | |||
| 21b77ad5c2 | |||
| 2f5dba488e | |||
| 7e86eacf30 | |||
| 15c1fe3929 | |||
| 428f2b5636 | |||
| 199ac26986 | |||
| 5f70d615a5 | |||
| 06d0955074 | |||
| a22e0699be | |||
| 28ff7c3a66 | |||
| 59ae8adb3c | |||
| c0b78478a0 | |||
| 59fa40ec0e | |||
| a1f7f5d4d0 | |||
| 20687aa5f6 | |||
| fc52b81d52 | |||
| ae1aee2b6c | |||
| 423242017e | |||
| 95c5df5958 | |||
| 2cb907a836 | |||
| 2f2fd465a4 | |||
| 6da355c3e1 | |||
| f2106692b1 | |||
| 4557881cf8 | |||
| af0ad6561c | |||
| 35125d2513 | |||
| 87f5c6e5b7 | |||
| a8a750ab05 | |||
| 13a95ae499 | |||
| da9f4dfcf4 | |||
| ac4318a2fa | |||
| bb2fa6f63f | |||
| ba59ef4950 | |||
| 8b93541522 | |||
| 7b22d59b4a | |||
| 798bca62c6 | |||
| 8218c4b60b | |||
| 2430f52bb9 | |||
| 944098a4e0 | |||
| 2ce0485e6f | |||
| a08cbc02e7 | |||
| 55027132f3 | |||
| 09e175f02c | |||
| 5c5b1183c8 | |||
| f04142ea28 | |||
| aaedefb92e | |||
| d54dfecb00 | |||
| 4b8d926062 | |||
| 74c84501ed | |||
| 4581b79bbd | |||
| 2be8847ef6 | |||
| cb2ad9abf2 | |||
| 73c8593077 | |||
| ac75079e21 | |||
| 5390fb37d2 | |||
| 8d7e694849 | |||
| 5fdab52dd7 | |||
| 541bedd1a9 | |||
| 98e18a64aa | |||
| 0a45bff472 | |||
| 263524d381 | |||
| 52c59cf0ce | |||
| c5f8edfe03 | |||
| 69f0aa899d | |||
| e7cd0bcc5a | |||
| ade6c45275 | |||
| 9eafd10fcd | |||
| 3436c027f2 | |||
| 6a8749e65a | |||
| 1a5bebd927 | |||
| 83155e8fbe | |||
| 6d6f875345 | |||
| a4fe51da3b | |||
| ee5a5352fd | |||
| 9cb2195e61 | |||
| 15213ec212 | |||
| 9171c76bb4 | |||
| 64fb1f2620 | |||
| f49eaf8bf2 | |||
| f701ce08f9 | |||
| 1cc0e4173d | |||
| d4ae7988da | |||
| 5ac14f633a | |||
| 9918b748be | |||
| 6ecac8e71a | |||
| 823adb2319 | |||
| 21e74c2d2e | |||
| 6c5a05ad49 | |||
| 192ff61f5d | |||
| 935c1018da | |||
| 78a6291666 | |||
| 53b6f522a5 |
@@ -1,3 +1,407 @@
|
||||
<a name="1.0.0rc10"></a>
|
||||
# 1.0.0rc10 tesseract-giftwrapping (2012-05-23)
|
||||
|
||||
## Features
|
||||
|
||||
- **$timeout:** add `$timeout` service that supersedes `$defer`
|
||||
([4511d39c](https://github.com/angular/angular.js/commit/4511d39cc748288df70bdc258f98a8f36652e683),
|
||||
[#704](https://github.com/angular/angular.js/issues/704),
|
||||
[#532](https://github.com/angular/angular.js/issues/532))
|
||||
- **scope:** add `event.preventDefault()` and `event.defaultPrevented`
|
||||
([84542d24](https://github.com/angular/angular.js/commit/84542d2431d20de42d6ec27c9d3435dd72dbe2ee))
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **ngRepeat:** expose `$first`, `$middle` and `$last` instead of `$position`
|
||||
([1d388676](https://github.com/angular/angular.js/commit/1d388676e3b97b6171fc498e82545bd437ee6fd1),
|
||||
[#912](https://github.com/angular/angular.js/issues/912))
|
||||
- **jqLite:** use the same expando store structure as jQuery
|
||||
([acf095d1](https://github.com/angular/angular.js/commit/acf095d1783e30e750d046ef24e81b5a0a31fbd4))
|
||||
- **$rootScope:** infinite digest exception does not clear $$phase
|
||||
([5989a1ed](https://github.com/angular/angular.js/commit/5989a1eda2b9e289b467ef9741fb1476549c8fd9),
|
||||
[#979](https://github.com/angular/angular.js/issues/979))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **ngRepeat - `$position` is not exposed in repeater scopes any more**
|
||||
|
||||
To update, search for `/\$position/` and replace it with one of `$first`, `$middle` or `$last`.
|
||||
([1d388676](https://github.com/angular/angular.js/commit/1d388676e3b97b6171fc498e82545bd437ee6fd1))
|
||||
|
||||
- **scope event's `cancel` method was renamed to `stopPropagation`**
|
||||
|
||||
The name was corrected in order to align better with DOM terminology.
|
||||
To update, search for `/\.\s*cancel\s*(/` and replace it with `.stopPropagation(` or
|
||||
`.preventDefault(` (or both) depending on what you actually need.
|
||||
([91db9920](https://github.com/angular/angular.js/commit/91db99208e197a73584a88a8d835eeb55c466335))
|
||||
|
||||
|
||||
## Deprecation Warnings
|
||||
|
||||
- **`$defer` service has been deprecated in favor of `$timeout` service**
|
||||
|
||||
The `$defer` service will be removed before 1.0 final, so please migrate your code.
|
||||
([4511d39c](https://github.com/angular/angular.js/commit/4511d39cc748288df70bdc258f98a8f36652e683))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.0rc9"></a>
|
||||
# 1.0.0rc9 eggplant-teleportation (2012-05-14)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$location:**
|
||||
- single quote in url causes infinite digest in FF
|
||||
([679cb8a7](https://github.com/angular/angular.js/commit/679cb8a74a684454fe38fa9e1ddad396bb598c52),
|
||||
[#920](https://github.com/angular/angular.js/issues/920))
|
||||
- support urls with any protocol
|
||||
([c1533ef5](https://github.com/angular/angular.js/commit/c1533ef5762199bea18d3bf3bcba7fcf89272931))
|
||||
- don't use buggy history.pushState api on Android < 4
|
||||
([7b739c97](https://github.com/angular/angular.js/commit/7b739c97028be2a5d5aef679ef1f8064cd10d386),
|
||||
[#904](https://github.com/angular/angular.js/issues/904))
|
||||
- work around Opera's base href issue
|
||||
([b99f65f6](https://github.com/angular/angular.js/commit/b99f65f64d1e54315b3210d78a9a9adbcf34c96c),
|
||||
[#938](https://github.com/angular/angular.js/issues/938))
|
||||
- **docs app:** get docs app to work on IE8
|
||||
([aa025348](https://github.com/angular/angular.js/commit/aa02534865c8e43dcef9e218b12c8c717c837205))
|
||||
|
||||
|
||||
|
||||
<a name="1.0.0rc8"></a>
|
||||
# 1.0.0rc8 blooming-touch (2012-05-06)
|
||||
|
||||
## Features
|
||||
|
||||
- **jqLite:** support data() getter and data(obj) setter
|
||||
([ee579a07](https://github.com/angular/angular.js/commit/ee579a071a91cbade729d3cb97e097568e71f8fc))
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- have $observe return registration function
|
||||
([7f0eb151](https://github.com/angular/angular.js/commit/7f0eb1516165fcb73f1c9953018b7c9b70acfae1))
|
||||
- ignore ws when checking if template has single root
|
||||
([9c0418cf](https://github.com/angular/angular.js/commit/9c0418cf1abd609bf0ffbe71fbdfa75905cf8e0f),
|
||||
[#910](https://github.com/angular/angular.js/issues/910))
|
||||
- fix replaceWith
|
||||
([b431ee38](https://github.com/angular/angular.js/commit/b431ee38509724ba9098a7be7a8d6c5dcded4fe9))
|
||||
- attach scope to the directive element when templateUrl and replace=true
|
||||
([705f4bbf](https://github.com/angular/angular.js/commit/705f4bbf115d2408e33b25f56edbf1f383aabb82))
|
||||
- prevent duplicate directive controller instantiation
|
||||
([843f762c](https://github.com/angular/angular.js/commit/843f762c573e38a044f920c5575c6feb46bc7226),
|
||||
[#876](https://github.com/angular/angular.js/issues/876))
|
||||
- **$parse:** support methods on falsy primitive types
|
||||
([499a76a0](https://github.com/angular/angular.js/commit/499a76a08cc7a7604dab5e1dd9cca675b8e29333))
|
||||
- **ngModel:** use keydown/change events on IE9 instead of input
|
||||
([49dfdf8f](https://github.com/angular/angular.js/commit/49dfdf8f0238ef8c473fcb44694f6b5696ecde70),
|
||||
[#879](https://github.com/angular/angular.js/issues/879))
|
||||
- **ngSrc,ngHref:** binding should set element prop as well as attr
|
||||
([b24cc63b](https://github.com/angular/angular.js/commit/b24cc63bcbd45741d21757653f05d54db09e0f20),
|
||||
[#935](https://github.com/angular/angular.js/issues/935))
|
||||
- **scenario:** make browser().location() working if ng-app on other than <html>
|
||||
([5bcb749a](https://github.com/angular/angular.js/commit/5bcb749abb91dba0847cb9bc900777a67fd55aa8))
|
||||
- **select:** don't interfere with selection if not databound
|
||||
([3bd3cc57](https://github.com/angular/angular.js/commit/3bd3cc571dcd721f9d71f971aefee23115a5e458),
|
||||
[#926](https://github.com/angular/angular.js/issues/926))
|
||||
|
||||
|
||||
## Docs
|
||||
|
||||
- Brand new bootstrap-based skin for api docs: <http://docs.angularjs.org/>
|
||||
|
||||
|
||||
<a name="1.0.0rc7"></a>
|
||||
# 1.0.0rc7 rc-generation (2012-04-30)
|
||||
|
||||
## Features
|
||||
|
||||
- **$parse:** CSP compatibility
|
||||
([2b87c814](https://github.com/angular/angular.js/commit/2b87c814ab70eaaff6359ce1a118f348c8bd2197),
|
||||
[#893](https://github.com/angular/angular.js/issues/893))
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **jqlite:**
|
||||
- correctly reset event properties in IE8
|
||||
([a18926f9](https://github.com/angular/angular.js/commit/a18926f986166048a21097636f03ab29f107b154))
|
||||
- mouseenter on FF no longer throws exceptions
|
||||
([43d15f83](https://github.com/angular/angular.js/commit/43d15f830f9d419c41c41f0682e47e86839e3917))
|
||||
|
||||
|
||||
## Docs
|
||||
|
||||
- Tutorial has been finally updated to AngularJS v1.0! Check it out and provide feedback to make it
|
||||
even better: <http://docs.angularjs.org/tutorial>
|
||||
- <http://docs-next.angularjs.org> now redirects to <http://docs.angularjs.org>
|
||||
|
||||
|
||||
|
||||
<a name="v1.0.0rc6"></a>
|
||||
# v1.0.0rc6 runny-nose (2012-04-20)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **select:** properly handle empty & unknown options without ngOptions
|
||||
([904b69c7](https://github.com/angular/angular.js/commit/904b69c745ea4afc1d6ecd2a5f3138c6f947b157))
|
||||
- **compiler:** reading comment throws error in ie
|
||||
([46bb08a9](https://github.com/angular/angular.js/commit/46bb08a9d0780fafef6dc5c1140c71912462887a))
|
||||
- **document:** accidental clobbering of document.getAttribute
|
||||
([eafe15f5](https://github.com/angular/angular.js/commit/eafe15f54c686d5c83f777fd319f4c568e209432),
|
||||
[#877](https://github.com/angular/angular.js/issues/877))
|
||||
- **script:** Incorrectly reading script text on ie
|
||||
([94dd6857](https://github.com/angular/angular.js/commit/94dd68570952f6f31abfa351b1159afcd3588a57))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$resource:** support HTTP PATCH method
|
||||
([e61fd1b4](https://github.com/angular/angular.js/commit/e61fd1b43a55496c11c63da7ca2fc05b88d44043),
|
||||
[#887](https://github.com/angular/angular.js/issues/887))
|
||||
- **jquery:** jquery 1.7.2 support
|
||||
([8ebe5ccd](https://github.com/angular/angular.js/commit/8ebe5ccd9ace7807bedc7317d605370fe82b773d))
|
||||
|
||||
|
||||
|
||||
<a name="1.0.0rc5"></a>
|
||||
# 1.0.0rc5 reality-distortion (2012-04-12)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$location:** properly rewrite urls in html5 mode with base url set + don't rewrite links to
|
||||
different base paths
|
||||
([6d7e7fde](https://github.com/angular/angular.js/commit/6d7e7fdea6c3d6551ff40c150aa42e1375d2cb5f),
|
||||
[0a5050eb](https://github.com/angular/angular.js/commit/0a5050eb3c1f1ed84134f23a44b97a7261114060))
|
||||
- **e2eRunner:** $browser.location should delegate to apps $location
|
||||
([df72852f](https://github.com/angular/angular.js/commit/df72852f3496d7640bb4f70837338e464b7ed69f))
|
||||
- **input.radio:** support 2-way binding in a repeater
|
||||
([93d62860](https://github.com/angular/angular.js/commit/93d62860e988a09fb64e594f50f6cd55a1fc5748),
|
||||
[#869](https://github.com/angular/angular.js/issues/869))
|
||||
- **ngBindHtml:** clear contents when model is falsy
|
||||
([10daefc6](https://github.com/angular/angular.js/commit/10daefc6f466a21d9418437666461c80cf24fcfe),
|
||||
[#864](https://github.com/angular/angular.js/issues/864))
|
||||
- lots of doc fixes
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$http:** expose the defaults config as $http.defaults
|
||||
([dceafd32](https://github.com/angular/angular.js/commit/dceafd32ee140c8af5c7a0ca6cb808395fffeed3))
|
||||
- **docs:** steps 0-4 of the Tutorial have been updated and improved
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- `ng-ext-link` directive was removed because it's unnecessary
|
||||
([6d7e7fde](https://github.com/angular/angular.js/commit/6d7e7fdea6c3d6551ff40c150aa42e1375d2cb5f))
|
||||
|
||||
apps that relied on ng-ext-link should simply replace it with `target="_self"`
|
||||
|
||||
- `$browser.addCss` was removed - it was never meant to be a public api
|
||||
([13d5528a](https://github.com/angular/angular.js/commit/13d5528a5f5a2f0feee5c742788a914d2371841e))
|
||||
|
||||
apps the depend on this functionality should write a simple utility function specific to the app
|
||||
(see this diff for hints).
|
||||
|
||||
- `$browser.addJs` method was removed - it was never meant to be a public api
|
||||
([fbaa1968](https://github.com/angular/angular.js/commit/fbaa1968b7c596ccb63ea8b4be1d3bd92eda50d8))
|
||||
|
||||
apps that depended on this functionality should either use many of the existing script loaders or
|
||||
create a simple helper method specific to the app.
|
||||
|
||||
- `$sanitize` service, `ngBindHtml` directive and `linky` filter were moved to the `ngSanitize` module
|
||||
([5bcd7198](https://github.com/angular/angular.js/commit/5bcd7198664dca2bf85ddf8b3a89f417cd4e4796))
|
||||
|
||||
apps that depend on any of these will need to load `angular-sanitize.js` and include `ngSanitize`
|
||||
in their dependency list: `var myApp = angular.module('myApp', ['ngSanitize']);`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.0rc4"></a>
|
||||
# 1.0.0rc4 insomnia-induction (2012-04-05)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:** relax the restriction that directives can not add siblings
|
||||
([7e86eacf](https://github.com/angular/angular.js/commit/7e86eacf301934335c22908ec6dbd1a083d88fab))
|
||||
- **$location:** search setter should not double-encode the value
|
||||
([59fa40ec](https://github.com/angular/angular.js/commit/59fa40ec0e851759d35fb0ea5fd01019d1403049),
|
||||
[#751](https://github.com/angular/angular.js/issues/751))
|
||||
- **$q:** $q.reject should forward callbacks if missing
|
||||
([c0b78478](https://github.com/angular/angular.js/commit/c0b78478a0e64942a69aba7c1bfa4eb01c0e9a5e),
|
||||
[#845](https://github.com/angular/angular.js/issues/845))
|
||||
- **build:** move `'use strict';` flag into the angular closure
|
||||
([637817e3](https://github.com/angular/angular.js/commit/637817e3ba48d149e7a9628533d21e81c650d988))
|
||||
- **Directives**:
|
||||
- **ngModel:** update model on each key stroke (revert ngModelInstant)
|
||||
([06d09550](https://github.com/angular/angular.js/commit/06d0955074f79de553cc34fbf945045dc458e064))
|
||||
- **booleanAttrs:** always convert the model to boolean before setting the element property
|
||||
([dcb8e076](https://github.com/angular/angular.js/commit/dcb8e0767fbf0a7a55f3b0045fd01b2532ea5441))
|
||||
- **form:** preperly clean up when invalid widget is removed
|
||||
([21b77ad5](https://github.com/angular/angular.js/commit/21b77ad5c231ab0e05eb89f22005f7ed8d40a6c1))
|
||||
- **ngHref:** copy even if no binding
|
||||
([2f5dba48](https://github.com/angular/angular.js/commit/2f5dba488e855bcdbb9304aa809efcb9de7b43e9))
|
||||
- **ngInclude:** fire $includeContentLoaded on proper (child) scope
|
||||
([199ac269](https://github.com/angular/angular.js/commit/199ac269869a57bb63d60c9b3f510d546bf0c9b2))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$http:** add `withCredentials` config option
|
||||
([86182a94](https://github.com/angular/angular.js/commit/86182a9415b9209662b16c25c180b958ba7e6cf9))
|
||||
- **$route:** allow chaining of whens and otherwise
|
||||
([15ecc6f3](https://github.com/angular/angular.js/commit/15ecc6f3668885ebc5c7130dd34e00059ddf79ae))
|
||||
- **ngInclude:** allow ngInclude as css class
|
||||
([428f2b56](https://github.com/angular/angular.js/commit/428f2b563663315df4f235ca19cef4bdcf82e2ab))
|
||||
|
||||
|
||||
## Docs
|
||||
- reintroduced the tutorial docs - currently only steps 0-3 are up to date and the code is not split
|
||||
up into step specific commits yet. See
|
||||
[this branch](https://github.com/angular/angular-phonecat/tree/v1.0-update) instead.
|
||||
- various other doc fixes
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
We removed two useless features:
|
||||
|
||||
- $routeProvider.when used to return the route definition object but now it returns self
|
||||
([15ecc6f3](https://github.com/angular/angular.js/commit/15ecc6f3668885ebc5c7130dd34e00059ddf79ae))
|
||||
- ngInclude does not have scope attribute anymore
|
||||
([5f70d615](https://github.com/angular/angular.js/commit/5f70d615a5f7e102424c6adc15d7a6f697870b6e))
|
||||
- ngModelInstant directive is no more and ngModel behaves just as ngModelInstant used to. This
|
||||
doesn't really break anything, just remember to remove all ngModelInstant references from your
|
||||
template as they serve no purpose now.
|
||||
([06d09550](https://github.com/angular/angular.js/commit/06d0955074f79de553cc34fbf945045dc458e064))
|
||||
|
||||
|
||||
|
||||
<a name="1.0.0rc3"></a>
|
||||
# 1.0.0rc3 barefoot-telepathy (2012-03-29)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- properly clone attr.$observers in ng-repeat
|
||||
([f2106692](https://github.com/angular/angular.js/commit/f2106692b1ebf00aa5f8b2accd75f014b6cd4faa))
|
||||
- create new (isolate) scopes for directives on root elements
|
||||
([5390fb37](https://github.com/angular/angular.js/commit/5390fb37d2c01937922613fc57df4986af521787),
|
||||
[#817](https://github.com/angular/angular.js/issues/817))
|
||||
- **angular.forEach:** should ignore prototypically inherited properties
|
||||
([8d7e6948](https://github.com/angular/angular.js/commit/8d7e6948496ff26ef1da8854ba02fcb8eebfed61),
|
||||
[#813](https://github.com/angular/angular.js/issues/813))
|
||||
- **initialization:** use jQuery#ready for initialization if available
|
||||
([cb2ad9ab](https://github.com/angular/angular.js/commit/cb2ad9abf24e6f855cc749efe3155bd7987ece9d),
|
||||
[#818](https://github.com/angular/angular.js/issues/818))
|
||||
- **$q:** resolve all of nothing to nothing
|
||||
([ac75079e](https://github.com/angular/angular.js/commit/ac75079e2113949d5d64adbcf23d56f3cf295d41))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$compile:** do not interpolate boolean attribute directives, rather evaluate them
|
||||
([a08cbc02](https://github.com/angular/angular.js/commit/a08cbc02e78e789a66e9af771c410e8ad1646e25))
|
||||
- **$controller:** support controller registration via $controllerProvider
|
||||
([d54dfecb](https://github.com/angular/angular.js/commit/d54dfecb00fba41455536c5ddd55310592fdaf84))
|
||||
- **$http:**
|
||||
- make the `transformRequest` and `transformResponse` default to an array
|
||||
([a8a750ab](https://github.com/angular/angular.js/commit/a8a750ab05bdff73ba3af0b98f3f284ff8d1e743))
|
||||
- added `params` parameter
|
||||
([73c85930](https://github.com/angular/angular.js/commit/73c8593077155a9f2e8ef42efd4c497eba0bef4f))
|
||||
- **TzDate:** add support for toISOString method
|
||||
([da9f4dfc](https://github.com/angular/angular.js/commit/da9f4dfcf4f3d0c21821d8474ac0bb19a3c51415))
|
||||
- **jqLite:** make injector() and scope() work with the document object
|
||||
([5fdab52d](https://github.com/angular/angular.js/commit/5fdab52dd7c269f99839f4fa6b5854d9548269fa))
|
||||
- **ngValue:** directive that allows radio inputs to have non string values
|
||||
([09e175f0](https://github.com/angular/angular.js/commit/09e175f02cca0f4a295fd0c9b980cd8f432e722b),
|
||||
[#816](https://github.com/angular/angular.js/issues/816))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- `$resource`, `$cookies` and `$cookieStore` services are now distributed as separate modules, see
|
||||
`angular-resource.js` and `angular-cookies.js`.
|
||||
([798bca62](https://github.com/angular/angular.js/commit/798bca62c6f64775b85deda3713e7b6bcc7a4b4d),
|
||||
[7b22d59b](https://github.com/angular/angular.js/commit/7b22d59b4a16d5c50c2eee054178ba17f8038880))
|
||||
- angular.fromJson doesn't deserialize date strings into date objects.
|
||||
([ac4318a2](https://github.com/angular/angular.js/commit/ac4318a2fa5c6d306dbc19466246292a81767fca))
|
||||
- angular.toJson always use native JSON.parse and JSON.stringify - this might break code that
|
||||
consumes the output in whitespace-sensitive way
|
||||
([35125d25](https://github.com/angular/angular.js/commit/35125d25137ac2da13ed1ca3e652ec8f2c945053))
|
||||
- IE7 and older have are now required to polyfill the JSON global object
|
||||
([87f5c6e5](https://github.com/angular/angular.js/commit/87f5c6e5b716100e203ec59c5874c3e927f83fa0))
|
||||
- boolean attr directives (ng-disabled, ng-required, etc) are evaluated rather than interpolated
|
||||
([a08cbc02](https://github.com/angular/angular.js/commit/a08cbc02e78e789a66e9af771c410e8ad1646e25))
|
||||
- `ng-bind-attr` directive removed
|
||||
([55027132](https://github.com/angular/angular.js/commit/55027132f3d57e5dcf94683e6e6bd7b0aae0087d))
|
||||
- any app that depends on $sniffer service should use Modernizr instead
|
||||
([aaedefb9](https://github.com/angular/angular.js/commit/aaedefb92e6bec6626e173e5155072c91471596a))
|
||||
|
||||
|
||||
|
||||
<a name="1.0.0rc2"></a>
|
||||
# 1.0.0rc2 silence-absorption (2012-03-20)
|
||||
|
||||
## Features
|
||||
|
||||
- **$route:** when matching consider trailing slash as optional
|
||||
([a4fe51da](https://github.com/angular/angular.js/commit/a4fe51da3ba0dc297ecd389e230d6664f250c9a6))
|
||||
- **jqLite:** add .controller() method
|
||||
([6c5a05ad](https://github.com/angular/angular.js/commit/6c5a05ad49a1e083570c3dfe331403398f899dbe))
|
||||
- **scope.$eval:** allow passing locals to the expression
|
||||
([192ff61f](https://github.com/angular/angular.js/commit/192ff61f5d61899e667c6dbce4d3e6e399429d8b))
|
||||
- **input[type=radio]:** allow the value attribute to be interpolated
|
||||
([ade6c452](https://github.com/angular/angular.js/commit/ade6c452753145c84884d17027a7865bf4b34b0c))
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$http:** don't send Content-Type header when no data
|
||||
([1a5bebd9](https://github.com/angular/angular.js/commit/1a5bebd927ecd22f9c34617642fdf58fe3f62efb),
|
||||
[#749](https://github.com/angular/angular.js/issues/749))
|
||||
- **$resource:** support escaping of ':' in resource url
|
||||
([6d6f8753](https://github.com/angular/angular.js/commit/6d6f875345e01f2c6c63ef95164f6f39e923da15))
|
||||
- **$compile:**
|
||||
- don't touch static element attributes
|
||||
([9cb2195e](https://github.com/angular/angular.js/commit/9cb2195e61a78e99020ec19d687a221ca88b5900))
|
||||
- merge interpolated css class when replacing an element
|
||||
([f49eaf8b](https://github.com/angular/angular.js/commit/f49eaf8bf2df5f4e0e82d6c89e849a4f82c8d414))
|
||||
- allow transclusion of root elements
|
||||
([9918b748](https://github.com/angular/angular.js/commit/9918b748be01266eb10db39d51b4d3098d54ab66))
|
||||
- **$log:** avoid console.log.apply calls in IE
|
||||
([15213ec2](https://github.com/angular/angular.js/commit/15213ec212769837cb2b7e781ffc5bfd598d27ca),
|
||||
[#805](https://github.com/angular/angular.js/issues/805))
|
||||
- **json:** added support for iso8061 timezone
|
||||
([5ac14f63](https://github.com/angular/angular.js/commit/5ac14f633a69f49973b5512780c6ec7752405967))
|
||||
- **e2e runner:** fix typo that caused errors on IE8
|
||||
([ee5a5352](https://github.com/angular/angular.js/commit/ee5a5352fd4b94cedee6ef20d4bf2d43ce77e00b),
|
||||
[#806](https://github.com/angular/angular.js/issues/806))
|
||||
- **directives:**
|
||||
- **select:** multiselect failes to update view on selection insert
|
||||
([6ecac8e7](https://github.com/angular/angular.js/commit/6ecac8e71a84792a434d21db2c245b3648c55f18))
|
||||
- **ngForm:** alias name||ngForm
|
||||
([823adb23](https://github.com/angular/angular.js/commit/823adb231995e917bc060bfa49453e2a96bac2b6))
|
||||
- **ngView:** publish the controller
|
||||
([21e74c2d](https://github.com/angular/angular.js/commit/21e74c2d2e8e985b23711785287feb59965cbd90))
|
||||
- **ngRepeat:** correct variable reference in error message
|
||||
([935c1018](https://github.com/angular/angular.js/commit/935c1018da05dbf3124b2dd33619c4a3c82d7a2a))
|
||||
- various doc fixes (some contributed by Daniel Zen)
|
||||
|
||||
|
||||
|
||||
<a name="1.0.0rc1"></a>
|
||||
# 1.0.0rc1 moiré-vision (2012-03-13)
|
||||
|
||||
@@ -63,9 +467,9 @@ docs. The biggest improvements and changes are listed below.
|
||||
So instead of `{{ someRawHtml | html }}` use `<div ng-bind-html="someRawHtml"></div>` and
|
||||
instead of `{{ someRawHtml | html:"unsafe" }}` use `<div ng-bind-html-unsafe="someRawHtml"></div>`.
|
||||
Please check out the
|
||||
[ng-bind-html](http://docs-next.angularjs.org/api/angular.module.ng.$compileProvider.directive.ng-bind-html)
|
||||
[ng-bind-html](http://docs-next.angularjs.org/api/angular.module.ng.$compileProvider.directive.ngBindHtml)
|
||||
and
|
||||
[ng-bind-html-unsafe](http://docs-next.angularjs.org/api/angular.module.ng.$compileProvider.directive.ng-bind-html-unsafe)
|
||||
[ng-bind-html-unsafe](http://docs-next.angularjs.org/api/angular.module.ng.$compileProvider.directive.ngBindHtmlUnsafe)
|
||||
directive docs.
|
||||
|
||||
- Custom markup has been used by developers only to switch from `{{ }}` markup to `(( ))` or
|
||||
@@ -214,13 +618,13 @@ behavior and migrate your controllers one at a time: <https://gist.github.com/16
|
||||
|
||||
## New directives:
|
||||
|
||||
- [ng-mouseleave](http://docs-next.angularjs.org/api/angular.module.ng.$compileProvider.directive.ng-mouseleave)
|
||||
- [ng-mousemove](http://docs-next.angularjs.org/api/angular.module.ng.$compileProvider.directive.ng-mousemove)
|
||||
- [ng-mouseover](http://docs-next.angularjs.org/api/angular.module.ng.$compileProvider.directive.ng-mouseover)
|
||||
- [ng-mouseup](http://docs-next.angularjs.org/api/angular.module.ng.$compileProvider.directive.ng-mouseup)
|
||||
- [ng-mousedown](http://docs-next.angularjs.org/api/angular.module.ng.$compileProvider.directive.ng-mousedown)
|
||||
- [ng-dblclick](http://docs-next.angularjs.org/api/angular.module.ng.$compileProvider.directive.ng-dblclick)
|
||||
- [ng-model-instant](http://docs-next.angularjs.org/api/angular.module.ng.$compileProvider.directive.ng-model-instant)
|
||||
- [ng-mouseleave](http://docs-next.angularjs.org/api/angular.module.ng.$compileProvider.directive.ngMouseleave)
|
||||
- [ng-mousemove](http://docs-next.angularjs.org/api/angular.module.ng.$compileProvider.directive.ngMousemove)
|
||||
- [ng-mouseover](http://docs-next.angularjs.org/api/angular.module.ng.$compileProvider.directive.ngMouseover)
|
||||
- [ng-mouseup](http://docs-next.angularjs.org/api/angular.module.ng.$compileProvider.directive.ngMouseup)
|
||||
- [ng-mousedown](http://docs-next.angularjs.org/api/angular.module.ng.$compileProvider.directive.ngMousedown)
|
||||
- [ng-dblclick](http://docs-next.angularjs.org/api/angular.module.ng.$compileProvider.directive.ngDblclick)
|
||||
- [ng-model-instant](http://docs-next.angularjs.org/api/angular.module.ng.$compileProvider.directive.ngModelInstant)
|
||||
|
||||
|
||||
## $injector / modules
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010 Adam Abrons and Misko Hevery http://getangular.com
|
||||
Copyright (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -2,7 +2,10 @@ require 'yaml'
|
||||
include FileUtils
|
||||
|
||||
content = File.open('angularFiles.js', 'r') {|f| f.read }
|
||||
files = eval(content.gsub(/\};(\s|\S)*/, '}').gsub(/angularFiles = /, '').gsub(/:/, '=>'));
|
||||
files = eval(content.gsub(/\};(\s|\S)*/, '}').
|
||||
gsub(/angularFiles = /, '').
|
||||
gsub(/:/, '=>').
|
||||
gsub(/\/\//, '#'));
|
||||
|
||||
BUILD_DIR = 'build'
|
||||
|
||||
@@ -35,37 +38,23 @@ end
|
||||
desc 'Compile Scenario'
|
||||
task :compile_scenario => :init do
|
||||
|
||||
deps = [
|
||||
concat_file('angular-scenario.js', [
|
||||
'lib/jquery/jquery.js',
|
||||
'src/scenario/angular.prefix',
|
||||
'src/ngScenario/angular.prefix',
|
||||
files['angularSrc'],
|
||||
files['angularScenario'],
|
||||
'src/scenario/angular.suffix',
|
||||
]
|
||||
|
||||
concat = 'cat ' + deps.flatten.join(' ')
|
||||
|
||||
File.open(path_to('angular-scenario.js'), 'w') do |f|
|
||||
f.write(%x{#{concat}}.gsub('"NG_VERSION_FULL"', NG_VERSION.full))
|
||||
f.write(gen_css('css/angular.css') + "\n")
|
||||
f.write(gen_css('css/angular-scenario.css'))
|
||||
end
|
||||
'src/ngScenario/angular.suffix',
|
||||
], gen_css('css/angular.css') + "\n" + gen_css('css/angular-scenario.css'))
|
||||
end
|
||||
|
||||
desc 'Compile JSTD Scenario Adapter'
|
||||
task :compile_jstd_scenario_adapter => :init do
|
||||
|
||||
deps = [
|
||||
'src/jstd-scenario-adapter/angular.prefix',
|
||||
'src/jstd-scenario-adapter/Adapter.js',
|
||||
'src/jstd-scenario-adapter/angular.suffix',
|
||||
]
|
||||
|
||||
concat = 'cat ' + deps.flatten.join(' ')
|
||||
|
||||
File.open(path_to('jstd-scenario-adapter.js'), 'w') do |f|
|
||||
f.write(%x{#{concat}}.gsub('"NG_VERSION_FULL"', NG_VERSION.full))
|
||||
end
|
||||
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)
|
||||
@@ -80,67 +69,51 @@ end
|
||||
desc 'Compile JavaScript'
|
||||
task :compile => [:init, :compile_scenario, :compile_jstd_scenario_adapter] do
|
||||
|
||||
deps = [
|
||||
'src/angular.prefix',
|
||||
files['angularSrc'],
|
||||
'src/angular.suffix',
|
||||
]
|
||||
concat_file('angular.js', [
|
||||
'src/angular.prefix',
|
||||
files['angularSrc'],
|
||||
'src/angular.suffix',
|
||||
], gen_css('css/angular.css', true))
|
||||
|
||||
File.open(path_to('angular.js'), 'w') do |f|
|
||||
concat = 'cat ' + deps.flatten.join(' ')
|
||||
FileUtils.cp_r 'src/ngLocale', path_to('i18n')
|
||||
|
||||
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
|
||||
gsub(/'USE STRICT'/, "'use strict'") # rename the placeholder in angular.prefix
|
||||
|
||||
f.write(content)
|
||||
f.write(gen_css('css/angular.css', true))
|
||||
end
|
||||
|
||||
%x(java -jar lib/closure-compiler/compiler.jar \
|
||||
--compilation_level SIMPLE_OPTIMIZATIONS \
|
||||
--language_in ECMASCRIPT5_STRICT \
|
||||
--js #{path_to('angular.js')} \
|
||||
--js_output_file #{path_to('angular.min.js')})
|
||||
|
||||
FileUtils.cp_r 'i18n/locale', path_to('i18n')
|
||||
|
||||
File.open(path_to('angular-loader.js'), 'w') do |f|
|
||||
concat = 'cat ' + [
|
||||
concat_file('angular-loader.js', [
|
||||
'src/loader.prefix',
|
||||
'src/loader.js',
|
||||
'src/loader.suffix'].flatten.join(' ')
|
||||
'src/loader.suffix'])
|
||||
|
||||
content = %x{#{concat}}.
|
||||
gsub('"NG_VERSION_FULL"', NG_VERSION.full).
|
||||
gsub(/^\s*['"]use strict['"];?\s*$/, '') # remove all file-specific strict mode flags
|
||||
|
||||
f.write(content)
|
||||
end
|
||||
concat_module('sanitize', [
|
||||
'src/ngSanitize/sanitize.js',
|
||||
'src/ngSanitize/directive/ngBindHtml.js',
|
||||
'src/ngSanitize/filter/linky.js'])
|
||||
|
||||
%x(java -jar lib/closure-compiler/compiler.jar \
|
||||
--compilation_level SIMPLE_OPTIMIZATIONS \
|
||||
--language_in ECMASCRIPT5_STRICT \
|
||||
--js #{path_to('angular-loader.js')} \
|
||||
--js_output_file #{path_to('angular-loader.min.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')
|
||||
|
||||
closure_compile('angular.js')
|
||||
closure_compile('angular-cookies.js')
|
||||
closure_compile('angular-loader.js')
|
||||
closure_compile('angular-resource.js')
|
||||
closure_compile('angular-sanitize.js')
|
||||
closure_compile('angular-bootstrap.js')
|
||||
closure_compile('angular-bootstrap-prettify.js')
|
||||
|
||||
end
|
||||
|
||||
|
||||
desc 'Generate docs'
|
||||
task :docs => [:init] do
|
||||
`node docs/src/gen-docs.js`
|
||||
File.open(path_to('docs/.htaccess'), File::RDWR) do |f|
|
||||
text = f.read
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write text.sub('"NG_VERSION_FULL"', NG_VERSION.full)
|
||||
rewrite_file(path_to('docs/.htaccess')) do |content|
|
||||
content.sub!('"NG_VERSION_FULL"', NG_VERSION.full)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -153,103 +126,62 @@ task :package => [:clean, :compile, :docs] do
|
||||
FileUtils.rm_r(path_to('pkg'), :force => true)
|
||||
FileUtils.mkdir_p(pkg_dir)
|
||||
|
||||
['src/angular-mocks.js',
|
||||
path_to('angular.js'),
|
||||
path_to('angular-loader.js'),
|
||||
[ path_to('angular.js'),
|
||||
path_to('angular.min.js'),
|
||||
path_to('angular-loader.js'),
|
||||
path_to('angular-loader.min.js'),
|
||||
path_to('angular-bootstrap.js'),
|
||||
path_to('angular-bootstrap.min.js'),
|
||||
path_to('angular-bootstrap-prettify.js'),
|
||||
path_to('angular-bootstrap-prettify.min.js'),
|
||||
path_to('angular-mocks.js'),
|
||||
path_to('angular-cookies.js'),
|
||||
path_to('angular-cookies.min.js'),
|
||||
path_to('angular-resource.js'),
|
||||
path_to('angular-resource.min.js'),
|
||||
path_to('angular-sanitize.js'),
|
||||
path_to('angular-sanitize.min.js'),
|
||||
path_to('angular-scenario.js'),
|
||||
path_to('jstd-scenario-adapter.js'),
|
||||
path_to('jstd-scenario-adapter-config.js'),
|
||||
].each do |src|
|
||||
dest = src.gsub(/^[^\/]+\//, '').gsub(/((\.min)?\.js)$/, "-#{NG_VERSION.full}\\1")
|
||||
dest = src.gsub(/^.*\//, '').gsub(/((\.min)?\.js)$/, "-#{NG_VERSION.full}\\1")
|
||||
FileUtils.cp(src, pkg_dir + '/' + dest)
|
||||
end
|
||||
|
||||
FileUtils.cp_r path_to('i18n'), "#{pkg_dir}/i18n-#{NG_VERSION.full}"
|
||||
FileUtils.cp_r path_to('docs'), "#{pkg_dir}/docs-#{NG_VERSION.full}"
|
||||
|
||||
File.open("#{pkg_dir}/angular-mocks-#{NG_VERSION.full}.js", File::RDWR) do |f|
|
||||
text = f.read
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write text.sub('"NG_VERSION_FULL"', NG_VERSION.full)
|
||||
end
|
||||
|
||||
File.open("#{pkg_dir}/docs-#{NG_VERSION.full}/index.html", File::RDWR) do |f|
|
||||
text = f.read
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write text.sub('angular.min.js', "angular-#{NG_VERSION.full}.min.js").
|
||||
sub('/build/docs/', "/#{NG_VERSION.full}/docs-#{NG_VERSION.full}/")
|
||||
rewrite_file("#{pkg_dir}/angular-mocks-#{NG_VERSION.full}.js") do |content|
|
||||
content.sub!('"NG_VERSION_FULL"', NG_VERSION.full)
|
||||
end
|
||||
|
||||
|
||||
File.open("#{pkg_dir}/docs-#{NG_VERSION.full}/index-jq.html", File::RDWR) do |f|
|
||||
text = f.read
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write text.sub('angular.min.js', "angular-#{NG_VERSION.full}.min.js").
|
||||
sub('/build/docs/', "/#{NG_VERSION.full}/docs-#{NG_VERSION.full}/")
|
||||
[ "#{pkg_dir}/docs-#{NG_VERSION.full}/index.html",
|
||||
"#{pkg_dir}/docs-#{NG_VERSION.full}/index-jq.html",
|
||||
"#{pkg_dir}/docs-#{NG_VERSION.full}/index-nocache.html",
|
||||
"#{pkg_dir}/docs-#{NG_VERSION.full}/index-jq-nocache.html",
|
||||
"#{pkg_dir}/docs-#{NG_VERSION.full}/index-debug.html",
|
||||
"#{pkg_dir}/docs-#{NG_VERSION.full}/index-jq-debug.html"
|
||||
].each do |src|
|
||||
rewrite_file(src) do |content|
|
||||
content.gsub!(/'angular(.*)\.js/, '\'angular\1-' + NG_VERSION.full + '.js')
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
File.open("#{pkg_dir}/docs-#{NG_VERSION.full}/index-nocache.html", File::RDWR) do |f|
|
||||
text = f.read
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write text.sub('angular.min.js', "angular-#{NG_VERSION.full}.min.js").
|
||||
sub('/build/docs/', "/#{NG_VERSION.full}/docs-#{NG_VERSION.full}/")
|
||||
rewrite_file("#{pkg_dir}/docs-#{NG_VERSION.full}/docs-scenario.html") do |content|
|
||||
content.sub!('angular-scenario.js', "angular-scenario-#{NG_VERSION.full}.js")
|
||||
end
|
||||
|
||||
|
||||
File.open("#{pkg_dir}/docs-#{NG_VERSION.full}/index-jq-nocache.html", File::RDWR) do |f|
|
||||
text = f.read
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write text.sub('angular.min.js', "angular-#{NG_VERSION.full}.min.js").
|
||||
sub('/build/docs/', "/#{NG_VERSION.full}/docs-#{NG_VERSION.full}/")
|
||||
end
|
||||
|
||||
|
||||
File.open("#{pkg_dir}/docs-#{NG_VERSION.full}/index-debug.html", File::RDWR) do |f|
|
||||
text = f.read
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write text.sub('../angular.js', "../angular-#{NG_VERSION.full}.js").
|
||||
sub('/build/docs/', "/#{NG_VERSION.full}/docs-#{NG_VERSION.full}/")
|
||||
end
|
||||
|
||||
|
||||
File.open("#{pkg_dir}/docs-#{NG_VERSION.full}/index-jq-debug.html", File::RDWR) do |f|
|
||||
text = f.read
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write text.sub('../angular.js', "../angular-#{NG_VERSION.full}.js").
|
||||
sub('/build/docs/', "/#{NG_VERSION.full}/docs-#{NG_VERSION.full}/")
|
||||
end
|
||||
|
||||
File.open("#{pkg_dir}/docs-#{NG_VERSION.full}/docs-scenario.html", File::RDWR) do |f|
|
||||
text = f.read
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write text.sub('angular-scenario.js', "angular-scenario-#{NG_VERSION.full}.js")
|
||||
end
|
||||
|
||||
File.open("#{pkg_dir}/docs-#{NG_VERSION.full}/appcache.manifest", File::RDWR) do |f|
|
||||
text = f.read
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write text.sub('angular.min.js', "angular-#{NG_VERSION.full}.min.js").
|
||||
sub('/build/docs/', "/#{NG_VERSION.full}/docs-#{NG_VERSION.full}/")
|
||||
end
|
||||
|
||||
File.open("#{pkg_dir}/docs-#{NG_VERSION.full}/appcache-offline.manifest", File::RDWR) do |f|
|
||||
text = f.read
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write text.sub('angular.min.js', "angular-#{NG_VERSION.full}.min.js").
|
||||
sub('/build/docs/', "/#{NG_VERSION.full}/docs-#{NG_VERSION.full}/")
|
||||
[ "#{pkg_dir}/docs-#{NG_VERSION.full}/appcache.manifest",
|
||||
"#{pkg_dir}/docs-#{NG_VERSION.full}/appcache-offline.manifest"
|
||||
].each do |src|
|
||||
rewrite_file(src) do |content|
|
||||
content.sub!('../angular.min.js', "angular-#{NG_VERSION.full}.min.js").
|
||||
sub!('/build/docs/', "/#{NG_VERSION.full}/docs-#{NG_VERSION.full}/")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -336,3 +268,61 @@ end
|
||||
def path_to(filename)
|
||||
return File.join(BUILD_DIR, *filename)
|
||||
end
|
||||
|
||||
|
||||
def closure_compile(filename)
|
||||
puts "Compiling #{filename} ..."
|
||||
|
||||
min_path = path_to(filename.gsub(/\.js$/, '.min.js'))
|
||||
|
||||
%x(java -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 "Building #{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
|
||||
|
||||
@@ -3,81 +3,112 @@ angularFiles = {
|
||||
'src/Angular.js',
|
||||
'src/loader.js',
|
||||
'src/AngularPublic.js',
|
||||
'src/JSON.js',
|
||||
'src/Injector.js',
|
||||
'src/Resource.js',
|
||||
'src/jqLite.js',
|
||||
'src/apis.js',
|
||||
'src/service/anchorScroll.js',
|
||||
'src/service/browser.js',
|
||||
'src/service/cacheFactory.js',
|
||||
'src/service/compiler.js',
|
||||
'src/service/controller.js',
|
||||
'src/service/cookieStore.js',
|
||||
'src/service/cookies.js',
|
||||
'src/service/defer.js',
|
||||
'src/service/document.js',
|
||||
'src/service/exceptionHandler.js',
|
||||
'src/service/filter.js',
|
||||
'src/service/filter/filter.js',
|
||||
'src/service/filter/filters.js',
|
||||
'src/service/filter/limitTo.js',
|
||||
'src/service/filter/orderBy.js',
|
||||
'src/service/interpolate.js',
|
||||
'src/service/location.js',
|
||||
'src/service/log.js',
|
||||
'src/service/resource.js',
|
||||
'src/service/parse.js',
|
||||
'src/service/q.js',
|
||||
'src/service/route.js',
|
||||
'src/service/routeParams.js',
|
||||
'src/service/scope.js',
|
||||
'src/service/sanitize.js',
|
||||
'src/service/sniffer.js',
|
||||
'src/service/window.js',
|
||||
'src/service/http.js',
|
||||
'src/service/httpBackend.js',
|
||||
'src/service/locale.js',
|
||||
'src/directive/directives.js',
|
||||
'src/directive/a.js',
|
||||
'src/directive/booleanAttrDirs.js',
|
||||
'src/directive/form.js',
|
||||
'src/directive/input.js',
|
||||
'src/directive/ngBind.js',
|
||||
'src/directive/ngClass.js',
|
||||
'src/directive/ngCloak.js',
|
||||
'src/directive/ngController.js',
|
||||
'src/directive/ngEventDirs.js',
|
||||
'src/directive/ngInclude.js',
|
||||
'src/directive/ngInit.js',
|
||||
'src/directive/ngNonBindable.js',
|
||||
'src/directive/ngPluralize.js',
|
||||
'src/directive/ngRepeat.js',
|
||||
'src/directive/ngShowHide.js',
|
||||
'src/directive/ngStyle.js',
|
||||
'src/directive/ngSwitch.js',
|
||||
'src/directive/ngTransclude.js',
|
||||
'src/directive/ngView.js',
|
||||
'src/directive/script.js',
|
||||
'src/directive/select.js',
|
||||
'src/directive/style.js'
|
||||
|
||||
'src/auto/injector.js',
|
||||
|
||||
'src/ng/anchorScroll.js',
|
||||
'src/ng/browser.js',
|
||||
'src/ng/cacheFactory.js',
|
||||
'src/ng/compile.js',
|
||||
'src/ng/controller.js',
|
||||
'src/ng/defer.js',
|
||||
'src/ng/document.js',
|
||||
'src/ng/exceptionHandler.js',
|
||||
'src/ng/interpolate.js',
|
||||
'src/ng/location.js',
|
||||
'src/ng/log.js',
|
||||
'src/ng/parse.js',
|
||||
'src/ng/q.js',
|
||||
'src/ng/route.js',
|
||||
'src/ng/routeParams.js',
|
||||
'src/ng/rootScope.js',
|
||||
'src/ng/sniffer.js',
|
||||
'src/ng/window.js',
|
||||
'src/ng/http.js',
|
||||
'src/ng/httpBackend.js',
|
||||
'src/ng/locale.js',
|
||||
'src/ng/timeout.js',
|
||||
|
||||
'src/ng/filter.js',
|
||||
'src/ng/filter/filter.js',
|
||||
'src/ng/filter/filters.js',
|
||||
'src/ng/filter/limitTo.js',
|
||||
'src/ng/filter/orderBy.js',
|
||||
|
||||
'src/ng/directive/directives.js',
|
||||
'src/ng/directive/a.js',
|
||||
'src/ng/directive/booleanAttrs.js',
|
||||
'src/ng/directive/form.js',
|
||||
'src/ng/directive/input.js',
|
||||
'src/ng/directive/ngBind.js',
|
||||
'src/ng/directive/ngClass.js',
|
||||
'src/ng/directive/ngCloak.js',
|
||||
'src/ng/directive/ngController.js',
|
||||
'src/ng/directive/ngCsp.js',
|
||||
'src/ng/directive/ngEventDirs.js',
|
||||
'src/ng/directive/ngInclude.js',
|
||||
'src/ng/directive/ngInit.js',
|
||||
'src/ng/directive/ngNonBindable.js',
|
||||
'src/ng/directive/ngPluralize.js',
|
||||
'src/ng/directive/ngRepeat.js',
|
||||
'src/ng/directive/ngShowHide.js',
|
||||
'src/ng/directive/ngStyle.js',
|
||||
'src/ng/directive/ngSwitch.js',
|
||||
'src/ng/directive/ngTransclude.js',
|
||||
'src/ng/directive/ngView.js',
|
||||
'src/ng/directive/script.js',
|
||||
'src/ng/directive/select.js',
|
||||
'src/ng/directive/style.js'
|
||||
],
|
||||
|
||||
'angularSrcModules': [
|
||||
'src/ngCookies/cookies.js',
|
||||
'src/ngResource/resource.js',
|
||||
'src/ngSanitize/sanitize.js',
|
||||
'src/ngSanitize/directive/ngBindHtml.js',
|
||||
'src/ngSanitize/filter/linky.js',
|
||||
'src/ngMock/angular-mocks.js',
|
||||
|
||||
'src/bootstrap/bootstrap.js'
|
||||
],
|
||||
|
||||
'angularScenario': [
|
||||
'src/scenario/Scenario.js',
|
||||
'src/scenario/Application.js',
|
||||
'src/scenario/Describe.js',
|
||||
'src/scenario/Future.js',
|
||||
'src/scenario/ObjectModel.js',
|
||||
'src/scenario/Describe.js',
|
||||
'src/scenario/Runner.js',
|
||||
'src/scenario/SpecRunner.js',
|
||||
'src/scenario/dsl.js',
|
||||
'src/scenario/matchers.js',
|
||||
'src/scenario/output/Html.js',
|
||||
'src/scenario/output/Json.js',
|
||||
'src/scenario/output/Xml.js',
|
||||
'src/scenario/output/Object.js'
|
||||
'src/ngScenario/Scenario.js',
|
||||
'src/ngScenario/Application.js',
|
||||
'src/ngScenario/Describe.js',
|
||||
'src/ngScenario/Future.js',
|
||||
'src/ngScenario/ObjectModel.js',
|
||||
'src/ngScenario/Describe.js',
|
||||
'src/ngScenario/Runner.js',
|
||||
'src/ngScenario/SpecRunner.js',
|
||||
'src/ngScenario/dsl.js',
|
||||
'src/ngScenario/matchers.js',
|
||||
'src/ngScenario/output/Html.js',
|
||||
'src/ngScenario/output/Json.js',
|
||||
'src/ngScenario/output/Xml.js',
|
||||
'src/ngScenario/output/Object.js'
|
||||
],
|
||||
|
||||
'angularTest': [
|
||||
'test/testabilityPatch.js',
|
||||
'test/matchers.js',
|
||||
'test/ngScenario/*.js',
|
||||
'test/ngScenario/output/*.js',
|
||||
'test/ngScenario/jstd-scenario-adapter/*.js',
|
||||
'test/*.js',
|
||||
'test/auto/*.js',
|
||||
'test/bootstrap/*.js',
|
||||
'test/ng/*.js',
|
||||
'test/ng/directive/*.js',
|
||||
'test/ng/filter/*.js',
|
||||
'test/ngCookies/*.js',
|
||||
'test/ngResource/*.js',
|
||||
'test/ngSanitize/*.js',
|
||||
'test/ngSanitize/directive/*.js',
|
||||
'test/ngSanitize/filter/*.js',
|
||||
'test/ngMock/*.js'
|
||||
],
|
||||
|
||||
'jstd': [
|
||||
@@ -86,28 +117,19 @@ angularFiles = {
|
||||
'lib/jquery/jquery.js',
|
||||
'test/jquery_remove.js',
|
||||
'@angularSrc',
|
||||
'src/publishExternalApis.js',
|
||||
'@angularSrcModules',
|
||||
'@angularScenario',
|
||||
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
|
||||
'@angularTest',
|
||||
'example/personalLog/*.js',
|
||||
'test/testabilityPatch.js',
|
||||
'test/matchers.js',
|
||||
'src/scenario/Scenario.js',
|
||||
'src/scenario/output/*.js',
|
||||
'src/jstd-scenario-adapter/*.js',
|
||||
'src/scenario/*.js',
|
||||
'src/angular-mocks.js',
|
||||
'test/scenario/*.js',
|
||||
'test/scenario/output/*.js',
|
||||
'test/jstd-scenario-adapter/*.js',
|
||||
'test/*.js',
|
||||
'test/service/*.js',
|
||||
'test/service/filter/*.js',
|
||||
'test/directive/*.js',
|
||||
'example/personalLog/test/*.js'
|
||||
],
|
||||
|
||||
'jstdExclude': [
|
||||
'test/jquery_alias.js',
|
||||
'src/angular-bootstrap.js',
|
||||
'src/scenario/angular-bootstrap.js'
|
||||
'src/ngScenario/angular-bootstrap.js'
|
||||
],
|
||||
|
||||
'jstdScenario': [
|
||||
@@ -117,28 +139,39 @@ angularFiles = {
|
||||
'build/docs/docs-scenario.js'
|
||||
],
|
||||
|
||||
'jstdMocks': [
|
||||
"jstdModules": [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'build/angular.js',
|
||||
'src/angular-mocks.js',
|
||||
'src/ngMock/angular-mocks.js',
|
||||
'src/ngCookies/cookies.js',
|
||||
'src/ngResource/resource.js',
|
||||
'src/ngSanitize/sanitize.js',
|
||||
'src/ngSanitize/directive/ngBindHtml.js',
|
||||
'src/ngSanitize/filter/linky.js',
|
||||
'test/matchers.js',
|
||||
'test/angular-mocksSpec.js'
|
||||
'test/ngMock/*.js',
|
||||
'test/ngCookies/*.js',
|
||||
'test/ngResource/*.js',
|
||||
'test/ngSanitize/*.js',
|
||||
'test/ngSanitize/directive/*.js',
|
||||
'test/ngSanitize/filter/*.js'
|
||||
],
|
||||
|
||||
'jstdPerf': [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'angularSrc',
|
||||
'src/angular-mocks.js',
|
||||
'@angularSrc',
|
||||
'@angularSrcModules',
|
||||
'src/ngMock/angular-mocks.js',
|
||||
'perf/data/*.js',
|
||||
'perf/testUtils.js',
|
||||
'perf/*.js'
|
||||
],
|
||||
|
||||
'jstdPerfExclude': [
|
||||
'src/angular-bootstrap.js',
|
||||
'src/scenario/angular-bootstrap.js'
|
||||
'src/ng/angular-bootstrap.js',
|
||||
'src/ngScenario/angular-bootstrap.js'
|
||||
],
|
||||
|
||||
'jstdJquery': [
|
||||
@@ -147,41 +180,49 @@ angularFiles = {
|
||||
'lib/jquery/jquery.js',
|
||||
'test/jquery_alias.js',
|
||||
'@angularSrc',
|
||||
'src/publishExternalApis.js',
|
||||
'@angularSrcModules',
|
||||
'@angularScenario',
|
||||
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
|
||||
'@angularTest',
|
||||
'example/personalLog/*.js',
|
||||
'test/testabilityPatch.js',
|
||||
'test/matchers.js',
|
||||
'src/scenario/Scenario.js',
|
||||
'src/scenario/output/*.js',
|
||||
'src/jstd-scenario-adapter/*.js',
|
||||
'src/scenario/*.js',
|
||||
'src/angular-mocks.js',
|
||||
'test/scenario/*.js',
|
||||
'test/scenario/output/*.js',
|
||||
'test/jstd-scenario-adapter/*.js',
|
||||
'test/*.js',
|
||||
'test/service/*.js',
|
||||
'test/directive/*.js',
|
||||
|
||||
'example/personalLog/test/*.js'
|
||||
],
|
||||
|
||||
'jstdJqueryExclude': [
|
||||
'src/angular-bootstrap.js',
|
||||
'src/scenario/angular-bootstrap.js',
|
||||
'src/ngScenario/angular-bootstrap.js',
|
||||
'test/jquery_remove.js'
|
||||
]
|
||||
};
|
||||
|
||||
// Execute only in slim-jim
|
||||
if (typeof JASMINE_ADAPTER !== 'undefined') {
|
||||
// SlimJim config
|
||||
files = [JASMINE, JASMINE_ADAPTER];
|
||||
angularFiles.jstd.forEach(function(pattern) {
|
||||
// replace angular source
|
||||
if (pattern === '@angularSrc') files = files.concat(angularFiles.angularSrc);
|
||||
// ignore jstd and jasmine files
|
||||
else if (!/jstd|jasmine/.test(pattern)) files.push(pattern);
|
||||
// Testacular config
|
||||
var mergedFiles = [];
|
||||
angularFiles.jstd.forEach(function(file) {
|
||||
// replace @ref
|
||||
var match = file.match(/^\@(.*)/);
|
||||
if (match) {
|
||||
var deps = angularFiles[match[1]];
|
||||
if (!deps) {
|
||||
console.log('No dependency:' + file)
|
||||
}
|
||||
mergedFiles = mergedFiles.concat(deps);
|
||||
} else {
|
||||
mergedFiles.push(file);
|
||||
}
|
||||
});
|
||||
|
||||
files = [JASMINE, JASMINE_ADAPTER];
|
||||
|
||||
mergedFiles.forEach(function(file){
|
||||
if (/jstd|jasmine/.test(file)) return;
|
||||
files.push(file);
|
||||
});
|
||||
|
||||
|
||||
exclude = angularFiles.jstdExclude;
|
||||
|
||||
autoWatch = true;
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// TODO(vojta): pre-commit hook for validating messages
|
||||
// TODO(vojta): report errors, currently Q silence everything which really sucks
|
||||
|
||||
var child = require('child_process');
|
||||
var fs = require('fs');
|
||||
var util = require('util');
|
||||
var q = require('qq');
|
||||
|
||||
var GIT_LOG_CMD = 'git log --grep="%s" -E --format=%s %s..HEAD';
|
||||
var GIT_TAG_CMD = 'git describe --tags --abbrev=0';
|
||||
|
||||
var HEADER_TPL = '<a name="%s"></a>\n# %s (%s)\n\n';
|
||||
var LINK_ISSUE = '[#%s](https://github.com/angular/angular.js/issues/%s)';
|
||||
var LINK_COMMIT = '[%s](https://github.com/angular/angular.js/commit/%s)';
|
||||
|
||||
var EMPTY_COMPONENT = '$$';
|
||||
var MAX_SUBJECT_LENGTH = 80;
|
||||
|
||||
|
||||
var warn = function() {
|
||||
console.log('WARNING:', util.format.apply(null, arguments));
|
||||
};
|
||||
|
||||
|
||||
var parseRawCommit = function(raw) {
|
||||
if (!raw) return null;
|
||||
|
||||
var lines = raw.split('\n');
|
||||
var msg = {}, match;
|
||||
|
||||
msg.hash = lines.shift();
|
||||
msg.subject = lines.shift();
|
||||
msg.closes = [];
|
||||
msg.breaks = [];
|
||||
|
||||
lines.forEach(function(line) {
|
||||
match = line.match(/Closes\s#(\d+)/);
|
||||
if (match) msg.closes.push(parseInt(match[1]));
|
||||
});
|
||||
|
||||
match = raw.match(/BREAKING CHANGE:([\s\S]*)/);
|
||||
if (match) {
|
||||
console.log('found!!!')
|
||||
msg.breaks.push(match[1]);
|
||||
}
|
||||
|
||||
|
||||
msg.body = lines.join('\n');
|
||||
match = msg.subject.match(/^(.*)\((.*)\)\:\s(.*)$/);
|
||||
|
||||
if (!match || !match[1] || !match[3]) {
|
||||
warn('Incorrect message: %s %s', msg.hash, msg.subject);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (match[3].length > MAX_SUBJECT_LENGTH) {
|
||||
warn('Too long subject: %s %s', msg.hash, msg.subject);
|
||||
match[3] = match[3].substr(0, MAX_SUBJECT_LENGTH);
|
||||
}
|
||||
|
||||
msg.type = match[1];
|
||||
msg.component = match[2];
|
||||
msg.subject = match[3];
|
||||
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
var linkToIssue = function(issue) {
|
||||
return util.format(LINK_ISSUE, issue, issue);
|
||||
};
|
||||
|
||||
|
||||
var linkToCommit = function(hash) {
|
||||
return util.format(LINK_COMMIT, hash.substr(0, 8), hash);
|
||||
};
|
||||
|
||||
|
||||
var currentDate = function() {
|
||||
var now = new Date();
|
||||
var pad = function(i) {
|
||||
return ('0' + i).substr(-2);
|
||||
};
|
||||
|
||||
return util.format('%d-%s-%s', now.getFullYear(), pad(now.getMonth() + 1), pad(now.getDate()));
|
||||
};
|
||||
|
||||
|
||||
var printSection = function(stream, title, section) {
|
||||
var components = Object.getOwnPropertyNames(section).sort();
|
||||
|
||||
if (!components.length) return;
|
||||
|
||||
stream.write(util.format('\n## %s\n\n', title));
|
||||
|
||||
components.forEach(function(name) {
|
||||
var prefix = '-';
|
||||
var nested = section[name].length > 1;
|
||||
|
||||
if (name !== EMPTY_COMPONENT) {
|
||||
if (nested) {
|
||||
stream.write(util.format('- **%s:**\n', name));
|
||||
prefix = ' -';
|
||||
} else {
|
||||
prefix = util.format('- **%s:**', name);
|
||||
}
|
||||
}
|
||||
|
||||
section[name].forEach(function(commit) {
|
||||
stream.write(util.format('%s %s (%s', prefix, commit.subject, linkToCommit(commit.hash)));
|
||||
if (commit.closes.length) {
|
||||
stream.write(', closes ' + commit.closes.map(linkToIssue).join(', '));
|
||||
}
|
||||
stream.write(')\n');
|
||||
});
|
||||
});
|
||||
|
||||
stream.write('\n');
|
||||
};
|
||||
|
||||
|
||||
var readGitLog = function(grep, from) {
|
||||
var deffered = q.defer();
|
||||
|
||||
// TODO(vojta): if it's slow, use spawn and stream it instead
|
||||
child.exec(util.format(GIT_LOG_CMD, grep, '%H%n%s%n%b%n==END==', from), function(code, stdout, stderr) {
|
||||
var commits = [];
|
||||
|
||||
stdout.split('\n==END==\n').forEach(function(rawCommit) {
|
||||
var commit = parseRawCommit(rawCommit);
|
||||
if (commit) commits.push(commit);
|
||||
});
|
||||
|
||||
deffered.resolve(commits);
|
||||
});
|
||||
|
||||
return deffered.promise;
|
||||
};
|
||||
|
||||
|
||||
var writeChangelog = function(stream, commits, version) {
|
||||
var sections = {
|
||||
fix: {},
|
||||
feat: {},
|
||||
breaks: {}
|
||||
};
|
||||
|
||||
sections.breaks[EMPTY_COMPONENT] = [];
|
||||
|
||||
commits.forEach(function(commit) {
|
||||
var section = sections[commit.type];
|
||||
var component = commit.component || EMPTY_COMPONENT;
|
||||
|
||||
if (section) {
|
||||
section[component] = section[component] || [];
|
||||
section[component].push(commit);
|
||||
}
|
||||
|
||||
commit.breaks.forEach(function(breakMsg) {
|
||||
sections.breaks[EMPTY_COMPONENT].push({
|
||||
subject: breakMsg,
|
||||
hash: commit.hash,
|
||||
closes: []
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
stream.write(util.format(HEADER_TPL, version, version, currentDate()));
|
||||
printSection(stream, 'Bug Fixes', sections.fix);
|
||||
printSection(stream, 'Features', sections.feat);
|
||||
printSection(stream, 'Breaking Changes', sections.breaks);
|
||||
}
|
||||
|
||||
|
||||
var getPreviousTag = function() {
|
||||
var deffered = q.defer();
|
||||
child.exec(GIT_TAG_CMD, function(code, stdout, stderr) {
|
||||
if (code) deffered.reject('Cannot get the previous tag.');
|
||||
else deffered.resolve(stdout.replace('\n', ''));
|
||||
});
|
||||
return deffered.promise;
|
||||
};
|
||||
|
||||
|
||||
var generate = function(version, file) {
|
||||
getPreviousTag().then(function(tag) {
|
||||
console.log('Reading git log since', tag);
|
||||
readGitLog('^fix|^feat|Breaks', tag).then(function(commits) {
|
||||
console.log('Parsed', commits.length, 'commits');
|
||||
console.log('Generating changelog to', file || 'stdout', '(', version, ')');
|
||||
writeChangelog(file ? fs.createWriteStream(file) : process.stdout, commits, version);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// publish for testing
|
||||
exports.parseRawCommit = parseRawCommit;
|
||||
|
||||
// hacky start if not run by jasmine :-D
|
||||
if (process.argv.join('').indexOf('jasmine-node') === -1) {
|
||||
generate(process.argv[2], process.argv[3]);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
describe('changelog.js', function() {
|
||||
var ch = require('./changelog');
|
||||
|
||||
describe('parseRawCommit', function() {
|
||||
it('should parse raw commit', function() {
|
||||
var msg = ch.parseRawCommit(
|
||||
'9b1aff905b638aa274a5fc8f88662df446d374bd\n' +
|
||||
'feat(scope): broadcast $destroy event on scope destruction\n' +
|
||||
'perf testing shows that in chrome this change adds 5-15% overhead\n' +
|
||||
'when destroying 10k nested scopes where each scope has a $destroy listener\n');
|
||||
|
||||
expect(msg.type).toBe('feat');
|
||||
expect(msg.hash).toBe('9b1aff905b638aa274a5fc8f88662df446d374bd');
|
||||
expect(msg.subject).toBe('broadcast $destroy event on scope destruction');
|
||||
expect(msg.body).toBe('perf testing shows that in chrome this change adds 5-15% overhead\n' +
|
||||
'when destroying 10k nested scopes where each scope has a $destroy listener\n')
|
||||
expect(msg.component).toBe('scope');
|
||||
});
|
||||
|
||||
|
||||
it('should parse closed issues', function() {
|
||||
var msg = ch.parseRawCommit(
|
||||
'13f31602f396bc269076ab4d389cfd8ca94b20ba\n' +
|
||||
'feat(ng-list): Allow custom separator\n' +
|
||||
'bla bla bla\n\n' +
|
||||
'Closes #123\nCloses #25\n');
|
||||
|
||||
expect(msg.closes).toEqual([123, 25]);
|
||||
});
|
||||
|
||||
|
||||
it('should parse breaking changes', function() {
|
||||
var msg = ch.parseRawCommit(
|
||||
'13f31602f396bc269076ab4d389cfd8ca94b20ba\n' +
|
||||
'feat(ng-list): Allow custom separator\n' +
|
||||
'bla bla bla\n\n' +
|
||||
'Breaks first breaking change\nsomething else\n' +
|
||||
'Breaks another breaking change\n');
|
||||
|
||||
expect(msg.breaks).toEqual(['first breaking change', 'another breaking change']);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,80 @@
|
||||
<a name="v1.0.0rc3"></a>
|
||||
# v1.0.0rc3 (2012-03-27)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- create new (isolate) scopes for directives on root elements ([5390fb37](https://github.com/angular/angular.js/commit/5390fb37d2c01937922613fc57df4986af521787), closes [#817](https://github.com/angular/angular.js/issues/817))
|
||||
- don't touch static element attributes ([9cb2195e](https://github.com/angular/angular.js/commit/9cb2195e61a78e99020ec19d687a221ca88b5900))
|
||||
- Merge interpolated css class when replacing an element ([f49eaf8b](https://github.com/angular/angular.js/commit/f49eaf8bf2df5f4e0e82d6c89e849a4f82c8d414))
|
||||
- **$http:**
|
||||
- don't send Content-Type header when no data ([1a5bebd9](https://github.com/angular/angular.js/commit/1a5bebd927ecd22f9c34617642fdf58fe3f62efb), closes [#749](https://github.com/angular/angular.js/issues/749))
|
||||
- **$log:**
|
||||
- avoid console.log.apply calls in IE ([15213ec2](https://github.com/angular/angular.js/commit/15213ec212769837cb2b7e781ffc5bfd598d27ca), closes [#805](https://github.com/angular/angular.js/issues/805))
|
||||
- **$resource:**
|
||||
- support escaping of ':' in resource url ([6d6f8753](https://github.com/angular/angular.js/commit/6d6f875345e01f2c6c63ef95164f6f39e923da15))
|
||||
- **compiler:**
|
||||
- allow transclusion of root elements ([9918b748](https://github.com/angular/angular.js/commit/9918b748be01266eb10db39d51b4d3098d54ab66))
|
||||
- **e2e runner:**
|
||||
- fix typo that caused errors on IE8 ([ee5a5352](https://github.com/angular/angular.js/commit/ee5a5352fd4b94cedee6ef20d4bf2d43ce77e00b), closes [#806](https://github.com/angular/angular.js/issues/806))
|
||||
- **forEach:**
|
||||
- should ignore prototypically inherited properties ([8d7e6948](https://github.com/angular/angular.js/commit/8d7e6948496ff26ef1da8854ba02fcb8eebfed61), closes [#813](https://github.com/angular/angular.js/issues/813))
|
||||
- **forms:**
|
||||
- Remove double registering of form ([1faafa31](https://github.com/angular/angular.js/commit/1faafa31582c4e9413f48dc7d12f5b681f9fe9fd))
|
||||
- Set ng-valid/ng-invalid correctly ([08bfea18](https://github.com/angular/angular.js/commit/08bfea183a850b29da270eac47f80b598cbe600f))
|
||||
- **init:**
|
||||
- use jQuery#ready for init if available ([cb2ad9ab](https://github.com/angular/angular.js/commit/cb2ad9abf24e6f855cc749efe3155bd7987ece9d), closes [#818](https://github.com/angular/angular.js/issues/818))
|
||||
- **json:**
|
||||
- added support for iso8061 timezone ([5ac14f63](https://github.com/angular/angular.js/commit/5ac14f633a69f49973b5512780c6ec7752405967))
|
||||
- **matchers.toHaveClass:**
|
||||
- Correct reference to angular.mock.dump ([f701ce08](https://github.com/angular/angular.js/commit/f701ce08f9d63be05fc3b92f57ad473e1e749b2d))
|
||||
- **ng-switch:**
|
||||
- properly destroy child scopes ([2315d9b3](https://github.com/angular/angular.js/commit/2315d9b3610994b36c44e4a97fb1427d59471ce8))
|
||||
- **ngDocSpec:**
|
||||
- fix broken tests ([53b6f522](https://github.com/angular/angular.js/commit/53b6f522a56eea314cbd084816e08f24b2c7879f))
|
||||
- **ngForm:**
|
||||
- alias name||ngForm ([823adb23](https://github.com/angular/angular.js/commit/823adb231995e917bc060bfa49453e2a96bac2b6))
|
||||
- **ngRepeat:**
|
||||
- correct variable reference in error message ([935c1018](https://github.com/angular/angular.js/commit/935c1018da05dbf3124b2dd33619c4a3c82d7a2a))
|
||||
- **ngView:**
|
||||
- controller not published ([21e74c2d](https://github.com/angular/angular.js/commit/21e74c2d2e8e985b23711785287feb59965cbd90))
|
||||
- **q:**
|
||||
- resolve all of nothing to nothing ([ac75079e](https://github.com/angular/angular.js/commit/ac75079e2113949d5d64adbcf23d56f3cf295d41))
|
||||
- **select:**
|
||||
- multiselect failes to update view on selection insert ([6ecac8e7](https://github.com/angular/angular.js/commit/6ecac8e71a84792a434d21db2c245b3648c55f18))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$compile:**
|
||||
- do not interpolate boolean attributes, rather evaluate them ([a08cbc02](https://github.com/angular/angular.js/commit/a08cbc02e78e789a66e9af771c410e8ad1646e25))
|
||||
- **$controller:**
|
||||
- support controller registration via $controllerProvider ([d54dfecb](https://github.com/angular/angular.js/commit/d54dfecb00fba41455536c5ddd55310592fdaf84))
|
||||
- **$route:**
|
||||
- when matching consider trailing slash as optional ([a4fe51da](https://github.com/angular/angular.js/commit/a4fe51da3ba0dc297ecd389e230d6664f250c9a6), closes [#784](https://github.com/angular/angular.js/issues/784))
|
||||
- **assertArgFn:**
|
||||
- should support array annotated fns ([4b8d9260](https://github.com/angular/angular.js/commit/4b8d926062eb4d4483555bdbdec4656f585ab40b))
|
||||
- **http:**
|
||||
- added params parameter ([73c85930](https://github.com/angular/angular.js/commit/73c8593077155a9f2e8ef42efd4c497eba0bef4f))
|
||||
- **injector:**
|
||||
- infer _foo_ as foo ([f13dd339](https://github.com/angular/angular.js/commit/f13dd3393dfb7a33565c9360342c193bc0bddcb6))
|
||||
- **input.radio:**
|
||||
- Allow value attribute to be interpolated ([ade6c452](https://github.com/angular/angular.js/commit/ade6c452753145c84884d17027a7865bf4b34b0c))
|
||||
- **jqLite:**
|
||||
- make injector() and scope() work with the document object ([5fdab52d](https://github.com/angular/angular.js/commit/5fdab52dd7c269f99839f4fa6b5854d9548269fa))
|
||||
- add .controller() method ([6c5a05ad](https://github.com/angular/angular.js/commit/6c5a05ad49a1e083570c3dfe331403398f899dbe))
|
||||
- **ngValue:**
|
||||
- allow radio inputs to have non string values ([09e175f0](https://github.com/angular/angular.js/commit/09e175f02cca0f4a295fd0c9b980cd8f432e722b), closes [#816](https://github.com/angular/angular.js/issues/816))
|
||||
- **scope:**
|
||||
- broadcast $destroy event on scope destruction ([9b1aff90](https://github.com/angular/angular.js/commit/9b1aff905b638aa274a5fc8f88662df446d374bd))
|
||||
- **scope.$eval:**
|
||||
- Allow passing locals to the expression ([192ff61f](https://github.com/angular/angular.js/commit/192ff61f5d61899e667c6dbce4d3e6e399429d8b))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- boolean attrs are evaluated rather than interpolated ([a08cbc02](https://github.com/angular/angular.js/commit/a08cbc02e78e789a66e9af771c410e8ad1646e25))
|
||||
- ng-bind-attr directive removed ([55027132](https://github.com/angular/angular.js/commit/55027132f3d57e5dcf94683e6e6bd7b0aae0087d))
|
||||
- any app that depends on this service and its fallback to Modernizr, please ([aaedefb9](https://github.com/angular/angular.js/commit/aaedefb92e6bec6626e173e5155072c91471596a))
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
rake compile
|
||||
gzip -c < build/angular.min.js > build/angular.min.js.gzip
|
||||
ls -l build/angular.min.*
|
||||
@@ -2,62 +2,6 @@
|
||||
@name API Reference
|
||||
@description
|
||||
|
||||
## Angular Compiler API
|
||||
|
||||
* {@link angular.module.ng.$compileProvider.directive Directives} - Angular DOM element attributes
|
||||
* {@link angular.module.ng.$filter Filters} - Angular output filters
|
||||
* {@link angular.module.ng.$compile $compile} - Template compiler
|
||||
|
||||
## Angular Scope API
|
||||
|
||||
* {@link angular.module.ng.$rootScope.Scope Scope Object} - Angular scope object
|
||||
|
||||
|
||||
## Angular Services & Dependency Injection API
|
||||
|
||||
* {@link angular.module.ng Angular Services}
|
||||
* {@link angular.injector angular.injector() }
|
||||
|
||||
|
||||
## Angular Testing API
|
||||
|
||||
* {@link angular.module.ngMock Testing Mocks API} - Mock objects for testing
|
||||
* {@link guide/dev_guide.e2e-testing Angular Scenario Runner} - Automated scenario testing
|
||||
documentation
|
||||
|
||||
|
||||
## Angular Utility Functions
|
||||
|
||||
### HTML & DOM Manipulation
|
||||
|
||||
* {@link angular.element angular.element()}
|
||||
|
||||
### Misc
|
||||
|
||||
* {@link angular.bind angular.bind() }
|
||||
* {@link angular.extend angular.extend() }
|
||||
* {@link angular.forEach angular.forEach() }
|
||||
* {@link angular.identity angular.identity() }
|
||||
* {@link angular.noop angular.noop() }
|
||||
|
||||
|
||||
## Type Identification
|
||||
|
||||
* {@link angular.isArray angular.isArray() }
|
||||
* {@link angular.isDate angular.isDate() }
|
||||
* {@link angular.isDefined angular.isDefined() }
|
||||
* {@link angular.isFunction angular.isFunction() }
|
||||
* {@link angular.isNumber angular.isNumber() }
|
||||
* {@link angular.isObject angular.isObject() }
|
||||
* {@link angular.isString angular.isString() }
|
||||
* {@link angular.isUndefined angular.isUndefined() }
|
||||
|
||||
## Strings
|
||||
|
||||
* {@link angular.lowercase angular.lowercase() }
|
||||
* {@link angular.uppercase angular.uppercase() }
|
||||
|
||||
### JSON
|
||||
|
||||
* {@link angular.fromJson angular.fromJson() }
|
||||
* {@link angular.toJson angular.toJson() }
|
||||
Use the API Refference documentation when you need more information about a specific feature. Check out
|
||||
{@link guide/ Developer Guide} for AngularJS concepts. If you are new to AngularJS we recomend the
|
||||
{@link tutorial/ Tutorial}.
|
||||
|
||||
@@ -85,8 +85,8 @@ detection, and preventing invalid form submission.
|
||||
<input type="text" ng-model="contact.value" required/>
|
||||
[ <a href="" ng-click="removeContact(contact)">X</a> ]
|
||||
</div>
|
||||
<button ng-click="cancel()" ng-disabled="{{isCancelDisabled()}}">Cancel</button>
|
||||
<button ng-click="save()" ng-disabled="{{isSaveDisabled()}}">Save</button>
|
||||
<button ng-click="cancel()" ng-disabled="isCancelDisabled()">Cancel</button>
|
||||
<button ng-click="save()" ng-disabled="isSaveDisabled()">Save</button>
|
||||
</form>
|
||||
|
||||
<hr/>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
Deep linking allows you to encode the state of the application in the URL so that it can be
|
||||
bookmarked and the application can be restored from the URL to the same state.
|
||||
|
||||
While <angular/> does not force you to deal with bookmarks in any particular way, it has services
|
||||
While angular does not force you to deal with bookmarks in any particular way, it has services
|
||||
which make the common case described here very easy to implement.
|
||||
|
||||
# Assumptions
|
||||
@@ -30,72 +30,111 @@ In this example we have a simple app which consist of two screens:
|
||||
* Welcome: url `welcome` Show the user contact information.
|
||||
* Settings: url `settings` Show an edit screen for user contact information.
|
||||
|
||||
<example module="deepLinking" deps="angular-sanitize.js">
|
||||
<file name="script.js">
|
||||
angular.module('deepLinking', ['ngSanitize'])
|
||||
.config(function($routeProvider) {
|
||||
$routeProvider.
|
||||
when("/welcome", {template:'welcome.html', controller:WelcomeCntl}).
|
||||
when("/settings", {template:'settings.html', controller:SettingsCntl});
|
||||
});
|
||||
|
||||
The two partials are defined in the following URLs:
|
||||
AppCntl.$inject = ['$scope', '$route']
|
||||
function AppCntl($scope, $route) {
|
||||
$scope.$route = $route;
|
||||
|
||||
* <a href="./examples/settings.html" ng-ext-link>./examples/settings.html</a>
|
||||
* <a href="./examples/welcome.html" ng-ext-link>./examples/welcome.html</a>
|
||||
// initialize the model to something useful
|
||||
$scope.person = {
|
||||
name:'anonymous',
|
||||
contacts:[{type:'email', url:'anonymous@example.com'}]
|
||||
};
|
||||
}
|
||||
|
||||
<doc:example module="deepLinking">
|
||||
<doc:source jsfiddle="false">
|
||||
<script>
|
||||
angular.module('deepLinking', [])
|
||||
.config(function($routeProvider) {
|
||||
$routeProvider.when("/welcome", {template:'./examples/welcome.html', controller:WelcomeCntl});
|
||||
$routeProvider.when("/settings", {template:'./examples/settings.html', controller:SettingsCntl});
|
||||
});
|
||||
function WelcomeCntl($scope) {
|
||||
$scope.greet = function() {
|
||||
alert("Hello " + $scope.person.name);
|
||||
};
|
||||
}
|
||||
|
||||
AppCntl.$inject = ['$scope', '$route']
|
||||
function AppCntl($scope, $route) {
|
||||
// initialize the model to something useful
|
||||
$scope.person = {
|
||||
name:'anonymous',
|
||||
contacts:[{type:'email', url:'anonymous@example.com'}]
|
||||
};
|
||||
}
|
||||
function SettingsCntl($scope, $location) {
|
||||
$scope.cancel = function() {
|
||||
$scope.form = angular.copy($scope.person);
|
||||
};
|
||||
|
||||
function WelcomeCntl($scope) {
|
||||
$scope.greet = function() {
|
||||
alert("Hello " + $scope.person.name);
|
||||
};
|
||||
}
|
||||
$scope.save = function() {
|
||||
angular.copy($scope.form, $scope.person);
|
||||
$location.path('/welcome');
|
||||
};
|
||||
|
||||
function SettingsCntl($scope, $location) {
|
||||
$scope.cancel = function() {
|
||||
$scope.form = angular.copy($scope.person);
|
||||
};
|
||||
$scope.cancel();
|
||||
}
|
||||
</file>
|
||||
<file name="style.css">
|
||||
[ng-view] {
|
||||
border: 1px solid blue;
|
||||
margin: 0;
|
||||
padding:1em;
|
||||
}
|
||||
|
||||
$scope.save = function() {
|
||||
angular.copy($scope.form, $scope.person);
|
||||
$location.path('/welcome');
|
||||
};
|
||||
|
||||
$scope.cancel();
|
||||
}
|
||||
</script>
|
||||
.partial-info {
|
||||
background-color: blue;
|
||||
color: white;
|
||||
padding: 3px;
|
||||
}
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="AppCntl">
|
||||
<h1>Your App Chrome</h1>
|
||||
[ <a href="welcome">Welcome</a> | <a href="settings">Settings</a> ]
|
||||
<hr/>
|
||||
<span style="background-color: blue; color: white; padding: 3px;">
|
||||
<span class="partial-info">
|
||||
Partial: {{$route.current.template}}
|
||||
</span>
|
||||
<ng:view style="border: 1px solid blue; margin: 0; display:block; padding:1em;"></ng:view>
|
||||
<div ng-view></div>
|
||||
<small>Your app footer </small>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
</file>
|
||||
<file name="settings.html">
|
||||
<label>Name:</label>
|
||||
<input type="text" ng:model="form.name" required>
|
||||
|
||||
<div ng:repeat="contact in form.contacts">
|
||||
<select ng:model="contact.type">
|
||||
<option>url</option>
|
||||
<option>email</option>
|
||||
<option>phone</option>
|
||||
</select>
|
||||
<input type="text" ng:model="contact.url">
|
||||
[ <a href="" ng:click="form.contacts.$remove(contact)">X</a> ]
|
||||
</div>
|
||||
<div>
|
||||
[ <a href="" ng:click="form.contacts.$add()">add</a> ]
|
||||
</div>
|
||||
|
||||
<button ng:click="cancel()">Cancel</button>
|
||||
<button ng:click="save()">Save</button>
|
||||
</file>
|
||||
<file name="welcome.html">
|
||||
Hello {{person.name}},
|
||||
<div>
|
||||
Your contact information:
|
||||
<div ng:repeat="contact in person.contacts">{{contact.type}}:
|
||||
<span ng-bind-html="contact.url|linky"></span>
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="scenario.js">
|
||||
it('should navigate to URL', function() {
|
||||
element('a:contains(Welcome)').click();
|
||||
expect(element('ng\\:view').text()).toMatch(/Hello anonymous/);
|
||||
element('a:contains(Settings)').click();
|
||||
input('form.name').enter('yourname');
|
||||
element(':button:contains(Save)').click();
|
||||
element('a:contains(Welcome)').click();
|
||||
expect(element('ng\\:view').text()).toMatch(/Hello yourname/);
|
||||
element('a:contains(Welcome)').click();
|
||||
expect(element('[ng-view]').text()).toMatch(/Hello anonymous/);
|
||||
element('a:contains(Settings)').click();
|
||||
input('form.name').enter('yourname');
|
||||
element(':button:contains(Save)').click();
|
||||
element('a:contains(Welcome)').click();
|
||||
expect(element('[ng-view]').text()).toMatch(/Hello yourname/);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
|
||||
|
||||
@@ -106,7 +145,7 @@ The two partials are defined in the following URLs:
|
||||
routes.
|
||||
* The {@link api/angular.module.ng.$route $route} service then watches the URL and instantiates the
|
||||
appropriate controller when the URL changes.
|
||||
* The {@link api/angular.module.ng.$compileProvider.directive.ng-view ng-view} widget loads the
|
||||
* The {@link api/angular.module.ng.$compileProvider.directive.ngView ngView} widget loads the
|
||||
view when the URL changes. It also sets the view scope to the newly instantiated controller.
|
||||
* Changing the URL is sufficient to change the controller and view. It makes no difference whether
|
||||
the URL is changed programatically or by the user.
|
||||
|
||||
@@ -20,7 +20,7 @@ allow a user to enter data.
|
||||
$scope.zip = /^\d\d\d\d\d$/;
|
||||
|
||||
$scope.addContact = function() {
|
||||
$scope.user.contacts.push({type:'', value:''});
|
||||
$scope.user.contacts.push({type:'email', value:''});
|
||||
};
|
||||
|
||||
$scope.removeContact = function(contact) {
|
||||
@@ -34,16 +34,16 @@ allow a user to enter data.
|
||||
</script>
|
||||
<div ng-controller="FormController" class="example">
|
||||
|
||||
<label>Name:</label><br/>
|
||||
<input type="text" ng-model="user.name" required/> <br/><br/>
|
||||
<label>Name:</label><br>
|
||||
<input type="text" ng-model="user.name" required/> <br><br>
|
||||
|
||||
<label>Address:</label><br/>
|
||||
<input type="text" ng-model="user.address.line1" size="33" required> <br/>
|
||||
<label>Address:</label><br>
|
||||
<input type="text" ng-model="user.address.line1" size="33" required> <br>
|
||||
<input type="text" ng-model="user.address.city" size="12" required>,
|
||||
<input type="text" ng-model="user.address.state" size="2"
|
||||
ng-pattern="state" required>
|
||||
<input type="text" ng-model="user.address.state"
|
||||
ng-pattern="state" size="2" required>
|
||||
<input type="text" ng-model="user.address.zip" size="5"
|
||||
ng-pattern="zip" required><br/><br/>
|
||||
ng-pattern="zip" required><br><br>
|
||||
|
||||
<label>Phone:</label>
|
||||
[ <a href="" ng-click="addContact()">add</a> ]
|
||||
@@ -54,12 +54,12 @@ allow a user to enter data.
|
||||
<option>pager</option>
|
||||
<option>IM</option>
|
||||
</select>
|
||||
<input type="text" ng-model="contact.value" required/>
|
||||
<input type="text" ng-model="contact.value" required>
|
||||
[ <a href="" ng-click="removeContact(contact)">X</a> ]
|
||||
</div>
|
||||
<hr/>
|
||||
Debug View:
|
||||
<pre>user={{user}}</pre>
|
||||
<pre>user={{user | json}}</pre>
|
||||
</div>
|
||||
|
||||
</doc:source>
|
||||
@@ -102,7 +102,7 @@ allow a user to enter data.
|
||||
|
||||
# Things to notice
|
||||
|
||||
* The user data model is initialized {@link api/angular.module.ng.$compileProvider.directive.ng-controller controller} and is
|
||||
* The user data model is initialized {@link api/angular.module.ng.$compileProvider.directive.ngController controller} and is
|
||||
available in the {@link api/angular.module.ng.$rootScope.Scope scope} with the initial data.
|
||||
* For debugging purposes we have included a debug view of the model to better understand what
|
||||
is going on.
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
@name Developer Guide: Initializing Angular: Automatic Initialization
|
||||
@description
|
||||
|
||||
Angular initializes automatically when you load the angular script into your page that contains an element
|
||||
with `ng-app` directive:
|
||||
For Angular to manage the DOM for your application, it needs to compile some or all of an HTML page. Angular does this initialization automatically when you load the angular.js script into your page and insert an `ngApp` directive (attribute) into one of the page's elements. For example, we can tell Angular to initialize the entire document:
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
@@ -17,30 +16,36 @@ with `ng-app` directive:
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
From a high-level view, this is what happens during angular's automatic initialization process:
|
||||
You can also tell Angular to manage only a portion of a page. You would want to do this if you are using some other framework to manage other parts of the page. You do this by placing the `ngApp` directive on one or more container elements in the document. For example:
|
||||
|
||||
1. The browser loads the page, and then runs the angular script. Angular waits for the
|
||||
`DOMContentLoaded` (or 'Load') event to attempt to bootstrap.
|
||||
<pre>
|
||||
<div ng-app>
|
||||
I can add: {{ 1+2 }}
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
2. Angular looks for the `ng-app` directive. If found it then proceeds to compile the DOM element and its children.
|
||||
Optionally the `ng-app` may specify a {@link api/angular.module module} to load before the compilation. For details on
|
||||
how the compiler works, see {@link dev_guide.compiler Angular HTML Compiler}.
|
||||
You can also ask `ngApp` to load additional {@link api/angular.module modules} containing services, directives or filers that you'll use on the page.
|
||||
|
||||
<pre>
|
||||
<div ng-app="AwesomeModule">
|
||||
...
|
||||
</div>
|
||||
</pre
|
||||
|
||||
|
||||
## Initialization Options
|
||||
From a high-level, here's what Angular does during the initialization process:
|
||||
|
||||
The reason why `ng-app` exists is because angular should not assume that the entire HTML
|
||||
document should be processed just because the `angular.js` script is included. In order to compile
|
||||
only a part of the document set the `ng-app` on the root element of this portion.
|
||||
1. The browser loads the page, and then runs the Angular script. Angular then waits for the
|
||||
`DOMContentLoaded` (or 'Load') event to attempt to initialize.
|
||||
|
||||
## Global Angular Object
|
||||
2. Angular looks for the `ngApp` directive. If found it compilies the DOM element containing `ngApp` and its children.
|
||||
|
||||
The angular script creates a single global variable `angular` in the global namespace. All angular
|
||||
APIs are bound to fields of this global object.
|
||||
3. Angular creates a global variable `angular` and binds all Angular APIs to this object's fields.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.compiler Angular HTML Compiler}
|
||||
* {@link dev_guide.bootstrap Initializing Angular}
|
||||
* {@link dev_guide.bootstrap.manual_bootstrap Manual Initialization}
|
||||
|
||||
|
||||
@@ -2,13 +2,26 @@
|
||||
@name Developer Guide: Initializing Angular: Manual Initialization
|
||||
@description
|
||||
|
||||
Letting angular handle the initialization process (bootstrapping) is a handy way to start using
|
||||
angular, but advanced users who want more control over the initialization process can choose to use
|
||||
the manual bootstrapping method instead.
|
||||
In the vast majority of cases you'll want to let Angular handle initialization automatically.
|
||||
If, however, you need to delay Angular from managing the page right after the DOMContentLoaded
|
||||
event fires, you'll need to control this initialization manually.
|
||||
|
||||
The best way to get started with manual bootstrapping is to look at the what happens when you use
|
||||
{@link api/angular.module.ng.$compileProvider.directive.ng-app ng-app}, by showing each step of the process
|
||||
explicitly.
|
||||
To initialize Angular -- after you've done your own special-purpose initialization -- just call
|
||||
the {@link api/angular.bootstrap bootstrap()} function with the HTML container node that you want
|
||||
Angular to manage. In automatic initialization you'd do this by adding the `ngApp` attribute to
|
||||
the same node. Now, you won't use `ngApp` anywhere in your document.
|
||||
|
||||
To show the contrast of manual vs. automatic initialization, this automatic method:
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html ng-app>
|
||||
<head>
|
||||
<script src="http://code.angularjs.org/angular.js"></script>
|
||||
...
|
||||
</pre
|
||||
|
||||
is the same as this manual method:
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
@@ -21,19 +34,9 @@ explicitly.
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Hello {{'World'}}!
|
||||
</body>
|
||||
</html>
|
||||
...
|
||||
</pre>
|
||||
|
||||
This is the sequence that your code should follow if you bootstrap angular on your own:
|
||||
|
||||
1. After the page is loaded, find the root of the HTML template, which is typically the root of
|
||||
the document.
|
||||
2. Call {@link api/angular.bootstrap} to {@link dev_guide.compiler compile} the template into
|
||||
an executable, bi-directionally bound application.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
@name Developer Guide: Initializing Angular
|
||||
@description
|
||||
|
||||
Initializing angular consists of loading the `angular.js` script in your page, and specifying how
|
||||
angular should process and manage the page. To initialize angular you do the following:
|
||||
Initializing Angular consists of loading the `angular.js` script in your page, and specifying how
|
||||
Angular should process and manage the page. To initialize Angular you do the following:
|
||||
|
||||
* Specify the angular namespace in the `<html>` page
|
||||
* Choose which flavor of angular script to load (debug or production)
|
||||
* Specify whether or not angular should process and manage the page automatically (`ng-app`)
|
||||
* Specify the Angular namespace in the `<html>` page
|
||||
* Choose which flavor of Angular script to load (debug or production)
|
||||
* Specify whether or not Angular should process and manage the page automatically (`ngApp`)
|
||||
|
||||
The simplest way to initialize angular is to load the angular script and tell angular to compile
|
||||
The simplest way to initialize Angular is to load the Angular script and tell Angular to compile
|
||||
and manage the whole page. You do this as follows:
|
||||
|
||||
<pre>
|
||||
@@ -29,8 +29,8 @@ and manage the whole page. You do this as follows:
|
||||
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
|
||||
You need to add the angular namespace declaration if you use `ng:something` style of declaring
|
||||
angular directives and you write your templates as XHTML. Or when you are targeting Internet
|
||||
You need to add the Angular namespace declaration if you use `ng:something` style of declaring
|
||||
Angular directives and you write your templates as XHTML. Or when you are targeting Internet
|
||||
Explorer older than version 9 (because older versions of IE do not render namespace
|
||||
properly for either HTML or XHTML). For more info please read {@link ie Internet Explorer
|
||||
Compatibility} doc.
|
||||
@@ -38,8 +38,8 @@ Compatibility} doc.
|
||||
|
||||
## Creating Your Own Namespaces
|
||||
|
||||
When you are ready to define your own {@link api/angular.module.ng.$compileProvider.directive
|
||||
directive}, you may chose to create your own namespace in addition to specifying the angular
|
||||
When you are ready to define your own {@link guide/directive
|
||||
directive}, you may chose to create your own namespace in addition to specifying the Angular
|
||||
namespace. You use your own namespace to form the fully qualified name for directives that you
|
||||
create.
|
||||
|
||||
@@ -52,7 +52,7 @@ it to your unique domain:
|
||||
|
||||
## Loading the Angular Bootstrap Script
|
||||
|
||||
The angular bootstrap script comes in two flavors; a debug script, and a production script:
|
||||
The Angular bootstrap script comes in two flavors; a debug script, and a production script:
|
||||
|
||||
* angular-[version].js - This is a human-readable file, suitable for development and debugging.
|
||||
* angular-[version].min.js - This is a compressed and obfuscated file, suitable for use in
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
@name Developer Guide: Angular HTML Compiler
|
||||
@description
|
||||
|
||||
The core of angular is its HTML compiler. The compiler processes angular directives allowing them
|
||||
to transform a static HTML page into a dynamic web application.
|
||||
The core of Angular is its HTML compiler. The compiler processes Angular
|
||||
{@link guide/directive directives} allowing them to transform a
|
||||
static HTML page into a dynamic web application.
|
||||
|
||||
The default HTML transformations that the angular compiler provides are useful for building generic
|
||||
The default HTML transformations that the Angular compiler provides are useful for building generic
|
||||
apps, but you can also extend the compiler to create a domain-specific language for building
|
||||
specific types of web applications.
|
||||
|
||||
@@ -20,4 +21,4 @@ All compilation takes place in the web browser; no server is involved.
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.module.ng.$compile Angular Compiler API}
|
||||
* {@link api/angular.module.ng.$compileProvider.directive Directives API}
|
||||
* {@link guide/directive Directives API}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
@description
|
||||
|
||||
The {@link api/angular.module.ng.$compile compiler} is responsible for applying
|
||||
{@link api/angular.module.ng.$compileProvider.directive directives} to the HTML. The directives
|
||||
{@link guide/directive directives} to the HTML. The directives
|
||||
extend the behavior of HTML elements and can effect the DOM structure, presentation, and behavior.
|
||||
This allows Angular to teach the browser new tricks.
|
||||
|
||||
@@ -21,7 +21,7 @@ Since directives allow attachment of behavior to the HTML, the angular philosoph
|
||||
HTML as Domain Specific Language (DSL) when building an application. For example it may be useful
|
||||
to declare `TabPanel` directive, or `KeyboardShortcut` directive when for an application.
|
||||
|
||||
For details on how directives are created see {@link api/angular.module.ng.$compileProvider.directive
|
||||
For details on how directives are created see {@link guide/directive
|
||||
directives}
|
||||
|
||||
## Related Topics
|
||||
|
||||
@@ -27,9 +27,9 @@ In the illustration above, the dependency injection sequence proceeds as follows
|
||||
|
||||
1. Module "phonecat" is created and all the service providers are registered with this module.
|
||||
(the "ng" module is created by Angular behind the scenes as well)
|
||||
2. `ng-app` triggers bootstrap sequence on given element, during which angular creates injector,
|
||||
2. `ngApp` triggers bootstrap sequence on given element, during which angular creates injector,
|
||||
loads "phonecat" and "ng" modules and compiles the template.
|
||||
3. The `ng-controller` directive implicitly creates a new child scope and instantiates
|
||||
3. The `ngController` directive implicitly creates a new child scope and instantiates
|
||||
`PhoneListCtrl` controller.
|
||||
4. Injector identifies the `$http` service as `PhoneListCtrl` controller's only dependency.
|
||||
5. Injector checks its instances cache whether the `$http` service has already been instantiated.
|
||||
|
||||
@@ -6,18 +6,17 @@ The most common place to use dependency injection in angular applications is in
|
||||
dev_guide.mvc.understanding_controller controllers}. Here is a simple example:
|
||||
|
||||
<pre>
|
||||
function MyController($route){
|
||||
// configure the route service
|
||||
$route.when(...);
|
||||
function MyController($location){
|
||||
// do stuff with the $location service
|
||||
}
|
||||
MyController.$inject = ['$route'];
|
||||
MyController.$inject = ['$location'];
|
||||
</pre>
|
||||
|
||||
In this example, the `MyController` constructor function takes one argument, the {@link
|
||||
api/angular.module.ng.$route $route} service. Angular is then responsible for supplying the instance
|
||||
of `$route` to the controller when the constructor is instantiated. There are two ways to cause
|
||||
controller instantiation – by configuring routes with the `$route` service, or by referencing the
|
||||
controller from the HTML template, as follows:
|
||||
api/angular.module.ng.$location $location} service. Angular is then responsible for supplying the
|
||||
instance of `$location` to the controller when the constructor is instantiated. There are two ways
|
||||
to cause controller instantiation – by configuring routes with the `$location` service, or by
|
||||
referencing the controller from the HTML template, as follows:
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
@@ -35,7 +34,7 @@ we have to supply this information to angular in the form of an additional prope
|
||||
controller constructor function called `$inject`. Think of it as annotations for JavaScript.
|
||||
|
||||
<pre>
|
||||
MyController.$inject = ['$route'];
|
||||
MyController.$inject = ['$location'];
|
||||
</pre>
|
||||
|
||||
The information in `$inject` is then used by the {@link api/angular.injector injector} to call the
|
||||
|
||||
@@ -45,7 +45,7 @@ the only button on the page, and then it verifies that there are 10 items listed
|
||||
The API section below lists the available commands and expectations for the Runner.
|
||||
|
||||
# API
|
||||
Source: {@link https://github.com/angular/angular.js/blob/master/src/scenario/dsl.js}
|
||||
Source: {@link https://github.com/angular/angular.js/blob/master/src/ngScenario/dsl.js}
|
||||
|
||||
## pause()
|
||||
Pauses the execution of the tests until you call `resume()` in the console (or click the resume
|
||||
|
||||
@@ -12,15 +12,15 @@ Server-side validation is still necessary for a secure application.
|
||||
|
||||
|
||||
# Simple form
|
||||
The key directive in understanding two-way data-binding is {@link api/angular.module.ng.$compileProvider.directive.ng-model ng-model}.
|
||||
The `ng-model` provides the two-way data-binding by synchronizing the model to the view, as well as view to the model.
|
||||
In addition it provides {@link api/angular.module.ng.$compileProvider.directive.ng-model.NgModelController API} for other directives to augment its behavior.
|
||||
The key directive in understanding two-way data-binding is {@link api/angular.module.ng.$compileProvider.directive.ngModel ngModel}.
|
||||
The `ngModel` directive provides the two-way data-binding by synchronizing the model to the view, as well as view to the model.
|
||||
In addition it provides {@link api/angular.module.ng.$compileProvider.directive.ngModel.NgModelController API} for other directives to augment its behavior.
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<div ng-controller="Controller">
|
||||
<form novalidate class="simple-form">
|
||||
Name: <input type="text" ng-model="user.name" ng-model-instant /><br />
|
||||
Name: <input type="text" ng-model="user.name" /><br />
|
||||
E-mail: <input type="email" ng-model="user.email" /><br />
|
||||
Gender: <input type="radio" ng-model="user.gender" value="male" />male
|
||||
<input type="radio" ng-model="user.gender" value="female" />female<br />
|
||||
@@ -31,7 +31,7 @@ In addition it provides {@link api/angular.module.ng.$compileProvider.directive.
|
||||
<pre>master = {{master | json}}</pre>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
function Controller($scope) {
|
||||
$scope.master= {};
|
||||
|
||||
@@ -50,17 +50,13 @@ In addition it provides {@link api/angular.module.ng.$compileProvider.directive.
|
||||
</doc:example>
|
||||
|
||||
|
||||
Note that:
|
||||
|
||||
* the {@link api/angular.module.ng.$compileProvider.directive.ng-model-instant ng-model-instant} causes the `user.name` to be updated immediately.
|
||||
|
||||
* `novalidate` is used to disable browser's native form validation.
|
||||
Note that `novalidate` is used to disable browser's native form validation.
|
||||
|
||||
|
||||
|
||||
# Using CSS classes
|
||||
|
||||
To allow styling of form as well as controls, `ng-model` add these CSS classes:
|
||||
To allow styling of form as well as controls, `ngModel` add these CSS classes:
|
||||
|
||||
- `ng-valid`
|
||||
- `ng-invalid`
|
||||
@@ -76,7 +72,7 @@ This ensures that the user is not distracted with an error until after interacti
|
||||
<div ng-controller="Controller">
|
||||
<form novalidate class="css-form">
|
||||
Name:
|
||||
<input type="text" ng-model="user.name" ng-model-instant required /><br />
|
||||
<input type="text" ng-model="user.name" required /><br />
|
||||
E-mail: <input type="email" ng-model="user.email" required /><br />
|
||||
Gender: <input type="radio" ng-model="user.gender" value="male" />male
|
||||
<input type="radio" ng-model="user.gender" value="female" />female<br />
|
||||
@@ -95,7 +91,7 @@ This ensures that the user is not distracted with an error until after interacti
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
function Controller($scope) {
|
||||
$scope.master= {};
|
||||
|
||||
@@ -119,7 +115,7 @@ This ensures that the user is not distracted with an error until after interacti
|
||||
|
||||
A form is in instance of {@link api/angular.module.ng.$compileProvider.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/angular.module.ng.$compileProvider.directive.ng-model.NgModelController NgModelController}.
|
||||
Similarly control is an instance of {@link api/angular.module.ng.$compileProvider.directive.ngModel.NgModelController NgModelController}.
|
||||
The control instance can similarly be published into the form instance using the `name` attribute.
|
||||
This implies that the internal state of both the form and the control is available for binding in the view using the standard binding primitives.
|
||||
|
||||
@@ -147,16 +143,16 @@ This allows us to extend the above example with these features:
|
||||
|
||||
<input type="checkbox" ng-model="user.agree" name="userAgree" required />
|
||||
I agree: <input ng-show="user.agree" type="text" ng-model="user.agreeSign"
|
||||
ng-model-instant required /><br />
|
||||
required /><br />
|
||||
<div ng-show="!user.agree || !user.agreeSign">Please agree and sign.</div>
|
||||
|
||||
<button ng-click="reset()" disabled="{{isUnchanged(user)}}">RESET</button>
|
||||
<button ng-click="reset()" ng-disabled="isUnchanged(user)">RESET</button>
|
||||
<button ng-click="update(user)"
|
||||
disabled="{{form.$invalid || isUnchanged(user)}}">SAVE</button>
|
||||
ng-disabled="form.$invalid || isUnchanged(user)">SAVE</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
function Controller($scope) {
|
||||
$scope.master= {};
|
||||
|
||||
@@ -185,16 +181,16 @@ This allows us to extend the above example with these features:
|
||||
Angular provides basic implementation for most common html5 {@link api/angular.module.ng.$compileProvider.directive.input input}
|
||||
types: ({@link api/angular.module.ng.$compileProvider.directive.input.text text}, {@link api/angular.module.ng.$compileProvider.directive.input.number number}, {@link api/angular.module.ng.$compileProvider.directive.input.url url}, {@link api/angular.module.ng.$compileProvider.directive.input.email email}, {@link api/angular.module.ng.$compileProvider.directive.input.radio radio}, {@link api/angular.module.ng.$compileProvider.directive.input.checkbox checkbox}), as well as some directives for validation (`required`, `pattern`, `minlength`, `maxlength`, `min`, `max`).
|
||||
|
||||
Defining your own validator can be done by defining your own directive which adds a custom validation function to the `ng-model` {@link api/angular.module.ng.$compileProvider.directive.ng-model.NgModelController controller}.
|
||||
Defining your own validator can be done by defining your own directive which adds a custom validation function to the `ngModel` {@link api/angular.module.ng.$compileProvider.directive.ngModel.NgModelController controller}.
|
||||
To get a hold of the controller the directive specifies a dependency as shown in the example below.
|
||||
The validation can occur in two places:
|
||||
|
||||
* **Model to View update** -
|
||||
Whenever the bound model changes, all functions in {@link api/angular.module.ng.$compileProvider.directive.ng-model.NgModelController#$formatters NgModelController#$formatters} array are pipe-lined, so that each of these functions has an opportunity to format the value and change validity state of the form control through {@link api/angular.module.ng.$compileProvider.directive.ng-model.NgModelController#$setValidity NgModelController#$setValidity}.
|
||||
Whenever the bound model changes, all functions in {@link api/angular.module.ng.$compileProvider.directive.ngModel.NgModelController#$formatters NgModelController#$formatters} array are pipe-lined, so that each of these functions has an opportunity to format the value and change validity state of the form control through {@link api/angular.module.ng.$compileProvider.directive.ngModel.NgModelController#$setValidity NgModelController#$setValidity}.
|
||||
|
||||
* **View to Model update** -
|
||||
In a similar way, whenever a user interacts with a control, the controll calls {@link api/angular.module.ng.$compileProvider.directive.ng-model.NgModelController#$setViewValue NgModelController#$setViewValue}.
|
||||
This in turn pipelines all functions in {@link api/angular.module.ng.$compileProvider.directive.ng-model.NgModelController#$parsers NgModelController#$parsers} array, so that each of these functions has an opportunity to convert the value and change validity state of the form control through {@link api/angular.module.ng.$compileProvider.directive.ng-model.NgModelController#$setValidity NgModelController#$setValidity}.
|
||||
In a similar way, whenever a user interacts with a control, the controll calls {@link api/angular.module.ng.$compileProvider.directive.ngModel.NgModelController#$setViewValue NgModelController#$setViewValue}.
|
||||
This in turn pipelines all functions in {@link api/angular.module.ng.$compileProvider.directive.ngModel.NgModelController#$parsers NgModelController#$parsers} array, so that each of these functions has an opportunity to convert the value and change validity state of the form control through {@link api/angular.module.ng.$compileProvider.directive.ngModel.NgModelController#$setValidity NgModelController#$setValidity}.
|
||||
|
||||
In the following example we create two directives.
|
||||
|
||||
@@ -231,7 +227,7 @@ In the following example we create two directives.
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
var app = angular.module('form-example1', []);
|
||||
|
||||
var INTEGER_REGEXP = /^\-?\d*$/;
|
||||
@@ -276,22 +272,22 @@ In the following example we create two directives.
|
||||
</doc:example>
|
||||
|
||||
|
||||
# Implementing custom form control (using ng-model)
|
||||
# Implementing custom form control (using `ngModel`)
|
||||
Angular implements all of the basic HTML form controls ({@link api/angular.module.ng.$compileProvider.directive.input input}, {@link api/angular.module.ng.$compileProvider.directive.select select}, {@link api/angular.module.ng.$compileProvider.directive.textarea textarea}), which should be sufficient for most cases.
|
||||
However, if you need more flexibility, you can write your own form control as a directive.
|
||||
|
||||
In order for custom control to work with `ng-model` and to achieve two-way data-binding it needs to:
|
||||
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/angular.module.ng.$compileProvider.directive.ng-model.NgModelController#$formatters NgModelController#$formatters},
|
||||
- implement `render` method, which is responsible for rendering the data after it passed the {@link api/angular.module.ng.$compileProvider.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 api/angular.module.ng.$compileProvider.directive $compileProvider.directive} for more info.
|
||||
See {@link guide/directive $compileProvider.directive} for more info.
|
||||
|
||||
The following example shows how to add two-way data-binding to contentEditable elements.
|
||||
|
||||
<doc:example module="form-example2">
|
||||
<doc:source>
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
angular.module('form-example2', []).directive('contenteditable', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
|
||||
@@ -22,7 +22,7 @@ http://docs.angularjs.org/#!/api/angular.module.ng.$filter.number number} and {@
|
||||
http://docs.angularjs.org/#!/api/angular.module.ng.$filter.currency currency} filters.
|
||||
|
||||
Additionally, Angular supports localizable pluralization support provided by the {@link
|
||||
api/angular.module.ng.$compileProvider.directive.ng-pluralize ng-pluralize directive}.
|
||||
api/angular.module.ng.$compileProvider.directive.ngPluralize ngPluralize directive}.
|
||||
|
||||
All localizable Angular components depend on locale-specific rule sets managed by the {@link
|
||||
api/angular.module.ng.$locale $locale service}.
|
||||
|
||||
@@ -30,10 +30,6 @@ function GreetingCtrl($scope) {
|
||||
|
||||
The `GreetingCtrl` controller creates a `greeting` model which can be referred to in a template.
|
||||
|
||||
When a controller function is applied to an angular scope object, the `this` of the controller
|
||||
function becomes the scope of the angular scope object, so any assignment to `this` within the
|
||||
controller function happens on the angular scope object.
|
||||
|
||||
# Adding Behavior to a Scope Object
|
||||
|
||||
Behavior on an angular scope object is in the form of scope method properties available to the
|
||||
@@ -41,13 +37,8 @@ template/view. This behavior interacts with and modifies the application model.
|
||||
|
||||
As discussed in the {@link dev_guide.mvc.understanding_model Model} section of this guide, any
|
||||
objects (or primitives) assigned to the scope become model properties. Any functions assigned to
|
||||
the scope, along with any prototype methods of the controller type, become functions available in
|
||||
the template/view, and can be invoked via angular expressions and `ng-` event handlers (e.g. {@link
|
||||
api/angular.module.ng.$compileProvider.directive.ng-click ng-click}). These controller methods are always evaluated within the
|
||||
context of the angular scope object that the controller function was applied to (which means that
|
||||
the `this` keyword of any controller method is always bound to the scope that the controller
|
||||
augments). This is how the second task of adding behavior to the scope is accomplished.
|
||||
|
||||
the scope are available in the template/view, and can be invoked via angular expressions
|
||||
and `ng` event handler directives (e.g. {@link api/angular.module.ng.$compileProvider.directive.ngClick ngClick}).
|
||||
|
||||
# Using Controllers Correctly
|
||||
|
||||
@@ -66,7 +57,7 @@ manipulation—the presentation logic of an application—is well known for bein
|
||||
Putting any presentation logic into controllers significantly affects testability of the business
|
||||
logic. Angular offers {@link dev_guide.templates.databinding} for automatic DOM manipulation. If
|
||||
you have to perform your own manual DOM manipulation, encapsulate the presentation logic in
|
||||
{@link api/angular.module.ng.$compileProvider.directive directives}.
|
||||
{@link guide/directive directives}.
|
||||
- Input formatting — Use {@link dev_guide.forms angular form controls} instead.
|
||||
- Output filtering — Use {@link dev_guide.templates.filters angular filters} instead.
|
||||
- Run stateless or stateful code shared across controllers — Use {@link dev_guide.services angular
|
||||
@@ -78,7 +69,7 @@ instances).
|
||||
# Associating Controllers with Angular Scope Objects
|
||||
|
||||
You can associate controllers with scope objects explicitly via the {@link api/angular.module.ng.$rootScope.Scope#$new
|
||||
scope.$new} api or implicitly via the {@link api/angular.module.ng.$compileProvider.directive.ng-controller ng-controller
|
||||
scope.$new} api or implicitly via the {@link api/angular.module.ng.$compileProvider.directive.ngController ngController
|
||||
directive} or {@link api/angular.module.ng.$route $route service}.
|
||||
|
||||
|
||||
@@ -108,28 +99,34 @@ string "very". Depending on which button is clicked, the `spice` model is set to
|
||||
function SpicyCtrl($scope) {
|
||||
$scope.spice = 'very';
|
||||
$scope.chiliSpicy = function() {
|
||||
this.spice = 'chili';
|
||||
$scope.spice = 'chili';
|
||||
}
|
||||
$scope.jalapenoSpicy = function() {
|
||||
$scope.spice = 'jalapeño';
|
||||
}
|
||||
}
|
||||
|
||||
SpicyCtrl.prototype.jalapenoSpicy = function() {
|
||||
this.spice = 'jalapeño';
|
||||
}
|
||||
|
||||
</pre>
|
||||
|
||||
Things to notice in the example above:
|
||||
|
||||
- The `ng-controller` directive is used to (implicitly) create a scope for our template, and the
|
||||
- The `ngController` directive is used to (implicitly) create a scope for our template, and the
|
||||
scope is augmented (managed) by the `SpicyCtrl` controller.
|
||||
- `SpicyCtrl` is just a plain JavaScript function. As an (optional) naming convention the name
|
||||
starts with capital letter and ends with "Ctrl" or "Controller".
|
||||
- The JavaScript keyword `this` in the `SpicyCtrl` function is bound to the scope that the
|
||||
controller augments.
|
||||
- Assigning a property to `this` creates or updates the model.
|
||||
- Controller methods can be created through direct assignment to scope (the `chiliSpicy` method) or
|
||||
as prototype methods of the controller constructor function(the `jalapenoSpicy` method)
|
||||
- Assigning a property to `$scope` creates or updates the model.
|
||||
- Controller methods can be created through direct assignment to scope (the `chiliSpicy` method)
|
||||
- Both controller methods are available in the template (for the `body` element and and its
|
||||
children).
|
||||
- NB: Previous versions of Angular (pre 1.0 RC) allowed you to use `this` interchangeably with
|
||||
the $scope method, but this is no longer the case. Inside of methods defined on the scope
|
||||
`this` and $scope are interchangeable (angular sets `this` to $scope), but not otherwise
|
||||
inside your controller constructor.
|
||||
- NB: Previous versions of Angular (pre 1.0 RC) added prototype methods into the scope
|
||||
automatically, but this is no longer the case; all methods need to be added manually to
|
||||
the scope.
|
||||
|
||||
|
||||
Controller methods can also take arguments, as demonstrated in the following variation of the
|
||||
previous example.
|
||||
@@ -147,7 +144,7 @@ previous example.
|
||||
function SpicyCtrl($scope) {
|
||||
$scope.spice = 'very';
|
||||
$scope.spicy = function(spice) {
|
||||
this.spice = spice;
|
||||
$scope.spice = spice;
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
@@ -186,7 +183,7 @@ function BabyCtrl($scope) {
|
||||
}
|
||||
</pre>
|
||||
|
||||
Notice how we nested three `ng-controller` directives in our template. This template construct will
|
||||
Notice how we nested three `ngController` directives in our template. This template construct will
|
||||
result in 4 scopes being created for our view:
|
||||
|
||||
- The root scope
|
||||
@@ -206,19 +203,17 @@ are applied to the scope object.
|
||||
|
||||
## Testing Controllers
|
||||
|
||||
The way to test a controller depends upon how complicated the controller is.
|
||||
|
||||
- If your controller doesn't use DI or scope methods — create the controller with the `new`
|
||||
operator and test away. For example:
|
||||
Although there are many ways to test a controller, one of the best conventions, shown below,
|
||||
involves injecting the `$rootScope` and `$controller`
|
||||
|
||||
Controller Function:
|
||||
<pre>
|
||||
function myController() {
|
||||
this.spices = [{"name":"pasilla", "spiciness":"mild"},
|
||||
function myController($scope) {
|
||||
$scope.spices = [{"name":"pasilla", "spiciness":"mild"},
|
||||
{"name":"jalapeno", "spiceiness":"hot hot hot!"},
|
||||
{"name":"habanero", "spiceness":"LAVA HOT!!"}];
|
||||
|
||||
this.spice = "habanero";
|
||||
$scope.spice = "habanero";
|
||||
}
|
||||
</pre>
|
||||
|
||||
@@ -227,27 +222,51 @@ Controller Test:
|
||||
describe('myController function', function() {
|
||||
|
||||
describe('myController', function() {
|
||||
var ctrl;
|
||||
var scope;
|
||||
|
||||
beforeEach(function() {
|
||||
ctrl = new myController();
|
||||
});
|
||||
beforeEach(inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
var ctrl = $controller(myController, {$scope: scope});
|
||||
}));
|
||||
|
||||
it('should create "spices" model with 3 spices', function() {
|
||||
expect(ctrl.spices.length).toBe(3);
|
||||
expect(scope.spices.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should set the default value of spice', function() {
|
||||
expect(ctrl.spice).toBe('habanero');
|
||||
expect(scope.spice).toBe('habanero');
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
- If your controller does use DI or scope methods — create a root scope, then create the controller
|
||||
in the root scope with `scope.$new(MyController)`. Test the controller using `$eval`, if necessary.
|
||||
- If you need to test a nested controller that depends on its parent's state — create a root scope,
|
||||
create a parent scope, create a child scope, and test the controller using $eval if necessary.
|
||||
|
||||
If you need to test a nested controller one needs to create the same scope hierarchy
|
||||
in your test as exist in the DOM.
|
||||
|
||||
<pre>
|
||||
describe('state', function() {
|
||||
var mainScope, childScope, babyScope;
|
||||
|
||||
beforeEach(inject(function($rootScope, $controller) {
|
||||
mainScope = $rootScope.$new();
|
||||
var mainCtrl = $controller(MainCtrl, {$scope: mainScope});
|
||||
childScope = mainScope.$new();
|
||||
var childCtrl = $controller(ChildCtrl, {$scope: childScope});
|
||||
babyScope = $rootScope.$new();
|
||||
var babyCtrl = $controller(BabyCtrl, {$scope: babyScope});
|
||||
}));
|
||||
|
||||
it('should have over and selected', function() {
|
||||
expect(mainScope.timeOfDay).toBe('morning');
|
||||
expect(mainScope.name).toBe('Nikki');
|
||||
expect(childScope.timeOfDay).toBe('morning');
|
||||
expect(childScope.name).toBe('Mattie');
|
||||
expect(babyScope.timeOfDay).toBe('evening');
|
||||
expect(babyScope.name).toBe('Gingerbreak Baby');
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
@@ -30,7 +30,7 @@ occurs in controllers:
|
||||
|
||||
<button ng-click="{{foos='ball'}}">Click me</button>
|
||||
|
||||
* Use {@link api/angular.module.ng.$compileProvider.directive.ng-init ng-init directive} in templates (for toy/example apps
|
||||
* Use {@link api/angular.module.ng.$compileProvider.directive.ngInit ngInit directive} in templates (for toy/example apps
|
||||
only, not recommended for real applications):
|
||||
|
||||
<body ng-init=" foo = 'bar' ">
|
||||
@@ -45,7 +45,7 @@ when processing the following template constructs:
|
||||
The code above creates a model called "query" on the current scope with the value set to "fluffy
|
||||
cloud".
|
||||
|
||||
* An iterator declaration in {@link api/angular.module.ng.$compileProvider.directive.ng-repeat ng-repeater}:
|
||||
* An iterator declaration in {@link api/angular.module.ng.$compileProvider.directive.ngRepeat ngRepeater}:
|
||||
|
||||
<p ng-repeat="phone in phones"></p>
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ the DOM based on information in the template, controller and model.
|
||||
|
||||
In the angular implementation of MVC, the view has knowledge of both the model and the controller.
|
||||
The view knows about the model where two-way data-binding occurs. The view has knowledge of the
|
||||
controller through angular directives, such as {@link api/angular.module.ng.$compileProvider.directive.ng-controller
|
||||
ng-controller} and {@link api/angular.module.ng.$compileProvider.directive.ng-view ng-view}, and through bindings of this form:
|
||||
controller through angular directives, such as {@link api/angular.module.ng.$compileProvider.directive.ngController
|
||||
ngController} and {@link api/angular.module.ng.$compileProvider.directive.ngView ngView}, and through bindings of this form:
|
||||
`{{someControllerFunction()}}`. In these ways, the view can call functions in an associated
|
||||
controller function.
|
||||
|
||||
|
||||
@@ -79,8 +79,8 @@ easier a web developer's life can if they're using angular:
|
||||
Try out the Live Preview above, and then let's walk through the example and describe what's going
|
||||
on.
|
||||
|
||||
In the `<html>` tag we specify that this is an angular application with the `ng-app` directive.
|
||||
The `ng-app' will cause the angular to {@link dev_guide.bootstrap auto initialize} your application.
|
||||
In the `<html>` tag we specify that this is an angular application with the `ngApp` directive.
|
||||
The `ngApp' will cause the angular to {@link dev_guide.bootstrap auto initialize} your application.
|
||||
|
||||
<html ng-app>
|
||||
|
||||
@@ -88,7 +88,7 @@ We load the angular using the `<script>` tag:
|
||||
|
||||
<script src="http://code.angularjs.org/angular-?.?.?.min.js"></script>
|
||||
|
||||
From the `ng-model` attribute of the `<input>` tags, angular automatically sets up two-way data
|
||||
From the `ngModel` 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 >
|
||||
@@ -112,7 +112,7 @@ And finally, the mysterious `{{ double curly braces }}`:
|
||||
This notation, `{{ _expression_ }}`, is a bit of built-in angular binding markup, a shortcut for
|
||||
displaying data to the user. The expression within curly braces is monitored and its evaluated value
|
||||
is updated into the view by angular's template compiler. Alternatively, one could use angular's
|
||||
{@link api/angular.module.ng.$compileProvider.directive.ng-bind ng-bind}) directive. The expression
|
||||
{@link api/angular.module.ng.$compileProvider.directive.ngBind ngBind}) directive. The expression
|
||||
itself can be a combination of both an expression and a {@link dev_guide.templates.filters filter}:
|
||||
`{{ expression | filter }}`. Angular provides filters for formatting display data.
|
||||
|
||||
|
||||
@@ -46,8 +46,8 @@ reside on a child scope, if a property read does not find the property on a scop
|
||||
recursively check the parent scope, grandparent scope, etc. all the way to the root scope before
|
||||
defaulting to undefined.
|
||||
|
||||
{@link api/angular.module.ng.$compileProvider.directive directives} associated with elements
|
||||
(ng-controller, ng-repeat, ng-include, etc.) create new child scopes that inherit properties from
|
||||
{@link guide/directive directives} associated with elements
|
||||
(ngController, ngRepeat, ngInclude, etc.) create new child scopes that inherit properties from
|
||||
the current parent scope. Any code in Angular is free to create a new scope. Whether or not your
|
||||
code does so is an implementation detail of the directive, that is, you can decide when or if this
|
||||
happens. Inheritance typically mimics HTML DOM element nesting, but does not do so with the same
|
||||
@@ -117,14 +117,14 @@ scopes come into play throughout and get a sense of their interactions.
|
||||
1. At application compile time, a root scope is created and is attached to the root `<HTML>` DOM
|
||||
element.
|
||||
2. During the compilation phase, the {@link dev_guide.compiler compiler} matches {@link
|
||||
api/angular.module.ng.$compileProvider.directive directives} against the DOM template. The directives
|
||||
guide/directive directives} against the DOM template. The directives
|
||||
usually fall into one of two categories:
|
||||
- Observing {@link api/angular.module.ng.$compileProvider.directive directives}, such as double-curly
|
||||
- Observing {@link guide/directive directives}, such as double-curly
|
||||
expressions `{{expression}}`, register listeners using the {@link
|
||||
api/angular.module.ng.$rootScope.Scope#$watch $watch()} method. This type of directive needs to
|
||||
be notified whenever the expression changes so that it can update the view.
|
||||
- Listener directives, such as {@link api/angular.module.ng.$compileProvider.directive.ng-click
|
||||
ng-click}, register a listener with the DOM. When the DOM listener fires, the directive executes
|
||||
- Listener directives, such as {@link api/angular.module.ng.$compileProvider.directive.ngClick
|
||||
ngClick}, register a listener with the DOM. When the DOM listener fires, the directive executes
|
||||
the associated expression and updates the view using the {@link
|
||||
api/angular.module.ng.$rootScope.Scope#$apply $apply()} method.
|
||||
3. When an external event (such as a user action, timer or XHR) is received, the associated {@link
|
||||
@@ -133,9 +133,9 @@ api/angular.module.ng.$rootScope.Scope#$apply $apply()} method so that all liste
|
||||
|
||||
|
||||
### Directives that create scopes
|
||||
In most cases, {@link api/angular.module.ng.$compileProvider.directive directives} and scopes interact but do not create new
|
||||
instances of scope. However, some directives, such as {@link api/angular.module.ng.$compileProvider.directive.ng-controller
|
||||
ng-controller} and {@link api/angular.module.ng.$compileProvider.directive.ng-repeat ng-repeat}, create new child scopes using
|
||||
In most cases, {@link guide/directive directives} and scopes interact but do not create new
|
||||
instances of scope. However, some directives, such as {@link api/angular.module.ng.$compileProvider.directive.ngController
|
||||
ngController} and {@link api/angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat}, create new child scopes using
|
||||
the {@link api/angular.module.ng.$rootScope.Scope#$new $new()} method and then attach the child scope to the
|
||||
corresponding DOM element. You can retrieve a scope for any DOM element by using an
|
||||
`angular.element(aDomElement).scope()` method call.)
|
||||
@@ -144,7 +144,7 @@ corresponding DOM element. You can retrieve a scope for any DOM element by using
|
||||
### Controllers and scopes
|
||||
Scopes and controllers interact with each other in the following situations:
|
||||
- Controllers use scopes to expose controller methods to templates (see {@link
|
||||
api/angular.module.ng.$compileProvider.directive.ng-controller ng-controller}).
|
||||
api/angular.module.ng.$compileProvider.directive.ngController ngController}).
|
||||
- Controllers define methods (behavior) that can mutate the model (properties on the scope).
|
||||
- Controllers may register {@link api/angular.module.ng.$rootScope.Scope#$watch watches} on the model. These watches
|
||||
execute immediately after the controller behavior executes, but before the DOM gets updated.
|
||||
@@ -170,7 +170,7 @@ $watch-ers firing and view getting updated. Similarly, when a request to fetch d
|
||||
is made and the response comes back, the data is written into the model (scope) within an $apply,
|
||||
which then pushes updates through to the view and any other dependents.
|
||||
|
||||
A widget that creates scopes (such as {@link api/angular.module.ng.$compileProvider.directive.ng-repeat ng-repeat}) via `$new`,
|
||||
A widget that creates scopes (such as {@link api/angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat}) via `$new`,
|
||||
doesn't need to worry about propagating the `$digest` call from the parent scope to child scopes.
|
||||
This happens automatically.
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ the contexts in which Angular creates data-bindings between the model and the vi
|
||||
In addition to providing the context in which data is evaluated, Angular scope objects watch for
|
||||
model changes. The scope objects also notify all components interested in any model changes (for
|
||||
example, functions registered through {@link api/angular.module.ng.$rootScope.Scope#$watch $watch}, bindings created by
|
||||
{@link api/angular.module.ng.$compileProvider.directive.ng-bind ng-bind}, or HTML input elements).
|
||||
{@link api/angular.module.ng.$compileProvider.directive.ngBind ngBind}, or HTML input elements).
|
||||
|
||||
Angular scope objects:
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ code, observe how the value of `name` changes, based on the HTML element it is d
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
The angular {@link api/angular.module.ng.$compileProvider.directive.ng-repeat ng-repeat} directive creates a new scope for each
|
||||
The angular {@link api/angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat} directive creates a new scope for each
|
||||
element that it repeats (in this example the elements are list items). In the `<ul>` element, we
|
||||
initialized `name` to "Hank", and we created an array called `names` to use as the data source for
|
||||
the list items. In each `<li>` element, `name` is overridden. Outside of the `<li>` repeater, the
|
||||
|
||||
@@ -305,7 +305,7 @@ history API or not; the `$location` service makes this transparent to you.
|
||||
### Html link rewriting
|
||||
|
||||
When you use the history API mode, you will need different links in different browser, but all you
|
||||
have to do is specify regular URL links, such as: `<a href="/some?foo=bar">link</a>`
|
||||
have to do is specify regular URL links, such as: `<a href="/some?foo=bar">link</a>`
|
||||
|
||||
When a user clicks on this link,
|
||||
|
||||
@@ -316,12 +316,13 @@ When a user clicks on this link,
|
||||
In cases like the following, links are not rewritten; instead, the browser will perform a full page
|
||||
reload to the original link.
|
||||
|
||||
- Links with an `ng-ext-link` directive<br />
|
||||
Example: `<a href="/ext/link?a=b" ng-ext-link>link</a>`
|
||||
- Links that contain `target="_blank"`<br />
|
||||
Example: `<a href="/ext/link?a=b" target="_blank">link</a>`
|
||||
- Absolute links that go to a different domain<br />
|
||||
- Links that contain `target` element<br>
|
||||
Example: `<a href="/ext/link?a=b" target="_self">link</a>`
|
||||
- Absolute links that go to a different domain<br>
|
||||
Example: `<a href="http://angularjs.org/">link</a>`
|
||||
- Links starting with '/' that lead to a different base path when base is defined<br>
|
||||
Example: `<a href="/not-my-base/link">link</a>`
|
||||
|
||||
|
||||
### Server side
|
||||
|
||||
@@ -341,11 +342,13 @@ Applications Crawlable}.
|
||||
|
||||
### Relative links
|
||||
|
||||
Be sure to check all relative links, images, scripts etc. You must use an absolute path because the
|
||||
path is going to be rewritten. You can use `<base href="" />` tag as well.
|
||||
Be sure to check all relative links, images, scripts etc. You must either specify the url base in
|
||||
the head of your main html file (`<base href="/my-base">`) or you must use absolute urls
|
||||
(starting with `/`) everywhere because relative urls will be resolved to absolute urls using the
|
||||
initial absolute url of the document, which is often different from the root of the application.
|
||||
|
||||
Running Angular apps with the History API enabled from document root is strongly encouraged as it
|
||||
takes care of all relative link issues. **Otherwise you have to specify <base href="" /> !**
|
||||
takes care of all relative link issues.
|
||||
|
||||
### Sending links among different browsers
|
||||
|
||||
@@ -372,34 +375,34 @@ In this examples we use `<base href="/base/index.html" />`
|
||||
<div ng-non-bindable class="html5-hashbang-example">
|
||||
<div id="html5-mode" ng-controller="Html5Cntl">
|
||||
<h3>Browser with History API</h3>
|
||||
<ng-address-bar browser="html5"></ng-address-bar><br /><br />
|
||||
$location.protocol() = {{$location.protocol()}}<br />
|
||||
$location.host() = {{$location.host()}}<br />
|
||||
$location.port() = {{$location.port()}}<br />
|
||||
$location.path() = {{$location.path()}}<br />
|
||||
$location.search() = {{$location.search()}}<br />
|
||||
$location.hash() = {{$location.hash()}}<br />
|
||||
<a href="/base/first?a=b">/base/first?a=b</a> |
|
||||
<a href="sec/ond?flag#hash">sec/ond?flag#hash</a> |
|
||||
<a href="/base/another?search" ng-ext-link>external</a>
|
||||
<div ng-address-bar browser="html5"></div><br><br>
|
||||
$location.protocol() = {{$location.protocol()}}<br>
|
||||
$location.host() = {{$location.host()}}<br>
|
||||
$location.port() = {{$location.port()}}<br>
|
||||
$location.path() = {{$location.path()}}<br>
|
||||
$location.search() = {{$location.search()}}<br>
|
||||
$location.hash() = {{$location.hash()}}<br>
|
||||
<a href="http://www.host.com/base/first?a=b">/base/first?a=b</a> |
|
||||
<a href="http://www.host.com/base/sec/ond?flag#hash">sec/ond?flag#hash</a> |
|
||||
<a href="/other-base/another?search">external</a>
|
||||
</div>
|
||||
|
||||
<div id="hashbang-mode" ng-controller="HashbangCntl">
|
||||
<h3>Browser without History API</h3>
|
||||
<ng-address-bar browser="hashbang"></ng-address-bar><br /><br />
|
||||
$location.protocol() = {{$location.protocol()}}<br />
|
||||
$location.host() = {{$location.host()}}<br />
|
||||
$location.port() = {{$location.port()}}<br />
|
||||
$location.path() = {{$location.path()}}<br />
|
||||
$location.search() = {{$location.search()}}<br />
|
||||
$location.hash() = {{$location.hash()}}<br />
|
||||
<a href="/base/first?a=b">/base/first?a=b</a> |
|
||||
<a href="sec/ond?flag#hash">sec/ond?flag#hash</a> |
|
||||
<a href="/base/another?search" ng-ext-link>external</a>
|
||||
<div ng-address-bar browser="hashbang"></div><br><br>
|
||||
$location.protocol() = {{$location.protocol()}}<br>
|
||||
$location.host() = {{$location.host()}}<br>
|
||||
$location.port() = {{$location.port()}}<br>
|
||||
$location.path() = {{$location.path()}}<br>
|
||||
$location.search() = {{$location.search()}}<br>
|
||||
$location.hash() = {{$location.hash()}}<br>
|
||||
<a href="http://www.host.com/base/first?a=b">/base/first?a=b</a> |
|
||||
<a href="http://www.host.com/base/sec/ond?flag#hash">sec/ond?flag#hash</a> |
|
||||
<a href="/other-base/another?search">external</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
function FakeBrowser(initUrl, baseHref) {
|
||||
this.onUrlChange = function(fn) {
|
||||
this.urlChange = fn;
|
||||
@@ -417,7 +420,6 @@ In this examples we use `<base href="/base/index.html" />`
|
||||
return baseHref;
|
||||
};
|
||||
|
||||
this.hover = angular.noop;
|
||||
this.notifyWhenOutstandingRequests = angular.noop;
|
||||
}
|
||||
|
||||
@@ -426,20 +428,19 @@ In this examples we use `<base href="/base/index.html" />`
|
||||
hashbang: new FakeBrowser('http://www.host.com/base/index.html#!/path?a=b#h', '/base/index.html')
|
||||
};
|
||||
|
||||
function Html5Cntl($location) {
|
||||
this.$location = $location;
|
||||
function Html5Cntl($scope, $location) {
|
||||
$scope.$location = $location;
|
||||
}
|
||||
|
||||
function HashbangCntl($location) {
|
||||
this.$location = $location;
|
||||
function HashbangCntl($scope, $location) {
|
||||
$scope.$location = $location;
|
||||
}
|
||||
|
||||
function initEnv(name) {
|
||||
var root = angular.element(document.getElementById(name + '-mode'));
|
||||
angular.bootstrap(root, [function($compileProvider, $locationProvider, $provide){
|
||||
$locationProvider.html5Mode = true;
|
||||
$locationProvider.hashPrefix = '!';
|
||||
|
||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||
|
||||
$provide.value('$browser', browsers[name]);
|
||||
$provide.value('$document', root);
|
||||
$provide.value('$sniffer', {history: name == 'html5'});
|
||||
@@ -447,7 +448,7 @@ In this examples we use `<base href="/base/index.html" />`
|
||||
$compileProvider.directive('ngAddressBar', function() {
|
||||
return function(scope, elm, attrs) {
|
||||
var browser = browsers[attrs.browser],
|
||||
input = angular.element('<input type="text" />').val(browser.url()),
|
||||
input = angular.element('<input type="text">').val(browser.url()),
|
||||
delay;
|
||||
|
||||
input.bind('keypress keyup keydown', function() {
|
||||
|
||||
@@ -30,7 +30,7 @@ myController.$inject = ['$location', '$log'];
|
||||
|
||||
<doc:example module="MyServiceModule">
|
||||
<doc:source>
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
angular.
|
||||
module('MyServiceModule', []).
|
||||
factory('notify', ['$window', function(win) {
|
||||
@@ -74,7 +74,7 @@ Let's rewrite the above example to show the use of this implicit dependency inje
|
||||
|
||||
<doc:example module="MyServiceModuleDI">
|
||||
<doc:source>
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
angular.
|
||||
module('MyServiceModuleDI', []).
|
||||
factory('notify', function($window) {
|
||||
|
||||
@@ -9,10 +9,10 @@ Angular sets these CSS classes. It is up to your application to provide useful s
|
||||
|
||||
* `ng-invalid`, `ng-valid`
|
||||
- **Usage:** angular applies this class to an input widget element if that element's input does
|
||||
notpass validation. (see {@link api/angular.module.ng.$compileProvider.directive.input input} widget).
|
||||
notpass validation. (see {@link api/angular.module.ng.$compileProvider.directive.input input} directive).
|
||||
|
||||
* `ng-pristine`, `ng-dirty`
|
||||
- **Usage:** angular {@link api/angular.module.ng.$compileProvider.directive.input input} widget applies `ng-pristine` class
|
||||
- **Usage:** angular {@link api/angular.module.ng.$compileProvider.directive.input input} directive applies `ng-pristine` class
|
||||
to a new input widget element which did not have user interaction. Once the user interacts with
|
||||
the input widget the class is changed to `ng-dirty`.
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ text upper-case and assigns color.
|
||||
|
||||
<doc:example module="MyReverseModule">
|
||||
<doc:source>
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
angular.module('MyReverseModule', []).
|
||||
filter('reverse', function() {
|
||||
return function(input, uppercase) {
|
||||
|
||||
@@ -10,7 +10,7 @@ the dynamic view DOM.
|
||||
|
||||
These are the types of angular elements and element attributes you can use in a template:
|
||||
|
||||
* {@link api/angular.module.ng.$compileProvider.directive Directive} — An attribute or element that
|
||||
* {@link guide/directive Directive} — An attribute or element that
|
||||
augments an existing DOM element or represents a reusable DOM component - a widget.
|
||||
* {@link api/angular.module.ng.$interpolate Markup} — The double
|
||||
curly brace notation `{{ }}` to bind expressions to elements is built-in angular markup.
|
||||
@@ -21,12 +21,12 @@ Note: In addition to declaring the elements above in templates, you can also ac
|
||||
in JavaScript code.
|
||||
|
||||
The following code snippet shows a simple angular template made up of standard HTML tags along with
|
||||
angular {@link api/angular.module.ng.$compileProvider.directive directives} and curly-brace bindings
|
||||
angular {@link guide/directive directives} and curly-brace bindings
|
||||
with {@link dev_guide.expressions expressions}:
|
||||
|
||||
<pre>
|
||||
<html ng-app>
|
||||
<!-- Body tag augmented with ng-controller directive -->
|
||||
<!-- Body tag augmented with ngController directive -->
|
||||
<body ng-controller="MyController">
|
||||
<input ng-model="foo" value="bar">
|
||||
<!-- Button tag with ng-click directive, and
|
||||
@@ -42,7 +42,7 @@ In a simple single-page app, the template consists of HTML, CSS, and angular dir
|
||||
in just one HTML file (usually `index.html`). In a more complex app, you can display multiple views
|
||||
within one main page using "partials", which are segments of template located in separate HTML
|
||||
files. You "include" the partials in the main page using the {@link api/angular.module.ng.$route
|
||||
$route} service in conjunction with the {@link api/angular.module.ng.$compileProvider.directive.ng-view ng-view} directive. An
|
||||
$route} service in conjunction with the {@link api/angular.module.ng.$compileProvider.directive.ngView ngView} directive. An
|
||||
example of this technique is shown in the {@link tutorial/ angular tutorial}, in steps seven and
|
||||
eight.
|
||||
|
||||
|
||||
@@ -92,8 +92,8 @@ State & Singletons}
|
||||
|
||||
The class above is hard to test since we have to change global state:
|
||||
<pre>
|
||||
var oldXHR = glabal.xhr;
|
||||
glabal.xhr = function mockXHR() {};
|
||||
var oldXHR = global.xhr;
|
||||
global.xhr = function mockXHR() {};
|
||||
var myClass = new MyClass();
|
||||
myClass.doWork();
|
||||
// assert that mockXHR got called with the right arguments
|
||||
@@ -126,12 +126,12 @@ there is only one global variable to be reset).
|
||||
|
||||
The class above is hard to test since we have to change global state:
|
||||
<pre>
|
||||
var oldServiceLocator = glabal.serviceLocator;
|
||||
glabal.serviceLocator.set('xhr', function mockXHR() {});
|
||||
var oldServiceLocator = global.serviceLocator;
|
||||
global.serviceLocator.set('xhr', function mockXHR() {});
|
||||
var myClass = new MyClass();
|
||||
myClass.doWork();
|
||||
// assert that mockXHR got called with the right arguments
|
||||
glabal.serviceLocator = oldServiceLocator; // if you forget this bad things will happen
|
||||
global.serviceLocator = oldServiceLocator; // if you forget this bad things will happen
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@ngdoc overview
|
||||
@name angular.module.ng.$compileProvider.directive
|
||||
@name directive
|
||||
@description
|
||||
|
||||
Directives are a way to teach HTML new tricks. During DOM compilation directives are matched
|
||||
@@ -38,7 +38,7 @@ the following example.
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="Ctrl1">
|
||||
Hello <input ng-model='name' ng-model-instant> <hr/>
|
||||
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/>
|
||||
@@ -47,7 +47,7 @@ the following example.
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should load template1.html', function() {
|
||||
it('should show off bindings', function() {
|
||||
expect(element('div[ng-controller="Ctrl1"] span[ng-bind]').text()).toBe('angular');
|
||||
});
|
||||
</doc:scenario>
|
||||
@@ -55,11 +55,11 @@ the following example.
|
||||
|
||||
# String interpolation
|
||||
|
||||
During the compilation process the {@link angular.module.ng.$compile compiler} matches text and
|
||||
attributes using the {@link angular.module.ng.$interpolate $interpolate} service to see if they
|
||||
During the compilation process the {@link api/angular.module.ng.$compile compiler} matches text and
|
||||
attributes using the {@link api/angular.module.ng.$interpolate $interpolate} service to see if they
|
||||
contain embedded expressions. These expressions are registered as {@link
|
||||
angular.module.ng.$rootScope.Scope#$watch watches} and will update as part of normal {@link
|
||||
angular.module.ng.$rootScope.Scope#$digest digest} cycle. An example of interpolation is shown
|
||||
api/angular.module.ng.$rootScope.Scope#$watch watches} and will update as part of normal {@link
|
||||
api/angular.module.ng.$rootScope.Scope#$digest digest} cycle. An example of interpolation is shown
|
||||
here:
|
||||
|
||||
<pre>
|
||||
@@ -74,21 +74,21 @@ Compilation of HTML happens in three phases:
|
||||
realize because the templates must be parsable HTML. This is in contrast to most templating
|
||||
systems that operate on strings, rather then on DOM elements.
|
||||
|
||||
2. The compilation of the DOM is performed by the call to {@link angular.module.ng.$compile
|
||||
2. The compilation of the DOM is performed by the call to {@link api/angular.module.ng.$compile
|
||||
$compile()} method. The method traverses the DOM and matches the directives. If a match is found
|
||||
it is added to the list of directives associated with the given DOM element. Once all directives
|
||||
for a given DOM element have been identified they are sorted by priority and their `compile()`
|
||||
functions are executed. The directive compile function has a chance to modify the DOM structure
|
||||
and is responsible for producing a `link()` function explained next. The {@link
|
||||
angular.module.ng.$compile $compile()} method returns a combined linking function, which is a
|
||||
api/angular.module.ng.$compile $compile()} method returns a combined linking function, which is a
|
||||
collection of all of the linking functions returned from the individual directive compile
|
||||
functions.
|
||||
|
||||
3. Link the template with scope by calling the liking function returned from the previous step.
|
||||
3. Link the template with scope by calling the linking function returned from the previous step.
|
||||
This in turn will call the linking function of the individual directives allowing them to
|
||||
register any listeners on the elements and set up any {@link
|
||||
angular.module.ng.$rootScope.Scope#$watch watches} with the {@link
|
||||
angular.module.ng.$rootScope.Scope scope}. The result of this is a live binding between the
|
||||
api/angular.module.ng.$rootScope.Scope#$watch watches} with the {@link
|
||||
api/angular.module.ng.$rootScope.Scope scope}. The result of this is a live binding between the
|
||||
scope and the DOM. A change in the scope is reflected in the DOM.
|
||||
|
||||
<pre>
|
||||
@@ -125,14 +125,14 @@ The short answer is that compile and link separation is needed any time a change
|
||||
a change in DOM structure such as in repeaters.
|
||||
|
||||
When the above example is compiled, the compiler visits every node and looks for directives. The
|
||||
`{{user}}` is an example of {@link angular.module.ng.$interpolate interpolation} directive. {@link
|
||||
angular.module.ng.$compileProvider.directive.ng-repeat ng-repeat} is another directive. But {@link
|
||||
angular.module.ng.$compileProvider.directive.ng-repeat ng-repeat} has a dilemma. It needs to be
|
||||
`{{user}}` is an example of {@link api/angular.module.ng.$interpolate interpolation} directive. {@link
|
||||
api/angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat} is another directive. But {@link
|
||||
api/angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat} has a dilemma. It needs to be
|
||||
able to quickly stamp out new `li`s for every `action` in `user.actions`. This means that it needs
|
||||
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 angular.module.ng.$rootScope.Scope
|
||||
`{{action.descriptions}}` evaluate against the right {@link api/angular.module.ng.$rootScope.Scope
|
||||
scope}. A naive method would be to simply insert a copy of the `li` elemnt 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
|
||||
@@ -140,17 +140,17 @@ repeater which needs to unroll 100 items we would quickly run into performance p
|
||||
|
||||
The solution is to break the compilation process into two phases the compile phase where all of
|
||||
the directives are identified and sorted by priority, and a linking phase where any work which
|
||||
links a specific instance of the {@link angular.module.ng.$rootScope.Scope scope} and the specific
|
||||
links a specific instance of the {@link api/angular.module.ng.$rootScope.Scope scope} and the specific
|
||||
instance of an `li` is performed.
|
||||
|
||||
{@link angular.module.ng.$compileProvider.directive.ng-repeat ng-repeat} works by preventing the
|
||||
{@link api/angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat} works by preventing the
|
||||
compilation process form descending into `li` element. Instead the {@link
|
||||
angular.module.ng.$compileProvider.directive.ng-repeat ng-repeat} directive compiles `li`
|
||||
api/angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat} directive compiles `li`
|
||||
seperatly. The result of of the `li` element compilation is a linking function which contains all
|
||||
of the directives contained in the `li` element ready to be attached to a specific clone of `li`
|
||||
element. At runtime the {@link angular.module.ng.$compileProvider.directive.ng-repeat ng-repeat}
|
||||
element. At runtime the {@link api/angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat}
|
||||
watches the expression and as items are added to the array it clones the `li` element, creates a
|
||||
new {@link angular.module.ng.$rootScope.Scope scope} for the cloned `li` element and calls the
|
||||
new {@link api/angular.module.ng.$rootScope.Scope scope} for the cloned `li` element and calls the
|
||||
link function on the cloned `li`.
|
||||
|
||||
Summary:
|
||||
@@ -288,14 +288,14 @@ further simplification:
|
||||
## Factory method
|
||||
|
||||
The factory method is responsible for creating the directive. It is invoked only once, when the
|
||||
{@link angular.module.ng.$compile compiler} matches the directive for the first time. You can
|
||||
{@link api/angular.module.ng.$compile compiler} matches the directive for the first time. You can
|
||||
perform any initialization work here. The method is invoked using the {@link
|
||||
http://localhost:8000/build/docs/api/angular.module.AUTO.$injector#invoke $injector.invoke} which
|
||||
makes it injectable following all of the rules of injection annotation.
|
||||
|
||||
## Directive Definition Object
|
||||
|
||||
The directive definition object provides instructions to the {@link angular.module.ng.$compile
|
||||
The directive definition object provides instructions to the {@link api/angular.module.ng.$compile
|
||||
compiler}. The attributes are:
|
||||
|
||||
* `name` - Name of the current scope. Optional defaults to the name at registration.
|
||||
@@ -407,7 +407,7 @@ compiler}. The attributes are:
|
||||
migrates all of the attributes / classes from the old element to the new one. See Creating
|
||||
Widgets section below for more information.
|
||||
|
||||
* `templateURL` - Same as `template` but the template is loaded from the specified URL. Because
|
||||
* `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
|
||||
is loaded.
|
||||
|
||||
@@ -415,8 +415,8 @@ compiler}. The attributes are:
|
||||
append the template to the element.
|
||||
|
||||
* `transclude` - compile the content of the element and make it available to the directive.
|
||||
Typically used with {@link api/angular.module.ng.$compileProvider.directive.ng-transclude
|
||||
ng-transclude}. The advantage of transclusion is that the linking function receives a
|
||||
Typically used with {@link api/angular.module.ng.$compileProvider.directive.ngTransclude
|
||||
ngTransclude}. The advantage of transclusion is that the linking function receives a
|
||||
transclusion function which is pre-bound to the correct scope. In a typical setup the widget
|
||||
creates an `isolate` scope, but the transclusion is not a child, but a sibling of the `isolate`
|
||||
scope. This makes it possible for the widget to have private state, and the transclusion to
|
||||
@@ -440,8 +440,8 @@ compiler}. The attributes are:
|
||||
Compile function deals with transforming the template DOM. Since most directives do not do
|
||||
template transformation, it is not used often. Examples which require compile functions are
|
||||
directives which transform template DOM such as {@link
|
||||
angular.module.ng.$compileProvider.directive.ng-repeat ng-repeat} or load the contents
|
||||
asynchronously such as {@link angular.module.ng.$compileProvider.directive.ng-view ng-view}. The
|
||||
api/angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat} or load the contents
|
||||
asynchronously such as {@link api/angular.module.ng.$compileProvider.directive.ngView ngView}. The
|
||||
compile functions takes the following arguments.
|
||||
|
||||
* `tElement` - template element - The element where the directive has been declared. It is
|
||||
@@ -478,8 +478,8 @@ Link function is responsible for registering DOM listeners as well as updating t
|
||||
executed after the template has been cloned. This is where most of the directive logic will be
|
||||
put.
|
||||
|
||||
* `scope` - {@link angular.module.ng.$rootScope.Scope Scope} - The scope to be used by the
|
||||
directive for registering {@link angular.module.ng.$rootScope.Scope#$watch watches}.
|
||||
* `scope` - {@link api/angular.module.ng.$rootScope.Scope Scope} - The scope to be used by the
|
||||
directive for registering {@link api/angular.module.ng.$rootScope.Scope#$watch watches}.
|
||||
|
||||
* `iElement` - instance element - The element where the directive is to be used. It is safe to
|
||||
manipulate the children of the element only in `postLink` function since the children have
|
||||
@@ -584,10 +584,10 @@ expects as follows:
|
||||
|
||||
<pre>
|
||||
scope: {
|
||||
title: 'bind', // set up title to accept data-binding
|
||||
onOk: 'exp', // create a delegate onOk function
|
||||
onCancel: 'exp', // create a delegate onCancel function
|
||||
show: 'prop' // create a getter/setter function for visibility.
|
||||
title: 'bind', // set up title to accept data-binding
|
||||
onOk: 'expression', // create a delegate onOk function
|
||||
onCancel: 'expression', // create a delegate onCancel function
|
||||
show: 'accessor' // create a getter/setter function for visibility.
|
||||
}
|
||||
</pre>
|
||||
|
||||
@@ -618,10 +618,10 @@ Therefore the final directive definition looks something like this:
|
||||
<pre>
|
||||
transclude: true,
|
||||
scope: {
|
||||
title: 'bind', // set up title to accept data-binding
|
||||
onOk: 'exp', // create a delegate onOk function
|
||||
onCancel: 'exp', // create a delegate onCancel function
|
||||
show: 'prop' // create a getter/setter function for visibility.
|
||||
title: 'bind', // set up title to accept data-binding
|
||||
onOk: 'expression', // create a delegate onOk function
|
||||
onCancel: 'expression', // create a delegate onCancel function
|
||||
show: 'accessor' // create a getter/setter function for visibility.
|
||||
}
|
||||
</pre>
|
||||
|
||||
@@ -33,7 +33,7 @@ of the following documents before returning here to the Developer Guide:
|
||||
|
||||
## {@link dev_guide.compiler Angular HTML Compiler}
|
||||
|
||||
* {@link api/angular.module.ng.$compileProvider.directive Understanding Angular Directives}
|
||||
* {@link guide/directive Understanding Angular Directives}
|
||||
|
||||
## {@link dev_guide.templates Angular Templates}
|
||||
|
||||
|
||||
@@ -190,7 +190,7 @@ modules require it.
|
||||
Modules are a way of managing $injector configuration, and have nothing to do with loading of
|
||||
scripts into a VM. There are existing projects which deal with script loading, which may be used
|
||||
with Angular. Because modules do nothing at load time they can be loaded into the VM in any order
|
||||
and thus script loaders can take advantage of this property and paralyze the loading process.
|
||||
and thus script loaders can take advantage of this property and parallelize the loading process.
|
||||
|
||||
|
||||
# Unit Testing
|
||||
@@ -232,7 +232,7 @@ describe('myApp', function() {
|
||||
$provide.value('$window', {
|
||||
alert: jasmine.createSpy('alert')
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
// The inject() will create the injector and inject the greet and
|
||||
// $window into the tests. The test need not concern itself with
|
||||
@@ -251,7 +251,7 @@ describe('myApp', function() {
|
||||
});
|
||||
inject(function(greet) {
|
||||
greet('World');
|
||||
expect(alertSpy).toHaveBeenCalledWith('World');
|
||||
expect(alertSpy).toHaveBeenCalledWith('Hello World!');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Type
|
||||
@description
|
||||
@@ -65,19 +65,19 @@ This example demonstrates angular's two-way data binding:
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Your name: <input type="text" ng-model="yourname" value="World"/>
|
||||
<hr/>
|
||||
Hello {{yourname}}!
|
||||
Your name: <input type="text" ng-model="yourname" placeholder="World">
|
||||
<hr>
|
||||
Hello {{yourname || 'World'}}!
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
After the refresh, the page should look something like this:
|
||||
|
||||
<img class="left" src="img/helloworld_2way.png" border="1" />
|
||||
<img class="left" src="img/helloworld_2way.png" border="1" >
|
||||
|
||||
These are some of the important points to note from this example:
|
||||
|
||||
* The text input {@link api/angular.module.ng.$compileProvider.directive directive}
|
||||
* The text input {@link guide/directive directive}
|
||||
is bound to a model variable called `yourname`.
|
||||
* The double curly braces notation binds the `yourname` model to the greeting text.
|
||||
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
@ngdoc overview
|
||||
@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.
|
||||
|
||||
<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:
|
||||
|
||||
* 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!
|
||||
|
||||
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.
|
||||
|
||||
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 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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Working with the code
|
||||
|
||||
You can follow this tutorial and hack on the code in either the Mac/Linux or the Windows
|
||||
environment. Options for working with the tutorial are to use the Git versioning system for source
|
||||
code management or to use scripts that copy snapshots of project files into your workspace
|
||||
(`sandbox`) directory. Select one of the tabs below and follow the instructions for setting up your
|
||||
computer for your preferred option.
|
||||
|
||||
<div class="tabbable" show="true">
|
||||
<div class="tab-pane well" id="git-mac" title="Git on Mac/Linux">
|
||||
<ol>
|
||||
<li><p>Verify that you have <a href="http://java.com/">Java</a> installed by running the
|
||||
following command in a terminal window:</p>
|
||||
<pre>java -version</pre>
|
||||
<p>You will need Java to run unit tests.</p></li>
|
||||
<li><p>Download Git from the <a href="http://git-scm.com/download">Git</a> site.</p>
|
||||
<p>You can build Git from source or use the pre-compiled package.</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 <a
|
||||
href="http://nodejs.org/#download">install node.js</a>. 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 Java to run unit tests, so run the following command to verify that you
|
||||
have <a href="http://java.com/">Java</a> installed and that the <code>java</code> executable is on
|
||||
your <code>PATH</code>.</p>
|
||||
<pre>java -version</pre>
|
||||
<p></p></li>
|
||||
<li><p>Install msysGit from <a href="http://git-scm.com/download">the Git</a> site.</p></li>
|
||||
<li><p>Open msysGit bash and 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 msysGit bash.</p>
|
||||
<p>Other commands like <code>test-server.bat</code> or <code>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 install <a href="http://nodejs.org/#download">node.js</a>. Make sure that
|
||||
<code>nodejs\bin</code> was added into your <code>PATH</code>. 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="ss-mac" title="Snapshots on Mac/Linux">
|
||||
<ol>
|
||||
<li><p>You need Java to run unit tests, so verify that you have <a
|
||||
href="http://java.com/">Java</a> installed by running the following command in a terminal
|
||||
window:</p>
|
||||
<pre>java -version</pre>
|
||||
<li><p>Download the <a href="http://code.angularjs.org/angular-phonecat/">zip archive</a>
|
||||
containing all of the files and unzip them into the [tutorial-dir] directory</p>.</li>
|
||||
<li><p>Change your current directory to [tutorial-dir]/sandbox, as follows:</p>
|
||||
<pre>cd [tutorial-dir]/sandbox</pre>
|
||||
<p>The tutorial instructions assume you are running all commands from your
|
||||
<code>sandbox</code> directory.</p></li>
|
||||
<li><p>You need an http server running on your system and Mac and Linux machines typically
|
||||
have Apache pre-installed. If you don't have an http server installed, you can <a
|
||||
href="http://nodejs.org/#download">install node.js</a> and use it to run
|
||||
<code>scripts/web-server.js</code>, a simple bundled http server.</p></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane well" id="ss-win" title="Snapshots on Windows">
|
||||
<ol>
|
||||
<li><p>Verify that you have <a href="http://java.com/">Java</a> installed and that the
|
||||
<code>java</code> executable is on your <code>PATH</code> by running the following command in the
|
||||
Windows command line:</p>
|
||||
<pre>java -version</pre>
|
||||
<p>You need Java to run unit tests, so download the <a
|
||||
href="http://code.angularjs.org/angular-phonecat/">zip archive</a> that contains all of the files
|
||||
and unzip the files into the [tutorial-dir] directory</p></li>
|
||||
<li><p>Change your current directory to [tutorial-dir]/sandbox, as follows:</p>
|
||||
<pre>cd [tutorial-dir]/sandbox</pre>
|
||||
<p>The tutorial instructions assume you are running all commands from this directory.</p></li>
|
||||
<li><p>You need an http server running on your system, but if you don't already have one
|
||||
already installed, you can install <a href="http://nodejs.org/#download">node.js</a>. Make sure that
|
||||
<code>nodejs\bin</code> was added into your <code>PATH</code>. Use <code>node</code> to run
|
||||
<code>scripts\web-server.js</code>, a simple bundled http server.</p></li>
|
||||
</ol>
|
||||
</div>
|
||||
</divs>
|
||||
|
||||
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>}
|
||||
@@ -0,0 +1,275 @@
|
||||
@ngdoc overview
|
||||
@name Tutorial: 0 - Bootstrapping
|
||||
@description
|
||||
|
||||
<ul doc-tutorial-nav="0"></ul>
|
||||
|
||||
|
||||
You are now ready to build the AngularJS phonecat app. In this step, you will become familiar
|
||||
with the most important source code files, learn how to start the development servers bundled with
|
||||
angular-seed, and run the application in the browser.
|
||||
|
||||
|
||||
<div class="tabbable" show="true" ng-model="$cookies.platformPreference">
|
||||
<div class="tab-pane well" id="git-mac" title="Git on Mac/Linux" value="gitUnix">
|
||||
<ol>
|
||||
<li><p>In angular-phonecat directory, run this command:</p>
|
||||
<pre>git checkout -f step-0</pre>
|
||||
<p>This resets your workspace to step 0 of the tutorial app.</p>
|
||||
<p>You must repeat this for every future step in the tutorial and change the number to
|
||||
the number of the step you are on. This will cause any changes you made within
|
||||
your working directory to be lost.</p></li>
|
||||
|
||||
<li>To see the app running in a browser, do one of the following:
|
||||
<ul>
|
||||
<li><b>For node.js users:</b>
|
||||
<ol>
|
||||
<li>In a <i>separate</i> terminal tab or window, run
|
||||
<code>./scripts/web-server.js</code> to start the web server.</li>
|
||||
<li>Open a browser window for the app and navigate to <a
|
||||
href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html</a></li>
|
||||
</ol>
|
||||
</li>
|
||||
<li><b>For other http servers:</b>
|
||||
<ol>
|
||||
<li>Configure the server to serve the files in the <code>angular-phonecat</code>
|
||||
directory.</li>
|
||||
<li>Navigate in your browser to
|
||||
<code>http://localhost:[port-number]/[context-path]/app/index.html</code>.</li>
|
||||
</ol>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="tab-pane well" id="git-win" title="Git on Windows" value="gitWin">
|
||||
<ol>
|
||||
<li><p>Open msysGit bash and run this command (in angular-phonecat directory):</p>
|
||||
<pre>git checkout -f step-0</pre>
|
||||
<p>This resets your workspace to step 0 of the tutorial app.</p>
|
||||
<p>You must repeat this for every future step in the tutorial and change the number to
|
||||
the number of the step you are on. This will cause any changes you made within
|
||||
your working directory to be lost.</p></li>
|
||||
<li>To see the app running in a browser, do one of the following:
|
||||
<ul>
|
||||
<li><b>For node.js users:</b>
|
||||
<ol>
|
||||
<li>In a <i>separate</i> terminal tab or window, run <code>node
|
||||
scripts\web-server.js</code> to start the web server.</li>
|
||||
<li>Open a browser window for the app and navigate to <a
|
||||
href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html</a></li>
|
||||
</ol>
|
||||
</li>
|
||||
<li><b>For other http servers:</b>
|
||||
<ol>
|
||||
<li>Configure the server to serve the files in the <code>angular-phonecat</code>
|
||||
directory.</li>
|
||||
<li>Navigate in your browser to
|
||||
<code>http://localhost:[port-number]/[context-path]/app/index.html</code>.</li>
|
||||
</ol>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="tab-pane well" id="ss-mac" title="Snapshots on Mac/Linux" value="snapshotUnix">
|
||||
<ol>
|
||||
<li><p>In the angular-phonecat directory, run this command:</p>
|
||||
<pre>./goto_step.sh 0</pre>
|
||||
<p>This resets your workspace to step 0 of the tutorial app.</p>
|
||||
<p>You must repeat this for every future step in the tutorial and change the number to
|
||||
the number of the step you are on. This will cause any changes you made within
|
||||
your working directory to be lost.</p></li>
|
||||
<li>To see the app running in a browser, do one of the following:
|
||||
<ul>
|
||||
<li><b>For node.js users:</b>
|
||||
<ol>
|
||||
<li>In a <i>separate</i> terminal tab or window, run
|
||||
<code>./scripts/web-server.js</code> to start the web server.</li>
|
||||
<li>Open a browser window for the app and navigate to <a
|
||||
href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html</a></li>
|
||||
</ol>
|
||||
</li>
|
||||
<li><b>For other http servers:</b>
|
||||
<ol>
|
||||
<li>Configure the server to serve the files in the angular-phonecat
|
||||
<code>sandbox</code> directory.</li>
|
||||
<li>Navigate in your browser to
|
||||
<code>http://localhost:[port-number]/[context-path]/app/index.html</code>.</li>
|
||||
</ol>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="tab-pane well" id="ss-win" title="Snapshots on Windows" value="snapshotWin">
|
||||
<ol>
|
||||
<li><p>Open windows command line and run this command (in the angular-phonecat directory):</p>
|
||||
<pre>goto_step.bat 0</pre>
|
||||
<p>This resets your workspace to step 0 of the tutorial app.</p>
|
||||
<p>You must repeat this for every future step in the tutorial and change the number to
|
||||
the number of the step you are on. This will cause any changes you made within
|
||||
your working directory to be lost.</p></li>
|
||||
<li>To see the app running in a browser, do one of the following:
|
||||
<ul>
|
||||
<li><b>For node.js users:</b>
|
||||
<ol>
|
||||
<li>In a <i>separate</i> terminal tab or window, run <code>node
|
||||
scripts\web-server.js</code> to start the web server.</li>
|
||||
<li>Open a browser window for the app and navigate to <a
|
||||
href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html</a></li>
|
||||
</ol>
|
||||
</li>
|
||||
<li><b>For other http servers:</b>
|
||||
<ol>
|
||||
<li>Configure the server to serve the files in the angular-phonecat
|
||||
<code>sandbox</code> directory.</li>
|
||||
<li>Navigate in your browser to
|
||||
<code>http://localhost:[port-number]/[context-path]/app/index.html</code>.</li>
|
||||
</ol>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
You can now see the page in your browser. It's not very exciting, but that's OK.
|
||||
|
||||
The HTML page that displays "Nothing here yet!" was constructed with the HTML code shown below.
|
||||
The code contains some key Angular elements that we will need going forward.
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html lang="en" ng-app>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>My HTML File</title>
|
||||
<link rel="stylesheet" href="css/app.css">
|
||||
<link rel="stylesheet" href="css/bootstrap.css">
|
||||
<script src="lib/angular/angular.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>Nothing here {{'yet' + '!'}}</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
## What is the code doing?
|
||||
|
||||
* `ng-app` directive:
|
||||
|
||||
<html ng-app>
|
||||
|
||||
The `ng-app` attribute is represents an Angular directive 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 portion of it should be treated as the
|
||||
Angular application.
|
||||
|
||||
* AngularJS script tag:
|
||||
|
||||
<script src="lib/angular/angular.js">
|
||||
|
||||
This code downloads the `angular.js` script and registers a callback that will be executed by the
|
||||
browser when the containing HTML page is fully downloaded. When the callback is executed, Angular
|
||||
looks for the {@link api/angular.module.ng.$compileProvider.directive.ngApp ngApp} directive. If
|
||||
Angular finds the directive, it will bootstrap the application with the root of the application DOM
|
||||
being the element on which the `ngApp` directive was defined.
|
||||
|
||||
* Double-curly binding with an expression:
|
||||
|
||||
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.
|
||||
|
||||
The binding tells Angular, that it should evaluate an expression and insert the result into the
|
||||
DOM in place of the binding. Rather than a one-time insert, as we'll see in the next steps, a
|
||||
binding will result in efficient continuous updates whenever the result of the expression
|
||||
evaluation changes.
|
||||
|
||||
{@link guide/dev_guide.expressions Angular expression} is a JavaScript-like code snippet that is
|
||||
evaluated by Angular in the context of the current model scope, rather than within the scope of
|
||||
the global context (`window`).
|
||||
|
||||
As expected, once this template is processed by Angular, the html page will contains text:
|
||||
"Nothing here yet!".
|
||||
|
||||
## Bootstrapping AngularJS apps
|
||||
|
||||
Bootstrapping AngularJS apps automatically using the `ngApp` directive is very easy and suitable
|
||||
for most cases. In advanced cases, such as when using script loaders, you can use
|
||||
{@link guide/dev_guide.bootstrap.manual_bootstrap imperative / manual way} to bootstrap the app.
|
||||
|
||||
There are 3 important things that happen during the app bootstrap:
|
||||
|
||||
1. The {@link api/angular.module.AUTO.$injector injector} that will be used for dependency injection
|
||||
within this app is created.
|
||||
|
||||
2. The injector will then create the {@link api/angular.module.ng.$rootScope root scope} that will
|
||||
become the context for the model of our application.
|
||||
|
||||
3. Angular will then "compile" the DOM starting at the `ngApp` root element, processing any
|
||||
directives and bindings found along the way.
|
||||
|
||||
|
||||
Once an application is bootstrapped, it will then wait for incoming browser events (such as mouse
|
||||
click, key press or incoming HTTP response) that might change the model. Once such event occurs,
|
||||
Angular detects if it caused any model changes and if changes are found, Angular will reflect them
|
||||
in the view by updating all of the affected bindings.
|
||||
|
||||
The structure of our application is currently very simple. The template contains just one directive
|
||||
and one static binding, and our model is empty. That will soon change!
|
||||
|
||||
<img class="diagram" src="img/tutorial/tutorial_00.png">
|
||||
|
||||
|
||||
## What are all these files in my working directory?
|
||||
|
||||
Most of the files in your working directory come from the {@link
|
||||
https://github.com/angular/angular-seed angular-seed project} which is typically used to bootstrap
|
||||
new Angular projects. The seed project includes the latest Angular libraries, test libraries,
|
||||
scripts and a simple example app, all pre-configured for developing a typical web app.
|
||||
|
||||
For the purposes of this tutorial, we modified the angular-seed with the following changes:
|
||||
|
||||
* Removed the example app
|
||||
* Added phone images to `app/img/phones/`
|
||||
* Added phone data files (JSON) to `app/phones/`
|
||||
* Added [Bootstrap](http://twitter.github.com/bootstrap/) files to `app/css/` and `app/img/`
|
||||
|
||||
|
||||
|
||||
# Experiments
|
||||
|
||||
* Try adding a new expression to the `index.html` that will do some math:
|
||||
|
||||
<p>1 + 2 = {{ 1 + 2 }}</p>
|
||||
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
Now let's go to {@link step_01 step 1} and add some content to the web app.
|
||||
|
||||
|
||||
<ul doc-tutorial-nav="0"></ul>
|
||||
|
||||
<div style="display: none">
|
||||
Note: During the bootstrap the injector and the root scope will then be associated with the
|
||||
element on which the `ngApp` directive was declared, so when debugging the app you can retrieve
|
||||
them from browser console via `angular.element(rootElement).scope()` and
|
||||
`angular.element(rootElement).injector()`.
|
||||
</div>
|
||||
@@ -0,0 +1,55 @@
|
||||
@ngdoc overview
|
||||
@name Tutorial: 1 - Static Template
|
||||
@description
|
||||
|
||||
<ul doc-tutorial-nav="1"></ul>
|
||||
|
||||
|
||||
In order to illustrate how angular enhances standard HTML, you will create a purely *static* HTML
|
||||
page and then examine how we can turn this HTML code into a template that angular will use to
|
||||
dynamically display the same result with any set of data.
|
||||
|
||||
In this step you will add some basic information about two cell phones to an HTML page.
|
||||
|
||||
|
||||
<div doc-tutorial-reset="1"></div>
|
||||
|
||||
|
||||
The page now contains a list with information about two phones.
|
||||
|
||||
The most important changes are listed below. You can see the full diff on {@link
|
||||
https://github.com/angular/angular-phonecat/compare/step-0...step-1 GitHub}:
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
<ul>
|
||||
<li>
|
||||
<span>Nexus S</span>
|
||||
<p>
|
||||
Fast just got faster with Nexus S.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<span>Motorola XOOM™ with Wi-Fi</span>
|
||||
<p>
|
||||
The Next, Next Generation tablet.
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</pre>
|
||||
|
||||
|
||||
# Experiments
|
||||
|
||||
* Try adding more static HTML to `index.html`. For example:
|
||||
|
||||
<p>Total number of phones: 2</p>
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
This addition to your app uses static HTML to display the list. Now, let's go to {@link step_02
|
||||
step 2} to learn how to use AngularJS to dynamically generate the same list.
|
||||
|
||||
|
||||
<ul doc-tutorial-nav="1"></ul>
|
||||
@@ -0,0 +1,212 @@
|
||||
@ngdoc overview
|
||||
@name Tutorial: 2 - Angular Templates
|
||||
@description
|
||||
|
||||
<ul doc-tutorial-nav="2"></ul>
|
||||
|
||||
|
||||
Now it's time to make the web page dynamic — with AngularJS. We'll also add a test that verifies the
|
||||
code for the controller we are going to add.
|
||||
|
||||
There are many ways to structure the code for an application. For Angular apps, we encourage the
|
||||
use of {@link http://en.wikipedia.org/wiki/Model–View–Controller the Model-View-Controller (MVC)
|
||||
design pattern} to decouple the code and to separate concerns. With that in mind, let's use a
|
||||
little Angular and JavaScript to add model, view, and controller components to our app.
|
||||
|
||||
|
||||
<div doc-tutorial-reset="2"></div>
|
||||
|
||||
|
||||
The app now contains a list with three phones.
|
||||
|
||||
The most important changes are listed below. You can see the full diff on {@link
|
||||
https://github.com/angular/angular-phonecat/compare/step-1...step-2 GitHub}:
|
||||
|
||||
|
||||
## View and Template
|
||||
|
||||
In Angular, the __view__ is a projection of the model through the HTML __template__. This means that
|
||||
whenever the model changes, Angular refreshes the appropriate binding points, which updates the
|
||||
view.
|
||||
|
||||
The view component is constructed by Angular from this template:
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
<html ng-app>
|
||||
<head>
|
||||
...
|
||||
<script src="lib/angular/angular.js"></script>
|
||||
<script src="js/controllers.js"></script>
|
||||
</head>
|
||||
<body ng-controller="PhoneListCtrl">
|
||||
|
||||
<ul>
|
||||
<li ng-repeat="phone in phones">
|
||||
{{phone.name}}
|
||||
<p>{{phone.snippet}}</p>
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
We replaced the hard-coded phone list with the
|
||||
{@link api/angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat directive} and two
|
||||
{@link guide/dev_guide.expressions Angular expressions} enclosed in curly braces:
|
||||
`{{phone.name}}` and `{{phone.snippet}}`:
|
||||
|
||||
* The `ng-repeat="phone in phones"` statement in the `<li>` tag is an Angular repeater. The
|
||||
repeater tells Angular to create a `<li>` element for each phone in the list using the first `<li>`
|
||||
tag as the template.
|
||||
|
||||
* As we've learned in step 0, the curly braces around `phone.name` and `phone.snippet` denote
|
||||
bindings. As opposed to evaluating constants, these expression are refering to our application
|
||||
model, which was set up in our `PhoneListCtrl` controller.
|
||||
|
||||
<img class="diagram" src="img/tutorial/tutorial_02.png">
|
||||
|
||||
|
||||
## Model and Controller
|
||||
|
||||
The data __model__ (a simple array of phones in object literal notation) is instantiated within
|
||||
the `PhoneListCtrl` __controller__:
|
||||
|
||||
__`app/js/controllers.js`:__
|
||||
<pre>
|
||||
function PhoneListCtrl($scope) {
|
||||
$scope.phones = [
|
||||
{"name": "Nexus S",
|
||||
"snippet": "Fast just got faster with Nexus S."},
|
||||
{"name": "Motorola XOOM™ with Wi-Fi",
|
||||
"snippet": "The Next, Next Generation tablet."},
|
||||
{"name": "MOTOROLA XOOM™",
|
||||
"snippet": "The Next, Next Generation tablet."}
|
||||
];
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
Although the controller is not yet doing very much controlling, it is playing a crucial role. By
|
||||
providing context for our data model, the controller allows us to establish data-binding between
|
||||
the model and the view. We connected the dots between the presentation, data, and logic components
|
||||
as follows:
|
||||
|
||||
* `PhoneListCtrl` — the name of our controller function (located in the JavaScript file
|
||||
`controllers.js`), matches the value of the
|
||||
{@link api/angular.module.ng.$compileProvider.directive.ngController ngController} directive located
|
||||
on the `<body>` tag.
|
||||
|
||||
* The phone data is then attached to the *scope* (`$scope`) that was injected into our controller
|
||||
function. The controller scope is a prototypically descendant of the root scope that was created
|
||||
when the application bootstrapped. This controller scope is available to all bindings located within
|
||||
the `<body ng-controller="PhoneListCtrl">` tag.
|
||||
|
||||
The concept of a scope in Angular is crucial; a scope can be seen as the glue which allows the
|
||||
template, model and controller to work together. Angular uses scopes, along with the information
|
||||
contained in the template, data model, and controller, to keep models and views separate, but in
|
||||
sync. Any changes made to the model are reflected in the view; any changes that occur in the view
|
||||
are reflected in the model.
|
||||
|
||||
To learn more about Angular scopes, see the {@link api/angular.module.ng.$rootScope.Scope angular scope documentation}.
|
||||
|
||||
|
||||
## Tests
|
||||
|
||||
The "Angular way" makes it easy to test code as it is being developed. Take a look at the following
|
||||
unit test for your newly created controller:
|
||||
|
||||
__`test/unit/controllersSpec.js`:__
|
||||
<pre>
|
||||
describe('PhoneCat controllers', function() {
|
||||
|
||||
describe('PhoneListCtrl', function(){
|
||||
|
||||
it('should create "phones" model with 3 phones', function() {
|
||||
var scope = {},
|
||||
ctrl = new PhoneListCtrl(scope);
|
||||
|
||||
expect(scope.phones.length).toBe(3);
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
The test verifies that we have three records in the phones array and the example demonstrates how
|
||||
easy it is to create a unit test for code in Angular. Since testing is such a critical part of
|
||||
software development, we make it easy to create tests in Angular so that developers are encouraged
|
||||
to write them.
|
||||
|
||||
Angular developers prefer the syntax of Jasmine's Behavior-driven Development (BDD) framework when
|
||||
writing tests. Although Angular does not require you to use Jasmine, we wrote all of the tests in
|
||||
this tutorial in Jasmine. You can learn about Jasmine on the {@link
|
||||
http://pivotal.github.com/jasmine/ Jasmine home page} and on the {@link
|
||||
https://github.com/pivotal/jasmine/wiki Jasmine wiki}.
|
||||
|
||||
The angular-seed project is pre-configured to run all unit tests using {@link
|
||||
http://code.google.com/p/js-test-driver/ JsTestDriver}. 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-server.sh` to start the test web server.
|
||||
|
||||
2. Open a new browser window and navigate to {@link http://localhost:9876}.
|
||||
|
||||
3. Choose "Capture this browser in strict mode".
|
||||
|
||||
At this point, you can leave this window open and forget about it. JsTestDriver will use it to
|
||||
execute the tests and report the results in the terminal.
|
||||
|
||||
4. Execute the test by running `./scripts/test.sh`
|
||||
|
||||
You should see the following or similar output:
|
||||
|
||||
Chrome: Runner reset.
|
||||
.
|
||||
Total 1 tests (Passed: 1; Fails: 0; Errors: 0) (2.00 ms)
|
||||
Chrome 19.0.1084.36 Mac OS: Run 1 tests (Passed: 1; Fails: 0; Errors 0) (2.00 ms)
|
||||
|
||||
Yay! The test passed! Or not...
|
||||
|
||||
Note: If you see errors after you run the test, close the browser window and go back to the
|
||||
terminal and kill the script, then repeat the procedure above.
|
||||
|
||||
# Experiments
|
||||
|
||||
* Add another binding to `index.html`. For example:
|
||||
|
||||
<p>Total number of phones: {{phones.length}}</p>
|
||||
|
||||
* Create a new model property in the controller and bind to it from the template. For example:
|
||||
|
||||
$scope.hello = "Hello, World!"
|
||||
|
||||
Refresh your browser to make sure it says, "Hello, World!"
|
||||
|
||||
* Create a repeater that constructs a simple table:
|
||||
|
||||
<table>
|
||||
<tr><th>row number</th></tr>
|
||||
<tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i}}</td></tr>
|
||||
</table>
|
||||
|
||||
Now, make the list 1-based by incrementing `i` by one in the binding:
|
||||
|
||||
<table>
|
||||
<tr><th>row number</th></tr>
|
||||
<tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i+1}}</td></tr>
|
||||
</table>
|
||||
|
||||
* Make the unit test fail by changing the `toBe(3)` statement to `toBe(4)`, and rerun the
|
||||
`./scripts/test.sh` script.
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
You now have a dynamic app that features separate model, view, and controller components, and you
|
||||
are testing as you go. Now, let's go to {@link step_03 step 3} to learn how to add full text search
|
||||
to the app.
|
||||
|
||||
|
||||
<ul doc-tutorial-nav="2"></ul>
|
||||
@@ -0,0 +1,194 @@
|
||||
@ngdoc overview
|
||||
@name Tutorial: 3 - Filtering Repeaters
|
||||
@description
|
||||
|
||||
<ul doc-tutorial-nav="3"></ul>
|
||||
|
||||
|
||||
We did a lot of work in laying a foundation for the app in the last step, so now we'll do something
|
||||
simple; we will add full text search (yes, it will be simple!). We will also write an end-to-end
|
||||
test, because a good end-to-end test is a good friend. It stays with your app, keeps an eye on it,
|
||||
and quickly detects regressions.
|
||||
|
||||
|
||||
<div doc-tutorial-reset="3"></div>
|
||||
|
||||
|
||||
The app now has a search box. Notice that the phone list on the page changes depending on what a
|
||||
user types into the search box.
|
||||
|
||||
The most important differences between Steps 2 and 3 are listed below. You can see the full diff on
|
||||
{@link https://github.com/angular/angular-phonecat/compare/step-2...step-3
|
||||
GitHub}:
|
||||
|
||||
|
||||
## Controller
|
||||
|
||||
We made no changes to the controller.
|
||||
|
||||
|
||||
## Template
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
<div class="container-fluid">
|
||||
<div class="row-fluid">
|
||||
<div class="span2">
|
||||
<!--Sidebar content-->
|
||||
|
||||
Search: <input ng-model="query">
|
||||
|
||||
</div>
|
||||
<div class="span10">
|
||||
<!--Body content-->
|
||||
|
||||
<ul class="phones">
|
||||
<li ng-repeat="phone in phones | filter:query">
|
||||
{{phone.name}}
|
||||
<p>{{phone.snippet}}</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
We added a standard HTML `<input>` tag and used angular's
|
||||
{@link api/angular.module.ng.$filter.filter $filter} function to process the input for the
|
||||
`ngRepeate` directive.
|
||||
|
||||
This lets a user enter search criteria and immediately see the effects of their search on the phone
|
||||
list. This new code demonstrates the following:
|
||||
|
||||
* Data-binding. This is one of the core features in Angular. When the page loads, Angular binds the
|
||||
name of the input box to a variable of the same name in the data model and keeps the two in sync.
|
||||
|
||||
In this code, the data that a user types into the input box (named __`query`__) is immediately
|
||||
available as a filter input in the list repeater (`phone in phones | filter:`__`query`__). When
|
||||
changes to the data model cause the repeater's input to change, the repeater efficiently updates
|
||||
the DOM to reflect the current state of the model.
|
||||
|
||||
<img class="diagram" src="img/tutorial/tutorial_03.png">
|
||||
|
||||
* Use of `filter` filter. The {@link api/angular.module.ng.$filter.filter filter} function uses the
|
||||
`query` value to create a new array that contains only those records that match the `query`.
|
||||
|
||||
`ngRepeat` automatically updates the view in response to the changing number of phones returned
|
||||
by the `filter` filter. The process is completely transparent to the developer.
|
||||
|
||||
## Test
|
||||
|
||||
In Step 2, we learned how to write and run unit tests. Unit tests are perfect for testing
|
||||
controllers and other components of our application written in JavaScript, but they can't easily
|
||||
test DOM manipulation or the wiring of our application. For these, an end-to-end test is a much
|
||||
better choice.
|
||||
|
||||
The search feature was fully implemented via templates and data-binding, so we'll write our first
|
||||
end-to-end test, to verify that the feature works.
|
||||
|
||||
__`test/e2e/scenarios.js`:__
|
||||
<pre>
|
||||
describe('PhoneCat App', function() {
|
||||
|
||||
describe('Phone list view', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
browser().navigateTo('../../app/index.html');
|
||||
});
|
||||
|
||||
|
||||
it('should filter the phone list as user types into the search box', function() {
|
||||
expect(repeater('.phones li').count()).toBe(3);
|
||||
|
||||
input('query').enter('nexus');
|
||||
expect(repeater('.phones li').count()).toBe(1);
|
||||
|
||||
input('query').enter('motorola');
|
||||
expect(repeater('.phones li').count()).toBe(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
Even though the syntax of this test looks very much like our controller unit test written with
|
||||
Jasmine, the end-to-end test uses APIs of {@link guide/dev_guide.e2e-testing Angular's end-to-end
|
||||
test runner}.
|
||||
|
||||
To run the end-to-end test, open one of the following in a new browser tab:
|
||||
|
||||
* node.js users: {@link http://localhost:8000/test/e2e/runner.html}
|
||||
* users with other http servers:
|
||||
`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}
|
||||
|
||||
This test verifies that the search box and the repeater are correctly wired together. Notice how
|
||||
easy it is to write end-to-end tests in Angular. Although this example is for a simple test, it
|
||||
really is that easy to set up any functional, readable, end-to-end test.
|
||||
|
||||
# Experiments
|
||||
|
||||
* Display the current value of the `query` model by adding a `{{query}}` binding into the
|
||||
`index.html` template, and see how it changes when you type in the input box.
|
||||
|
||||
* Let's see how we can get the current value of the `query` model to appear in the HTML page title.
|
||||
|
||||
You might think you could just add the {{query}} to the title tag element as follows:
|
||||
|
||||
<title>Google Phone Gallery: {{query}}</title>
|
||||
|
||||
However, when you reload the page, you won't see the expected result. This is because the "query"
|
||||
model lives in the scope defined by the body element:
|
||||
|
||||
<body ng-controller="PhoneListCtrl">
|
||||
|
||||
If you want to bind to the query model from the `<title>` element, you must __move__ the
|
||||
`ngController` declaration to the HTML element because it is the common parent of both the body
|
||||
and title elements:
|
||||
|
||||
<html ng-app ng-controller="PhoneListCtrl">
|
||||
|
||||
Be sure to *remove* the `ng-controller` declaration from the body element.
|
||||
|
||||
While using double curlies works fine in within the title element, you might have noticed that
|
||||
for a split second they are actually displayed to the user while the page is loading. A better
|
||||
solution would be to use the {@link api/angular.module.ng.$compileProvider.directive.ngBind
|
||||
ngBind} or {@link api/angular.module.ng.$compileProvider.directive.ngBindTemplate
|
||||
ngBindTemplate} directives, which are invisible to the user while the page is loading:
|
||||
|
||||
<title ng-bind-template="Google Phone Gallery: {{query}}">Google Phone Gallery</title>
|
||||
|
||||
* Add the following end-to-end test into the `describe` block within `test/e2e/scenarios.js`:
|
||||
|
||||
<pre>
|
||||
it('should display the current filter value within an element with id "status"',
|
||||
function() {
|
||||
expect(element('#status').text()).toMatch(/Current filter: \s*$/);
|
||||
|
||||
input('query').enter('nexus');
|
||||
|
||||
expect(element('#status').text()).toMatch(/Current filter: nexus\s*$/);
|
||||
|
||||
//alternative version of the last assertion that tests just the value of the binding
|
||||
using('#status').expect(binding('query')).toBe('nexus');
|
||||
});
|
||||
</pre>
|
||||
|
||||
Refresh the browser tab with the end-to-end test runner to see the test fail. To make the test
|
||||
pass, edit the `index.html` template to add a `div` or `p` element with `id` `"status"` and content
|
||||
with the `query` binding.
|
||||
|
||||
* Add a `pause()` statement into an end-to-end test and rerun it. You'll see the runner pause; this
|
||||
gives you the opportunity to explore the state of your application while it is displayed in the
|
||||
browser. The app is live! You can change the search query to prove it. Notice how useful this is
|
||||
for troubleshooting end-to-end tests.
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
We have now added full text search and included a test to verify that search works! Now let's go on
|
||||
to {@link step_04 step 4} to learn how to add sorting capability to the phone app.
|
||||
|
||||
|
||||
<ul doc-tutorial-nav="3"></ul>
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
@ngdoc overview
|
||||
@name Tutorial: 4 - Two-way Data Binding
|
||||
@description
|
||||
|
||||
<ul doc-tutorial-nav="4"></ul>
|
||||
|
||||
|
||||
In this step, you will add a feature to let your users control the order of the items in the phone
|
||||
list. The dynamic ordering is implemented by creating a new model property, wiring it together with
|
||||
the repeater, and letting the data binding magic do the rest of the work.
|
||||
|
||||
|
||||
<div doc-tutorial-reset="4"></div>
|
||||
|
||||
|
||||
You should see that in addition to the search box, the app displays a drop down menu that allows
|
||||
users to control the order in which the phones are listed.
|
||||
|
||||
The most important differences between Steps 3 and 4 are listed below. You can see the full diff on
|
||||
{@link https://github.com/angular/angular-phonecat/compare/step-3...step-4 GitHub}:
|
||||
|
||||
|
||||
## Template
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
Search: <input ng-model="query">
|
||||
Sort by:
|
||||
<select ng-model="orderProp">
|
||||
<option value="name">Alphabetical</option>
|
||||
<option value="age">Newest</option>
|
||||
</select>
|
||||
|
||||
|
||||
<ul class="phones">
|
||||
<li ng-repeat="phone in phones | filter:query | orderBy:orderProp">
|
||||
{{phone.name}}
|
||||
<p>{{phone.snippet}}</p>
|
||||
</li>
|
||||
</ul>
|
||||
</pre>
|
||||
|
||||
We made the following changes to the `index.html` template:
|
||||
|
||||
* First, we added a `<select>` html element named `orderProp`, so that our users can pick from the
|
||||
two provided sorting options.
|
||||
|
||||
<img class="diagram" src="img/tutorial/tutorial_04.png">
|
||||
|
||||
* We then chained the `filter` filter with {@link api/angular.module.ng.$filter.orderBy `orderBy`}
|
||||
filter to further process the input into the repeater. `orderBy` is a filter that takes an input
|
||||
array, copies it and reorders the copy which is then returned.
|
||||
|
||||
Angular creates a two way data-binding between the select element and the `orderProp` model.
|
||||
`orderProp` is then used as the input for the `orderBy` filter.
|
||||
|
||||
As we discussed in the section about data-binding and the repeater in step 3, whenever the model
|
||||
changes (for example because a user changes the order with the select drop down menu), Angular's
|
||||
data-binding will cause the view to automatically update. No bloated DOM manipulation code is
|
||||
necessary!
|
||||
|
||||
|
||||
|
||||
## Controller
|
||||
|
||||
__`app/js/controller.js`:__
|
||||
<pre>
|
||||
function PhoneListCtrl($scope) {
|
||||
$scope.phones = [
|
||||
{"name": "Nexus S",
|
||||
"snippet": "Fast just got faster with Nexus S.",
|
||||
"age": 0},
|
||||
{"name": "Motorola XOOM™ with Wi-Fi",
|
||||
"snippet": "The Next, Next Generation tablet.",
|
||||
"age": 1},
|
||||
{"name": "MOTOROLA XOOM™",
|
||||
"snippet": "The Next, Next Generation tablet.",
|
||||
"age": 2}
|
||||
];
|
||||
|
||||
$scope.orderProp = 'age';
|
||||
}
|
||||
</pre>
|
||||
|
||||
* We modified the `phones` model - the array of phones - and added an `age` property to each phone
|
||||
record. This property is used to order phones by age.
|
||||
|
||||
* We added a line to the controller that sets the default value of `orderProp` to `age`. If we had
|
||||
not set the default value here, the model would stay uninitialized until our user would pick an
|
||||
option from the drop down menu.
|
||||
|
||||
This is a good time to talk about two-way data-binding. Notice that when the app is loaded in the
|
||||
browser, "Newest" is selected in the drop down menu. This is because we set `orderProp` to `'age'`
|
||||
in the controller. So the binding works in the direction from our model to the UI. Now if you
|
||||
select "Alphabetically" in the drop down menu, the model will be updated as well and the phones
|
||||
will be reordered. That is the data-binding doing its job in the opposite direction — from the UI
|
||||
to the model.
|
||||
|
||||
|
||||
|
||||
## Test
|
||||
|
||||
The changes we made should be verified with both a unit test and an end-to-end test. Let's look at
|
||||
the unit test first.
|
||||
|
||||
__`test/unit/controllerSpec.js`:__
|
||||
<pre>
|
||||
describe('PhoneCat controllers', function() {
|
||||
|
||||
describe('PhoneListCtrl', function(){
|
||||
var scope, ctrl;
|
||||
|
||||
beforeEach(function() {
|
||||
scope = {},
|
||||
ctrl = new PhoneListCtrl(scope);
|
||||
});
|
||||
|
||||
|
||||
it('should create "phones" model with 3 phones', function() {
|
||||
expect(scope.phones.length).toBe(3);
|
||||
});
|
||||
|
||||
|
||||
it('should set the default value of orderProp model', function() {
|
||||
expect(scope.orderProp).toBe('age');
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
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.
|
||||
|
||||
To run the unit tests, once again execute the `./scripts/test.sh` script and you should see the
|
||||
following output.
|
||||
|
||||
Chrome: Runner reset.
|
||||
..
|
||||
Total 2 tests (Passed: 2; Fails: 0; Errors: 0) (3.00 ms)
|
||||
Chrome 19.0.1084.36 Mac OS: Run 2 tests (Passed: 2; Fails: 0; Errors 0) (3.00 ms)
|
||||
|
||||
|
||||
Let's turn our attention to the end-to-end test.
|
||||
|
||||
__`test/e2e/scenarios.js`:__
|
||||
<pre>
|
||||
...
|
||||
it('should be possible to control phone order via the drop down select box',
|
||||
function() {
|
||||
//let's narrow the dataset to make the test assertions shorter
|
||||
input('query').enter('tablet');
|
||||
|
||||
expect(repeater('.phones li', 'Phone List').column('phone.name')).
|
||||
toEqual(["Motorola XOOM\u2122 with Wi-Fi",
|
||||
"MOTOROLA XOOM\u2122"]);
|
||||
|
||||
select('orderProp').option('Alphabetical');
|
||||
|
||||
expect(repeater('.phones li', 'Phone List').column('phone.name')).
|
||||
toEqual(["MOTOROLA XOOM\u2122",
|
||||
"Motorola XOOM\u2122 with Wi-Fi"]);
|
||||
});
|
||||
...
|
||||
</pre>
|
||||
|
||||
The end-to-end test verifies that the ordering mechanism of the select box is working correctly.
|
||||
|
||||
You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you
|
||||
can see them running on {@link
|
||||
http://angular.github.com/angular-phonecat/step-4/test/e2e/runner.html
|
||||
Angular's server}.
|
||||
|
||||
# Experiments
|
||||
|
||||
* In the `PhoneListCtrl` controller, remove the statement that sets the `orderProp` value and
|
||||
you'll see that Angular will temporarily add a new "unknown" option to the drop-down list and the
|
||||
ordering will default to unordered/natural order.
|
||||
|
||||
* Add an `{{orderProp}}` binding into the `index.html` template to display its current value as
|
||||
text.
|
||||
|
||||
# Summary
|
||||
|
||||
Now that you have added list sorting and tested the app, go to {@link step_05 step 5} to learn
|
||||
about Angular services and how Angular uses dependency injection.
|
||||
|
||||
|
||||
<ul doc-tutorial-nav="4"></ul>
|
||||
@@ -0,0 +1,238 @@
|
||||
@ngdoc overview
|
||||
@name Tutorial: 5 - XHRs & Dependency Injection
|
||||
@description
|
||||
|
||||
<ul doc-tutorial-nav="5"></ul>
|
||||
|
||||
|
||||
Enough of building an app with three phones in a hard-coded dataset! Let's fetch a larger dataset
|
||||
from our server using one of angular's built-in {@link api/angular.module.ng services} called {@link
|
||||
api/angular.module.ng.$http $http}. We will use angular's {@link guide/dev_guide.di dependency
|
||||
injection (DI)} to provide the service to the `PhoneListCtrl` controller.
|
||||
|
||||
|
||||
<div doc-tutorial-reset="5"></div>
|
||||
|
||||
|
||||
You should now see a list of 20 phones.
|
||||
|
||||
The most important changes are listed below. You can see the full diff on {@link
|
||||
https://github.com/angular/angular-phonecat/compare/step-4...step-5
|
||||
GitHub}:
|
||||
|
||||
## Data
|
||||
|
||||
The `app/phones/phone.json` file in your project is a dataset that contains a larger list of phones
|
||||
stored in the JSON format.
|
||||
|
||||
Following is a sample of the file:
|
||||
<pre>
|
||||
[
|
||||
{
|
||||
"age": 13,
|
||||
"id": "motorola-defy-with-motoblur",
|
||||
"name": "Motorola DEFY\u2122 with MOTOBLUR\u2122",
|
||||
"snippet": "Are you ready for everything life throws your way?"
|
||||
...
|
||||
},
|
||||
...
|
||||
]
|
||||
</pre>
|
||||
|
||||
|
||||
## Controller
|
||||
|
||||
We'll use angular's {@link api/angular.module.ng.$http $http} service in our controller to make an HTTP
|
||||
request to your web server to fetch the data in the `app/phones/phones.json` file. `$http` is just
|
||||
one of several built-in {@link api/angular.module.ng angular services} that handle common operations
|
||||
in web apps. Angular injects these services for you where you need them.
|
||||
|
||||
Services are managed by angular's {@link guide/dev_guide.di DI subsystem}. Dependency injection
|
||||
helps to make your web apps both well-structured (e.g., separate components for presentation, data,
|
||||
and control) and loosely coupled (dependencies between components are not resolved by the
|
||||
components themselves, but by the DI subsystem).
|
||||
|
||||
__`app/js/controllers.js:`__
|
||||
<pre>
|
||||
function PhoneListCtrl($scope, $http) {
|
||||
$http.get('phones/phones.json').success(function(data) {
|
||||
$scope.phones = data;
|
||||
});
|
||||
|
||||
$scope.orderProp = 'age';
|
||||
}
|
||||
|
||||
//PhoneListCtrl.$inject = ['$scope', '$http'];
|
||||
</pre>
|
||||
|
||||
`$http` makes an HTTP GET request to our web server, asking for `phone/phones.json` (the url is
|
||||
relative to our `index.html` file). The server responds by providing the data in the json file.
|
||||
(The response might just as well have been dynamically generated by a backend server. To the
|
||||
browser and our app they both look the same. For the sake of simplicity we used a json file in this
|
||||
tutorial.)
|
||||
|
||||
The `$http` service returns a {@link api/angular.module.ng.$q promise object} with a `success`
|
||||
method. We call this method to handle the asynchronous response and assign the phone data to the
|
||||
scope controlled by this controller, as a model called `phones`. Notice that angular detected the
|
||||
json response and parsed it for us!
|
||||
|
||||
To use a service in angular, you simply declare the names of the dependencies you need as arguments
|
||||
to the controller's constructor function, as follows:
|
||||
|
||||
function PhoneListCtrl($scope, $http) {...}
|
||||
|
||||
Angular's dependency injector provides services to your controller when the controller is being
|
||||
constructed. The dependency injector also takes care of creating any transitive dependencies the
|
||||
service may have (services often depend upon other services).
|
||||
|
||||
Note that the names of arguments are significant, because the injector uses these to look up the
|
||||
dependencies.
|
||||
|
||||
|
||||
<img class="diagram" src="img/tutorial/xhr_service_final.png">
|
||||
|
||||
|
||||
### '$' Prefix Naming Convention
|
||||
|
||||
You can create your own services, and in fact we will do exactly that in step 11. As a naming
|
||||
convention, angular's built-in services, Scope methods and a few other angular APIs have a '$'
|
||||
prefix in front of the name. Don't use a '$' prefix when naming your services and models, in order
|
||||
to avoid any possible naming collisions.
|
||||
|
||||
### A Note on Minification
|
||||
|
||||
Since angular infers the controller's dependencies from the names of arguments to the controller's
|
||||
constructor function, if you were to {@link http://en.wikipedia.org/wiki/Minification_(programming)
|
||||
minify} the JavaScript code for `PhoneListCtrl` controller, all of its function arguments would be
|
||||
minified as well, and the dependency injector would not being able to identify services correctly.
|
||||
|
||||
To overcome issues caused by minification, just assign an array with service identifier strings
|
||||
into the `$inject` property of the controller function, just like the last line in the snippet
|
||||
(commented out) suggests:
|
||||
|
||||
PhoneListCtrl.$inject = ['$scope', '$http'];
|
||||
|
||||
There is also one more way to specify this dependency list and avoid minification issues — using the
|
||||
bracket notation which wraps the function to be injected into an array of strings (representing the
|
||||
dependency names) followed by the function to be injected:
|
||||
|
||||
var PhoneListCtrl = ['$scope', '$http', function($scope, $http) { /* constructor body */ }];
|
||||
|
||||
Both of these methods work with any function that can be injected by Angular, so it's up to your
|
||||
project's style guide to decide which one you use.
|
||||
|
||||
|
||||
## Test
|
||||
|
||||
__`test/unit/controllersSpec.js`:__
|
||||
|
||||
Because we started using dependency injection and our controller has dependencies, constructing the
|
||||
controller in our tests is a bit more complicated. We could use the `new` operator and provide the
|
||||
constructor with some kind of fake `$http` implementation. However, the recommended (and easier) way
|
||||
is to create a controller in the test environment in the same way that angular does it in the
|
||||
production code behind the scenes, as follows:
|
||||
|
||||
<pre>
|
||||
describe('PhoneCat controllers', function() {
|
||||
|
||||
describe('PhoneListCtrl', function(){
|
||||
var scope, ctrl, $httpBackend;
|
||||
|
||||
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
|
||||
$httpBackend = _$httpBackend_;
|
||||
$httpBackend.expectGET('phones/phones.json').
|
||||
respond([{name: 'Nexus S'}, {name: 'Motorola DROID'}]);
|
||||
|
||||
scope = $rootScope.$new();
|
||||
ctrl = $controller(PhoneListCtrl, {$scope: scope});
|
||||
}));
|
||||
</pre>
|
||||
|
||||
Note: Because we loaded Jasmine and `angular-mocks.js` in our test environment, we got two helper
|
||||
methods {@link api/angular.mock.module module} and {@link api/angular.mock.inject inject} that we'll
|
||||
use to access and configure the injector.
|
||||
|
||||
We created the controller in the test environment, as follows:
|
||||
|
||||
* We used the `inject` helper method to inject instances of
|
||||
{@link api/angular.module.ng.$rootScope $rootScope},
|
||||
{@link api/angular.module.ng.$controller $controller} and
|
||||
{@link api/angular.module.ng.$httpBackend $httpBackend} services into the Jasmine's `beforeEach`
|
||||
function. These instances come from an injector which is recreated from scratch for every single
|
||||
test. This guarantees that each test starts from a well known starting point and each test is
|
||||
isolated from the work done in other tests.
|
||||
|
||||
* We created a new scope for our controller by calling `$rootScope.$new()`
|
||||
|
||||
* We called `scope.$new(PhoneListCtrl)` to get Angular to create the child scope associated with
|
||||
the `PhoneListCtrl` controller.
|
||||
|
||||
Because our code now uses the `$http` service to fetch the phone list data in our controller, before
|
||||
we create the `PhoneListCtrl` child scope, we need to tell the testing harness to expect an
|
||||
incoming request from the controller. To do this we:
|
||||
|
||||
* Request `$httpBackend` service to be injected into our `beforeEach` function. This is a mock
|
||||
mock version of the service that in production environment facilitates all XHR and JSONP requests.
|
||||
The mock version of this service allows you to write tests without having to deal with
|
||||
native APIs and the global state associated with them — both of which make testing a nightmare.
|
||||
|
||||
* Use the `$httpBackend.expectGET` method to train the `$httpBackend` service to expect an incoming
|
||||
HTTP request and tell it what to respond with. Note that the responses are not returned until we call
|
||||
the `$httpBackend.flush` method.
|
||||
|
||||
Now, we will make assertions to verify that the `phones` model doesn't exist on the scope, before
|
||||
the response is received:
|
||||
|
||||
<pre>
|
||||
it('should create "phones" model with 2 phones fetched from xhr', function() {
|
||||
expect(scope.phones).toBeUndefined();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(scope.phones).toEqual([{name: 'Nexus S'},
|
||||
{name: 'Motorola DROID'}]);
|
||||
});
|
||||
</pre>
|
||||
|
||||
* We flush the request queue in the browser by calling `$httpBackend.flush()`. This causes the
|
||||
promise returned by the `$http` service to be resolved with the trained response.
|
||||
|
||||
* We make the assertions, verifying that the phone model now exists on the scope.
|
||||
|
||||
Finally, we verify that the default value of `orderProp` is set correctly:
|
||||
|
||||
<pre>
|
||||
it('should set the default value of orderProp model', function() {
|
||||
expect(scope.orderProp).toBe('age');
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
To run the unit tests, execute the `./scripts/test.sh` script and you should see the following
|
||||
output.
|
||||
|
||||
Chrome: Runner reset.
|
||||
..
|
||||
Total 2 tests (Passed: 2; Fails: 0; Errors: 0) (3.00 ms)
|
||||
Chrome 19.0.1084.36 Mac OS: Run 2 tests (Passed: 2; Fails: 0; Errors 0) (3.00 ms)
|
||||
|
||||
|
||||
# Experiments
|
||||
|
||||
* At the bottom of `index.html`, add a `{{phones | json}}` binding to see the list of phones
|
||||
displayed in json format.
|
||||
|
||||
* In the `PhoneListCtrl` controller, pre-process the http response by limiting the number of phones
|
||||
to the first 5 in the list. Use the following code in the $http callback:
|
||||
|
||||
$scope.phones = data.splice(0, 5);
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
Now that you have learned how easy it is to use angular services (thanks to Angular's dependency
|
||||
injection), go to {@link step_06 step 6}, where you will add some
|
||||
thumbnail images of phones and some links.
|
||||
|
||||
|
||||
<ul doc-tutorial-nav="5"></ul>
|
||||
@@ -0,0 +1,106 @@
|
||||
@ngdoc overview
|
||||
@name Tutorial: 6 - Templating Links & Images
|
||||
@description
|
||||
|
||||
<ul doc-tutorial-nav="6"></ul>
|
||||
|
||||
|
||||
In this step, you will add thumbnail images for the phones in the phone list, and links that, for
|
||||
now, will go nowhere. In subsequent steps you will use the links to display additional information
|
||||
about the phones in the catalog.
|
||||
|
||||
|
||||
<div doc-tutorial-reset="6"></div>
|
||||
|
||||
|
||||
You should now see links and images of the phones in the list.
|
||||
|
||||
The most important changes are listed below. You can see the full diff on {@link
|
||||
https://github.com/angular/angular-phonecat/compare/step-5...step-6
|
||||
GitHub}:
|
||||
|
||||
|
||||
## Data
|
||||
|
||||
Note that the `phones.json` file contains unique ids and image urls for each of the phones. The
|
||||
urls point to the `app/img/phones/` directory.
|
||||
|
||||
__`app/phones/phones.json`__ (sample snippet):
|
||||
<pre>
|
||||
[
|
||||
{
|
||||
...
|
||||
"id": "motorola-defy-with-motoblur",
|
||||
"imageUrl": "img/phones/motorola-defy-with-motoblur.0.jpg",
|
||||
"name": "Motorola DEFY\u2122 with MOTOBLUR\u2122",
|
||||
...
|
||||
},
|
||||
...
|
||||
]
|
||||
</pre>
|
||||
|
||||
|
||||
## Template
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
...
|
||||
<ul class="phones">
|
||||
<li ng-repeat="phone in phones | filter:query | orderBy:orderProp" class="thumbnail">
|
||||
<a href="#/phones/{{phone.id}}" class="thumb"><img ng-src="{{phone.imageUrl}}"></a>
|
||||
<a href="#/phones/{{phone.id}}">{{phone.name}}</a>
|
||||
<p>{{phone.snippet}}</p>
|
||||
</li>
|
||||
</ul>
|
||||
...
|
||||
</pre>
|
||||
|
||||
To dynamically generate links that will in the future lead to phone detail pages, we used the
|
||||
now-familiar double-curly brace binding in the `href` attribute values. In step 2, we added the
|
||||
`{{phone.name}}` binding as the element content. In this step the `{{phone.id}}` binding is used in
|
||||
the element attribute.
|
||||
|
||||
We also added phone images next to each record using an image tag with the {@link
|
||||
api/angular.module.ng.$compileProvider.directive.ngSrc ngSrc} directive. That directive prevents the
|
||||
browser from treating the angular `{{ expression }}` markup literally, and initiating a request to
|
||||
invalid url `http://localhost:8000/app/{{phone.imageUrl}}`, which it would have done if we had only
|
||||
specified an attribute binding in a regular `src` attribute (`<img class="diagram" src="{{phone.imageUrl}}">`).
|
||||
Using `ngSrc` (`ng-src`) prevents the browser from making an http request to an invalid location.
|
||||
|
||||
|
||||
## Test
|
||||
|
||||
__`test/e2e/scenarios.js`__:
|
||||
<pre>
|
||||
...
|
||||
it('should render phone specific links', function() {
|
||||
input('query').enter('nexus');
|
||||
element('.phones li a').click();
|
||||
expect(browser().location().url()).toBe('/phones/nexus-s');
|
||||
});
|
||||
...
|
||||
</pre>
|
||||
|
||||
We added a new end-to-end test to verify that the app is generating correct links to the phone
|
||||
views that we will implement in the upcoming steps.
|
||||
|
||||
You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you
|
||||
can see them running on {@link
|
||||
http://angular.github.com/angular-phonecat/step-6/test/e2e/runner.html
|
||||
angular's server}.
|
||||
|
||||
# Experiments
|
||||
|
||||
* Replace the `ng-src` directive with a plain old `src` attribute. Using tools such as Firebug,
|
||||
or Chrome's Web Inspector, or inspecting the webserver access logs, confirm that the app is indeed
|
||||
making an extraneous request to `/app/%7B%7Bphone.imageUrl%7D%7D` (or
|
||||
`/app/{{phone.imageUrl}}`).
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
Now that you have added phone images and links, go to {@link step_07 step 7} to learn about angular
|
||||
layout templates and how angular makes it easy to create applications that have multiple views.
|
||||
|
||||
|
||||
<ul doc-tutorial-nav="6"></ul>
|
||||
@@ -0,0 +1,261 @@
|
||||
@ngdoc overview
|
||||
@name Tutorial: 7 - Routing & Multiple Views
|
||||
@description
|
||||
|
||||
<ul doc-tutorial-nav="7"></ul>
|
||||
|
||||
|
||||
In this step, you will learn how to create a layout template and how to build an app that has
|
||||
multiple views by adding routing.
|
||||
|
||||
|
||||
<div doc-tutorial-reset="7"></div>
|
||||
|
||||
|
||||
Note that when you now navigate to `app/index.html`, you are redirected to `app/index.html#/phones`
|
||||
and the same phone list appears in the browser. When you click on a phone link the stub of a phone
|
||||
detail page is displayed.
|
||||
|
||||
|
||||
The most important changes are listed below. You can see the full diff on {@link
|
||||
https://github.com/angular/angular-phonecat/compare/step-6...step-7
|
||||
GitHub}:
|
||||
|
||||
|
||||
## Multiple Views, Routing and Layout Template
|
||||
|
||||
Our app is slowly growing and becoming more complex. Before step 7, the app provided our users with
|
||||
a single view (the list of all phones), and all of the template code was located in the
|
||||
`index.html` file. The next step in building the app is to add a view that will show detailed
|
||||
information about each of the devices in our list.
|
||||
|
||||
To add the detailed view, we could expand the `index.html` file to contain template code for both
|
||||
views, but that would get messy very quickly. Instead, we are going to turn the `index.html`
|
||||
template into what we call a "layout template". This is a template that is common for all views in
|
||||
our application. Other "partial templates" are then included into this layout template depending on
|
||||
the current "route" — the view that is currently displayed to the user.
|
||||
|
||||
Application routes in angular are declared via the
|
||||
{@link api/angular.module.ng.$routeProvider $routeProvider}, which is the provider of the
|
||||
{@link api/angular.module.ng.$route $route service}. This service makes it easy to wire together
|
||||
controllers, view templates, and the current
|
||||
URL location in the browser. Using this feature we can implement {@link
|
||||
http://en.wikipedia.org/wiki/Deep_linking deep linking}, which lets us utilize the browser's
|
||||
history (back and forward navigation) and bookmarks.
|
||||
|
||||
|
||||
### A Note About DI, Injector and Providers
|
||||
|
||||
As you noticed the dependency injection is the core feature of AngularJS, so it's important for you
|
||||
to understand a thing or two about how it works.
|
||||
|
||||
When the application bootstraps, Angular creates an injector that will be used for all DI stuff in
|
||||
this app. The injector itself doesn't know anything about what `$http` or `$route` services do, in
|
||||
fact it doesn't even know about the existence of these services unless it is configured with proper
|
||||
module definitions. The sole responsibilities of the injector are to load specified module
|
||||
definition(s), register all service providers defined in these modules and when asked inject
|
||||
a specified function with dependencies (services) that it lazily instantiates via their providers.
|
||||
|
||||
Providers are objects that provide (create) instances of services and expose configuration apis
|
||||
that can be used to control the creation and runtime behavior of a service. In case of the `$route`
|
||||
service, the `$routeProvider` exposes apis that allow you to define routes for your application.
|
||||
|
||||
Angular modules solve the problem of removing global state from the application and provide a way
|
||||
of configuring the injector. As opposed to AMD or require.js modules, Angular modules don't try to
|
||||
solve the problem of script load ordering or lazy script fetching. These goals are orthogonal and
|
||||
both module systems can live side by side and fulfil their goals.
|
||||
|
||||
## The App Module
|
||||
|
||||
__`app/js/app.js`:__
|
||||
<pre>
|
||||
angular.module('phonecat', []).
|
||||
config(['$routeProvider', function($routeProvider) {
|
||||
$routeProvider.
|
||||
when('/phones', {template: 'partials/phone-list.html', controller: PhoneListCtrl}).
|
||||
when('/phones/:phoneId', {template: 'partials/phone-detail.html', controller: PhoneDetailCtrl}).
|
||||
otherwise({redirectTo: '/phones'});
|
||||
}]);
|
||||
</pre>
|
||||
|
||||
In order to configure our application with routes, we need to create a module for our application.
|
||||
We call this module `phonecatApp` and using the `config` api we request the `$routeProvider` to be
|
||||
injected into our config function and use `$routeProvider.when` api to define our routes.
|
||||
|
||||
Note that during the injector configuration phase, the providers can be injected as well, but they
|
||||
will not be available for injection once the injector is created and starts creating service
|
||||
instances.
|
||||
|
||||
Our application routes were defined as follows:
|
||||
|
||||
* The phone list view will be shown when the URL hash fragment is `/phones`. To construct this
|
||||
view, Angular will use the `phone-list.html` template and the `PhoneListCtrl` controller.
|
||||
|
||||
* The phone details view will be shown when the URL hash fragment matches '/phone/:phoneId', where
|
||||
`:phoneId` is a variable part of the URL. To construct the phone details view, angular will use the
|
||||
`phone-detail.html` template and the `PhoneDetailCtrl` controller.
|
||||
|
||||
We reused the `PhoneListCtrl` controller that we constructed in previous steps and we added a new,
|
||||
empty `PhoneDetailCtrl` controller to the `app/js/controllers.js` file for the phone details view.
|
||||
|
||||
The statement `$route.otherwise({redirectTo: '/phones'})` triggers a redirection to `/phones` when
|
||||
the browser address doesn't match either of our routes.
|
||||
|
||||
Note the use of the `:phoneId` parameter in the second route declaration. The `$route` service uses
|
||||
the route declaration — `'/phones/:phoneId'` — as a template that is matched against the current
|
||||
URL. All variables defined with the `:` notation are extracted into the
|
||||
{@link api/angular.module.ng.$routeParams $routeParams} object.
|
||||
|
||||
|
||||
In order for our application to bootstrap with our newly created module we'll also need to specify
|
||||
the module name as the value of the {@link api/angular.module.ng.$compileProvider.directive.ngApp ngApp}
|
||||
directive:
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html ng-app="phonecat">
|
||||
...
|
||||
</pre>
|
||||
|
||||
|
||||
## Controllers
|
||||
|
||||
__`app/js/controller.js`:__
|
||||
<pre>
|
||||
...
|
||||
function PhoneDetailCtrl($scope, $routeParams) {
|
||||
$scope.phoneId = $routeParams.phoneId;
|
||||
}
|
||||
|
||||
//PhoneDetailCtrl.$inject = ['$scope', '$routeParams'];
|
||||
</pre>
|
||||
|
||||
|
||||
## Template
|
||||
|
||||
The `$route` service is usually used in conjunction with the {@link api/angular.module.ng.$compileProvider.directive.ngView
|
||||
ngView} directive. The role of the `ngView` directive is to include the view template for the current
|
||||
route into the layout template, which makes it a perfect fit for our `index.html` template.
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
<html ng-app="phonecat">
|
||||
<head>
|
||||
...
|
||||
<script src="lib/angular/angular.js"></script>
|
||||
<script src="js/app.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div ng-view></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
Note that we removed most of the code in the `index.html` template and replaced it with a single
|
||||
line containing a div with `ng-view` attribute. The code that we removed was placed into the
|
||||
`phone-list.html` template:
|
||||
|
||||
__`app/partials/phone-list.html`:__
|
||||
<pre>
|
||||
<div class="container-fluid">
|
||||
<div class="row-fluid">
|
||||
<div class="span2">
|
||||
<!--Sidebar content-->
|
||||
|
||||
Search: <input ng-model="query">
|
||||
Sort by:
|
||||
<select ng-model="orderProp">
|
||||
<option value="name">Alphabetical</option>
|
||||
<option value="age">Newest</option>
|
||||
</select>
|
||||
|
||||
</div>
|
||||
<div class="span10">
|
||||
<!--Body content-->
|
||||
|
||||
<ul class="phones">
|
||||
<li ng-repeat="phone in phones | filter:query | orderBy:orderProp" class="thumbnail">
|
||||
<a href="#/phones/{{phone.id}}" class="thumb"><img ng-src="{{phone.imageUrl}}"></a>
|
||||
<a href="#/phones/{{phone.id}}">{{phone.name}}</a>
|
||||
<p>{{phone.snippet}}</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
<div style="display:none">
|
||||
TODO!
|
||||
<img class="diagram" src="img/tutorial/tutorial_07_final.png">
|
||||
</div>
|
||||
|
||||
We also added a placeholder template for the phone details view:
|
||||
|
||||
__`app/partials/phone-detail.html`:__
|
||||
<pre>
|
||||
TBD: detail view for {{phoneId}}
|
||||
</pre>
|
||||
|
||||
Note how we are using `phoneId` model defined in the `PhoneDetailCtrl` controller.
|
||||
|
||||
|
||||
## Test
|
||||
|
||||
To automatically verify that everything is wired properly, we wrote end-to-end tests that navigate
|
||||
to various URLs and verify that the correct view was rendered.
|
||||
|
||||
<pre>
|
||||
...
|
||||
it('should redirect index.html to index.html#/phones', function() {
|
||||
browser().navigateTo('../../app/index.html');
|
||||
expect(browser().location().url()).toBe('/phones');
|
||||
});
|
||||
...
|
||||
|
||||
describe('Phone detail view', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
browser().navigateTo('../../app/index.html#/phones/nexus-s');
|
||||
});
|
||||
|
||||
|
||||
it('should display placeholder page with phoneId', function() {
|
||||
expect(binding('phoneId')).toBe('nexus-s');
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you
|
||||
can see them running on {@link
|
||||
http://angular.github.com/angular-phonecat/step-7/test/e2e/runner.html
|
||||
angular's server}.
|
||||
|
||||
|
||||
# Experiments
|
||||
|
||||
* Try to add an `{{orderProp}}` binding to `index.html`, and you'll see that nothing happens even
|
||||
when you are in the phone list view. This is because the `orderProp` model is visible only in the
|
||||
scope managed by `PhoneListCtrl`, which is associated with the `<div ng-view>` element. If you add
|
||||
the same binding into the `phone-list.html` template, the binding will work as expected.
|
||||
|
||||
<div style="display: none">
|
||||
* In `PhoneCatCtrl`, create a new model called "`hero`" with `this.hero = 'Zoro'`. In
|
||||
`PhoneListCtrl` let's shadow it with `this.hero = 'Batman'`, and in `PhoneDetailCtrl` we'll use
|
||||
`this.hero = "Captain Proton"`. Then add the `<p>hero = {{hero}}</p>` to all three of our templates
|
||||
(`index.html`, `phone-list.html`, and `phone-detail.html`). Open the app and you'll see scope
|
||||
inheritance and model property shadowing do some wonders.
|
||||
</div>
|
||||
|
||||
# Summary
|
||||
|
||||
With the routing set up and the phone list view implemented, we're ready to go to {@link step_08
|
||||
step 8} to implement the phone details view.
|
||||
|
||||
|
||||
<ul doc-tutorial-nav="7"></ul>
|
||||
@@ -0,0 +1,197 @@
|
||||
@ngdoc overview
|
||||
@name Tutorial: 8 - More Templating
|
||||
@description
|
||||
|
||||
<ul doc-tutorial-nav="8"></ul>
|
||||
|
||||
|
||||
In this step, you will implement the phone details view, which is displayed when a user clicks on a
|
||||
phone in the phone list.
|
||||
|
||||
|
||||
<div doc-tutorial-reset="8"></div>
|
||||
|
||||
|
||||
Now when you click on a phone on the list, the phone details page with phone-specific information
|
||||
is displayed.
|
||||
|
||||
To implement the phone details view we will use {@link api/angular.module.ng.$http $http} to fetch
|
||||
our data, and we'll flesh out the `phone-details.html` view template.
|
||||
|
||||
The most important changes are listed below. You can see the full diff on {@link
|
||||
https://github.com/angular/angular-phonecat/compare/step-7...step-8
|
||||
GitHub}:
|
||||
|
||||
## Data
|
||||
|
||||
In addition to `phones.json`, the `app/phones/` directory also contains one json file for each
|
||||
phone:
|
||||
|
||||
__`app/phones/nexus-s.json`:__ (sample snippet)
|
||||
<pre>
|
||||
{
|
||||
"additionalFeatures": "Contour Display, Near Field Communications (NFC),...",
|
||||
"android": {
|
||||
"os": "Android 2.3",
|
||||
"ui": "Android"
|
||||
},
|
||||
...
|
||||
"images": [
|
||||
"img/phones/nexus-s.0.jpg",
|
||||
"img/phones/nexus-s.1.jpg",
|
||||
"img/phones/nexus-s.2.jpg",
|
||||
"img/phones/nexus-s.3.jpg"
|
||||
],
|
||||
"storage": {
|
||||
"flash": "16384MB",
|
||||
"ram": "512MB"
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
Each of these files describes various properties of the phone using the same data structure. We'll
|
||||
show this data in the phone detail view.
|
||||
|
||||
|
||||
## Controller
|
||||
|
||||
We'll expand the `PhoneDetailCtrl` by using the `$http` service to fetch the json files. This works
|
||||
the same way as the phone list controller.
|
||||
|
||||
__`app/js/controller.js`:__
|
||||
<pre>
|
||||
function PhoneDetailCtrl($scope, $routeParams, $http) {
|
||||
$http.get('phones/' + $routeParams.phoneId + '.json').success(function(data) {
|
||||
$scope.phone = data;
|
||||
});
|
||||
}
|
||||
|
||||
//PhoneDetailCtrl.$inject = ['$scope', '$routeParams', '$http'];
|
||||
</pre>
|
||||
|
||||
To construct the URL for the HTTP request, we use `$routeParams.phoneId` extracted from the current
|
||||
route by the `$route` service.
|
||||
|
||||
|
||||
## Template
|
||||
|
||||
The TBD placeholder line has been replaced with lists and bindings that comprise the phone details.
|
||||
Note where we use the angular `{{expression}}` markup and `ngRepeater`s to project phone data from
|
||||
our model into the view.
|
||||
|
||||
|
||||
__`app/partials/phone-details.html`:__
|
||||
<pre>
|
||||
<img ng-src="{{phone.images[0]}}" class="phone">
|
||||
|
||||
<h1>{{phone.name}}</h1>
|
||||
|
||||
<p>{{phone.description}}</p>
|
||||
|
||||
<ul class="phone-thumbs">
|
||||
<li ng-repeat="img in phone.images">
|
||||
<img ng-src="{{img}}">
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="specs">
|
||||
<li>
|
||||
<span>Availability and Networks</span>
|
||||
<dl>
|
||||
<dt>Availability</dt>
|
||||
<dd ng-repeat="availability in phone.availability">{{availability}}</dd>
|
||||
</dl>
|
||||
</li>
|
||||
...
|
||||
</li>
|
||||
<span>Additional Features</span>
|
||||
<dd>{{phone.additionalFeatures}}</dd>
|
||||
</li>
|
||||
</ul>
|
||||
</pre>
|
||||
|
||||
<div style="display: none">
|
||||
TODO!
|
||||
<img class="diagram" src="img/tutorial/tutorial_08-09_final.png">
|
||||
</div>
|
||||
|
||||
## Test
|
||||
|
||||
We wrote a new unit test that is similar to the one we wrote for the `PhoneListCtrl` controller in
|
||||
step 5.
|
||||
|
||||
__`test/unit/controllerSpec.js`:__
|
||||
<pre>
|
||||
...
|
||||
describe('PhoneDetailCtrl', function(){
|
||||
var scope, $httpBackend, ctrl;
|
||||
|
||||
beforeEach(inject(function(_$httpBackend_, $rootScope, $routeParams, $controller) {
|
||||
$httpBackend = _$httpBackend_;
|
||||
$httpBackend.expectGET('phones/xyz.json').respond({name:'phone xyz'});
|
||||
|
||||
$routeParams.phoneId = 'xyz';
|
||||
scope = $rootScope.$new();
|
||||
ctrl = $controller(PhoneDetailCtrl, {$scope: scope});
|
||||
}));
|
||||
|
||||
|
||||
it('should fetch phone detail', function() {
|
||||
expect(scope.phone).toBeUndefined();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(scope.phone).toEqual({name:'phone xyz'});
|
||||
});
|
||||
});
|
||||
...
|
||||
</pre>
|
||||
|
||||
To run the unit tests, execute the `./scripts/test.sh` script and you should see the following
|
||||
output.
|
||||
|
||||
Chrome: Runner reset.
|
||||
...
|
||||
Total 3 tests (Passed: 3; Fails: 0; Errors: 0) (5.00 ms)
|
||||
Chrome 19.0.1084.36 Mac OS: Run 3 tests (Passed: 3; Fails: 0; Errors 0) (5.00 ms)
|
||||
|
||||
|
||||
We also added a new end-to-end test that navigates to the Nexus S detail page and verifies that the
|
||||
heading on the page is "Nexus S".
|
||||
|
||||
__`test/e2e/scenarios.js`:__
|
||||
<pre>
|
||||
...
|
||||
describe('Phone detail view', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
browser().navigateTo('../../app/index.html#/phones/nexus-s');
|
||||
});
|
||||
|
||||
|
||||
it('should display nexus-s page', function() {
|
||||
expect(binding('phone.name')).toBe('Nexus S');
|
||||
});
|
||||
});
|
||||
...
|
||||
</pre>
|
||||
|
||||
|
||||
You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you
|
||||
can see them running on {@link
|
||||
http://angular.github.com/angular-phonecat/step-8/test/e2e/runner.html
|
||||
angular's server}.
|
||||
|
||||
# Experiments
|
||||
|
||||
* Using the {@link guide/dev_guide.e2e-testing Angular's end-to-end test runner API}, write a test
|
||||
that verifies that we display 4 thumbnail images on the Nexus S details page.
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
Now that the phone details view is in place, proceed to {@link step_09 step 9} to learn how to
|
||||
write your own custom display filter.
|
||||
|
||||
|
||||
<ul doc-tutorial-nav="8"></ul>
|
||||
@@ -0,0 +1,143 @@
|
||||
@ngdoc overview
|
||||
@name Tutorial: 9 - Filters
|
||||
@description
|
||||
|
||||
<ul doc-tutorial-nav="9"></ul>
|
||||
|
||||
|
||||
In this step you will learn how to create your own custom display filter.
|
||||
|
||||
|
||||
<div doc-tutorial-reset="9"></div>
|
||||
|
||||
|
||||
Navigate to one of the detail pages.
|
||||
|
||||
In the previous step, the details page displayed either "true" or "false" to indicate whether
|
||||
certain phone features were present or not. We have used a custom filter to convert those text
|
||||
strings into glyphs: ✓ for "true", and ✘ for "false". Let's see, what the filter code looks like.
|
||||
|
||||
The most important changes are listed below. You can see the full diff on {@link
|
||||
https://github.com/angular/angular-phonecat/compare/step-8...step-9
|
||||
GitHub}:
|
||||
|
||||
|
||||
## Custom Filter
|
||||
|
||||
In order to create a new filter, you are going to create a `phonecatFilters` module and register
|
||||
your custom filter with this module:
|
||||
|
||||
__`app/js/filters.js`:__
|
||||
<pre>
|
||||
angular.module('phonecatFilters', []).filter('checkmark', function() {
|
||||
return function(input) {
|
||||
return input ? '\u2713' : '\u2718';
|
||||
};
|
||||
});
|
||||
</pre>
|
||||
|
||||
The name of our filter is "checkmark". The `input` evaluates to either `true` or `false`, and we
|
||||
return one of two unicode characters we have chosen to represent true or false (`\u2713` and
|
||||
`\u2718`).
|
||||
|
||||
Now that our filter is ready, we need to register the `phonecatFilters` module as a dependency for
|
||||
our main `phonecat` module.
|
||||
|
||||
__`app/js/app.js`:__
|
||||
<pre>
|
||||
...
|
||||
angular.module('phonecat', ['phonecatFilters']).
|
||||
...
|
||||
</pre>
|
||||
|
||||
|
||||
## Template
|
||||
|
||||
Since the filter code lives in the `app/js/filters.js` file, we need to include this file in our
|
||||
layout template.
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
...
|
||||
<script src="js/controllers.js"></script>
|
||||
<script src="js/filters.js"></script>
|
||||
...
|
||||
</pre>
|
||||
|
||||
The syntax for using filters in angular templates is as follows:
|
||||
|
||||
{{ expression | filter }}
|
||||
|
||||
Let's employ the filter in the phone details template:
|
||||
|
||||
|
||||
|
||||
__`app/partials/phone-detail.html`:__
|
||||
<pre>
|
||||
...
|
||||
<dl>
|
||||
<dt>Infrared</dt>
|
||||
<dd>{{phone.connectivity.infrared | checkmark}}</dd>
|
||||
<dt>GPS</dt>
|
||||
<dd>{{phone.connectivity.gps | checkmark}}</dd>
|
||||
</dl>
|
||||
...
|
||||
</pre>
|
||||
|
||||
|
||||
## Test
|
||||
|
||||
Filters, like any other component, should be tested and these tests are very easy to write.
|
||||
|
||||
__`test/unit/filtersSpec.js`:__
|
||||
<pre>
|
||||
describe('filter', function() {
|
||||
|
||||
beforeEach(module('phonecatFilters'));
|
||||
|
||||
|
||||
describe('checkmark', function() {
|
||||
|
||||
it('should convert boolean values to unicode checkmark or cross',
|
||||
inject(function(checkmarkFilter) {
|
||||
expect(checkmarkFilter(true)).toBe('\u2713');
|
||||
expect(checkmarkFilter(false)).toBe('\u2718');
|
||||
}));
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
Note that you need to configure our test injector with the `phonecatFilters` module before any of
|
||||
our filter tests execute.
|
||||
|
||||
To run the unit tests, execute the `./scripts/test.sh` script and you should see the following
|
||||
output.
|
||||
|
||||
Chrome: Runner reset.
|
||||
....
|
||||
Total 4 tests (Passed: 4; Fails: 0; Errors: 0) (3.00 ms)
|
||||
Chrome 19.0.1084.36 Mac OS: Run 4 tests (Passed: 4; Fails: 0; Errors 0) (3.00 ms)
|
||||
|
||||
|
||||
# Experiments
|
||||
|
||||
* Let's experiment with some of the {@link api/angular.module.ng.$filter built-in angular filters} and add the
|
||||
following bindings to `index.html`:
|
||||
* `{{ "lower cap string" | uppercase }}`
|
||||
* `{{ {foo: "bar", baz: 23} | json }}`
|
||||
* `{{ 1304375948024 | date }}`
|
||||
* `{{ 1304375948024 | date:"MM/dd/yyyy @ h:mma" }}`
|
||||
|
||||
* We can also create a model with an input element, and combine it with a filtered binding. Add
|
||||
the following to index.html:
|
||||
|
||||
<input ng-model="userInput"> Uppercased: {{ userInput | uppercase }}
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
Now that you have learned how to write and test a custom filter, go to {@link step_10 step 10} to
|
||||
learn how we can use angular to enhance the phone details page further.
|
||||
|
||||
|
||||
<ul doc-tutorial-nav="9"></ul>
|
||||
@@ -0,0 +1,141 @@
|
||||
@ngdoc overview
|
||||
@name Tutorial: 10 - Event Handlers
|
||||
@description
|
||||
|
||||
<ul doc-tutorial-nav="10"></ul>
|
||||
|
||||
|
||||
In this step, you will add a clickable phone image swapper to the phone details page.
|
||||
|
||||
|
||||
<div doc-tutorial-reset="10"></div>
|
||||
|
||||
|
||||
The phone details view displays one large image of the current phone and several smaller thumbnail
|
||||
images. It would be great if we could replace the large image with any of the thumbnails just by
|
||||
clicking on the desired thumbnail image. Let's have a look at how we can do this with angular.
|
||||
|
||||
The most important changes are listed below. You can see the full diff on {@link
|
||||
https://github.com/angular/angular-phonecat/compare/step-9...step-10
|
||||
GitHub}:
|
||||
|
||||
|
||||
## Controller
|
||||
|
||||
__`app/js/controllers.js`:__
|
||||
<pre>
|
||||
...
|
||||
function PhoneDetailCtrl($scope, $routeParams, $http) {
|
||||
$http.get('phones/' + $routeParams.phoneId + '.json').success(function(data) {
|
||||
$scope.phone = data;
|
||||
$scope.mainImageUrl = data.images[0];
|
||||
});
|
||||
|
||||
$scope.setImage = function(imageUrl) {
|
||||
$scope.mainImageUrl = imageUrl;
|
||||
}
|
||||
}
|
||||
|
||||
//PhoneDetailCtrl.$inject = ['$scope', '$routeParams', '$http'];
|
||||
</pre>
|
||||
|
||||
In the `PhoneDetailCtrl` controller, we created the `mainImageUrl` model property and set its
|
||||
default value to the first phone image url.
|
||||
|
||||
We also created a `setImage` event handler function that will change the value of `mainImageUrl`.
|
||||
|
||||
|
||||
## Template
|
||||
|
||||
__`app/partials/phone-detail.html`:__
|
||||
<pre>
|
||||
<img ng-src="{{mainImageUrl}}" class="phone">
|
||||
|
||||
...
|
||||
|
||||
<ul class="phone-thumbs">
|
||||
<li ng-repeat="img in phone.images">
|
||||
<img ng-src="{{img}}" ng-click="setImage(img)">
|
||||
</li>
|
||||
</ul>
|
||||
...
|
||||
</pre>
|
||||
|
||||
We bound the `ngSrc` directive of the large image to the `mainImageUrl` property.
|
||||
|
||||
We also registered an {@link api/angular.module.ng.$compileProvider.directive.ngClick `ngClick`}
|
||||
handler with thumbnail images. When a user clicks on one of the thumbnail images, the handler will
|
||||
use the `setImage` event handler function to change the value of the `mainImageUrl` property to the
|
||||
url of the thumbnail image.
|
||||
|
||||
<div style="display: none">
|
||||
TODO!
|
||||
<img class="diagram" src="img/tutorial/tutorial_10-11_final.png">
|
||||
</div>
|
||||
|
||||
## Test
|
||||
|
||||
To verify this new feature, we added two end-to-end tests. One verifies that the main image is set
|
||||
to the first phone image by default. The second test clicks on several thumbnail images and
|
||||
verifies that the main image changed appropriately.
|
||||
|
||||
__`test/e2e/scenarios.js`:__
|
||||
<pre>
|
||||
...
|
||||
describe('Phone detail view', function() {
|
||||
|
||||
...
|
||||
|
||||
it('should display the first phone image as the main phone image', function() {
|
||||
expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.0.jpg');
|
||||
});
|
||||
|
||||
|
||||
it('should swap main image if a thumbnail image is clicked on', function() {
|
||||
element('.phone-thumbs li:nth-child(3) img').click();
|
||||
expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.2.jpg');
|
||||
|
||||
element('.phone-thumbs li:nth-child(1) img').click();
|
||||
expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.0.jpg');
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you
|
||||
can see them running on {@link
|
||||
http://angular.github.com/angular-phonecat/step-8/test/e2e/runner.html
|
||||
angular's server}.
|
||||
|
||||
# Experiments
|
||||
|
||||
* Let's add a new controller method to `PhoneDetailCtrl`:
|
||||
|
||||
$scope.hello = function(name) {
|
||||
alert('Hello ' + (name || 'world') + '!');
|
||||
}
|
||||
|
||||
and add:
|
||||
|
||||
<button ng-click="hello('Elmo')">Hello</button>
|
||||
|
||||
to the `phone-details.html` template.
|
||||
|
||||
<div style="display: none">
|
||||
TODO!
|
||||
The controller methods are inherited between controllers/scopes, so you can use the same snippet
|
||||
in the `phone-list.html` template as well.
|
||||
|
||||
* Move the `hello` method from `PhoneCatCtrl` to `PhoneListCtrl` and you'll see that the button
|
||||
declared in `index.html` will stop working, while the one declared in the `phone-list.html`
|
||||
template remains operational.
|
||||
</div>
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
With the phone image swapper in place, we're ready for {@link step_11 step 11} (the last step!) to
|
||||
learn an even better way to fetch data.
|
||||
|
||||
|
||||
<ul doc-tutorial-nav="10"></ul>
|
||||
@@ -0,0 +1,223 @@
|
||||
@ngdoc overview
|
||||
@name Tutorial: 11 - REST and Custom Services
|
||||
@description
|
||||
|
||||
<ul doc-tutorial-nav="11"></ul>
|
||||
|
||||
|
||||
In this step, you will improve the way our app fetches data.
|
||||
|
||||
|
||||
<div doc-tutorial-reset="11"></div>
|
||||
|
||||
|
||||
The last improvement we will make to our app is to define a custom service that represents a {@link
|
||||
http://en.wikipedia.org/wiki/Representational_State_Transfer RESTful} client. Using this client we
|
||||
can make xhr requests for data in an easier way, without having to deal with the lower-level {@link
|
||||
api/angular.module.ng.$http $http} API, HTTP methods and URLs.
|
||||
|
||||
The most important changes are listed below. You can see the full diff on {@link
|
||||
https://github.com/angular/angular-phonecat/compare/step-10...step-11
|
||||
GitHub}:
|
||||
|
||||
|
||||
## Template
|
||||
|
||||
The custom service is defined in `app/js/services.js` so we need to include this file in our layout
|
||||
template. Additionally, we also need to load the `angular-resource.js` file, which contains the
|
||||
`ngResource` module and in it the `$resource` service, that we'll soon use:
|
||||
|
||||
__`app/index.html`.__
|
||||
<pre>
|
||||
...
|
||||
<script src="js/services.js"></script>
|
||||
<script src="lib/angular/angular-resource.js"></script>
|
||||
...
|
||||
</pre>
|
||||
|
||||
## Service
|
||||
|
||||
__`app/js/services.js`.__
|
||||
<pre>
|
||||
angular.module('phonecatServices', ['ngResource']).
|
||||
factory('Phone', function($resource){
|
||||
return $resource('phones/:phoneId.json', {}, {
|
||||
query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
We used the module API to register a custom service using a factory function. We passed in the name
|
||||
of the service - 'Phone' - and the factory function. The factory function is similar to a
|
||||
controller's constructor in that both can declare dependencies via function arguments. The Phone
|
||||
service declared a dependency on the `$resource` service.
|
||||
|
||||
The {@link api/angular.module.ngResource.$resource `$resource`} service makes it easy to create a
|
||||
{@link http://en.wikipedia.org/wiki/Representational_State_Transfer RESTful} client with just a few
|
||||
lines of code. This client can then be used in our application, instead of the lower-level {@link
|
||||
api/angular.module.ng.$http $http} service.
|
||||
|
||||
|
||||
## Controller
|
||||
|
||||
We simplified our sub-controllers (`PhoneListCtrl` and `PhoneDetailCtrl`) by factoring out the
|
||||
lower-level {@link api/angular.module.ng.$http $http} service, replacing it with a new service called
|
||||
`Phone`. Angular's {@link api/angular.module.ngResource.$resource `$resource`} service is easier to
|
||||
use than `$http for interacting with data sources exposed as RESTful resources. It is also easier
|
||||
now to understand what the code in our controllers is doing.
|
||||
|
||||
__`app/js/controllers.js`.__
|
||||
<pre>
|
||||
...
|
||||
|
||||
function PhoneListCtrl($scope, Phone) {
|
||||
$scope.phones = Phone.query();
|
||||
$scope.orderProp = 'age';
|
||||
}
|
||||
|
||||
//PhoneListCtrl.$inject = ['$scope', 'Phone'];
|
||||
|
||||
|
||||
|
||||
function PhoneDetailCtrl($scope, $routeParams, Phone) {
|
||||
$scope.phone = Phone.get({phoneId: $routeParams.phoneId}, function(phone) {
|
||||
$scope.mainImageUrl = phone.images[0];
|
||||
});
|
||||
|
||||
$scope.setImage = function(imageUrl) {
|
||||
$scope.mainImageUrl = imageUrl;
|
||||
}
|
||||
}
|
||||
|
||||
//PhoneDetailCtrl.$inject = ['$scope', '$routeParams', 'Phone'];
|
||||
</pre>
|
||||
|
||||
Notice how in `PhoneListCtrl` we replaced:
|
||||
|
||||
$http.get('phones/phones.json').success(function(data) {
|
||||
$scope.phones = data;
|
||||
});
|
||||
|
||||
with:
|
||||
|
||||
$scope.phones = Phone.query();
|
||||
|
||||
This is a simple statement that we want to query for all phones.
|
||||
|
||||
An important thing to notice in the code above is that we don't pass any callback functions when
|
||||
invoking methods of our Phone service. Although it looks as if the result were returned
|
||||
synchronously, that is not the case at all. What is returned synchronously is a "future" — an
|
||||
object, which will be filled with data when the xhr response returns. Because of the data-binding
|
||||
in angular, we can use this future and bind it to our template. Then, when the data arrives, the
|
||||
view will automatically update.
|
||||
|
||||
Sometimes, relying on the future object and data-binding alone is not sufficient to do everything
|
||||
we require, so in these cases, we can add a callback to process the server response. The
|
||||
`PhoneDetailCtrl` controller illustrates this by setting the `mainImageUrl` in a callback.
|
||||
|
||||
|
||||
## Test
|
||||
|
||||
We have modified our unit tests to verify that our new service is issuing HTTP requests and
|
||||
processing them as expected. The tests also check that our controllers are interacting with the
|
||||
service correctly.
|
||||
|
||||
The {@link api/angular.module.ngResource.$resource $resource} service augments the response object
|
||||
with methods for updating and deleting the resource. If we were to use the standard `toEqual`
|
||||
matcher, our tests would fail because the test values would not match the responses exactly. To
|
||||
solve the problem, we use a newly-defined `toEqualData` {@link
|
||||
http://pivotal.github.com/jasmine/jsdoc/symbols/jasmine.Matchers.html Jasmine matcher}. When the
|
||||
`toEqualData` matcher compares two objects, it takes only object properties into account and
|
||||
ignores methods.
|
||||
|
||||
|
||||
__`test/unit/controllersSpec.js`:__
|
||||
<pre>
|
||||
describe('PhoneCat controllers', function() {
|
||||
|
||||
beforeEach(function(){
|
||||
this.addMatchers({
|
||||
toEqualData: function(expected) {
|
||||
return angular.equals(this.actual, expected);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
beforeEach(module('phonecatServices'));
|
||||
|
||||
|
||||
describe('PhoneListCtrl', function(){
|
||||
var scope, ctrl, $httpBackend;
|
||||
|
||||
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
|
||||
$httpBackend = _$httpBackend_;
|
||||
$httpBackend.expectGET('phones/phones.json').
|
||||
respond([{name: 'Nexus S'}, {name: 'Motorola DROID'}]);
|
||||
|
||||
scope = $rootScope.$new();
|
||||
ctrl = $controller(PhoneListCtrl, {$scope: scope});
|
||||
}));
|
||||
|
||||
|
||||
it('should create "phones" model with 2 phones fetched from xhr', function() {
|
||||
expect(scope.phones).toEqual([]);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(scope.phones).toEqualData(
|
||||
[{name: 'Nexus S'}, {name: 'Motorola DROID'}]);
|
||||
});
|
||||
|
||||
|
||||
it('should set the default value of orderProp model', function() {
|
||||
expect(scope.orderProp).toBe('age');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('PhoneDetailCtrl', function(){
|
||||
var scope, $httpBackend, ctrl,
|
||||
xyzPhoneData = function() {
|
||||
return {
|
||||
name: 'phone xyz',
|
||||
images: ['image/url1.png', 'image/url2.png']
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
beforeEach(inject(function(_$httpBackend_, $rootScope, $routeParams, $controller) {
|
||||
$httpBackend = _$httpBackend_;
|
||||
$httpBackend.expectGET('phones/xyz.json').respond(xyzPhoneData());
|
||||
|
||||
$routeParams.phoneId = 'xyz';
|
||||
scope = $rootScope.$new();
|
||||
ctrl = $controller(PhoneDetailCtrl, {$scope: scope});
|
||||
}));
|
||||
|
||||
|
||||
it('should fetch phone detail', function() {
|
||||
expect(scope.phone).toEqualData({});
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(scope.phone).toEqualData(xyzPhoneData());
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
To run the unit tests, execute the `./scripts/test.sh` script and you should see the following
|
||||
output.
|
||||
|
||||
Chrome: Runner reset.
|
||||
....
|
||||
Total 4 tests (Passed: 4; Fails: 0; Errors: 0) (3.00 ms)
|
||||
Chrome 19.0.1084.36 Mac OS: Run 4 tests (Passed: 4; Fails: 0; Errors 0) (3.00 ms)
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
There you have it! We have created a web app in a relatively short amount of time. In the {@link
|
||||
the_end closing notes} we'll cover were to go from here.
|
||||
|
||||
|
||||
<ul doc-tutorial-nav="11"></ul>
|
||||
@@ -0,0 +1,21 @@
|
||||
@ngdoc overview
|
||||
@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` or `goto_step.sh` commands.
|
||||
|
||||
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}.
|
||||
|
||||
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.
|
||||
|
||||
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}.
|
||||
@@ -1,2 +0,0 @@
|
||||
controller: {{name}}<br />
|
||||
Book Id: {{params.bookId}}<br />
|
||||
@@ -1,3 +0,0 @@
|
||||
controller: {{name}}<br />
|
||||
Book Id: {{prams.bookId}}<br />
|
||||
Chapter Id: {{params.chapterId}}
|
||||
@@ -1 +0,0 @@
|
||||
Hello, $http!
|
||||
@@ -1 +0,0 @@
|
||||
Content of template1.html
|
||||
@@ -1 +0,0 @@
|
||||
Content of template2.html
|
||||
@@ -1,18 +0,0 @@
|
||||
<label>Name:</label>
|
||||
<input type="text" ng:model="form.name" required>
|
||||
|
||||
<div ng:repeat="contact in form.contacts">
|
||||
<select ng:model="contact.type">
|
||||
<option>url</option>
|
||||
<option>email</option>
|
||||
<option>phone</option>
|
||||
</select>
|
||||
<input type="text" ng:model="contact.url">
|
||||
[ <a href="" ng:click="form.contacts.$remove(contact)">X</a> ]
|
||||
</div>
|
||||
<div>
|
||||
[ <a href="" ng:click="form.contacts.$add()">add</a> ]
|
||||
</div>
|
||||
|
||||
<button ng:click="cancel()">Cancel</button>
|
||||
<button ng:click="save()">Save</button>
|
||||
@@ -1,5 +0,0 @@
|
||||
Hello {{person.name}},
|
||||
<div>
|
||||
Your contact information:
|
||||
<div ng:repeat="contact in person.contacts">{{contact.type}}: {{contact.url|linky}}</div>
|
||||
</div>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 692 B |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 141 KiB |
|
After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 158 KiB |
|
Before Width: | Height: | Size: 159 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 21 KiB |
@@ -35,19 +35,10 @@ describe('ngdoc', function() {
|
||||
var d2 = new Doc('@name a.b.ng-c').parse();
|
||||
var d3 = new Doc('@name some text: more text').parse();
|
||||
expect(ngdoc.metadata([d1])[0].shortName).toEqual('c');
|
||||
expect(ngdoc.metadata([d2])[0].shortName).toEqual('ng:c');
|
||||
expect(ngdoc.metadata([d2])[0].shortName).toEqual('ng-c');
|
||||
expect(ngdoc.metadata([d3])[0].shortName).toEqual('more text');
|
||||
});
|
||||
|
||||
it('should have depth information', function() {
|
||||
var d1 = new Doc('@name a.b.c').parse();
|
||||
var d2 = new Doc('@name a.b.ng-c').parse();
|
||||
var d3 = new Doc('@name some text: more text').parse();
|
||||
expect(ngdoc.metadata([d1])[0].depth).toEqual(2);
|
||||
expect(ngdoc.metadata([d2])[0].depth).toEqual(2);
|
||||
expect(ngdoc.metadata([d3])[0].depth).toEqual(1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('parse', function() {
|
||||
@@ -89,38 +80,6 @@ describe('ngdoc', function() {
|
||||
expect(doc.name).toEqual('friendly name');
|
||||
});
|
||||
|
||||
it('should escape <doc:source> element', function() {
|
||||
var doc = new Doc('@name a\n@description before <doc:example>' +
|
||||
'<doc:source>\n<>\n</doc:source></doc:example> after');
|
||||
doc.parse();
|
||||
expect(doc.description).toContain('<p>before </p><doc:example>' +
|
||||
'<pre class="doc-source">\n<>\n</pre></doc:example><p>after</p>');
|
||||
});
|
||||
|
||||
it('should preserve the source attribute', function() {
|
||||
var doc = new Doc('@name a\n@description before <doc:example>' +
|
||||
'<doc:source source="false">lala</doc:source></doc:example> after');
|
||||
doc.parse();
|
||||
expect(doc.description).toContain('<p>before </p><doc:example>' +
|
||||
'<pre class="doc-source" source="false">lala</pre></doc:example><p>after</p>');
|
||||
});
|
||||
|
||||
it('should preserve the jsfiddle attribute', function() {
|
||||
var doc = new Doc('@name a\n@description before <doc:example>' +
|
||||
'<doc:source jsfiddle="foo">lala</doc:source></doc:example> after');
|
||||
doc.parse();
|
||||
expect(doc.description).toContain('<p>before </p><doc:example>' +
|
||||
'<pre class="doc-source" jsfiddle="foo">lala</pre></doc:example><p>after</p>');
|
||||
});
|
||||
|
||||
it('should escape <doc:scenario> element', function() {
|
||||
var doc = new Doc('@name a\n@description before <doc:example>' +
|
||||
'<doc:scenario>\n<>\n</doc:scenario></doc:example> after');
|
||||
doc.parse();
|
||||
expect(doc.description).toContain('<p>before </p><doc:example>' +
|
||||
'<pre class="doc-scenario">\n<>\n</pre></doc:example><p>after</p>');
|
||||
});
|
||||
|
||||
it('should store all links', function() {
|
||||
var doc = new Doc('@name a\n@description {@link api/angular.link}');
|
||||
doc.parse();
|
||||
@@ -173,51 +132,33 @@ describe('ngdoc', function() {
|
||||
});
|
||||
|
||||
describe('markdown', function() {
|
||||
it('should replace angular in markdown', function() {
|
||||
expect(new Doc().markdown('<angular/>')).
|
||||
toEqual('<p><tt><angular/></tt></p>');
|
||||
});
|
||||
|
||||
it('should not replace anything in <pre>, but escape the html escape the content', function() {
|
||||
expect(new Doc().markdown('bah x\n<pre>\n<b>angular</b>.k\n</pre>\n asdf x')).
|
||||
toEqual(
|
||||
'<p>bah x</p>' +
|
||||
'<div ng:non-bindable><pre class="brush: js; html-script: true;">\n' +
|
||||
'<p>bah x\n' +
|
||||
'<pre class="prettyprint linenums">\n' +
|
||||
'<b>angular</b>.k\n' +
|
||||
'</pre></div>' +
|
||||
'<p>asdf x</p>');
|
||||
});
|
||||
|
||||
it('should ignore doc widgets', function() {
|
||||
expect(new Doc().markdown('text<doc:example>do not touch</doc:example>')).
|
||||
toEqual('<p>text</p><doc:example>do not touch</doc:example>');
|
||||
|
||||
expect(new Doc().markdown('text<doc:tutorial-instructions>do not touch</doc:tutorial-instructions>')).
|
||||
toEqual('<p>text</p><doc:tutorial-instructions>do not touch</doc:tutorial-instructions>');
|
||||
});
|
||||
|
||||
it('should ignore doc widgets with params', function() {
|
||||
expect(new Doc().markdown('text<doc:tutorial-instructions id="10" show="true">do not touch</doc:tutorial-instructions>')).
|
||||
toEqual('<p>text</p><doc:tutorial-instructions id="10" show="true">do not touch</doc:tutorial-instructions>');
|
||||
'</pre>\n' +
|
||||
' asdf x</p>');
|
||||
});
|
||||
|
||||
it('should replace text between two <pre></pre> tags', function() {
|
||||
expect(new Doc().markdown('<pre>x</pre># One<pre>b</pre>')).
|
||||
toMatch('</div><h1>One</h1><div');
|
||||
expect(new Doc().markdown('<pre>x</pre>\n# One\n<pre>b</pre>')).
|
||||
toMatch('</pre>\n\n<h1>One</h1>\n\n<pre');
|
||||
});
|
||||
|
||||
it('should ignore nested doc widgets', function() {
|
||||
expect(new Doc().markdown(
|
||||
'before<doc:tutorial-instructions>\n' +
|
||||
'<doc:tutorial-instruction id="git-mac" ng:model="Git on Mac/Linux">' +
|
||||
'before<div class="tabbable">\n' +
|
||||
'<div class="tab-pane well" id="git-mac" ng:model="Git on Mac/Linux">' +
|
||||
'\ngit bla bla\n</doc:tutorial-instruction>\n' +
|
||||
'</doc:tutorial-instructions>')).toEqual(
|
||||
|
||||
'<p>before</p><doc:tutorial-instructions>\n' +
|
||||
'<doc:tutorial-instruction id="git-mac" ng:model="Git on Mac/Linux">\n' +
|
||||
'<p>before<div class="tabbable">\n' +
|
||||
'<div class="tab-pane well" id="git-mac" ng:model="Git on Mac/Linux">\n' +
|
||||
'git bla bla\n' +
|
||||
'</doc:tutorial-instruction>\n' +
|
||||
'</doc:tutorial-instructions>');
|
||||
'</doc:tutorial-instructions></p>');
|
||||
});
|
||||
|
||||
it('should unindent text before processing based on the second line', function() {
|
||||
@@ -437,18 +378,18 @@ describe('ngdoc', function() {
|
||||
var doc = new Doc("@name a\n@description <pre><b>abc</b></pre>");
|
||||
doc.parse();
|
||||
expect(doc.description).
|
||||
toBe('<div ng:non-bindable><pre class="brush: js; html-script: true;"><b>abc</b></pre></div>');
|
||||
toBe('<pre class="prettyprint linenums"><b>abc</b></pre>');
|
||||
});
|
||||
|
||||
it('should support multiple pre blocks', function() {
|
||||
var doc = new Doc("@name a\n@description foo \n<pre>abc</pre>\n#bah\nfoo \n<pre>cba</pre>");
|
||||
doc.parse();
|
||||
expect(doc.description).
|
||||
toBe('<p>foo </p>' +
|
||||
'<div ng:non-bindable><pre class="brush: js;">abc</pre></div>' +
|
||||
toBe('<p>foo \n' +
|
||||
'<pre class="prettyprint linenums">abc</pre>\n\n' +
|
||||
'<h1>bah</h1>\n\n' +
|
||||
'<p>foo </p>' +
|
||||
'<div ng:non-bindable><pre class="brush: js;">cba</pre></div>');
|
||||
'<p>foo \n' +
|
||||
'<pre class="prettyprint linenums">cba</pre>');
|
||||
|
||||
});
|
||||
|
||||
@@ -493,18 +434,6 @@ describe('ngdoc', function() {
|
||||
doc.parse();
|
||||
expect(doc.example).toEqual('<p>text {{ abc }}</p>');
|
||||
});
|
||||
|
||||
it('should support doc:example', function() {
|
||||
var doc = new Doc('@name a\n@ngdoc overview\n@example \n' +
|
||||
'<doc:example>\n' +
|
||||
' <doc:source><escapeme></doc:source>\n' +
|
||||
' <doc:scenario><scenario></doc:scenario>\n' +
|
||||
'</doc:example>').parse();
|
||||
var html = doc.html();
|
||||
expect(html).toContain('<pre class="doc-source"><escapeme></pre>');
|
||||
expect(html).toContain('<pre class="doc-scenario"><scenario></pre>');
|
||||
expect(doc.scenarios).toEqual(['<scenario>']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('@deprecated', function() {
|
||||
|
||||
@@ -70,9 +70,7 @@ DOM.prototype = {
|
||||
},
|
||||
|
||||
code: function(text) {
|
||||
this.tag('div', {'ng:non-bindable':''}, function() {
|
||||
this.tag('pre', {'class':"brush: js; html-script: true;"}, text);
|
||||
});
|
||||
this.tag('pre', {'class':"prettyprint linenums"}, text);
|
||||
},
|
||||
|
||||
div: function(attr, text) {
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
var seqCount = 0;
|
||||
var usedIds = {};
|
||||
var makeUnique = {
|
||||
'index.html': true,
|
||||
'style.css': true,
|
||||
'script.js': true,
|
||||
'unit.js': true,
|
||||
'spec.js': true,
|
||||
'scenario.js': true
|
||||
}
|
||||
|
||||
function ids(list) {
|
||||
return list.map(function(item) { return item.id; }).join(' ');
|
||||
};
|
||||
|
||||
|
||||
exports.Example = function(scenarios) {
|
||||
this.module = '';
|
||||
this.deps = ['angular.js'];
|
||||
this.html = [];
|
||||
this.css = [];
|
||||
this.js = [];
|
||||
this.unit = [];
|
||||
this.scenario = [];
|
||||
this.scenarios = scenarios;
|
||||
}
|
||||
|
||||
exports.Example.prototype.setModule = function(module) {
|
||||
if (module) {
|
||||
this.module = module;
|
||||
}
|
||||
};
|
||||
|
||||
exports.Example.prototype.addDeps = function(deps) {
|
||||
deps && deps.split(/[\s\,]/).forEach(function(dep) {
|
||||
if (dep) {
|
||||
this.deps.push(dep);
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
exports.Example.prototype.addSource = function(name, content) {
|
||||
var ext = name == 'scenario.js' ? 'scenario' : name.split('.')[1],
|
||||
id = name;
|
||||
|
||||
if (makeUnique[name] && usedIds[id]) {
|
||||
id = name + '-' + (seqCount++);
|
||||
}
|
||||
usedIds[id] = true;
|
||||
|
||||
this[ext].push({name: name, content: content, id: id});
|
||||
if (name.match(/\.js$/) && name !== 'spec.js' && name !== 'unit.js' && name != 'scenario.js') {
|
||||
this.deps.push(name);
|
||||
}
|
||||
if (ext == 'scenario') {
|
||||
this.scenarios.push(content);
|
||||
}
|
||||
};
|
||||
|
||||
exports.Example.prototype.toHtml = function() {
|
||||
return '<h1>Demo Source Code</h1>\n' +
|
||||
this.toHtmlEdit() +
|
||||
this.toHtmlTabs() +
|
||||
'<h1>Demo Source Code</h1>\n' +
|
||||
this.toHtmlEmbed();
|
||||
};
|
||||
|
||||
|
||||
exports.Example.prototype.toHtmlEdit = function() {
|
||||
var out = [];
|
||||
out.push('<div source-edit="' + this.module + '"');
|
||||
out.push(' source-edit-deps="' + this.deps.join(' ') + '"');
|
||||
out.push(' source-edit-html="' + ids(this.html) + '"');
|
||||
out.push(' source-edit-css="' + ids(this.css) + '"');
|
||||
out.push(' source-edit-js="' + ids(this.js) + '"');
|
||||
out.push(' source-edit-unit="' + ids(this.unit) + '"');
|
||||
out.push(' source-edit-scenario="' + ids(this.scenario) + '"');
|
||||
out.push('></div>\n');
|
||||
return out.join('');
|
||||
};
|
||||
|
||||
exports.Example.prototype.toHtmlTabs = function() {
|
||||
var out = [],
|
||||
self = this;
|
||||
|
||||
out.push('<div class="tabbable">');
|
||||
htmlTabs(this.html);
|
||||
htmlTabs(this.css);
|
||||
htmlTabs(this.js);
|
||||
htmlTabs(this.unit);
|
||||
htmlTabs(this.scenario);
|
||||
out.push('</div>');
|
||||
return out.join('');
|
||||
|
||||
function htmlTabs(sources) {
|
||||
sources.forEach(function(source) {
|
||||
var wrap = '',
|
||||
isCss = source.name.match(/\.css$/),
|
||||
name = source.name;
|
||||
|
||||
if (name === 'index.html') {
|
||||
wrap = ' ng-html-wrap="' + self.module + ' ' + self.deps.join(' ') + '"';
|
||||
}
|
||||
if (name == 'scenario.js') name = 'End to end test';
|
||||
|
||||
out.push(
|
||||
'<div class="tab-pane" title="' + name + '">\n' +
|
||||
'<pre class="prettyprint linenums" ng-set-text="' + source.id + '"' + wrap + '></pre>\n' +
|
||||
(isCss
|
||||
? ('<style type="text/css" id="' + source.id + '">' + source.content + '</style>\n')
|
||||
: ('<script type="text/ng-template" id="' + source.id + '">' + source.content + '</script>\n') ) +
|
||||
'</div>\n');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.Example.prototype.toHtmlEmbed = function() {
|
||||
var out = [];
|
||||
out.push('<div class="well doc-example-live"');
|
||||
out.push(' ng-embed-app="' + this.module + '"');
|
||||
out.push(' ng-set-html="' + this.html[0].id + '"');
|
||||
out.push(' ng-eval-javascript="' + ids(this.js) + '">');
|
||||
out.push('</div>');
|
||||
return out.join('');
|
||||
};
|
||||
|
||||
@@ -41,35 +41,35 @@ function writeTheRest(writesFuture) {
|
||||
var metadata = ngdoc.metadata(docs);
|
||||
|
||||
writesFuture.push(writer.copyDir('img'));
|
||||
writesFuture.push(writer.copyDir('examples'));
|
||||
|
||||
var manifest = 'manifest="/build/docs/appcache.manifest"';
|
||||
|
||||
writesFuture.push(writer.copy('docs/src/templates/index.html', 'build/docs/index.html',
|
||||
writesFuture.push(writer.copy('docs/src/templates/index.html', 'index.html',
|
||||
writer.replace, {'doc:manifest': ''})); //manifest //TODO(i): enable
|
||||
|
||||
writesFuture.push(writer.copy('docs/src/templates/index.html', 'build/docs/index-nocache.html',
|
||||
writesFuture.push(writer.copy('docs/src/templates/index.html', 'index-nocache.html',
|
||||
writer.replace, {'doc:manifest': ''}));
|
||||
|
||||
|
||||
writesFuture.push(writer.copy('docs/src/templates/index.html', 'build/docs/index-jq.html',
|
||||
writesFuture.push(writer.copy('docs/src/templates/index.html', 'index-jq.html',
|
||||
writer.replace, {'doc:manifest': manifest}));
|
||||
|
||||
writesFuture.push(writer.copy('docs/src/templates/index.html', 'build/docs/index-jq-nocache.html',
|
||||
writesFuture.push(writer.copy('docs/src/templates/index.html', 'index-jq-nocache.html',
|
||||
writer.replace, {'doc:manifest': ''}));
|
||||
|
||||
|
||||
writesFuture.push(writer.copy('docs/src/templates/index.html', 'build/docs/index-debug.html',
|
||||
writesFuture.push(writer.copy('docs/src/templates/index.html', 'index-debug.html',
|
||||
writer.replace, {'doc:manifest': ''}));
|
||||
|
||||
writesFuture.push(writer.copy('docs/src/templates/index.html', 'build/docs/index-jq-debug.html',
|
||||
writesFuture.push(writer.copy('docs/src/templates/index.html', 'index-jq-debug.html',
|
||||
writer.replace, {'doc:manifest': ''}));
|
||||
|
||||
writesFuture.push(writer.copyTpl('offline.html'));
|
||||
writesFuture.push(writer.copyTpl('docs-scenario.html'));
|
||||
writesFuture.push(writer.copyTpl('jquery.min.js'));
|
||||
writesFuture.push(writer.copyTpl('js/jquery.min.js'));
|
||||
writesFuture.push(writer.copyTpl('js/jquery.js'));
|
||||
|
||||
writesFuture.push(writer.output('docs-keywords.js',
|
||||
writesFuture.push(writer.output('js/docs-keywords.js',
|
||||
['NG_PAGES=', JSON.stringify(metadata).replace(/{/g, '\n{'), ';']));
|
||||
writesFuture.push(writer.output('sitemap.xml', new SiteMap(docs).render()));
|
||||
writesFuture.push(writer.output('docs-scenario.js', ngdoc.scenarios(docs)));
|
||||
@@ -77,19 +77,10 @@ function writeTheRest(writesFuture) {
|
||||
writesFuture.push(writer.output('appcache.manifest',appCache()));
|
||||
writesFuture.push(writer.copyTpl('.htaccess'));
|
||||
|
||||
writesFuture.push(writer.merge(['docs.js',
|
||||
'doc_widgets.js'],
|
||||
'docs-combined.js'));
|
||||
writesFuture.push(writer.merge(['docs.css',
|
||||
'doc_widgets.css'],
|
||||
'docs-combined.css'));
|
||||
writesFuture.push(writer.merge(['syntaxhighlighter/shCore.js',
|
||||
'syntaxhighlighter/shBrushJScript.js',
|
||||
'syntaxhighlighter/shBrushXml.js'],
|
||||
'syntaxhighlighter/syntaxhighlighter-combined.js'));
|
||||
writesFuture.push(writer.merge(['syntaxhighlighter/shCore.css',
|
||||
'syntaxhighlighter/shThemeDefault.css'],
|
||||
'syntaxhighlighter/syntaxhighlighter-combined.css'));
|
||||
writesFuture.push(writer.copy('docs/src/templates/js/docs.js', 'js/docs.js'));
|
||||
|
||||
writesFuture.push(writer.copy('docs/src/templates/css/bootstrap.min.css', 'css/bootstrap.min.css'));
|
||||
writesFuture.push(writer.copy('docs/src/templates/css/docs.css', 'css/docs.css'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
var Showdown = require('../../lib/showdown').Showdown;
|
||||
var DOM = require('./dom.js').DOM;
|
||||
var htmlEscape = require('./dom.js').htmlEscape;
|
||||
var Example = require('./example.js').Example;
|
||||
var NEW_LINE = /\n\r?/;
|
||||
var globalID = 0;
|
||||
|
||||
exports.trim = trim;
|
||||
exports.metadata = metadata;
|
||||
@@ -98,72 +100,85 @@ Doc.prototype = {
|
||||
if (!text) return text;
|
||||
|
||||
var self = this,
|
||||
IS_URL = /^(https?:\/\/|ftps?:\/\/|mailto:|\.|\/)/,
|
||||
IS_ANGULAR = /^(api\/)?angular\./,
|
||||
IS_HASH = /^#/,
|
||||
parts = trim(text).split(/(<pre>[\s\S]*?<\/pre>|<doc:(\S*).*?>[\s\S]*?<\/doc:\2>)/);
|
||||
IS_URL = /^(https?:\/\/|ftps?:\/\/|mailto:|\.|\/)/,
|
||||
IS_ANGULAR = /^(api\/)?angular\./,
|
||||
IS_HASH = /^#/,
|
||||
parts = trim(text).split(/(<pre>[\s\S]*?<\/pre>|<doc:example(\S*).*?>[\s\S]*?<\/doc:example>|<example[^>]*>[\s\S]*?<\/example>)/),
|
||||
seq = 0,
|
||||
placeholderMap = {};
|
||||
|
||||
function placeholder(text) {
|
||||
var id = 'REPLACEME' + (seq++);
|
||||
placeholderMap[id] = text;
|
||||
return id;
|
||||
}
|
||||
|
||||
parts.forEach(function(text, i) {
|
||||
parts[i] = (text || '').
|
||||
replace(/<example(?:\s+module="([^"]*)")?(?:\s+deps="([^"]*)")?>([\s\S]*?)<\/example>/gmi, function(_, module, deps, content) {
|
||||
var example = new Example(self.scenarios);
|
||||
|
||||
function isDocWidget(name) {
|
||||
if ((i + 1) % 3 != 2) return false;
|
||||
if (name) return parts[i+1] == name;
|
||||
return !!parts[i+1];
|
||||
}
|
||||
example.setModule(module);
|
||||
example.addDeps(deps);
|
||||
content.replace(/<file\s+name="([^"]*)"\s*>([\s\S]*?)<\/file>/gmi, function(_, name, content) {
|
||||
example.addSource(name, content);
|
||||
});
|
||||
return placeholder(example.toHtml());
|
||||
}).
|
||||
replace(/^<doc:example(\s+[^>]*)?>([\s\S]*)<\/doc:example>/mi, function(_, attrs, content) {
|
||||
var html, script, scenario,
|
||||
example = new Example(self.scenarios);
|
||||
|
||||
// ignore each third item which is doc widget tag
|
||||
if (!((i + 1) % 3)) {
|
||||
parts[i] = '';
|
||||
return;
|
||||
}
|
||||
example.setModule((attrs||'module=""').match(/^\s*module=["'](.*)["']\s*$/)[1]);
|
||||
content.
|
||||
replace(/<doc:source(\s+[^>]*)?>([\s\S]*)<\/doc:source>/mi, function(_, attrs, content) {
|
||||
example.addSource('index.html', content.
|
||||
replace(/<script>([\s\S]*)<\/script>/mi, function(_, script) {
|
||||
example.addSource('script.js', script);
|
||||
return '';
|
||||
}).
|
||||
replace(/<style>([\s\S]*)<\/style>/mi, function(_, style) {
|
||||
example.addSource('style.css', style);
|
||||
return '';
|
||||
})
|
||||
);
|
||||
}).
|
||||
replace(/(<doc:scenario>)([\s\S]*)(<\/doc:scenario>)/mi, function(_, before, content){
|
||||
example.addSource('scenario.js', content);
|
||||
});
|
||||
|
||||
if (text.match(/^<pre>/)) {
|
||||
text = text.replace(/^<pre>([\s\S]*)<\/pre>/mi, function(_, content){
|
||||
var clazz = 'brush: js;';
|
||||
if (content.match(/\<\w/)) {
|
||||
// we are HTML
|
||||
clazz += ' html-script: true;';
|
||||
}
|
||||
return '<div ng:non-bindable><pre class="' + clazz +'">' +
|
||||
content.replace(/</g, '<').replace(/>/g, '>') +
|
||||
'</pre></div>';
|
||||
return placeholder(example.toHtml());
|
||||
}).
|
||||
replace(/^<pre>([\s\S]*?)<\/pre>/mi, function(_, content){
|
||||
return placeholder(
|
||||
'<pre class="prettyprint linenums">' +
|
||||
content.replace(/</g, '<').replace(/>/g, '>') +
|
||||
'</pre>');
|
||||
}).
|
||||
replace(/<div([^>]*)><\/div>/, '<div$1>\n<\/div>').
|
||||
replace(/{@link\s+([^\s}]+)\s*([^}]*?)\s*}/g, function(_all, url, title){
|
||||
var isFullUrl = url.match(IS_URL),
|
||||
isAngular = url.match(IS_ANGULAR),
|
||||
isHash = url.match(IS_HASH),
|
||||
absUrl = isHash
|
||||
? url
|
||||
: (isFullUrl ? url : self.convertUrlToAbsolute(url));
|
||||
|
||||
if (!isFullUrl) self.links.push(absUrl);
|
||||
|
||||
return '<a href="' + absUrl + '">' +
|
||||
(isAngular ? '<code>' : '') +
|
||||
(title || url).replace(/^#/g, '').replace(/\n/g, ' ') +
|
||||
(isAngular ? '</code>' : '') +
|
||||
'</a>';
|
||||
});
|
||||
} else if (isDocWidget('example')) {
|
||||
text = text.replace(/<doc:source(\s+[^>]*)?>([\s\S]*)<\/doc:source>/mi,
|
||||
function(_, attrs, content){
|
||||
return '<pre class="doc-source"' + (attrs || '') +'>' +
|
||||
htmlEscape(content) +
|
||||
'</pre>';
|
||||
});
|
||||
text = text.replace(/(<doc:scenario>)([\s\S]*)(<\/doc:scenario>)/mi,
|
||||
function(_, before, content){
|
||||
self.scenarios.push(content);
|
||||
return '<pre class="doc-scenario">' + htmlEscape(content) + '</pre>';
|
||||
});
|
||||
} else if (!isDocWidget()) {
|
||||
text = text.replace(/<angular\/>/gm, '<tt><angular/></tt>');
|
||||
text = text.replace(/{@link\s+([^\s}]+)\s*([^}]*?)\s*}/g,
|
||||
function(_all, url, title){
|
||||
var isFullUrl = url.match(IS_URL),
|
||||
isAngular = url.match(IS_ANGULAR),
|
||||
isHash = url.match(IS_HASH),
|
||||
absUrl = isHash
|
||||
? url
|
||||
: (isFullUrl ? url : self.convertUrlToAbsolute(url));
|
||||
|
||||
if (!isFullUrl) self.links.push(absUrl);
|
||||
|
||||
return '<a href="' + absUrl + '">' +
|
||||
(isAngular ? '<code>' : '') +
|
||||
(title || url).replace(/^#/g, '').replace(/\n/g, ' ') +
|
||||
(isAngular ? '</code>' : '') +
|
||||
'</a>';
|
||||
});
|
||||
text = new Showdown.converter().makeHtml(text);
|
||||
}
|
||||
parts[i] = text;
|
||||
});
|
||||
return parts.join('');
|
||||
text = parts.join('');
|
||||
text = new Showdown.converter().makeHtml(text);
|
||||
text = text.replace(/(?:<p>)?(REPLACEME\d+)(?:<\/p>)?/g, function(_, id) {
|
||||
return placeholderMap[id];
|
||||
});
|
||||
return text;
|
||||
},
|
||||
|
||||
parse: function() {
|
||||
@@ -200,7 +215,7 @@ Doc.prototype = {
|
||||
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
|
||||
// 1 12 2 34 4 5 5 6 6 3 7 7
|
||||
if (!match) {
|
||||
throw new Error("Not a valid 'param' format: " + text);
|
||||
}
|
||||
@@ -233,11 +248,11 @@ Doc.prototype = {
|
||||
throw new Error("Not a valid 'property' format: " + text);
|
||||
}
|
||||
var property = new Doc({
|
||||
type: match[1],
|
||||
name: match[2],
|
||||
shortName: match[2],
|
||||
description: self.markdown(text.replace(match[0], match[4]))
|
||||
});
|
||||
type: match[1],
|
||||
name: match[2],
|
||||
shortName: match[2],
|
||||
description: self.markdown(text.replace(match[0], match[4]))
|
||||
});
|
||||
self.properties.push(property);
|
||||
} else if(atName == 'eventType') {
|
||||
match = text.match(/^([^\s]*)\s+on\s+([\S\s]*)/);
|
||||
@@ -252,9 +267,9 @@ Doc.prototype = {
|
||||
|
||||
html: function() {
|
||||
var dom = new DOM(),
|
||||
self = this;
|
||||
self = this;
|
||||
|
||||
dom.h(this.name, function() {
|
||||
dom.h(title(this.name), function() {
|
||||
notice('deprecated', 'Deprecated API', self.deprecated);
|
||||
|
||||
if (self.ngdoc != 'overview') {
|
||||
@@ -336,9 +351,11 @@ Doc.prototype = {
|
||||
|
||||
html_usage_function: function(dom){
|
||||
var self = this;
|
||||
var name = self.name.match(/^angular(\.mock)?\.(\w+)$/) ? self.name : self.name.split(/\./).pop()
|
||||
|
||||
dom.h('Usage', function() {
|
||||
dom.code(function() {
|
||||
dom.text(self.name.split(/\./).pop());
|
||||
dom.text(name);
|
||||
dom.text('(');
|
||||
self.parameters(dom, ', ');
|
||||
dom.text(');');
|
||||
@@ -372,10 +389,10 @@ Doc.prototype = {
|
||||
dom.text(')');
|
||||
dom.code(function() {
|
||||
dom.text('<');
|
||||
dom.text(self.shortName);
|
||||
dom.text(dashCase(self.shortName));
|
||||
renderParams('\n ', '="', '"');
|
||||
dom.text('>\n</');
|
||||
dom.text(self.shortName);
|
||||
dom.text(dashCase(self.shortName));
|
||||
dom.text('>');
|
||||
});
|
||||
}
|
||||
@@ -384,7 +401,7 @@ Doc.prototype = {
|
||||
dom.text('as attribute');
|
||||
dom.code(function() {
|
||||
dom.text('<' + element + ' ');
|
||||
dom.text(self.shortName);
|
||||
dom.text(dashCase(self.shortName));
|
||||
renderParams('\n ', '="', '"', true);
|
||||
dom.text('>\n ...\n');
|
||||
dom.text('</' + element + '>');
|
||||
@@ -395,7 +412,7 @@ Doc.prototype = {
|
||||
var element = self.element || 'ANY';
|
||||
dom.code(function() {
|
||||
dom.text('<' + element + ' class="');
|
||||
dom.text(self.shortName);
|
||||
dom.text(dashCase(self.shortName));
|
||||
renderParams(' ', ': ', ';', true);
|
||||
dom.text('">\n ...\n');
|
||||
dom.text('</' + element + '>');
|
||||
@@ -467,7 +484,7 @@ Doc.prototype = {
|
||||
(self.param||[]).forEach(function(param){
|
||||
dom.text('\n ');
|
||||
dom.text(param.optional ? ' [' : ' ');
|
||||
dom.text(param.name);
|
||||
dom.text(dashCase(param.name));
|
||||
dom.text(BOOLEAN_ATTR[param.name] ? '' : '="{' + param.type + '}"');
|
||||
dom.text(param.optional ? ']' : '');
|
||||
});
|
||||
@@ -549,12 +566,12 @@ Doc.prototype = {
|
||||
dom.div({class:'member property'}, function(){
|
||||
dom.h('Properties', self.properties, function(property){
|
||||
dom.h(property.shortName, function() {
|
||||
dom.html(property.description);
|
||||
if (!property.html_usage_returns) {
|
||||
console.log(property);
|
||||
}
|
||||
property.html_usage_returns(dom);
|
||||
dom.h('Example', property.example, dom.html);
|
||||
dom.html(property.description);
|
||||
if (!property.html_usage_returns) {
|
||||
console.log(property);
|
||||
}
|
||||
property.html_usage_returns(dom);
|
||||
dom.h('Example', property.example, dom.html);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -605,6 +622,74 @@ Doc.prototype = {
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
var GLOBALS = /^angular\.([^\.]*)$/,
|
||||
MODULE = /^angular\.module\.([^\.]*)$/,
|
||||
MODULE_MOCK = /^angular\.mock\.([^\.]*)$/,
|
||||
MODULE_DIRECTIVE = /^angular\.module\.([^\.]*)(?:\.\$compileProvider)?\.directive\.([^\.]*)$/,
|
||||
MODULE_DIRECTIVE_INPUT = /^angular\.module\.([^\.]*)\.\$compileProvider\.directive\.input\.([^\.]*)$/,
|
||||
MODULE_FILTER = /^angular\.module\.([^\.]*)\.\$?filter\.([^\.]*)$/,
|
||||
MODULE_SERVICE = /^angular\.module\.([^\.]*)\.([^\.]*?)(Provider)?$/,
|
||||
MODULE_TYPE = /^angular\.module\.([^\.]*)\..*\.([A-Z][^\.]*)$/;
|
||||
|
||||
function title(text) {
|
||||
if (!text) return text;
|
||||
var match,
|
||||
module,
|
||||
type,
|
||||
name;
|
||||
|
||||
if (text == 'angular.Module') {
|
||||
module = 'ng';
|
||||
name = 'Module';
|
||||
type = 'Type';
|
||||
} else if (match = text.match(GLOBALS)) {
|
||||
module = 'ng';
|
||||
name = 'angular.' + match[1];
|
||||
type = 'API';
|
||||
} else if (match = text.match(MODULE)) {
|
||||
module = match[1];
|
||||
} else if (match = text.match(MODULE_MOCK)) {
|
||||
module = 'ng';
|
||||
name = 'angular.mock.' + match[1];
|
||||
type = 'API';
|
||||
} else if (match = text.match(MODULE_DIRECTIVE)) {
|
||||
module = match[1];
|
||||
name = match[2];
|
||||
type = 'directive';
|
||||
} else if (match = text.match(MODULE_DIRECTIVE_INPUT)) {
|
||||
module = match[1];
|
||||
name = 'input [' + match[2] + ']';
|
||||
type = 'directive';
|
||||
} else if (match = text.match(MODULE_FILTER)) {
|
||||
module = match[1];
|
||||
name = match[2];
|
||||
type = 'filter';
|
||||
} else if (match = text.match(MODULE_SERVICE)) {
|
||||
module = match[1];
|
||||
name = match[2] + (match[3] || '');
|
||||
type = 'service';
|
||||
} else if (match = text.match(MODULE_TYPE)) {
|
||||
module = match[1];
|
||||
name = match[2];
|
||||
type = 'type';
|
||||
} else {
|
||||
return text;
|
||||
}
|
||||
return function() {
|
||||
this.tag('code', name);
|
||||
this.tag('span', { class: 'hint'}, function() {
|
||||
if (type) {
|
||||
this.text('(');
|
||||
this.text(type);
|
||||
this.text(' in module ');
|
||||
this.tag('code', module);
|
||||
this.text(')');
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function scenarios(docs){
|
||||
var specs = [];
|
||||
|
||||
@@ -629,7 +714,7 @@ function scenarios(docs){
|
||||
specs.push(' });');
|
||||
specs.push(' ');
|
||||
doc.scenarios.forEach(function(scenario){
|
||||
specs.push(indent(trim(scenario), 4));
|
||||
specs.push(indentCode(trim(scenario), 4));
|
||||
specs.push('');
|
||||
});
|
||||
specs.push('});');
|
||||
@@ -647,13 +732,16 @@ function metadata(docs){
|
||||
for ( var i = 1; i < path.length; i++) {
|
||||
path.splice(i, 1);
|
||||
}
|
||||
var depth = path.length - 1;
|
||||
var shortName = path.pop();
|
||||
|
||||
if (path.pop() == 'input') {
|
||||
shortName = 'input [' + shortName + ']';
|
||||
}
|
||||
|
||||
words.push({
|
||||
section: doc.section,
|
||||
id: doc.id,
|
||||
name: doc.name,
|
||||
depth: depth,
|
||||
name: title(doc.name),
|
||||
shortName: shortName,
|
||||
type: doc.ngdoc,
|
||||
keywords:doc.keywords()
|
||||
@@ -669,16 +757,19 @@ var KEYWORD_PRIORITY = {
|
||||
'.angular': 7,
|
||||
'.angular.Module': 7,
|
||||
'.angular.module': 8,
|
||||
'.angular.module.ng.$filter': 7,
|
||||
'.angular.module.ng.$rootScope.Scope': 7,
|
||||
'.angular.module.ng': 7,
|
||||
'.angular.mock': 8,
|
||||
'.angular.directive': 6,
|
||||
'.angular.module.ngMock': 8,
|
||||
'.angular.module.ng': 2,
|
||||
'.angular.module.AUTO': 1,
|
||||
'.dev_guide.overview': 1,
|
||||
'.dev_guide.bootstrap': 2,
|
||||
'.dev_guide.bootstrap.auto_bootstrap': 1,
|
||||
'.dev_guide.bootstrap.manual_bootstrap': 2,
|
||||
'.dev_guide.mvc': 3,
|
||||
'.dev_guide.mvc.understanding_model': 1,
|
||||
'.dev_guide.mvc.understanding_controller': 2,
|
||||
'.dev_guide.mvc.understanding_view': 3,
|
||||
'.dev_guide.scopes': 4,
|
||||
'.dev_guide.scopes.understanding_scopes': 1,
|
||||
'.dev_guide.scopes.internals': 2,
|
||||
'.dev_guide.compiler': 5,
|
||||
'.dev_guide.templates': 6,
|
||||
'.dev_guide.services': 7,
|
||||
@@ -711,7 +802,7 @@ function trim(text) {
|
||||
var minIndent = MAX_INDENT;
|
||||
var indentRegExp;
|
||||
var ignoreLine = (lines[0][0] != ' ' && lines.length > 1);
|
||||
// ignore first line if it has no indentation and there is more than one line
|
||||
// ignore first line if it has no indentation and there is more than one line
|
||||
|
||||
lines.forEach(function(line){
|
||||
if (ignoreLine) {
|
||||
@@ -743,10 +834,10 @@ function trim(text) {
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function indent(text, spaceCount) {
|
||||
function indentCode(text, spaceCount) {
|
||||
var lines = text.split('\n'),
|
||||
indent = '',
|
||||
fixedLines = [];
|
||||
indent = '',
|
||||
fixedLines = [];
|
||||
|
||||
while(spaceCount--) indent += ' ';
|
||||
|
||||
@@ -795,7 +886,7 @@ function merge(docs){
|
||||
var parent = byFullId['api/' + parentName];
|
||||
if (!parent)
|
||||
throw new Error("No parent named '" + parentName + "' for '" +
|
||||
doc.name + "' in @" + name + "Of.");
|
||||
doc.name + "' in @" + name + "Of.");
|
||||
|
||||
var listName = (name + 's').replace(/ys$/, 'ies');
|
||||
var list = parent[listName] = (parent[listName] || []);
|
||||
@@ -815,3 +906,11 @@ function property(name) {
|
||||
return value[name];
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
var DASH_CASE_REGEXP = /[A-Z]/g;
|
||||
function dashCase(name){
|
||||
return name.replace(DASH_CASE_REGEXP, function(letter, pos) {
|
||||
return (pos ? '-' : '') + letter.toLowerCase();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,668 +0,0 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
|
||||
body {
|
||||
background: #000 url("../images/texture_1.png");
|
||||
}
|
||||
|
||||
/*----- Layout Generic Styles -----*/
|
||||
|
||||
body,td,th {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 14px;
|
||||
color: #000;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
a:link {
|
||||
color: #5d6db6;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration: none;
|
||||
color: #7989D6;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
color: #5d6db6;
|
||||
}
|
||||
a:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
padding-right: 10px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
.h1 {
|
||||
font-size: 24px;
|
||||
color: #000;
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.h2 {
|
||||
font-size: 18px;
|
||||
text-align: left;
|
||||
text-indent: 10px;
|
||||
}
|
||||
|
||||
ul {
|
||||
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
list-style: url(images/bullet.png) outside;
|
||||
}
|
||||
|
||||
li {
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
list-style: url(../images/bullet.png) outside;
|
||||
}
|
||||
|
||||
|
||||
/*----- Global Layout -----*/
|
||||
|
||||
.twoCol #container {
|
||||
width: 1050px;
|
||||
text-align: left;
|
||||
margin: 0px auto;
|
||||
background: #FC0 url(../images/yellow_bkgnd.jpg) no-repeat 0px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.home #container {
|
||||
width: 1050px;
|
||||
text-align: left;
|
||||
margin: 0px auto;
|
||||
background: #FC0 url(../images/yellow_bkgnd.jpg) no-repeat;
|
||||
top: 0px;
|
||||
height: 1650px;
|
||||
}
|
||||
#homeFooter {
|
||||
float: right;
|
||||
height: 30px;
|
||||
width: 275px;
|
||||
margin-top: 80px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#twoColFooter {
|
||||
font-size: 12px;
|
||||
height: 30px;
|
||||
width: 275px;
|
||||
padding-left: 20px;
|
||||
margin-left: 700px;
|
||||
position: relative;
|
||||
bottom: 30px;
|
||||
margin-top: 100px;
|
||||
}
|
||||
|
||||
#navigationHome {
|
||||
background: #7989D6;
|
||||
height: 50px;
|
||||
width: 1050px;
|
||||
position: relative;
|
||||
top: 0px;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
#navigation {
|
||||
height: 50px;
|
||||
width: 1050px;
|
||||
position: fixed;
|
||||
z-index: 10;
|
||||
top: 0px;
|
||||
background: #7989D6;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*----- navigation styles -----*/
|
||||
|
||||
#navContainer {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
width: 1050px;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
#navContainer ul {
|
||||
margin:0;
|
||||
list-style:none;
|
||||
}
|
||||
|
||||
.navContainerStyle li{
|
||||
display: inline;
|
||||
list-style-type: none;
|
||||
padding-right:30px;
|
||||
font-size: 17px;
|
||||
color: #FFF;
|
||||
text-decoration: none;
|
||||
padding-left: 35px;
|
||||
}
|
||||
|
||||
.navContainerStyle li a:link, .navContainerStyle li a:visited{
|
||||
display:inline;
|
||||
font-size: 1em;
|
||||
font-weight:300;
|
||||
color:#FFF;
|
||||
text-decoration:none;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
.navContainerStyle li a:hover{
|
||||
color:#000
|
||||
}
|
||||
|
||||
.navContainerStyle li a.current, .navContainerStyle li a.current:hover, .navContainerStyle a.current:active {
|
||||
color: #FFF;
|
||||
cursor:auto;
|
||||
background:#000;
|
||||
-moz-border-radius: 15px;
|
||||
border-radius: 15px;
|
||||
-webkit-border-radius:15px;
|
||||
}
|
||||
|
||||
/*------ sidebar styles ------*/
|
||||
|
||||
#left {
|
||||
float: left;
|
||||
height: 1080px;
|
||||
width: 445px;
|
||||
margin-left: 40px;
|
||||
}
|
||||
|
||||
.twoCol #sidebar{
|
||||
float:left;
|
||||
width:245px;
|
||||
padding:0;
|
||||
margin-top: 120px;
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
#sidebarTop {
|
||||
height:49px;
|
||||
width: 235px;
|
||||
background: #7989D6;
|
||||
-moz-box-shadow: 4px 4px 6px #48577D;
|
||||
-moz-border-radius-topright: 15px;
|
||||
box-shadow: 4px 4px 6px #48577D;
|
||||
border-radius-topright: 15px;
|
||||
-webkit-box-shadow: 4px 4px 6px #48577D;
|
||||
-webkit-border-top-right-radius: 15px;
|
||||
border-top-right-radius: 15px;
|
||||
|
||||
}
|
||||
|
||||
#sidebarArea {
|
||||
width: 234px;
|
||||
top: 0px;
|
||||
padding-bottom: 1px;
|
||||
background: #FFF;
|
||||
-moz-box-shadow: 4px 4px 6px #48577D;
|
||||
-moz-border-radius-bottomright: 15px;
|
||||
box-shadow: 4px 4px 6px #48577D;
|
||||
border-radius-bottomright: 15px;
|
||||
-webkit-box-shadow: 4px 4px 6px #48577D;
|
||||
-webkit-border-bottom-right-radius: 15px;
|
||||
border-bottom-right-radius: 15px;
|
||||
|
||||
}
|
||||
|
||||
#sideBarContent1 {
|
||||
width: 200px;
|
||||
padding-top: 20px;
|
||||
padding-right: 20px;
|
||||
position: relative;
|
||||
padding-left: 10px;
|
||||
|
||||
}
|
||||
|
||||
#sidebarBottom {
|
||||
background: url(../images/sidebarBottom.png) no-repeat top;
|
||||
width: 234px;
|
||||
margin-bottom: 20px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
|
||||
/*----- textbox content -----*/
|
||||
|
||||
#textbox {
|
||||
float:right;
|
||||
width: 729px;
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
margin-top: 120px;
|
||||
margin-right: 35px; /*margin: 120px 40px 80px 278px;*/
|
||||
}
|
||||
|
||||
#textboxTop {
|
||||
position: relative;
|
||||
height: 49px;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
background: #7989D6;
|
||||
width: 729px;
|
||||
-moz-box-shadow: 4px 4px 6px #48577D;
|
||||
-moz-border-radius-topright: 15px;
|
||||
-moz-border-radius-topleft: 15px;
|
||||
box-shadow: 4px 4px 6px #48577D;
|
||||
border-radius-topright: 15px;
|
||||
border-radius-topleft: 15px;
|
||||
-webkit-box-shadow: 4px 4px 6px #48577D;
|
||||
-webkit-border-top-right-radius: 15px;
|
||||
-webkit-border-top-left-radius: 15px;
|
||||
border-top-right-radius: 15px;
|
||||
border-top-left-radius: 15px;
|
||||
}
|
||||
|
||||
#textbox_content {
|
||||
padding: 20px 20px 5px;
|
||||
width: 685px;
|
||||
position: relative;
|
||||
background: #FFF;
|
||||
-moz-box-shadow: 4px 4px 6px #48577D;
|
||||
-moz-border-radius-bottomright: 15px;
|
||||
-moz-border-radius-bottomleft: 15px;
|
||||
box-shadow: 4px 4px 6px #48577D;
|
||||
border-radius-bottomright: 15px;
|
||||
border-radius-bottomleft: 15px;
|
||||
-webkit-box-shadow: 4px 4px 6px #48577D;
|
||||
-webkit-border-radius-bottomright: 15px;
|
||||
-webkit-border-radius-bottomleft: 15px;
|
||||
border-right: 5px solid #FFF;
|
||||
border-bottom: 5px solid #FFF;
|
||||
-webkit-border-bottom-right-radius: 15px;
|
||||
-webkit-border-bottom-left-radius: 15px;
|
||||
border-bottom-right-radius: 15px;
|
||||
border-bottom-left-radius: 15px;
|
||||
}
|
||||
|
||||
.textboxContentHeader {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 28px;
|
||||
color: #FFF;
|
||||
padding: 9px 8px 8px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
|
||||
/*----- home page -----*/
|
||||
|
||||
#top {
|
||||
height: 400px;
|
||||
width: 750px;
|
||||
clear: both;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#logo {
|
||||
height: 72px;
|
||||
width: 440px;
|
||||
margin-top: 50px;
|
||||
margin-left: 45px;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
font-size: 68px;
|
||||
color: #7989D6;
|
||||
font-weight: 400;
|
||||
color:#7690CF;
|
||||
text-shadow:1.5px 1.5px 1.5px #48577D;
|
||||
text-shadow:#48577D 1.5px 1.5px 1.5px;
|
||||
letter-spacing: 1.5px;
|
||||
}
|
||||
|
||||
#twitterWidget {
|
||||
float: right;
|
||||
height: 300px;
|
||||
width: 250px;
|
||||
top: 0px;
|
||||
margin-top: 45px;
|
||||
margin-right: 40px;
|
||||
|
||||
}
|
||||
|
||||
#tagline {
|
||||
height: 120px;
|
||||
width: 600px;
|
||||
margin-left: 93px;
|
||||
clear: both;
|
||||
float: left;
|
||||
margin-top: 10px;
|
||||
font-size: 40px;
|
||||
color: #FFF;
|
||||
font-weight: bold;
|
||||
text-shadow:1.5px 1.5px 1.5px #48577D;
|
||||
text-shadow:#48577D 1.5px 1.5px 1.5px;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*----- buttons -----*/
|
||||
|
||||
#buttons {
|
||||
clear: both;
|
||||
float: left;
|
||||
height: 72px;
|
||||
width: 675px;
|
||||
margin-left: 40px;
|
||||
margin-top: 35px;
|
||||
|
||||
}
|
||||
|
||||
#downloadButton {
|
||||
clear: both;
|
||||
float: left;
|
||||
height: 72px;
|
||||
width: 230px;
|
||||
background: #FFF url(../images/download_arrow.png) no-repeat 15px 5px;
|
||||
-moz-box-shadow: 4px 4px 6px #48577D;
|
||||
-moz-border-radius: 10px;
|
||||
box-shadow: 4px 4px 6px #48577D;
|
||||
border-radius: 10px;
|
||||
-webkit-box-shadow: 4px 4px 6px #48577D;
|
||||
-webkit-border-radius:10px;
|
||||
}
|
||||
|
||||
#downloadText {
|
||||
padding-top: 7px;
|
||||
padding-left: 58px;
|
||||
width: 170px;
|
||||
}
|
||||
|
||||
#communityButton {
|
||||
float: right;
|
||||
height: 72px;
|
||||
width: 230px;
|
||||
background: #FFF url(../images/community_icon.png) no-repeat 147px 8px;
|
||||
-moz-box-shadow: 4px 4px 6px #48577D;
|
||||
-moz-border-radius: 10px;
|
||||
box-shadow: 4px 4px 6px #48577D;
|
||||
border-radius: 10px;
|
||||
-webkit-box-shadow: 4px 4px 6px #48577D;
|
||||
-webkit-border-radius:10px;
|
||||
}
|
||||
|
||||
#CommunityText {
|
||||
width: 150px;
|
||||
padding-top: 7px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
|
||||
.smallLinks {
|
||||
font-size: 10px;
|
||||
line-height: 14px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.smallLinks a:link {
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
.smallLinks a:visited {
|
||||
text-decoration: none;
|
||||
color: #000;
|
||||
}
|
||||
.smallLinks a:hover {
|
||||
text-decoration: underline;
|
||||
color: #7989D6;
|
||||
}
|
||||
.smallLinks a:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
.blueText {
|
||||
font-size: 13px;
|
||||
color: #7989D6;
|
||||
}
|
||||
.ButtonHeadings {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
/*----- Icon Divs ----- */
|
||||
|
||||
#icons {
|
||||
background: #FFF;
|
||||
width: 410px;
|
||||
clear: both;
|
||||
float: left;
|
||||
height: 520px;
|
||||
width: 410px;
|
||||
padding-top: 20px;
|
||||
-moz-box-shadow: 4px 4px 6px #48577D;
|
||||
-moz-border-radius: 15px;
|
||||
box-shadow: 4px 4px 6px #48577D;
|
||||
border-radius: 15px;
|
||||
-webkit-box-shadow: 4px 4px 6px #48577D;
|
||||
-webkit-border-radius:15px;
|
||||
}
|
||||
|
||||
#iconWrap{
|
||||
clear: right;
|
||||
float: left;
|
||||
height: 500px;
|
||||
width: 110px;
|
||||
margin-left: 30px;
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
#templatesDescription {
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
height: 95px;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
#Descriptions {
|
||||
float: left;
|
||||
height: 500px;
|
||||
width: 230px;
|
||||
padding-top: 16px;
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
#UItemplatesDescription {
|
||||
height: 95px;
|
||||
width: 230px;
|
||||
margin-bottom: 25px;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
#two-wayDescription {
|
||||
height: 85px;
|
||||
width: 230px;
|
||||
margin-bottom: 10px;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
#frameworkDescription {
|
||||
height: 95px;
|
||||
width: 230px;
|
||||
margin-bottom: 25px;
|
||||
padding-top: 7px;
|
||||
}
|
||||
|
||||
#mvcDescription {
|
||||
height: 75px;
|
||||
width: 230px;
|
||||
padding-top: 18px;
|
||||
}
|
||||
|
||||
#templateIcon {
|
||||
height: 90px;
|
||||
width: 95px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#two-wayIcon {
|
||||
height: 90px;
|
||||
width: 95px;
|
||||
|
||||
position: relative;
|
||||
}
|
||||
#frameworkIcon {
|
||||
height: 90px;
|
||||
width: 95px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#mvcIcon {
|
||||
height: 90px;
|
||||
width: 95px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#templatesLink {
|
||||
line-height: 12px;
|
||||
height: 28px;
|
||||
width: 95px;
|
||||
margin-bottom: 5px;
|
||||
padding-top: 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#two-wayLink {
|
||||
line-height: 12px;
|
||||
height: 28px;
|
||||
width: 95px;
|
||||
margin-bottom: 5px;
|
||||
padding-top: 3px;
|
||||
text-align: center;
|
||||
}
|
||||
#frameworkLink {
|
||||
line-height: 12px;
|
||||
height: 20px;
|
||||
width: 95px;
|
||||
margin-bottom: 5px;
|
||||
padding-top: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
#mvcLink {
|
||||
line-height: 12px;
|
||||
height: 28px;
|
||||
width: 95px;
|
||||
margin-bottom: 5px;
|
||||
padding-top: 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
/*----- What you need divs ---*/
|
||||
|
||||
#whatYouNeed {
|
||||
clear: both;
|
||||
float: left;
|
||||
margin-top: 35px;
|
||||
margin-bottom: 35px;
|
||||
background: #FFF;
|
||||
height: 240px;
|
||||
width: 410px;
|
||||
padding-top: 20px;
|
||||
-moz-box-shadow: 4px 4px 6px #48577D;
|
||||
-moz-border-radius: 15px;
|
||||
box-shadow: 4px 4px 6px #48577D;
|
||||
border-radius: 15px;
|
||||
-webkit-box-shadow: 4px 4px 6px #48577D;
|
||||
-webkit-border-radius:15px;
|
||||
}
|
||||
|
||||
#listText {
|
||||
width: 350px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
/*----- testimonial divs ---*/
|
||||
|
||||
|
||||
#testimonials {
|
||||
background: #FFF;
|
||||
height: 175px;
|
||||
width: 390px;
|
||||
clear: both;
|
||||
float: left;
|
||||
padding-top: 20px;
|
||||
padding-left: 20px;
|
||||
-moz-box-shadow: 4px 4px 6px #48577D;
|
||||
-moz-border-radius: 15px;
|
||||
box-shadow: 4px 4px 6px #48577D;
|
||||
border-radius: 15px;
|
||||
-webkit-box-shadow: 4px 4px 6px #48577D;
|
||||
-webkit-border-radius:15px;
|
||||
|
||||
}
|
||||
|
||||
#testimonialsText {
|
||||
width: 340px;
|
||||
margin-left: 20px;
|
||||
padding-top: 15px;
|
||||
|
||||
}
|
||||
|
||||
#sigName {
|
||||
width: 340px;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------ Live Example Divs ------ */
|
||||
|
||||
#right {
|
||||
width: 525px;
|
||||
clear: right;
|
||||
float: left;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#live {
|
||||
width: 525px;
|
||||
position: relative;
|
||||
margin-top: 370px;
|
||||
float: left;
|
||||
|
||||
}
|
||||
|
||||
|
||||
#LiveExText {
|
||||
font-size: 14px;
|
||||
background: #FFF;
|
||||
width: 475px;
|
||||
height: 1045px;
|
||||
padding-top: 20px;
|
||||
padding-left: 25px;
|
||||
padding-right: 25px;
|
||||
-moz-box-shadow: 4px 4px 6px #48577D;
|
||||
-moz-border-radius: 15px;
|
||||
box-shadow: 4px 4px 6px #48577D;
|
||||
border-radius: 15px;
|
||||
-webkit-box-shadow: 4px 4px 6px #48577D;
|
||||
-webkit-border-radius:15px;
|
||||
|
||||
}
|
||||
|
||||
#Example {
|
||||
font-size: 14px;
|
||||
width: 480px;
|
||||
padding-right: 25px;
|
||||
padding-top: 35px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
/*-----float clearing---*/
|
||||
|
||||
.clearFloat {
|
||||
clear: both;
|
||||
height:0px;
|
||||
font-size:1px;
|
||||
line-height:0px;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,689 @@
|
||||
article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;}
|
||||
audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}
|
||||
audio:not([controls]){display:none;}
|
||||
html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}
|
||||
a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
|
||||
a:hover,a:active{outline:0;}
|
||||
sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;}
|
||||
sup{top:-0.5em;}
|
||||
sub{bottom:-0.25em;}
|
||||
img{height:auto;border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;}
|
||||
button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;}
|
||||
button,input{*overflow:visible;line-height:normal;}
|
||||
button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;}
|
||||
button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;}
|
||||
input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;}
|
||||
input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;}
|
||||
textarea{overflow:auto;vertical-align:top;}
|
||||
.clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";}
|
||||
.clearfix:after{clear:both;}
|
||||
.hide-text{overflow:hidden;text-indent:100%;white-space:nowrap;}
|
||||
.input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;}
|
||||
body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#333333;background-color:#ffffff;}
|
||||
a{color:#0088cc;text-decoration:none;}
|
||||
a:hover{color:#005580;text-decoration:underline;}
|
||||
.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";}
|
||||
.row:after{clear:both;}
|
||||
[class*="span"]{float:left;margin-left:20px;}
|
||||
.container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
|
||||
.span12{width:940px;}
|
||||
.span11{width:860px;}
|
||||
.span10{width:780px;}
|
||||
.span9{width:700px;}
|
||||
.span8{width:620px;}
|
||||
.span7{width:540px;}
|
||||
.span6{width:460px;}
|
||||
.span5{width:380px;}
|
||||
.span4{width:300px;}
|
||||
.span3{width:220px;}
|
||||
.span2{width:140px;}
|
||||
.span1{width:60px;}
|
||||
.offset12{margin-left:980px;}
|
||||
.offset11{margin-left:900px;}
|
||||
.offset10{margin-left:820px;}
|
||||
.offset9{margin-left:740px;}
|
||||
.offset8{margin-left:660px;}
|
||||
.offset7{margin-left:580px;}
|
||||
.offset6{margin-left:500px;}
|
||||
.offset5{margin-left:420px;}
|
||||
.offset4{margin-left:340px;}
|
||||
.offset3{margin-left:260px;}
|
||||
.offset2{margin-left:180px;}
|
||||
.offset1{margin-left:100px;}
|
||||
.row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";}
|
||||
.row-fluid:after{clear:both;}
|
||||
.row-fluid>[class*="span"]{float:left;margin-left:2.127659574%;}
|
||||
.row-fluid>[class*="span"]:first-child{margin-left:0;}
|
||||
.row-fluid > .span12{width:99.99999998999999%;}
|
||||
.row-fluid > .span11{width:91.489361693%;}
|
||||
.row-fluid > .span10{width:82.97872339599999%;}
|
||||
.row-fluid > .span9{width:74.468085099%;}
|
||||
.row-fluid > .span8{width:65.95744680199999%;}
|
||||
.row-fluid > .span7{width:57.446808505%;}
|
||||
.row-fluid > .span6{width:48.93617020799999%;}
|
||||
.row-fluid > .span5{width:40.425531911%;}
|
||||
.row-fluid > .span4{width:31.914893614%;}
|
||||
.row-fluid > .span3{width:23.404255317%;}
|
||||
.row-fluid > .span2{width:14.89361702%;}
|
||||
.row-fluid > .span1{width:6.382978723%;}
|
||||
.container{margin-left:auto;margin-right:auto;*zoom:1;}.container:before,.container:after{display:table;content:"";}
|
||||
.container:after{clear:both;}
|
||||
.container-fluid{padding-left:20px;padding-right:20px;*zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";}
|
||||
.container-fluid:after{clear:both;}
|
||||
p{margin:0 0 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;}p small{font-size:11px;color:#999999;}
|
||||
.lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px;}
|
||||
h1,h2,h3,h4,h5,h6{margin:0;font-family:inherit;font-weight:bold;color:inherit;text-rendering:optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999999;}
|
||||
h1{font-size:30px;line-height:36px;}h1 small{font-size:18px;}
|
||||
h2{font-size:24px;line-height:36px;}h2 small{font-size:18px;}
|
||||
h3{line-height:27px;font-size:18px;}h3 small{font-size:14px;}
|
||||
h4,h5,h6{line-height:18px;}
|
||||
h4{font-size:14px;}h4 small{font-size:12px;}
|
||||
h5{font-size:12px;}
|
||||
h6{font-size:11px;color:#999999;text-transform:uppercase;}
|
||||
.page-header{padding-bottom:17px;margin:18px 0;border-bottom:1px solid #eeeeee;}
|
||||
.page-header h1{line-height:1;}
|
||||
ul,ol{padding:0;margin:0 0 9px 25px;}
|
||||
ul ul,ul ol,ol ol,ol ul{margin-bottom:0;}
|
||||
ul{list-style:disc;}
|
||||
ol{list-style:decimal;}
|
||||
li{line-height:18px;}
|
||||
ul.unstyled,ol.unstyled{margin-left:0;list-style:none;}
|
||||
dl{margin-bottom:18px;}
|
||||
dt,dd{line-height:18px;}
|
||||
dt{font-weight:bold;line-height:17px;}
|
||||
dd{margin-left:9px;}
|
||||
.dl-horizontal dt{float:left;clear:left;width:120px;text-align:right;}
|
||||
.dl-horizontal dd{margin-left:130px;}
|
||||
hr{margin:18px 0;border:0;border-top:1px solid #eeeeee;border-bottom:1px solid #ffffff;}
|
||||
strong{font-weight:bold;}
|
||||
em{font-style:italic;}
|
||||
.muted{color:#999999;}
|
||||
abbr[title]{border-bottom:1px dotted #ddd;cursor:help;}
|
||||
abbr.initialism{font-size:90%;text-transform:uppercase;}
|
||||
blockquote{padding:0 0 0 15px;margin:0 0 18px;border-left:5px solid #eeeeee;}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:22.5px;}
|
||||
blockquote small{display:block;line-height:18px;color:#999999;}blockquote small:before{content:'\2014 \00A0';}
|
||||
blockquote.pull-right{float:right;padding-left:0;padding-right:15px;border-left:0;border-right:5px solid #eeeeee;}blockquote.pull-right p,blockquote.pull-right small{text-align:right;}
|
||||
q:before,q:after,blockquote:before,blockquote:after{content:"";}
|
||||
address{display:block;margin-bottom:18px;line-height:18px;font-style:normal;}
|
||||
small{font-size:100%;}
|
||||
cite{font-style:normal;}
|
||||
code,pre{padding:0 3px 2px;font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
|
||||
code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;}
|
||||
pre{display:block;padding:8.5px;margin:0 0 9px;font-size:12.025px;line-height:18px;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;white-space:pre;white-space:pre-wrap;word-break:break-all;word-wrap:break-word;}pre.prettyprint{margin-bottom:18px;}
|
||||
pre code{padding:0;color:inherit;background-color:transparent;border:0;}
|
||||
.pre-scrollable{max-height:340px;overflow-y:scroll;}
|
||||
form{margin:0 0 18px;}
|
||||
fieldset{padding:0;margin:0;border:0;}
|
||||
legend{display:block;width:100%;padding:0;margin-bottom:27px;font-size:19.5px;line-height:36px;color:#333333;border:0;border-bottom:1px solid #eee;}legend small{font-size:13.5px;color:#999999;}
|
||||
label,input,button,select,textarea{font-size:13px;font-weight:normal;line-height:18px;}
|
||||
input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;}
|
||||
label{display:block;margin-bottom:5px;color:#333333;}
|
||||
input,textarea,select,.uneditable-input{display:inline-block;width:210px;height:18px;padding:4px;margin-bottom:9px;font-size:13px;line-height:18px;color:#555555;border:1px solid #cccccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
|
||||
.uneditable-textarea{width:auto;height:auto;}
|
||||
label input,label textarea,label select{display:block;}
|
||||
input[type="image"],input[type="checkbox"],input[type="radio"]{width:auto;height:auto;padding:0;margin:3px 0;*margin-top:0;line-height:normal;cursor:pointer;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;border:0 \9;}
|
||||
input[type="image"]{border:0;}
|
||||
input[type="file"]{width:auto;padding:initial;line-height:initial;border:initial;background-color:#ffffff;background-color:initial;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
|
||||
input[type="button"],input[type="reset"],input[type="submit"]{width:auto;height:auto;}
|
||||
select,input[type="file"]{height:28px;*margin-top:4px;line-height:28px;}
|
||||
input[type="file"]{line-height:18px \9;}
|
||||
select{width:220px;background-color:#ffffff;}
|
||||
select[multiple],select[size]{height:auto;}
|
||||
input[type="image"]{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
|
||||
textarea{height:auto;}
|
||||
input[type="hidden"]{display:none;}
|
||||
.radio,.checkbox{padding-left:18px;}
|
||||
.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-18px;}
|
||||
.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px;}
|
||||
.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle;}
|
||||
.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px;}
|
||||
input,textarea{-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-webkit-transition:border linear 0.2s,box-shadow linear 0.2s;-moz-transition:border linear 0.2s,box-shadow linear 0.2s;-ms-transition:border linear 0.2s,box-shadow linear 0.2s;-o-transition:border linear 0.2s,box-shadow linear 0.2s;transition:border linear 0.2s,box-shadow linear 0.2s;}
|
||||
input:focus,textarea:focus{border-color:rgba(82, 168, 236, 0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);outline:0;outline:thin dotted \9;}
|
||||
input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus,select:focus{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
|
||||
.input-mini{width:60px;}
|
||||
.input-small{width:90px;}
|
||||
.input-medium{width:150px;}
|
||||
.input-large{width:210px;}
|
||||
.input-xlarge{width:270px;}
|
||||
.input-xxlarge{width:530px;}
|
||||
input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{float:none;margin-left:0;}
|
||||
input,textarea,.uneditable-input{margin-left:0;}
|
||||
input.span12, textarea.span12, .uneditable-input.span12{width:930px;}
|
||||
input.span11, textarea.span11, .uneditable-input.span11{width:850px;}
|
||||
input.span10, textarea.span10, .uneditable-input.span10{width:770px;}
|
||||
input.span9, textarea.span9, .uneditable-input.span9{width:690px;}
|
||||
input.span8, textarea.span8, .uneditable-input.span8{width:610px;}
|
||||
input.span7, textarea.span7, .uneditable-input.span7{width:530px;}
|
||||
input.span6, textarea.span6, .uneditable-input.span6{width:450px;}
|
||||
input.span5, textarea.span5, .uneditable-input.span5{width:370px;}
|
||||
input.span4, textarea.span4, .uneditable-input.span4{width:290px;}
|
||||
input.span3, textarea.span3, .uneditable-input.span3{width:210px;}
|
||||
input.span2, textarea.span2, .uneditable-input.span2{width:130px;}
|
||||
input.span1, textarea.span1, .uneditable-input.span1{width:50px;}
|
||||
input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{background-color:#eeeeee;border-color:#ddd;cursor:not-allowed;}
|
||||
.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853;}
|
||||
.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853;border-color:#c09853;}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:0 0 6px #dbc59e;-moz-box-shadow:0 0 6px #dbc59e;box-shadow:0 0 6px #dbc59e;}
|
||||
.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853;}
|
||||
.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48;}
|
||||
.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48;border-color:#b94a48;}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:0 0 6px #d59392;-moz-box-shadow:0 0 6px #d59392;box-shadow:0 0 6px #d59392;}
|
||||
.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48;}
|
||||
.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847;}
|
||||
.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847;border-color:#468847;}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:0 0 6px #7aba7b;-moz-box-shadow:0 0 6px #7aba7b;box-shadow:0 0 6px #7aba7b;}
|
||||
.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847;}
|
||||
input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b;}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7;}
|
||||
.form-actions{padding:17px 20px 18px;margin-top:18px;margin-bottom:18px;background-color:#eeeeee;border-top:1px solid #ddd;*zoom:1;}.form-actions:before,.form-actions:after{display:table;content:"";}
|
||||
.form-actions:after{clear:both;}
|
||||
.uneditable-input{display:block;background-color:#ffffff;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);cursor:not-allowed;}
|
||||
:-moz-placeholder{color:#999999;}
|
||||
::-webkit-input-placeholder{color:#999999;}
|
||||
.help-block,.help-inline{color:#555555;}
|
||||
.help-block{display:block;margin-bottom:9px;}
|
||||
.help-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;padding-left:5px;}
|
||||
.input-prepend,.input-append{margin-bottom:5px;}.input-prepend input,.input-append input,.input-prepend select,.input-append select,.input-prepend .uneditable-input,.input-append .uneditable-input{*margin-left:0;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}.input-prepend input:focus,.input-append input:focus,.input-prepend select:focus,.input-append select:focus,.input-prepend .uneditable-input:focus,.input-append .uneditable-input:focus{position:relative;z-index:2;}
|
||||
.input-prepend .uneditable-input,.input-append .uneditable-input{border-left-color:#ccc;}
|
||||
.input-prepend .add-on,.input-append .add-on{display:inline-block;width:auto;min-width:16px;height:18px;padding:4px 5px;font-weight:normal;line-height:18px;text-align:center;text-shadow:0 1px 0 #ffffff;vertical-align:middle;background-color:#eeeeee;border:1px solid #ccc;}
|
||||
.input-prepend .add-on,.input-append .add-on,.input-prepend .btn,.input-append .btn{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
|
||||
.input-prepend .active,.input-append .active{background-color:#a9dba9;border-color:#46a546;}
|
||||
.input-prepend .add-on,.input-prepend .btn{margin-right:-1px;}
|
||||
.input-append input,.input-append select .uneditable-input{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
|
||||
.input-append .uneditable-input{border-left-color:#eee;border-right-color:#ccc;}
|
||||
.input-append .add-on,.input-append .btn{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
|
||||
.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
|
||||
.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
|
||||
.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
|
||||
.search-query{padding-left:14px;padding-right:14px;margin-bottom:0;-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px;}
|
||||
.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;margin-bottom:0;}
|
||||
.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none;}
|
||||
.form-search label,.form-inline label{display:inline-block;}
|
||||
.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0;}
|
||||
.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle;}
|
||||
.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-left:0;margin-right:3px;}
|
||||
.control-group{margin-bottom:9px;}
|
||||
legend+.control-group{margin-top:18px;-webkit-margin-top-collapse:separate;}
|
||||
.form-horizontal .control-group{margin-bottom:18px;*zoom:1;}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:"";}
|
||||
.form-horizontal .control-group:after{clear:both;}
|
||||
.form-horizontal .control-label{float:left;width:140px;padding-top:5px;text-align:right;}
|
||||
.form-horizontal .controls{margin-left:160px;*display:inline-block;*margin-left:0;*padding-left:20px;}
|
||||
.form-horizontal .help-block{margin-top:9px;margin-bottom:0;}
|
||||
.form-horizontal .form-actions{padding-left:160px;}
|
||||
table{max-width:100%;border-collapse:collapse;border-spacing:0;background-color:transparent;}
|
||||
.table{width:100%;margin-bottom:18px;}.table th,.table td{padding:8px;line-height:18px;text-align:left;vertical-align:top;border-top:1px solid #dddddd;}
|
||||
.table th{font-weight:bold;}
|
||||
.table thead th{vertical-align:bottom;}
|
||||
.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0;}
|
||||
.table tbody+tbody{border-top:2px solid #dddddd;}
|
||||
.table-condensed th,.table-condensed td{padding:4px 5px;}
|
||||
.table-bordered{border:1px solid #dddddd;border-left:0;border-collapse:separate;*border-collapse:collapsed;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.table-bordered th,.table-bordered td{border-left:1px solid #dddddd;}
|
||||
.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0;}
|
||||
.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-radius:4px 0 0 0;-moz-border-radius:4px 0 0 0;border-radius:4px 0 0 0;}
|
||||
.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-radius:0 4px 0 0;-moz-border-radius:0 4px 0 0;border-radius:0 4px 0 0;}
|
||||
.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;}
|
||||
.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child{-webkit-border-radius:0 0 4px 0;-moz-border-radius:0 0 4px 0;border-radius:0 0 4px 0;}
|
||||
.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9;}
|
||||
.table tbody tr:hover td,.table tbody tr:hover th{background-color:#f5f5f5;}
|
||||
table .span1{float:none;width:44px;margin-left:0;}
|
||||
table .span2{float:none;width:124px;margin-left:0;}
|
||||
table .span3{float:none;width:204px;margin-left:0;}
|
||||
table .span4{float:none;width:284px;margin-left:0;}
|
||||
table .span5{float:none;width:364px;margin-left:0;}
|
||||
table .span6{float:none;width:444px;margin-left:0;}
|
||||
table .span7{float:none;width:524px;margin-left:0;}
|
||||
table .span8{float:none;width:604px;margin-left:0;}
|
||||
table .span9{float:none;width:684px;margin-left:0;}
|
||||
table .span10{float:none;width:764px;margin-left:0;}
|
||||
table .span11{float:none;width:844px;margin-left:0;}
|
||||
table .span12{float:none;width:924px;margin-left:0;}
|
||||
table .span13{float:none;width:1004px;margin-left:0;}
|
||||
table .span14{float:none;width:1084px;margin-left:0;}
|
||||
table .span15{float:none;width:1164px;margin-left:0;}
|
||||
table .span16{float:none;width:1244px;margin-left:0;}
|
||||
table .span17{float:none;width:1324px;margin-left:0;}
|
||||
table .span18{float:none;width:1404px;margin-left:0;}
|
||||
table .span19{float:none;width:1484px;margin-left:0;}
|
||||
table .span20{float:none;width:1564px;margin-left:0;}
|
||||
table .span21{float:none;width:1644px;margin-left:0;}
|
||||
table .span22{float:none;width:1724px;margin-left:0;}
|
||||
table .span23{float:none;width:1804px;margin-left:0;}
|
||||
table .span24{float:none;width:1884px;margin-left:0;}
|
||||
[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat;*margin-right:.3em;}[class^="icon-"]:last-child,[class*=" icon-"]:last-child{*margin-left:0;}
|
||||
.icon-white{background-image:url("../img/glyphicons-halflings-white.png");}
|
||||
.icon-glass{background-position:0 0;}
|
||||
.icon-music{background-position:-24px 0;}
|
||||
.icon-search{background-position:-48px 0;}
|
||||
.icon-envelope{background-position:-72px 0;}
|
||||
.icon-heart{background-position:-96px 0;}
|
||||
.icon-star{background-position:-120px 0;}
|
||||
.icon-star-empty{background-position:-144px 0;}
|
||||
.icon-user{background-position:-168px 0;}
|
||||
.icon-film{background-position:-192px 0;}
|
||||
.icon-th-large{background-position:-216px 0;}
|
||||
.icon-th{background-position:-240px 0;}
|
||||
.icon-th-list{background-position:-264px 0;}
|
||||
.icon-ok{background-position:-288px 0;}
|
||||
.icon-remove{background-position:-312px 0;}
|
||||
.icon-zoom-in{background-position:-336px 0;}
|
||||
.icon-zoom-out{background-position:-360px 0;}
|
||||
.icon-off{background-position:-384px 0;}
|
||||
.icon-signal{background-position:-408px 0;}
|
||||
.icon-cog{background-position:-432px 0;}
|
||||
.icon-trash{background-position:-456px 0;}
|
||||
.icon-home{background-position:0 -24px;}
|
||||
.icon-file{background-position:-24px -24px;}
|
||||
.icon-time{background-position:-48px -24px;}
|
||||
.icon-road{background-position:-72px -24px;}
|
||||
.icon-download-alt{background-position:-96px -24px;}
|
||||
.icon-download{background-position:-120px -24px;}
|
||||
.icon-upload{background-position:-144px -24px;}
|
||||
.icon-inbox{background-position:-168px -24px;}
|
||||
.icon-play-circle{background-position:-192px -24px;}
|
||||
.icon-repeat{background-position:-216px -24px;}
|
||||
.icon-refresh{background-position:-240px -24px;}
|
||||
.icon-list-alt{background-position:-264px -24px;}
|
||||
.icon-lock{background-position:-287px -24px;}
|
||||
.icon-flag{background-position:-312px -24px;}
|
||||
.icon-headphones{background-position:-336px -24px;}
|
||||
.icon-volume-off{background-position:-360px -24px;}
|
||||
.icon-volume-down{background-position:-384px -24px;}
|
||||
.icon-volume-up{background-position:-408px -24px;}
|
||||
.icon-qrcode{background-position:-432px -24px;}
|
||||
.icon-barcode{background-position:-456px -24px;}
|
||||
.icon-tag{background-position:0 -48px;}
|
||||
.icon-tags{background-position:-25px -48px;}
|
||||
.icon-book{background-position:-48px -48px;}
|
||||
.icon-bookmark{background-position:-72px -48px;}
|
||||
.icon-print{background-position:-96px -48px;}
|
||||
.icon-camera{background-position:-120px -48px;}
|
||||
.icon-font{background-position:-144px -48px;}
|
||||
.icon-bold{background-position:-167px -48px;}
|
||||
.icon-italic{background-position:-192px -48px;}
|
||||
.icon-text-height{background-position:-216px -48px;}
|
||||
.icon-text-width{background-position:-240px -48px;}
|
||||
.icon-align-left{background-position:-264px -48px;}
|
||||
.icon-align-center{background-position:-288px -48px;}
|
||||
.icon-align-right{background-position:-312px -48px;}
|
||||
.icon-align-justify{background-position:-336px -48px;}
|
||||
.icon-list{background-position:-360px -48px;}
|
||||
.icon-indent-left{background-position:-384px -48px;}
|
||||
.icon-indent-right{background-position:-408px -48px;}
|
||||
.icon-facetime-video{background-position:-432px -48px;}
|
||||
.icon-picture{background-position:-456px -48px;}
|
||||
.icon-pencil{background-position:0 -72px;}
|
||||
.icon-map-marker{background-position:-24px -72px;}
|
||||
.icon-adjust{background-position:-48px -72px;}
|
||||
.icon-tint{background-position:-72px -72px;}
|
||||
.icon-edit{background-position:-96px -72px;}
|
||||
.icon-share{background-position:-120px -72px;}
|
||||
.icon-check{background-position:-144px -72px;}
|
||||
.icon-move{background-position:-168px -72px;}
|
||||
.icon-step-backward{background-position:-192px -72px;}
|
||||
.icon-fast-backward{background-position:-216px -72px;}
|
||||
.icon-backward{background-position:-240px -72px;}
|
||||
.icon-play{background-position:-264px -72px;}
|
||||
.icon-pause{background-position:-288px -72px;}
|
||||
.icon-stop{background-position:-312px -72px;}
|
||||
.icon-forward{background-position:-336px -72px;}
|
||||
.icon-fast-forward{background-position:-360px -72px;}
|
||||
.icon-step-forward{background-position:-384px -72px;}
|
||||
.icon-eject{background-position:-408px -72px;}
|
||||
.icon-chevron-left{background-position:-432px -72px;}
|
||||
.icon-chevron-right{background-position:-456px -72px;}
|
||||
.icon-plus-sign{background-position:0 -96px;}
|
||||
.icon-minus-sign{background-position:-24px -96px;}
|
||||
.icon-remove-sign{background-position:-48px -96px;}
|
||||
.icon-ok-sign{background-position:-72px -96px;}
|
||||
.icon-question-sign{background-position:-96px -96px;}
|
||||
.icon-info-sign{background-position:-120px -96px;}
|
||||
.icon-screenshot{background-position:-144px -96px;}
|
||||
.icon-remove-circle{background-position:-168px -96px;}
|
||||
.icon-ok-circle{background-position:-192px -96px;}
|
||||
.icon-ban-circle{background-position:-216px -96px;}
|
||||
.icon-arrow-left{background-position:-240px -96px;}
|
||||
.icon-arrow-right{background-position:-264px -96px;}
|
||||
.icon-arrow-up{background-position:-289px -96px;}
|
||||
.icon-arrow-down{background-position:-312px -96px;}
|
||||
.icon-share-alt{background-position:-336px -96px;}
|
||||
.icon-resize-full{background-position:-360px -96px;}
|
||||
.icon-resize-small{background-position:-384px -96px;}
|
||||
.icon-plus{background-position:-408px -96px;}
|
||||
.icon-minus{background-position:-433px -96px;}
|
||||
.icon-asterisk{background-position:-456px -96px;}
|
||||
.icon-exclamation-sign{background-position:0 -120px;}
|
||||
.icon-gift{background-position:-24px -120px;}
|
||||
.icon-leaf{background-position:-48px -120px;}
|
||||
.icon-fire{background-position:-72px -120px;}
|
||||
.icon-eye-open{background-position:-96px -120px;}
|
||||
.icon-eye-close{background-position:-120px -120px;}
|
||||
.icon-warning-sign{background-position:-144px -120px;}
|
||||
.icon-plane{background-position:-168px -120px;}
|
||||
.icon-calendar{background-position:-192px -120px;}
|
||||
.icon-random{background-position:-216px -120px;}
|
||||
.icon-comment{background-position:-240px -120px;}
|
||||
.icon-magnet{background-position:-264px -120px;}
|
||||
.icon-chevron-up{background-position:-288px -120px;}
|
||||
.icon-chevron-down{background-position:-313px -119px;}
|
||||
.icon-retweet{background-position:-336px -120px;}
|
||||
.icon-shopping-cart{background-position:-360px -120px;}
|
||||
.icon-folder-close{background-position:-384px -120px;}
|
||||
.icon-folder-open{background-position:-408px -120px;}
|
||||
.icon-resize-vertical{background-position:-432px -119px;}
|
||||
.icon-resize-horizontal{background-position:-456px -118px;}
|
||||
.dropdown{position:relative;}
|
||||
.dropdown-toggle{*margin-bottom:-3px;}
|
||||
.dropdown-toggle:active,.open .dropdown-toggle{outline:0;}
|
||||
.caret{display:inline-block;width:0;height:0;vertical-align:top;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid #000000;opacity:0.3;filter:alpha(opacity=30);content:"";}
|
||||
.dropdown .caret{margin-top:8px;margin-left:2px;}
|
||||
.dropdown:hover .caret,.open.dropdown .caret{opacity:1;filter:alpha(opacity=100);}
|
||||
.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;float:left;display:none;min-width:160px;padding:4px 0;margin:0;list-style:none;background-color:#ffffff;border-color:#ccc;border-color:rgba(0, 0, 0, 0.2);border-style:solid;border-width:1px;-webkit-border-radius:0 0 5px 5px;-moz-border-radius:0 0 5px 5px;border-radius:0 0 5px 5px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;*border-right-width:2px;*border-bottom-width:2px;}.dropdown-menu.pull-right{right:0;left:auto;}
|
||||
.dropdown-menu .divider{height:1px;margin:8px 1px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;*width:100%;*margin:-5px 0 5px;}
|
||||
.dropdown-menu a{display:block;padding:3px 15px;clear:both;font-weight:normal;line-height:18px;color:#333333;white-space:nowrap;}
|
||||
.dropdown-menu li>a:hover,.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#ffffff;text-decoration:none;background-color:#0088cc;}
|
||||
.dropdown.open{*z-index:1000;}.dropdown.open .dropdown-toggle{color:#ffffff;background:#ccc;background:rgba(0, 0, 0, 0.3);}
|
||||
.dropdown.open .dropdown-menu{display:block;}
|
||||
.pull-right .dropdown-menu{left:auto;right:0;}
|
||||
.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000000;content:"\2191";}
|
||||
.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px;}
|
||||
.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
|
||||
.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #eee;border:1px solid rgba(0, 0, 0, 0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);}
|
||||
.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
|
||||
.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
|
||||
.fade{-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-ms-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;opacity:0;}.fade.in{opacity:1;}
|
||||
.collapse{-webkit-transition:height 0.35s ease;-moz-transition:height 0.35s ease;-ms-transition:height 0.35s ease;-o-transition:height 0.35s ease;transition:height 0.35s ease;position:relative;overflow:hidden;height:0;}.collapse.in{height:auto;}
|
||||
.close{float:right;font-size:20px;font-weight:bold;line-height:18px;color:#000000;text-shadow:0 1px 0 #ffffff;opacity:0.2;filter:alpha(opacity=20);}.close:hover{color:#000000;text-decoration:none;opacity:0.4;filter:alpha(opacity=40);cursor:pointer;}
|
||||
.btn{display:inline-block;*display:inline;*zoom:1;padding:4px 10px 4px;margin-bottom:0;font-size:13px;line-height:18px;color:#333333;text-align:center;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);vertical-align:middle;background-color:#f5f5f5;background-image:-moz-linear-gradient(top, #ffffff, #e6e6e6);background-image:-ms-linear-gradient(top, #ffffff, #e6e6e6);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(top, #ffffff, #e6e6e6);background-image:-o-linear-gradient(top, #ffffff, #e6e6e6);background-image:linear-gradient(top, #ffffff, #e6e6e6);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);border:1px solid #cccccc;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);cursor:pointer;*margin-left:.3em;}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{background-color:#e6e6e6;}
|
||||
.btn:active,.btn.active{background-color:#cccccc \9;}
|
||||
.btn:first-child{*margin-left:0;}
|
||||
.btn:hover{color:#333333;text-decoration:none;background-color:#e6e6e6;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-ms-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;}
|
||||
.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
|
||||
.btn.active,.btn:active{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);background-color:#e6e6e6;background-color:#d9d9d9 \9;outline:0;}
|
||||
.btn.disabled,.btn[disabled]{cursor:default;background-image:none;background-color:#e6e6e6;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
|
||||
.btn-large{padding:9px 14px;font-size:15px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
|
||||
.btn-large [class^="icon-"]{margin-top:1px;}
|
||||
.btn-small{padding:5px 9px;font-size:11px;line-height:16px;}
|
||||
.btn-small [class^="icon-"]{margin-top:-1px;}
|
||||
.btn-mini{padding:2px 6px;font-size:11px;line-height:14px;}
|
||||
.btn-primary,.btn-primary:hover,.btn-warning,.btn-warning:hover,.btn-danger,.btn-danger:hover,.btn-success,.btn-success:hover,.btn-info,.btn-info:hover,.btn-inverse,.btn-inverse:hover{text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;}
|
||||
.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255, 255, 255, 0.75);}
|
||||
.btn-primary{background-color:#0074cc;background-image:-moz-linear-gradient(top, #0088cc, #0055cc);background-image:-ms-linear-gradient(top, #0088cc, #0055cc);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0055cc));background-image:-webkit-linear-gradient(top, #0088cc, #0055cc);background-image:-o-linear-gradient(top, #0088cc, #0055cc);background-image:linear-gradient(top, #0088cc, #0055cc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0055cc', GradientType=0);border-color:#0055cc #0055cc #003580;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{background-color:#0055cc;}
|
||||
.btn-primary:active,.btn-primary.active{background-color:#004099 \9;}
|
||||
.btn-warning{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);border-color:#f89406 #f89406 #ad6704;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{background-color:#f89406;}
|
||||
.btn-warning:active,.btn-warning.active{background-color:#c67605 \9;}
|
||||
.btn-danger{background-color:#da4f49;background-image:-moz-linear-gradient(top, #ee5f5b, #bd362f);background-image:-ms-linear-gradient(top, #ee5f5b, #bd362f);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));background-image:-webkit-linear-gradient(top, #ee5f5b, #bd362f);background-image:-o-linear-gradient(top, #ee5f5b, #bd362f);background-image:linear-gradient(top, #ee5f5b, #bd362f);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0);border-color:#bd362f #bd362f #802420;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{background-color:#bd362f;}
|
||||
.btn-danger:active,.btn-danger.active{background-color:#942a25 \9;}
|
||||
.btn-success{background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{background-color:#51a351;}
|
||||
.btn-success:active,.btn-success.active{background-color:#408140 \9;}
|
||||
.btn-info{background-color:#49afcd;background-image:-moz-linear-gradient(top, #5bc0de, #2f96b4);background-image:-ms-linear-gradient(top, #5bc0de, #2f96b4);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));background-image:-webkit-linear-gradient(top, #5bc0de, #2f96b4);background-image:-o-linear-gradient(top, #5bc0de, #2f96b4);background-image:linear-gradient(top, #5bc0de, #2f96b4);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0);border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{background-color:#2f96b4;}
|
||||
.btn-info:active,.btn-info.active{background-color:#24748c \9;}
|
||||
.btn-inverse{background-color:#414141;background-image:-moz-linear-gradient(top, #555555, #222222);background-image:-ms-linear-gradient(top, #555555, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#555555), to(#222222));background-image:-webkit-linear-gradient(top, #555555, #222222);background-image:-o-linear-gradient(top, #555555, #222222);background-image:linear-gradient(top, #555555, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#555555', endColorstr='#222222', GradientType=0);border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{background-color:#222222;}
|
||||
.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9;}
|
||||
button.btn,input[type="submit"].btn{*padding-top:2px;*padding-bottom:2px;}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0;}
|
||||
button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px;}
|
||||
button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px;}
|
||||
button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px;}
|
||||
.btn-group{position:relative;*zoom:1;*margin-left:.3em;}.btn-group:before,.btn-group:after{display:table;content:"";}
|
||||
.btn-group:after{clear:both;}
|
||||
.btn-group:first-child{*margin-left:0;}
|
||||
.btn-group+.btn-group{margin-left:5px;}
|
||||
.btn-toolbar{margin-top:9px;margin-bottom:9px;}.btn-toolbar .btn-group{display:inline-block;*display:inline;*zoom:1;}
|
||||
.btn-group .btn{position:relative;float:left;margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
|
||||
.btn-group .btn:first-child{margin-left:0;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;}
|
||||
.btn-group .btn:last-child,.btn-group .dropdown-toggle{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;}
|
||||
.btn-group .btn.large:first-child{margin-left:0;-webkit-border-top-left-radius:6px;-moz-border-radius-topleft:6px;border-top-left-radius:6px;-webkit-border-bottom-left-radius:6px;-moz-border-radius-bottomleft:6px;border-bottom-left-radius:6px;}
|
||||
.btn-group .btn.large:last-child,.btn-group .large.dropdown-toggle{-webkit-border-top-right-radius:6px;-moz-border-radius-topright:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;-moz-border-radius-bottomright:6px;border-bottom-right-radius:6px;}
|
||||
.btn-group .btn:hover,.btn-group .btn:focus,.btn-group .btn:active,.btn-group .btn.active{z-index:2;}
|
||||
.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0;}
|
||||
.btn-group .dropdown-toggle{padding-left:8px;padding-right:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);*padding-top:3px;*padding-bottom:3px;}
|
||||
.btn-group .btn-mini.dropdown-toggle{padding-left:5px;padding-right:5px;*padding-top:1px;*padding-bottom:1px;}
|
||||
.btn-group .btn-small.dropdown-toggle{*padding-top:4px;*padding-bottom:4px;}
|
||||
.btn-group .btn-large.dropdown-toggle{padding-left:12px;padding-right:12px;}
|
||||
.btn-group.open{*z-index:1000;}.btn-group.open .dropdown-menu{display:block;margin-top:1px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
|
||||
.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);}
|
||||
.btn .caret{margin-top:7px;margin-left:0;}
|
||||
.btn:hover .caret,.open.btn-group .caret{opacity:1;filter:alpha(opacity=100);}
|
||||
.btn-mini .caret{margin-top:5px;}
|
||||
.btn-small .caret{margin-top:6px;}
|
||||
.btn-large .caret{margin-top:6px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
|
||||
.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:0.75;filter:alpha(opacity=75);}
|
||||
.alert{padding:8px 35px 8px 14px;margin-bottom:18px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;color:#c09853;}
|
||||
.alert-heading{color:inherit;}
|
||||
.alert .close{position:relative;top:-2px;right:-21px;line-height:18px;}
|
||||
.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#468847;}
|
||||
.alert-danger,.alert-error{background-color:#f2dede;border-color:#eed3d7;color:#b94a48;}
|
||||
.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#3a87ad;}
|
||||
.alert-block{padding-top:14px;padding-bottom:14px;}
|
||||
.alert-block>p,.alert-block>ul{margin-bottom:0;}
|
||||
.alert-block p+p{margin-top:5px;}
|
||||
.nav{margin-left:0;margin-bottom:18px;list-style:none;}
|
||||
.nav>li>a{display:block;}
|
||||
.nav>li>a:hover{text-decoration:none;background-color:#eeeeee;}
|
||||
.nav .nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:18px;color:#999999;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);text-transform:uppercase;}
|
||||
.nav li+.nav-header{margin-top:9px;}
|
||||
.nav-list{padding-left:15px;padding-right:15px;margin-bottom:0;}
|
||||
.nav-list>li>a,.nav-list .nav-header{margin-left:-15px;margin-right:-15px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);}
|
||||
.nav-list>li>a{padding:3px 15px;}
|
||||
.nav-list>.active>a,.nav-list>.active>a:hover{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.2);background-color:#0088cc;}
|
||||
.nav-list [class^="icon-"]{margin-right:2px;}
|
||||
.nav-list .divider{height:1px;margin:8px 1px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;*width:100%;*margin:-5px 0 5px;}
|
||||
.nav-tabs,.nav-pills{*zoom:1;}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";}
|
||||
.nav-tabs:after,.nav-pills:after{clear:both;}
|
||||
.nav-tabs>li,.nav-pills>li{float:left;}
|
||||
.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px;}
|
||||
.nav-tabs{border-bottom:1px solid #ddd;}
|
||||
.nav-tabs>li{margin-bottom:-1px;}
|
||||
.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:18px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd;}
|
||||
.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555555;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;}
|
||||
.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
|
||||
.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#ffffff;background-color:#0088cc;}
|
||||
.nav-stacked>li{float:none;}
|
||||
.nav-stacked>li>a{margin-right:0;}
|
||||
.nav-tabs.nav-stacked{border-bottom:0;}
|
||||
.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
|
||||
.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}
|
||||
.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}
|
||||
.nav-tabs.nav-stacked>li>a:hover{border-color:#ddd;z-index:2;}
|
||||
.nav-pills.nav-stacked>li>a{margin-bottom:3px;}
|
||||
.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px;}
|
||||
.nav-tabs .dropdown-menu,.nav-pills .dropdown-menu{margin-top:1px;border-width:1px;}
|
||||
.nav-pills .dropdown-menu{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
|
||||
.nav-tabs .dropdown-toggle .caret,.nav-pills .dropdown-toggle .caret{border-top-color:#0088cc;border-bottom-color:#0088cc;margin-top:6px;}
|
||||
.nav-tabs .dropdown-toggle:hover .caret,.nav-pills .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580;}
|
||||
.nav-tabs .active .dropdown-toggle .caret,.nav-pills .active .dropdown-toggle .caret{border-top-color:#333333;border-bottom-color:#333333;}
|
||||
.nav>.dropdown.active>a:hover{color:#000000;cursor:pointer;}
|
||||
.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>.open.active>a:hover{color:#ffffff;background-color:#999999;border-color:#999999;}
|
||||
.nav .open .caret,.nav .open.active .caret,.nav .open a:hover .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:1;filter:alpha(opacity=100);}
|
||||
.tabs-stacked .open>a:hover{border-color:#999999;}
|
||||
.tabbable{*zoom:1;}.tabbable:before,.tabbable:after{display:table;content:"";}
|
||||
.tabbable:after{clear:both;}
|
||||
.tab-content{display:table;width:100%;}
|
||||
.tabs-below .nav-tabs,.tabs-right .nav-tabs,.tabs-left .nav-tabs{border-bottom:0;}
|
||||
.tab-content>.tab-pane,.pill-content>.pill-pane{display:none;}
|
||||
.tab-content>.active,.pill-content>.active{display:block;}
|
||||
.tabs-below .nav-tabs{border-top:1px solid #ddd;}
|
||||
.tabs-below .nav-tabs>li{margin-top:-1px;margin-bottom:0;}
|
||||
.tabs-below .nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}.tabs-below .nav-tabs>li>a:hover{border-bottom-color:transparent;border-top-color:#ddd;}
|
||||
.tabs-below .nav-tabs .active>a,.tabs-below .nav-tabs .active>a:hover{border-color:transparent #ddd #ddd #ddd;}
|
||||
.tabs-left .nav-tabs>li,.tabs-right .nav-tabs>li{float:none;}
|
||||
.tabs-left .nav-tabs>li>a,.tabs-right .nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px;}
|
||||
.tabs-left .nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd;}
|
||||
.tabs-left .nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;}
|
||||
.tabs-left .nav-tabs>li>a:hover{border-color:#eeeeee #dddddd #eeeeee #eeeeee;}
|
||||
.tabs-left .nav-tabs .active>a,.tabs-left .nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#ffffff;}
|
||||
.tabs-right .nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd;}
|
||||
.tabs-right .nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}
|
||||
.tabs-right .nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #eeeeee #dddddd;}
|
||||
.tabs-right .nav-tabs .active>a,.tabs-right .nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#ffffff;}
|
||||
.navbar{*position:relative;*z-index:2;overflow:visible;margin-bottom:18px;}
|
||||
.navbar-inner{padding-left:20px;padding-right:20px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);}
|
||||
.navbar .container{width:auto;}
|
||||
.btn-navbar{display:none;float:right;padding:7px 10px;margin-left:5px;margin-right:5px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);}.btn-navbar:hover,.btn-navbar:active,.btn-navbar.active,.btn-navbar.disabled,.btn-navbar[disabled]{background-color:#222222;}
|
||||
.btn-navbar:active,.btn-navbar.active{background-color:#080808 \9;}
|
||||
.btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);-moz-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);}
|
||||
.btn-navbar .icon-bar+.icon-bar{margin-top:3px;}
|
||||
.nav-collapse.collapse{height:auto;}
|
||||
.navbar{color:#999999;}.navbar .brand:hover{text-decoration:none;}
|
||||
.navbar .brand{float:left;display:block;padding:8px 20px 12px;margin-left:-20px;font-size:20px;font-weight:200;line-height:1;color:#ffffff;}
|
||||
.navbar .navbar-text{margin-bottom:0;line-height:40px;}
|
||||
.navbar .btn,.navbar .btn-group{margin-top:5px;}
|
||||
.navbar .btn-group .btn{margin-top:0;}
|
||||
.navbar-form{margin-bottom:0;*zoom:1;}.navbar-form:before,.navbar-form:after{display:table;content:"";}
|
||||
.navbar-form:after{clear:both;}
|
||||
.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px;}
|
||||
.navbar-form input,.navbar-form select{display:inline-block;margin-bottom:0;}
|
||||
.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px;}
|
||||
.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap;}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0;}
|
||||
.navbar-search{position:relative;float:left;margin-top:6px;margin-bottom:0;}.navbar-search .search-query{padding:4px 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;color:#ffffff;background-color:#626262;border:1px solid #151515;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none;}.navbar-search .search-query:-moz-placeholder{color:#cccccc;}
|
||||
.navbar-search .search-query::-webkit-input-placeholder{color:#cccccc;}
|
||||
.navbar-search .search-query:focus,.navbar-search .search-query.focused{padding:5px 10px;color:#333333;text-shadow:0 1px 0 #ffffff;background-color:#ffffff;border:0;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);outline:0;}
|
||||
.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0;}
|
||||
.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-left:0;padding-right:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
|
||||
.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
|
||||
.navbar-fixed-top{top:0;}
|
||||
.navbar-fixed-bottom{bottom:0;}
|
||||
.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0;}
|
||||
.navbar .nav.pull-right{float:right;}
|
||||
.navbar .nav>li{display:block;float:left;}
|
||||
.navbar .nav>li>a{float:none;padding:10px 10px 11px;line-height:19px;color:#999999;text-decoration:none;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);}
|
||||
.navbar .nav>li>a:hover{background-color:transparent;color:#ffffff;text-decoration:none;}
|
||||
.navbar .nav .active>a,.navbar .nav .active>a:hover{color:#ffffff;text-decoration:none;background-color:#222222;}
|
||||
.navbar .divider-vertical{height:40px;width:1px;margin:0 9px;overflow:hidden;background-color:#222222;border-right:1px solid #333333;}
|
||||
.navbar .nav.pull-right{margin-left:10px;margin-right:0;}
|
||||
.navbar .dropdown-menu{margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.navbar .dropdown-menu:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0, 0, 0, 0.2);position:absolute;top:-7px;left:9px;}
|
||||
.navbar .dropdown-menu:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:10px;}
|
||||
.navbar-fixed-bottom .dropdown-menu:before{border-top:7px solid #ccc;border-top-color:rgba(0, 0, 0, 0.2);border-bottom:0;bottom:-7px;top:auto;}
|
||||
.navbar-fixed-bottom .dropdown-menu:after{border-top:6px solid #ffffff;border-bottom:0;bottom:-6px;top:auto;}
|
||||
.navbar .nav .dropdown-toggle .caret,.navbar .nav .open.dropdown .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;}
|
||||
.navbar .nav .active .caret{opacity:1;filter:alpha(opacity=100);}
|
||||
.navbar .nav .open>.dropdown-toggle,.navbar .nav .active>.dropdown-toggle,.navbar .nav .open.active>.dropdown-toggle{background-color:transparent;}
|
||||
.navbar .nav .active>.dropdown-toggle:hover{color:#ffffff;}
|
||||
.navbar .nav.pull-right .dropdown-menu,.navbar .nav .dropdown-menu.pull-right{left:auto;right:0;}.navbar .nav.pull-right .dropdown-menu:before,.navbar .nav .dropdown-menu.pull-right:before{left:auto;right:12px;}
|
||||
.navbar .nav.pull-right .dropdown-menu:after,.navbar .nav .dropdown-menu.pull-right:after{left:auto;right:13px;}
|
||||
.breadcrumb{padding:7px 14px;margin:0 0 18px;list-style:none;background-color:#fbfbfb;background-image:-moz-linear-gradient(top, #ffffff, #f5f5f5);background-image:-ms-linear-gradient(top, #ffffff, #f5f5f5);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f5f5f5));background-image:-webkit-linear-gradient(top, #ffffff, #f5f5f5);background-image:-o-linear-gradient(top, #ffffff, #f5f5f5);background-image:linear-gradient(top, #ffffff, #f5f5f5);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0);border:1px solid #ddd;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;}.breadcrumb li{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 0 #ffffff;}
|
||||
.breadcrumb .divider{padding:0 5px;color:#999999;}
|
||||
.breadcrumb .active a{color:#333333;}
|
||||
.pagination{height:36px;margin:18px 0;}
|
||||
.pagination ul{display:inline-block;*display:inline;*zoom:1;margin-left:0;margin-bottom:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);}
|
||||
.pagination li{display:inline;}
|
||||
.pagination a{float:left;padding:0 14px;line-height:34px;text-decoration:none;border:1px solid #ddd;border-left-width:0;}
|
||||
.pagination a:hover,.pagination .active a{background-color:#f5f5f5;}
|
||||
.pagination .active a{color:#999999;cursor:default;}
|
||||
.pagination .disabled span,.pagination .disabled a,.pagination .disabled a:hover{color:#999999;background-color:transparent;cursor:default;}
|
||||
.pagination li:first-child a{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
|
||||
.pagination li:last-child a{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
|
||||
.pagination-centered{text-align:center;}
|
||||
.pagination-right{text-align:right;}
|
||||
.pager{margin-left:0;margin-bottom:18px;list-style:none;text-align:center;*zoom:1;}.pager:before,.pager:after{display:table;content:"";}
|
||||
.pager:after{clear:both;}
|
||||
.pager li{display:inline;}
|
||||
.pager a{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}
|
||||
.pager a:hover{text-decoration:none;background-color:#f5f5f5;}
|
||||
.pager .next a{float:right;}
|
||||
.pager .previous a{float:left;}
|
||||
.pager .disabled a,.pager .disabled a:hover{color:#999999;background-color:#fff;cursor:default;}
|
||||
.modal-open .dropdown-menu{z-index:2050;}
|
||||
.modal-open .dropdown.open{*z-index:2050;}
|
||||
.modal-open .popover{z-index:2060;}
|
||||
.modal-open .tooltip{z-index:2070;}
|
||||
.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000;}.modal-backdrop.fade{opacity:0;}
|
||||
.modal-backdrop,.modal-backdrop.fade.in{opacity:0.8;filter:alpha(opacity=80);}
|
||||
.modal{position:fixed;top:50%;left:50%;z-index:1050;overflow:auto;width:560px;margin:-250px 0 0 -280px;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.modal.fade{-webkit-transition:opacity .3s linear, top .3s ease-out;-moz-transition:opacity .3s linear, top .3s ease-out;-ms-transition:opacity .3s linear, top .3s ease-out;-o-transition:opacity .3s linear, top .3s ease-out;transition:opacity .3s linear, top .3s ease-out;top:-25%;}
|
||||
.modal.fade.in{top:50%;}
|
||||
.modal-header{padding:9px 15px;border-bottom:1px solid #eee;}.modal-header .close{margin-top:2px;}
|
||||
.modal-body{overflow-y:auto;max-height:400px;padding:15px;}
|
||||
.modal-form{margin-bottom:0;}
|
||||
.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;*zoom:1;}.modal-footer:before,.modal-footer:after{display:table;content:"";}
|
||||
.modal-footer:after{clear:both;}
|
||||
.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0;}
|
||||
.modal-footer .btn-group .btn+.btn{margin-left:-1px;}
|
||||
.tooltip{position:absolute;z-index:1020;display:block;visibility:visible;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);}.tooltip.in{opacity:0.8;filter:alpha(opacity=80);}
|
||||
.tooltip.top{margin-top:-2px;}
|
||||
.tooltip.right{margin-left:2px;}
|
||||
.tooltip.bottom{margin-top:2px;}
|
||||
.tooltip.left{margin-left:-2px;}
|
||||
.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
|
||||
.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;}
|
||||
.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;}
|
||||
.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;}
|
||||
.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;text-decoration:none;background-color:#000000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
|
||||
.tooltip-arrow{position:absolute;width:0;height:0;}
|
||||
.popover{position:absolute;top:0;left:0;z-index:1010;display:none;padding:5px;}.popover.top{margin-top:-5px;}
|
||||
.popover.right{margin-left:5px;}
|
||||
.popover.bottom{margin-top:5px;}
|
||||
.popover.left{margin-left:-5px;}
|
||||
.popover.top .arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
|
||||
.popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;}
|
||||
.popover.bottom .arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;}
|
||||
.popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;}
|
||||
.popover .arrow{position:absolute;width:0;height:0;}
|
||||
.popover-inner{padding:3px;width:280px;overflow:hidden;background:#000000;background:rgba(0, 0, 0, 0.8);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);}
|
||||
.popover-title{padding:9px 15px;line-height:1;background-color:#f5f5f5;border-bottom:1px solid #eee;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;}
|
||||
.popover-content{padding:14px;background-color:#ffffff;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0;}
|
||||
.thumbnails{margin-left:-20px;list-style:none;*zoom:1;}.thumbnails:before,.thumbnails:after{display:table;content:"";}
|
||||
.thumbnails:after{clear:both;}
|
||||
.thumbnails>li{float:left;margin:0 0 18px 20px;}
|
||||
.thumbnail{display:block;padding:4px;line-height:1;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);}
|
||||
a.thumbnail:hover{border-color:#0088cc;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);}
|
||||
.thumbnail>img{display:block;max-width:100%;margin-left:auto;margin-right:auto;}
|
||||
.thumbnail .caption{padding:9px;}
|
||||
.label{padding:1px 4px 2px;font-size:10.998px;font-weight:bold;line-height:13px;color:#ffffff;vertical-align:middle;white-space:nowrap;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#999999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
|
||||
.label:hover{color:#ffffff;text-decoration:none;}
|
||||
.label-important{background-color:#b94a48;}
|
||||
.label-important:hover{background-color:#953b39;}
|
||||
.label-warning{background-color:#f89406;}
|
||||
.label-warning:hover{background-color:#c67605;}
|
||||
.label-success{background-color:#468847;}
|
||||
.label-success:hover{background-color:#356635;}
|
||||
.label-info{background-color:#3a87ad;}
|
||||
.label-info:hover{background-color:#2d6987;}
|
||||
.label-inverse{background-color:#333333;}
|
||||
.label-inverse:hover{background-color:#1a1a1a;}
|
||||
.badge{padding:1px 9px 2px;font-size:12.025px;font-weight:bold;white-space:nowrap;color:#ffffff;background-color:#999999;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;}
|
||||
.badge:hover{color:#ffffff;text-decoration:none;cursor:pointer;}
|
||||
.badge-error{background-color:#b94a48;}
|
||||
.badge-error:hover{background-color:#953b39;}
|
||||
.badge-warning{background-color:#f89406;}
|
||||
.badge-warning:hover{background-color:#c67605;}
|
||||
.badge-success{background-color:#468847;}
|
||||
.badge-success:hover{background-color:#356635;}
|
||||
.badge-info{background-color:#3a87ad;}
|
||||
.badge-info:hover{background-color:#2d6987;}
|
||||
.badge-inverse{background-color:#333333;}
|
||||
.badge-inverse:hover{background-color:#1a1a1a;}
|
||||
@-webkit-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@-moz-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@-ms-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}.progress{overflow:hidden;height:18px;margin-bottom:18px;background-color:#f7f7f7;background-image:-moz-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-ms-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));background-image:-webkit-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-o-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:linear-gradient(top, #f5f5f5, #f9f9f9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#f9f9f9', GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
|
||||
.progress .bar{width:0%;height:18px;color:#ffffff;font-size:12px;text-align:center;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top, #149bdf, #0480be);background-image:-ms-linear-gradient(top, #149bdf, #0480be);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));background-image:-webkit-linear-gradient(top, #149bdf, #0480be);background-image:-o-linear-gradient(top, #149bdf, #0480be);background-image:linear-gradient(top, #149bdf, #0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#149bdf', endColorstr='#0480be', GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width 0.6s ease;-moz-transition:width 0.6s ease;-ms-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease;}
|
||||
.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px;}
|
||||
.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite;}
|
||||
.progress-danger .bar{background-color:#dd514c;background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-ms-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(top, #ee5f5b, #c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);}
|
||||
.progress-danger.progress-striped .bar{background-color:#ee5f5b;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
|
||||
.progress-success .bar{background-color:#5eb95e;background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-ms-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(top, #62c462, #57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);}
|
||||
.progress-success.progress-striped .bar{background-color:#62c462;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
|
||||
.progress-info .bar{background-color:#4bb1cf;background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-ms-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(top, #5bc0de, #339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);}
|
||||
.progress-info.progress-striped .bar{background-color:#5bc0de;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
|
||||
.progress-warning .bar{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);}
|
||||
.progress-warning.progress-striped .bar{background-color:#fbb450;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
|
||||
.accordion{margin-bottom:18px;}
|
||||
.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
|
||||
.accordion-heading{border-bottom:0;}
|
||||
.accordion-heading .accordion-toggle{display:block;padding:8px 15px;}
|
||||
.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5;}
|
||||
.carousel{position:relative;margin-bottom:18px;line-height:1;}
|
||||
.carousel-inner{overflow:hidden;width:100%;position:relative;}
|
||||
.carousel .item{display:none;position:relative;-webkit-transition:0.6s ease-in-out left;-moz-transition:0.6s ease-in-out left;-ms-transition:0.6s ease-in-out left;-o-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left;}
|
||||
.carousel .item>img{display:block;line-height:1;}
|
||||
.carousel .active,.carousel .next,.carousel .prev{display:block;}
|
||||
.carousel .active{left:0;}
|
||||
.carousel .next,.carousel .prev{position:absolute;top:0;width:100%;}
|
||||
.carousel .next{left:100%;}
|
||||
.carousel .prev{left:-100%;}
|
||||
.carousel .next.left,.carousel .prev.right{left:0;}
|
||||
.carousel .active.left{left:-100%;}
|
||||
.carousel .active.right{left:100%;}
|
||||
.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#ffffff;text-align:center;background:#222222;border:3px solid #ffffff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:0.5;filter:alpha(opacity=50);}.carousel-control.right{left:auto;right:15px;}
|
||||
.carousel-control:hover{color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90);}
|
||||
.carousel-caption{position:absolute;left:0;right:0;bottom:0;padding:10px 15px 5px;background:#333333;background:rgba(0, 0, 0, 0.75);}
|
||||
.carousel-caption h4,.carousel-caption p{color:#ffffff;}
|
||||
.hero-unit{padding:60px;margin-bottom:30px;background-color:#eeeeee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;color:inherit;letter-spacing:-1px;}
|
||||
.hero-unit p{font-size:18px;font-weight:200;line-height:27px;color:inherit;}
|
||||
.pull-right{float:right;}
|
||||
.pull-left{float:left;}
|
||||
.hide{display:none;}
|
||||
.show{display:block;}
|
||||
.invisible{visibility:hidden;}
|
||||
@@ -0,0 +1,172 @@
|
||||
img.AngularJS-small {
|
||||
width: 95px;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
|
||||
.clear-navbar {
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding-top: 2em;
|
||||
background-color: #333;
|
||||
color: white;
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
/* =============================== */
|
||||
|
||||
.form-search .dropdown-menu {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.form-search .code {
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.form-search > ul.nav > li.module {
|
||||
background-color: #d3d3d3;
|
||||
}
|
||||
|
||||
.form-search > ul.nav > li.section {
|
||||
background-color: #ebebeb;
|
||||
min-height: 14px;
|
||||
}
|
||||
|
||||
.form-search > ul.nav > li.last {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.form-search .well {
|
||||
border-color: #d3d3d3;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-search .well .nav-header {
|
||||
text-transform: none;
|
||||
margin-top: 0;
|
||||
margin-left: -15px;
|
||||
margin-right: -15px;
|
||||
}
|
||||
|
||||
.form-search .well .nav-header a {
|
||||
text-transform: none;
|
||||
color: black;
|
||||
}
|
||||
.form-search .well .nav-header a:hover {
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
.form-search .well li {
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
.form-search .well .guide {
|
||||
float: right;
|
||||
padding-top: 0;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
/* =============================== */
|
||||
/* Content */
|
||||
/* =============================== */
|
||||
|
||||
.hint {
|
||||
font-size: .7em;
|
||||
color: #c0c0c0;
|
||||
}
|
||||
|
||||
.content code {
|
||||
background-color: inherit;
|
||||
color: inherit;
|
||||
border: none;
|
||||
padding: 0;
|
||||
font-size: inherit;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.content h2,
|
||||
.content h3,
|
||||
.content h4,
|
||||
.content h5 {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
ul.parameters > li > p,
|
||||
.returns > p {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
ul.methods > li,
|
||||
ul.properties > li,
|
||||
ul.events > li {
|
||||
list-style: none;
|
||||
min-height: 20px;
|
||||
padding: 19px;
|
||||
margin-bottom: 20px;
|
||||
background-color: #f5f5f5;
|
||||
border: 1px solid #eee;
|
||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.member.method > h2,
|
||||
.member.property > h2,
|
||||
.member.event > h2 {
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
ul.methods > li > h3,
|
||||
ul.properties > li > h3,
|
||||
ul.events > li > h3 {
|
||||
margin: -19px -19px 1em -19px;
|
||||
padding: .25em 19px;
|
||||
background-color: #d3d3d3;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.diagram {
|
||||
display: block;
|
||||
margin: 2em auto;
|
||||
padding: 1em;
|
||||
border: 1px solid black;
|
||||
|
||||
-moz-box-shadow: 4px 4px 6px #48577D;
|
||||
-webkit-box-shadow: 4px 4px 6px #48577D;
|
||||
box-shadow: 4px 4px 6px #48577D;
|
||||
|
||||
-moz-border-radius: 15px;
|
||||
-webkit-border-radius: 15px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.tutorial-nav {
|
||||
margin-left: 175px;
|
||||
color: black;
|
||||
margin-top: 2em;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.tutorial-nav a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.tutorial-nav a:hover {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html xmlns:ng="http://angularjs.org" wiki:ng="http://angularjs.org">
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<head>
|
||||
<title><angular/> Docs Scenario Runner</title>
|
||||
<title>AngularJS Docs E2E Test Runner</title>
|
||||
<script type="text/javascript" src="../angular-scenario.js" ng:autotest></script>
|
||||
<script type="text/javascript" src="docs-scenario.js"></script>
|
||||
</head>
|
||||
|
||||
@@ -1,520 +0,0 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
|
||||
body {
|
||||
background: #000 url("img/texture_1.png");
|
||||
}
|
||||
|
||||
/*----- Layout Generic Styles -----*/
|
||||
|
||||
body,td,th {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 14px;
|
||||
color: #000;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a:link, a:visited, a:hover {
|
||||
color: #5d6db6;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
padding: 0.1em 1em;
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
li > p {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 1.5em 0 1em 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 1.8em 0 1em 0;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin: 2em 0 1em 0;
|
||||
}
|
||||
|
||||
li {
|
||||
margin: 0.3em 0 0.3em 0;
|
||||
}
|
||||
|
||||
.member {
|
||||
border: 1px solid gray;
|
||||
margin: 2em 0;
|
||||
}
|
||||
|
||||
.member p {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.member>h2 {
|
||||
background-color: lightgray;
|
||||
padding: .2em .4em;
|
||||
margin: 0;
|
||||
border-bottom: 1px solid gray;
|
||||
}
|
||||
|
||||
.member>ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.member>ul>li {
|
||||
border: 1px solid lightgray;
|
||||
margin: 1em;
|
||||
|
||||
}
|
||||
.member>ul>li>h3 {
|
||||
background-color: #EEE;
|
||||
padding: .2em .4em;
|
||||
margin: 0;
|
||||
font-family: monospace;
|
||||
border-bottom: 1px solid lightgray;
|
||||
}
|
||||
.member>ul>li>div {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
/*----- Upgrade IE Prompt -----*/
|
||||
|
||||
#oldIePrompt {
|
||||
margin: 0 auto;
|
||||
background-color: red;
|
||||
color: black;
|
||||
text-align: center;
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
|
||||
#oldIePrompt a,
|
||||
#oldIePrompt a:visited {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*----- Global Layout -----*/
|
||||
|
||||
#loading {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
margin: 1.5em -40px;
|
||||
width: 80px;
|
||||
padding: 0.2em 0.2em;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
color: #FFFFFF;
|
||||
border: 1px solid black;
|
||||
background-color: #7989D6;
|
||||
|
||||
-webkit-border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
|
||||
-webkit-box-shadow: 0 2px 8px rgba(0,0,0,0.6);
|
||||
-moz-box-shadow: 0 2px 8px rgba(0,0,0,0.6);
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.6);
|
||||
|
||||
z-index: 2; /* just below #fader */
|
||||
}
|
||||
|
||||
|
||||
#container {
|
||||
width: 1150px;
|
||||
margin: 0 auto;
|
||||
background: #F8AC09 url(img/yellow_bkgnd.jpg) no-repeat top left;
|
||||
}
|
||||
|
||||
|
||||
#footer {
|
||||
clear: both;
|
||||
padding: 2em 4em 1em;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#offline{
|
||||
text-decoration:underline;
|
||||
cursor:pointer;
|
||||
color:blue;
|
||||
}
|
||||
|
||||
#copyright {
|
||||
float:right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#version {
|
||||
margin-top: -1.2em;
|
||||
margin-left: -2em;
|
||||
margin-right: 0.5em;
|
||||
color: #251BE0;
|
||||
}
|
||||
|
||||
|
||||
/*----- navigation styles -----*/
|
||||
|
||||
#navbar {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
background: #7989D6;
|
||||
height: 3.5em;
|
||||
border-bottom: 4px solid #808080;
|
||||
}
|
||||
|
||||
|
||||
#navbar li {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
|
||||
#navbar a:link, #navbar a:visited {
|
||||
font-size: 1.2em;
|
||||
color: #FFF;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
width: 11em;
|
||||
margin-top: 0.4em;
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
|
||||
|
||||
#navbar a:hover {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
|
||||
#navbar a.current {
|
||||
background-color: #333;
|
||||
|
||||
-webkit-border-radius:10px;
|
||||
-moz-border-radius: 10px;
|
||||
border-radius: 10px;
|
||||
|
||||
-webkit-box-shadow: 4px 4px 6px #48577D;
|
||||
-moz-box-shadow: 4px 4px 6px #48577D;
|
||||
box-shadow: 4px 4px 6px #48577D;
|
||||
}
|
||||
|
||||
|
||||
#navbar a.current:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*------ sidebar styles ------*/
|
||||
|
||||
#sidebar {
|
||||
margin-top: 4em;
|
||||
width: 15.5em;
|
||||
padding: 0.8em 0.7em 0.7em 0.7em;
|
||||
background: #7989D6;
|
||||
overflow: hidden;
|
||||
float: left;
|
||||
|
||||
-moz-box-shadow: 4px 4px 6px #48577D;
|
||||
-webkit-box-shadow: 4px 4px 6px #48577D;
|
||||
box-shadow: 4px 4px 6px #48577D;
|
||||
|
||||
-moz-border-radius-topright: 15px;
|
||||
-webkit-border-top-right-radius: 15px;
|
||||
border-top-right-radius: 15px;
|
||||
|
||||
-moz-border-radius-bottomright: 15px;
|
||||
-webkit-border-bottom-right-radius: 15px;
|
||||
border-bottom-right-radius: 15px;
|
||||
}
|
||||
|
||||
|
||||
#sidebar input {
|
||||
width: 207px;
|
||||
}
|
||||
|
||||
|
||||
#sidebar #content-list.tutorial {
|
||||
list-style: none;
|
||||
text-indent: -1.2em;
|
||||
}
|
||||
|
||||
#sidebar #content-list.tutorial .level-0 {
|
||||
text-indent: 0;
|
||||
}
|
||||
|
||||
|
||||
#content-list {
|
||||
background: #fff;
|
||||
padding: 1em 0.4em 1em 2em;
|
||||
margin: 0.95em -0.95em -1em -0.6em;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
|
||||
#content-list .level-0 {
|
||||
font-size: 1.3em;
|
||||
list-style: none;
|
||||
margin-left: -1.2em;
|
||||
padding-bottom: 0.2em;
|
||||
}
|
||||
|
||||
#content-list .level-1 {
|
||||
margin-left: 0em;
|
||||
}
|
||||
|
||||
#content-list .level-2 {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
#content-list .level-3 {
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
#content-list .level-4 {
|
||||
margin-left: 3em;
|
||||
}
|
||||
|
||||
#content-list .level-5 {
|
||||
margin-left: 4em;
|
||||
}
|
||||
|
||||
#content-list .level-6 {
|
||||
margin-left: 5em;
|
||||
}
|
||||
|
||||
#content-list a.current {
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*----- content styles -----*/
|
||||
|
||||
.content-panel {
|
||||
float: right;
|
||||
margin-top: 4em;
|
||||
margin-right: 3em;
|
||||
width: 57em;
|
||||
padding: 0.5em 1em 1em 1em;
|
||||
background: #7989D6;
|
||||
overflow: hidden;
|
||||
|
||||
-moz-box-shadow: 4px 4px 6px #48577D;
|
||||
-webkit-box-shadow: 4px 4px 6px #48577D;
|
||||
box-shadow: 4px 4px 6px #48577D;
|
||||
|
||||
-moz-border-radius: 15px;
|
||||
-webkit-border-radius: 15px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.content-panel > h2 {
|
||||
font-size: 2em;
|
||||
font-weight: normal;
|
||||
color: #fff;
|
||||
margin: 0 4em 0 0;
|
||||
}
|
||||
|
||||
.content-panel-content {
|
||||
background-color: #fff;
|
||||
display: block;
|
||||
margin: 0.7em -1em -1em;
|
||||
padding: 0.5em 1.5em;
|
||||
}
|
||||
|
||||
|
||||
#content > h1 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
#content img:not(.ng-directive) {
|
||||
/* the negation rule above is to avoid applying this rule to images in buzz and other examples */
|
||||
display: block;
|
||||
margin: 2em auto;
|
||||
padding: 1em;
|
||||
border: 1px solid black;
|
||||
|
||||
-moz-box-shadow: 4px 4px 6px #48577D;
|
||||
-webkit-box-shadow: 4px 4px 6px #48577D;
|
||||
box-shadow: 4px 4px 6px #48577D;
|
||||
|
||||
-moz-border-radius: 15px;
|
||||
-webkit-border-radius: 15px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
|
||||
#content pre {
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
|
||||
|
||||
#content .syntaxhighlighter {
|
||||
margin: 1.5em 0 1.5em 0 !important;
|
||||
}
|
||||
|
||||
#content .parameters p:nth-child(2) {
|
||||
display: inline;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
#content .returns p:nth-child(2) {
|
||||
display: inline;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
#disqus #disqus_thread {
|
||||
margin: 0.7em -1em -1em;
|
||||
}
|
||||
|
||||
/* subpages */
|
||||
|
||||
#fader {
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: black;
|
||||
opacity: 0.8;
|
||||
-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=80)";
|
||||
filter: alpha(opacity=80);
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
#subpage > div {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 729px;
|
||||
margin-top: -140px;
|
||||
margin-left: -365px;
|
||||
z-index: 3;
|
||||
background-color: #7989D6;
|
||||
border-radius: 15px;
|
||||
-moz-border-radius: 15px;
|
||||
-webkit-border-radius: 15px;
|
||||
box-shadow: 4px 4px 6px #48577D;
|
||||
-webkit-box-shadow: 4px 4px 6px #48577D;
|
||||
-moz-box-shadow: 4px 4px 6px #48577D;
|
||||
}
|
||||
|
||||
#subpage h2 {
|
||||
height: 1.8em;
|
||||
-moz-border-radius-topright: 15px;
|
||||
-moz-border-radius-topleft: 15px;
|
||||
border-radius-topright: 15px;
|
||||
border-radius-topleft: 15px;
|
||||
-webkit-border-top-right-radius: 15px;
|
||||
-webkit-border-top-left-radius: 15px;
|
||||
border-top-right-radius: 15px;
|
||||
border-top-left-radius: 15px;
|
||||
padding: 0.6em 0 0 1em;
|
||||
margin: 0;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#subpage > div > a {
|
||||
color: black;
|
||||
float: right;
|
||||
margin: -40px 10px;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
#subpage > div > a:hover {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#subpage > div > p {
|
||||
background-color: white;
|
||||
padding: 0.5em 1em 0.5em 1em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
#cacheButton {
|
||||
margin: 0em 2em 1em 0em;
|
||||
float:right;
|
||||
}
|
||||
|
||||
.table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.table th:first-child {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.table th,
|
||||
.table td {
|
||||
border: 1px solid black;
|
||||
padding: .5em 1em;
|
||||
}
|
||||
.table th {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.table th.section {
|
||||
text-align: left;
|
||||
background-color: lightgray;
|
||||
}
|
||||
|
||||
[ng\:cloak], .ng-cloak {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.inline * {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 4px;
|
||||
border: 1px solid lightgray;
|
||||
}
|
||||
|
||||
td.head, thead td, .head td {
|
||||
background-color: #EEE;
|
||||
}
|
||||
|
||||
td.empty-corner-lt {
|
||||
border-left: 0;
|
||||
border-top: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* service.$location Html5 mode example */
|
||||
.html5-hashbang-example div input {
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
.doc-example-live .error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.odd {
|
||||
background-color: #808080;
|
||||
}
|
||||
|
||||
.even {
|
||||
background-color: #d3d3d3;
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
DocsController.$inject = ['$scope', '$location', '$window', '$cookies', '$filter'];
|
||||
function DocsController(scope, $location, $window, $cookies, $filter) {
|
||||
window.$root = scope.$root;
|
||||
|
||||
var OFFLINE_COOKIE_NAME = 'ng-offline',
|
||||
DOCS_PATH = /^\/(api)|(guide)|(cookbook)|(misc)|(tutorial)/,
|
||||
INDEX_PATH = /^(\/|\/index[^\.]*.html)$/,
|
||||
filter = $filter('filter');
|
||||
|
||||
scope.$location = $location;
|
||||
scope.versionNumber = angular.version.full;
|
||||
scope.version = angular.version.full + " " + angular.version.codeName;
|
||||
scope.subpage = false;
|
||||
scope.offlineEnabled = ($cookies[OFFLINE_COOKIE_NAME] == angular.version.full);
|
||||
scope.futurePartialTitle = null;
|
||||
scope.loading = 0;
|
||||
|
||||
if (!$location.path() || INDEX_PATH.test($location.path())) {
|
||||
$location.path('/api').replace();
|
||||
}
|
||||
|
||||
scope.$watch('$location.path()', function(path) {
|
||||
// ignore non-doc links which are used in examples
|
||||
if (DOCS_PATH.test(path)) {
|
||||
var parts = path.split('/');
|
||||
scope.sectionId = parts[1];
|
||||
scope.partialId = parts[2] || 'index';
|
||||
scope.pages = filter(NG_PAGES, {section: scope.sectionId});
|
||||
|
||||
var i = scope.pages.length;
|
||||
while (i--) {
|
||||
if (scope.pages[i].id == scope.partialId) break;
|
||||
}
|
||||
if (i<0) {
|
||||
scope.partialTitle = 'Error: Page Not Found!';
|
||||
delete scope.partialId;
|
||||
} else {
|
||||
// TODO(i): this is not ideal but better than updating the title before a partial arrives,
|
||||
// which results in the old partial being displayed with the new title
|
||||
scope.futurePartialTitle = scope.pages[i].name;
|
||||
scope.loading++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
scope.getUrl = function(page) {
|
||||
return page.section + (page.id == 'index' ? '' : '/' + page.id);
|
||||
};
|
||||
|
||||
scope.getCurrentPartial = function() {
|
||||
return this.partialId
|
||||
? ('./partials/' + this.sectionId + '/' + this.partialId.replace('angular.Module', 'angular.IModule') + '.html')
|
||||
: '';
|
||||
};
|
||||
|
||||
scope.getClass = function(page) {
|
||||
var depth = page.depth,
|
||||
cssClass = 'level-' + depth + (page.name == this.partialId ? ' selected' : '');
|
||||
|
||||
if (page.section == 'api')
|
||||
cssClass += ' monospace';
|
||||
|
||||
return cssClass;
|
||||
};
|
||||
|
||||
scope.selectedSection = function(section) {
|
||||
return section == scope.sectionId ? 'current' : '';
|
||||
};
|
||||
|
||||
scope.selectedPartial = function(partial) {
|
||||
return partial.id == scope.partialId ? 'current' : '';
|
||||
};
|
||||
|
||||
scope.afterPartialLoaded = function() {
|
||||
var currentPageId = $location.path();
|
||||
scope.loading--;
|
||||
scope.partialTitle = scope.futurePartialTitle;
|
||||
SyntaxHighlighter.highlight();
|
||||
$window._gaq.push(['_trackPageview', currentPageId]);
|
||||
loadDisqus(currentPageId);
|
||||
};
|
||||
|
||||
/** stores a cookie that is used by apache to decide which manifest ot send */
|
||||
scope.enableOffline = function() {
|
||||
//The cookie will be good for one year!
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime()+(365*24*60*60*1000));
|
||||
var expires = "; expires="+date.toGMTString();
|
||||
var value = angular.version.full;
|
||||
document.cookie = OFFLINE_COOKIE_NAME + "="+value+expires+"; path=" + $location.path;
|
||||
|
||||
//force the page to reload so server can serve new manifest file
|
||||
window.location.reload(true);
|
||||
};
|
||||
|
||||
// bind escape to hash reset callback
|
||||
angular.element(window).bind('keydown', function(e) {
|
||||
if (e.keyCode === 27) {
|
||||
scope.$apply(function() {
|
||||
scope.subpage = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function loadDisqus(currentPageId) {
|
||||
// http://docs.disqus.com/help/2/
|
||||
window.disqus_shortname = 'angularjs';
|
||||
window.disqus_identifier = currentPageId;
|
||||
window.disqus_url = 'http://docs-next.angularjs.org' + currentPageId;
|
||||
|
||||
if ($location.host() == 'localhost') {
|
||||
return; // don't display disqus on localhost, comment this out if needed
|
||||
//window.disqus_developer = 1;
|
||||
}
|
||||
|
||||
// http://docs.disqus.com/developers/universal/
|
||||
(function() {
|
||||
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
|
||||
dsq.src = 'http://angularjs.disqus.com/embed.js';
|
||||
(document.getElementsByTagName('head')[0] ||
|
||||
document.getElementsByTagName('body')[0]).appendChild(dsq);
|
||||
})();
|
||||
|
||||
angular.element(document.getElementById('disqus_thread')).html('');
|
||||
}
|
||||
}
|
||||
|
||||
SyntaxHighlighter['defaults'].toolbar = false;
|
||||
SyntaxHighlighter['defaults'].gutter = true;
|
||||
|
||||
/**
|
||||
* Controller for tutorial instructions
|
||||
* @param $cookieStore
|
||||
* @constructor
|
||||
*/
|
||||
function TutorialInstructionsCtrl($cookieStore) {
|
||||
this.selected = $cookieStore.get('selEnv') || 'git-mac';
|
||||
|
||||
this.currentCls = function(id, cls) {
|
||||
return this.selected == id ? cls || 'current' : '';
|
||||
};
|
||||
|
||||
this.select = function(id) {
|
||||
this.selected = id;
|
||||
$cookieStore.put('selEnv', id);
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('ngdocs', ['ngdocs.directives'], function($locationProvider, $filterProvider, $compileProvider) {
|
||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||
|
||||
$filterProvider.register('title', function(){
|
||||
return function(text) {
|
||||
return text && text.replace(/^angular\.module\.([^\.]+)(\.(.*))?$/, function(_, module, _0, name){
|
||||
return 'Module ' + module + (name ? ' - ' + name : '');
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
$compileProvider.directive('code', function() {
|
||||
return { restrict: 'E', terminal: true };
|
||||
});
|
||||
});
|
||||
@@ -1,13 +1,17 @@
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org/"
|
||||
xmlns:doc="http://docs.angularjs.org/"
|
||||
id="ng:app" ng:app="ngdocs"
|
||||
ng:controller="DocsController"
|
||||
doc:manifest>
|
||||
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7 ng-app: docsApp;" lang="en" ng-controller="DocsController"> <![endif]-->
|
||||
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8 ng-app: docsApp;" lang="en" ng-controller="DocsController"> <![endif]-->
|
||||
<!--[if IE 8]> <html class="no-js lt-ie9 ng-app: docsApp;" lang="en" ng-controller="DocsController"> <![endif]-->
|
||||
<!--[if gt IE 8]><!--> <html class="no-js ng-app: docsApp;" lang="en" ng-controller="DocsController"> <!--<![endif]-->
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="Description"
|
||||
content="AngularJS is what HTML would have been, had it been designed for building web-apps.
|
||||
Declarative templates with data-binding, MVC, dependency injection and great
|
||||
testability story all implemented with pure client-side JavaScript!">
|
||||
<meta name="fragment" content="!">
|
||||
<title ng:bind-template="AngularJS: {{partialTitle | title}}">AngularJS</title>
|
||||
<title ng-bind-template="AngularJS: {{partialTitle}}">AngularJS</title>
|
||||
<script type="text/javascript">
|
||||
// dynamically add base tag as well as css and javascript files.
|
||||
// we can't add css/js the usual way, because some browsers (FF) eagerly prefetch resources
|
||||
@@ -18,19 +22,26 @@
|
||||
baseUrl = location.href.replace(rUrl, indexFile),
|
||||
jQuery = /index-jq[^\.]*\.html$/.test(baseUrl),
|
||||
debug = /index[^\.]*-debug\.html$/.test(baseUrl),
|
||||
angularPath = debug ? '../angular.js' : '../angular.min.js',
|
||||
headEl = document.getElementsByTagName('head')[0],
|
||||
sync = true;
|
||||
|
||||
addTag('base', {href: baseUrl});
|
||||
addTag('link', {rel: 'stylesheet', href: 'docs-combined.css', type: 'text/css'});
|
||||
addTag('link', {rel: 'stylesheet', href: 'syntaxhighlighter/syntaxhighlighter-combined.css',
|
||||
type: 'text/css'});
|
||||
addTag('script', {src: 'syntaxhighlighter/syntaxhighlighter-combined.js'}, sync);
|
||||
if (jQuery) addTag('script', {src: 'jquery.min.js'});
|
||||
addTag('script', {src: angularPath}, sync);
|
||||
addTag('script', {src: 'docs-combined.js'}, sync);
|
||||
addTag('script', {src: 'docs-keywords.js'}, sync);
|
||||
addTag('link', {rel: 'stylesheet', href: 'css/bootstrap.min.css', type: 'text/css'});
|
||||
addTag('link', {rel: 'stylesheet', href: 'http://angularjs.org/css/font-awesome.css', type: 'text/css'});
|
||||
addTag('link', {rel: 'stylesheet', href: 'css/docs.css', type: 'text/css'});
|
||||
if (jQuery) addTag('script', {src: debug ? 'js/jquery.js' : 'js/jquery.min.js'});
|
||||
addTag('script', {src: path('angular.js')}, sync);
|
||||
addTag('script', {src: path('angular-resource.js') }, sync);
|
||||
addTag('script', {src: path('angular-cookies.js') }, sync);
|
||||
addTag('script', {src: path('angular-sanitize.js') }, sync);
|
||||
addTag('script', {src: path('angular-bootstrap.js') }, sync);
|
||||
addTag('script', {src: path('angular-bootstrap-prettify.js') }, sync);
|
||||
addTag('script', {src: 'js/docs.js'}, sync);
|
||||
addTag('script', {src: 'js/docs-keywords.js'}, sync);
|
||||
|
||||
function path(name) {
|
||||
return '../' + name.replace(/\.js$/, debug ? '.js' : '.min.js');
|
||||
}
|
||||
|
||||
function addTag(name, attributes, sync) {
|
||||
var el = document.createElement(name),
|
||||
@@ -80,70 +91,193 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="container">
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
<div id="oldIePrompt">
|
||||
You are using an old version of Internet Explorer.
|
||||
For better and safer browsing experience please <a href="http://www.microsoft.com/IE9">upgrade IE</a>
|
||||
or install <a href="http://google.com/chrome">Google Chrome browser</a>.
|
||||
<header class="header">
|
||||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="http://angularjs.org" style="padding-top: 6px; padding-bottom: 0px;">
|
||||
<img class="AngularJS-small" src="http://angularjs.org/img/AngularJS-small.png">
|
||||
</a>
|
||||
<ul class="nav">
|
||||
<li class="divider-vertical"></li>
|
||||
<li><a href="http://angularjs.org"><i class="icon-home icon-white"></i> Home</a></li>
|
||||
<li class="divider-vertical"></li>
|
||||
<li class="dropdown">
|
||||
<a href="" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="icon-eye-open icon-white"></i> Learn <b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="disabled"><a href="">Why AngularJS?</a></li>
|
||||
<li><a href="http://www.youtube.com/user/angularjs">Watch</a></li>
|
||||
<li><a href="tutorial">Tutorial</a></li>
|
||||
<li><a href="https://github.com/angular/angular.js/wiki/Projects-using-AngularJS">Case Studies</a></li>
|
||||
<li><a href="misc/faq">FAQ</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="divider-vertical"></li>
|
||||
<li class="dropdown active">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="icon-book icon-white"></i> Develop <b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="tutorial">Tutorial</a></li>
|
||||
<li><a href="guide/">Developer Guide</a></li>
|
||||
<li><a href="api/">API Reference</a></li>
|
||||
<li><a href="misc/contribute">Contribute</a></li>
|
||||
<li><a href="http://code.angularjs.org/">Download</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="divider-vertical"></li>
|
||||
<li class="dropdown">
|
||||
<a href="" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="icon-comment icon-white"></i> Discuss <b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="http://groups.google.com/group/angular">Mailing List</a></li>
|
||||
<li><a href="http://webchat.freenode.net/?channels=angularjs&uio=d4">Chat Room</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="https://twitter.com/#!/angularjs">Twitter</a></li>
|
||||
<li><a href="https://plus.google.com/110323587230527980117">Google+</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="https://github.com/angular/angular.js">GitHub</a></li>
|
||||
<li><a href="https://github.com/angular/angular.js/issues">Issue Tracker</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="divider-vertical"></li>
|
||||
</ul>
|
||||
<form class="navbar-search pull-right" method="GET" action="https://www.google.com/search">
|
||||
<input type="text" name="as_q" class="search-query" placeholder="Search">
|
||||
<input type="hidden" name="as_sitesearch" value="angularjs.org">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<![endif]-->
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<ul id="navbar">
|
||||
<li><a href="http://angularjs.org/">AngularJS</a></li>
|
||||
<li><a href="misc/started" ng:class="selectedSection('misc')">Getting Started</a></li>
|
||||
<!-- <li><a href="tutorial" ng:class="selectedSection('tutorial')">Tutorial</a></li> -->
|
||||
<li><a href="api" ng:class="selectedSection('api')">API Reference</a></li>
|
||||
<li><a href="cookbook" ng:class="selectedSection('cookbook')">Examples</a></li>
|
||||
<li><a href="guide" ng:class="selectedSection('guide')">Developer Guide</a></li>
|
||||
</ul>
|
||||
<div role="main" class="container">
|
||||
<div class="row clear-navbar"></div>
|
||||
|
||||
<div id="sidebar">
|
||||
<input type="text" ng:model="search" id="search-box" placeholder="search the docs"
|
||||
tabindex="1" accesskey="s" ng:model-instant>
|
||||
<div class="row">
|
||||
<div class="span12">
|
||||
<!--[if lt IE 7]>
|
||||
<p class="alert alert-error">Your browser is <em>ancient!</em>
|
||||
<a href="http://browsehappy.com/">Upgrade to a different browser</a> or
|
||||
<a href="http://www.google.com/chromeframe/?redirect=true">install Google Chrome Frame</a> to
|
||||
experience this site.
|
||||
</p>
|
||||
<![endif]-->
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
<div class="alert">
|
||||
You are using an old version of Internet Explorer.
|
||||
For better and safer browsing experience please <a href="http://www.microsoft.com/IE9">upgrade IE</a>
|
||||
or install <a href="http://google.com/chrome">Google Chrome browser</a>.
|
||||
</div>
|
||||
<![endif]-->
|
||||
</div>
|
||||
|
||||
<ul id="content-list" ng:class="sectionId" ng:cloak>
|
||||
<li ng:repeat="page in pages | filter:search" ng:class="getClass(page)">
|
||||
<a href="{{getUrl(page)}}" ng:class="selectedPartial(page)"
|
||||
ng:bind="page.shortName"
|
||||
tabindex="2"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="loading" ng:show="loading">Loading...</div>
|
||||
<div class="row">
|
||||
<div class="span3">
|
||||
<form class="well form-search" ng-submit="submitForm()">
|
||||
<div class="dropdown search"
|
||||
ng-class="{open: focused && bestMatch.rank > 0 && bestMatch.page != currentPage}">
|
||||
<input type="text" ng-model="search" placeholder="search the docs"
|
||||
tabindex="1" accesskey="s" class="input-medium search-query" focused="focused">
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a href="{{bestMatch.page.url}}">{{bestMatch.page.shortName}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="spacer"></div>
|
||||
<div ng-show="search">Filtered results:</div>
|
||||
|
||||
<ul class="nav nav-list" ng-hide="page">
|
||||
<li ng-repeat="page in pages" ng-class="navClass(page)">
|
||||
<a href="{{page.url}}" tabindex="2">{{page.shortName}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<div class="content-panel">
|
||||
<h2 ng:bind="partialTitle | title"></h2>
|
||||
<ng:include id="content" class="content-panel-content" autoscroll
|
||||
src="getCurrentPartial()" onload="afterPartialLoaded()"></ng:include>
|
||||
</div>
|
||||
<ul class="nav nav-list well" ng-repeat="module in modules">
|
||||
<li class="nav-header module">
|
||||
<a class="guide" href="{{URL.module}}">module</a>
|
||||
<a class="code" href="{{module.url}}">{{module.name}}</a>
|
||||
</li>
|
||||
|
||||
<div id="disqus" class="content-panel">
|
||||
<h2>Discussion</h2>
|
||||
<div id="disqus_thread" class="content-panel-content"></div>
|
||||
</div>
|
||||
<li class="nav-header section" ng-show="module.directives">
|
||||
<a href="{{URL.directive}}" class="guide">directive</a>
|
||||
</li>
|
||||
<li ng-repeat="page in module.directives" ng-class="navClass(page)">
|
||||
<a href="{{page.url}}" tabindex="2">{{page.shortName}}</a>
|
||||
</li>
|
||||
|
||||
<div id="footer" ng:cloak>
|
||||
<a id="version"
|
||||
ng:href="https://github.com/angular/angular.js/blob/master/CHANGELOG.md#{{versionNumber}}"
|
||||
ng:bind-template="v{{version}}">
|
||||
</a>
|
||||
<!-- TODO(i): enable
|
||||
<a ng:hide="offlineEnabled" ng:click ="subpage = true">(enable offline support)</a>
|
||||
<span ng:show="offlineEnabled">(offline support enabled)</span>
|
||||
-->
|
||||
<span id="copyright">© 2010-2012 AngularJS</span>
|
||||
<li class="nav-header section" ng-show="module.filters">
|
||||
<a href="{{URL.filter}}" class="guide">filter</a>
|
||||
</li>
|
||||
<li ng-repeat="page in module.filters" ng-class="navClass(page)">
|
||||
<a href="{{page.url}}" tabindex="2">{{page.shortName}}</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-header section" ng-show="module.services">
|
||||
<a href="{{URL.service}}" class="guide">service</a>
|
||||
</li>
|
||||
<li ng-repeat="service in module.services" ng-class="navClass(service.instance, service.provider)">
|
||||
<a ng-show="service.provider" class="pull-right" href="{{service.provider.url}}" tabindex="2"><i class="icon-cog"></i></a>
|
||||
<a href="{{service.instance.url}}" tabindex="2">{{service.name}}</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-header section" ng-show="module.types">
|
||||
<a href="{{URL.type}}" class="guide">Types</a>
|
||||
</li>
|
||||
<li ng-repeat="page in module.types" ng-class="navClass(page)">
|
||||
<a href="{{page.url}}" tabindex="2">{{page.shortName}}</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-header section" ng-show="module.globals">
|
||||
<a href="{{URL.api}}" class="global guide">global APIs</a>
|
||||
|
||||
</li>
|
||||
<li ng-repeat="page in module.globals" ng-class="navClass(page)">
|
||||
<a href="{{page.url}}" tabindex="2">{{page.id}}</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<div class="span9">
|
||||
|
||||
<ul class="breadcrumb">
|
||||
<li ng-repeat="crumb in breadcrumb">
|
||||
<span ng-hide="crumb.url">{{crumb.name}}</span>
|
||||
<a ng-show="crumb.url" href="{{crumb.url}}">{{crumb.name}}</a>
|
||||
<span ng-show="crumb.url" class="divider">/</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div id="loading" ng-show="loading">Loading...</div>
|
||||
|
||||
<div ng-hide="loading" ng-include src="currentPage.partialUrl" onload="afterPartialLoaded()" autoscroll class="content"></div>
|
||||
|
||||
<div id="disqus" class="disqus">
|
||||
<h2>Discussion</h2>
|
||||
<div id="disqus_thread" class="content-panel-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="fader" ng:show="subpage" style="display: none"></div>
|
||||
<div id="subpage" ng:show="subpage" style="display: none">
|
||||
|
||||
|
||||
<div id="fader" ng-show="subpage" style="display: none"></div>
|
||||
<div id="subpage" ng-show="subpage" style="display: none">
|
||||
<div>
|
||||
<h2>Would you like full offline support for this AngularJS Docs App?</h2>
|
||||
<a ng:click="subpage=false">✕</a>
|
||||
<a ng-click="subpage=false">✕</a>
|
||||
<p>
|
||||
If you want to be able to access the entire AngularJS documentation offline, click the
|
||||
button below. This will reload the current page and trigger background downloads of all the
|
||||
@@ -153,8 +287,34 @@
|
||||
This feature is supported on all modern browsers, except for IE9 which lacks application
|
||||
cache support.
|
||||
</p>
|
||||
<button id="cacheButton" ng:click="enableOffline()">Let me have them all!</button>
|
||||
<button id="cacheButton" ng-click="enableOffline()">Let me have them all!</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<p class="pull-right"><a href="#">Back to top</a></p>
|
||||
|
||||
<p>
|
||||
Super-powered by Google ©2010-2012
|
||||
( <a id="version"
|
||||
ng-href="https://github.com/angular/angular.js/blob/master/CHANGELOG.md#{{versionNumber}}"
|
||||
ng-bind-template="v{{version}}">
|
||||
</a>
|
||||
<!-- TODO(i): enable
|
||||
<a ng-hide="offlineEnabled" ng-click ="subpage = true">(enable offline support)</a>
|
||||
<span ng-show="offlineEnabled">(offline support enabled)</span>
|
||||
-->
|
||||
)
|
||||
</p>
|
||||
<p>
|
||||
Code licensed under the
|
||||
<a href="https://github.com/angular/angular.js/blob/master/LICENSE" target="_blank">The
|
||||
MIT License</a>. Documentation licensed under <a
|
||||
href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a>.
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../../../lib/jquery/jquery.min.js
|
||||
@@ -2,11 +2,11 @@ angular.module('ngdocs.directives', [], function($compileProvider) {
|
||||
|
||||
var angularJsUrl;
|
||||
var scripts = document.getElementsByTagName("script");
|
||||
var angularJsRegex = /^(|.*\/)angular(-.*?)?(\.min)?.js(\?[^#]*)?(#(.*))?$/;
|
||||
var angularJsRegex = /^(|.*\/)angular(-\d.*?)?(\.min)?.js(\?[^#]*)?(#(.*))?$/;
|
||||
for(var j = 0; j < scripts.length; j++) {
|
||||
var src = scripts[j].src;
|
||||
if (src && src.match(angularJsRegex)) {
|
||||
angularJsUrl = src.replace('docs.angularjs.org', 'code.angularjs.org');
|
||||
angularJsUrl = src.replace(/docs(-next)?\.angularjs\.org/, 'code.angularjs.org');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -291,14 +291,14 @@ angular.module('ngdocs.directives', [], function($compileProvider) {
|
||||
} else {
|
||||
prevStep = 'step_' + pad(step - 1);
|
||||
nextStep = 'step_' + pad(step + 1);
|
||||
codeDiff = 'step-' + step + '...step-' + step;
|
||||
codeDiff = 'step-' + (step-1) + '...step-' + step;
|
||||
}
|
||||
|
||||
content = angular.element(
|
||||
'<li><a href="#!/tutorial/' + prevStep + '">Previous</a></li>' +
|
||||
'<li><a href="tutorial/' + prevStep + '">Previous</a></li>' +
|
||||
'<li><a href="http://angular.github.com/angular-phonecat/step-' + step + '/app">Live Demo</a></li>' +
|
||||
'<li><a href="https://github.com/angular/angular-phonecat/compare/' + codeDiff + '">Code Diff</a></li>' +
|
||||
'<li><a href="#!/tutorial/' + nextStep + '">Next</a></li>'
|
||||
'<li><a href="tutorial/' + nextStep + '">Next</a></li>'
|
||||
);
|
||||
|
||||
element.attr('id', 'tutorial-nav');
|
||||
@@ -0,0 +1,516 @@
|
||||
var docsApp = {
|
||||
controller: {},
|
||||
directive: {},
|
||||
serviceFactory: {}
|
||||
};
|
||||
|
||||
|
||||
docsApp.directive.focused = function($defer) {
|
||||
return function(scope, element, attrs) {
|
||||
element[0].focus();
|
||||
element.bind('focus', function() {
|
||||
scope.$apply(attrs.focused + '=true');
|
||||
});
|
||||
element.bind('blur', function() {
|
||||
// have to use defer, so that we close the drop-down after the user clicks,
|
||||
// otherwise when the user clicks we process the closing before we process the click.
|
||||
$defer(attrs.focused + '=false');
|
||||
});
|
||||
scope.$eval(attrs.focused + '=true')
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
docsApp.directive.code = function() {
|
||||
return { restrict:'E', terminal: true };
|
||||
};
|
||||
|
||||
|
||||
docsApp.directive.sourceEdit = function(getEmbeddedTemplate) {
|
||||
return {
|
||||
template: '<button ng-click="fiddle($event)" class="btn btn-primary pull-right"><i class="icon-pencil icon-white"></i> Edit</button>\n',
|
||||
scope: true,
|
||||
controller: function($scope, $attrs, openJsFiddle) {
|
||||
var sources = {
|
||||
module: $attrs.sourceEdit,
|
||||
deps: read($attrs.sourceEditDeps),
|
||||
html: read($attrs.sourceEditHtml),
|
||||
css: read($attrs.sourceEditCss),
|
||||
js: read($attrs.sourceEditJs),
|
||||
unit: read($attrs.sourceEditUnit),
|
||||
scenario: read($attrs.sourceEditScenario)
|
||||
};
|
||||
$scope.fiddle = function(e) {
|
||||
e.stopPropagation();
|
||||
openJsFiddle(sources);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function read(text) {
|
||||
var files = [];
|
||||
angular.forEach(text ? text.split(' ') : [], function(refId) {
|
||||
files.push({name: refId.split('-')[0], content: getEmbeddedTemplate(refId)});
|
||||
});
|
||||
return files;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
docsApp.directive.docTutorialNav = function(templateMerge) {
|
||||
var pages = [
|
||||
'',
|
||||
'step_00', 'step_01', 'step_02', 'step_03', 'step_04',
|
||||
'step_05', 'step_06', 'step_07', 'step_08', 'step_09',
|
||||
'step_10', 'step_11', 'the_end'
|
||||
];
|
||||
return {
|
||||
compile: function(element, attrs) {
|
||||
var seq = 1 * attrs.docTutorialNav,
|
||||
props = {
|
||||
seq: seq,
|
||||
prev: pages[seq],
|
||||
next: pages[2 + seq],
|
||||
diffLo: seq ? (seq - 1): '0~1',
|
||||
diffHi: seq
|
||||
};
|
||||
|
||||
element.addClass('btn-group');
|
||||
element.addClass('tutorial-nav');
|
||||
element.append(templateMerge(
|
||||
'<li class="btn btn-primary"><a href="tutorial/{{prev}}"><i class="icon-step-backward"></i> Previous</a></li>\n' +
|
||||
'<li class="btn btn-primary"><a href="http://angular.github.com/angular-phonecat/step-{{seq}}/app"><i class="icon-play"></i> Live Demo</a></li>\n' +
|
||||
'<li class="btn btn-primary"><a href="https://github.com/angular/angular-phonecat/compare/step-{{diffLo}}...step-{{diffHi}}"><i class="icon-search"></i> Code Diff</a></li>\n' +
|
||||
'<li class="btn btn-primary"><a href="tutorial/{{next}}">Next <i class="icon-step-forward"></i></a></li>', props));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
docsApp.directive.docTutorialReset = function() {
|
||||
function tab(name, command, id, step) {
|
||||
return '' +
|
||||
' <div class=\'tab-pane well\' title="' + name + '" value="' + id + '">\n' +
|
||||
' <ol>\n' +
|
||||
' <li><p>Reset the workspace to step ' + step + '.</p>' +
|
||||
' <pre>' + command + '</pre></li>\n' +
|
||||
' <li><p>Refresh your browser or check the app out on <a href="http://angular.github.com/angular-phonecat/step-{{docTutorialReset}}/app">angular\'s server</a>.</p></li>\n' +
|
||||
' </ol>\n' +
|
||||
' </div>\n';
|
||||
}
|
||||
|
||||
return {
|
||||
compile: function(element, attrs) {
|
||||
var step = attrs.docTutorialReset;
|
||||
element.html(
|
||||
'<div ng-hide="show">' +
|
||||
'<p><a href="" ng-click="show=true;$event.stopPropagation()">Workspace Reset Instructions ➤</a></p>' +
|
||||
'</div>\n' +
|
||||
'<div class="tabbable" ng-show="show" ng-model="$cookies.platformPreference">\n' +
|
||||
tab('Git on Mac/Linux', 'git checkout -f step-' + step, 'gitUnix', step) +
|
||||
tab('Git on Windows', 'git checkout -f step-' + step, 'gitWin', step) +
|
||||
tab('Snapshots on Mac/Linux', './goto_step.sh ' + step, 'snapshotUnix', step) +
|
||||
tab('Snapshots on on Windows', './goto_step.bat ' + step, 'snapshotWin', step) +
|
||||
'</div>\n');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
docsApp.serviceFactory.angularUrls = function($document) {
|
||||
var urls = {};
|
||||
|
||||
angular.forEach($document.find('script'), function(script) {
|
||||
var match = script.src.match(/^.*\/(angular[^\/]*\.js)$/);
|
||||
if (match) {
|
||||
urls[match[1].replace(/(\-\d.*)?(\.min)?\.js$/, '.js')] = match[0];
|
||||
}
|
||||
});
|
||||
|
||||
return urls;
|
||||
}
|
||||
|
||||
|
||||
docsApp.serviceFactory.formPostData = function($document) {
|
||||
return function(url, fields) {
|
||||
var form = angular.element('<form style="display: none;" method="post" action="' + url + '" target="_blank"></form>');
|
||||
angular.forEach(fields, function(value, name) {
|
||||
var input = angular.element('<input type="hidden" name="' + name + '">');
|
||||
input.attr('value', value);
|
||||
form.append(input);
|
||||
});
|
||||
$document.find('body').append(form);
|
||||
form[0].submit();
|
||||
form.remove();
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
docsApp.serviceFactory.openJsFiddle = function(templateMerge, getEmbeddedTemplate, formPostData, angularUrls) {
|
||||
var HTML = '<div ng-app=\"{{module}}\">\n{{html:2}}</div>',
|
||||
CSS = '</style> <!-- Ugly Hack due to jsFiddle issue: http://goo.gl/BUfGZ --> \n' +
|
||||
'{{head:0}}<style>\n.ng-invalid { border: 1px solid red; }\n{{css}}',
|
||||
SCRIPT = '{{script}}',
|
||||
SCRIPT_CACHE = '\n\n<!-- {{name}} -->\n<script type="text/ng-template" id="{{name}}">\n{{content:2}}</script>';
|
||||
|
||||
return function(content) {
|
||||
var prop = {
|
||||
module: content.module,
|
||||
html: '',
|
||||
css: '',
|
||||
script: ''
|
||||
};
|
||||
|
||||
prop.head = templateMerge('<script src="{{url}}"></script>', {url: angularUrls['angular.js']});
|
||||
|
||||
angular.forEach(content.html, function(file, index) {
|
||||
if (index) {
|
||||
prop.html += templateMerge(SCRIPT_CACHE, file);
|
||||
} else {
|
||||
prop.html += file.content;
|
||||
}
|
||||
});
|
||||
|
||||
angular.forEach(content.js, function(file, index) {
|
||||
prop.script += file.content;
|
||||
});
|
||||
|
||||
angular.forEach(content.css, function(file, index) {
|
||||
prop.css += file.content;
|
||||
});
|
||||
|
||||
formPostData("http://jsfiddle.net/api/post/library/pure/", {
|
||||
title: 'AngularJS Example',
|
||||
html: templateMerge(HTML, prop),
|
||||
js: templateMerge(SCRIPT, prop),
|
||||
css: templateMerge(CSS, prop)
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
docsApp.serviceFactory.sections = function sections() {
|
||||
var sections = {
|
||||
guide: [],
|
||||
api: [],
|
||||
tutorial: [],
|
||||
misc: [],
|
||||
cookbook: [],
|
||||
getPage: function(sectionId, partialId) {
|
||||
var pages = sections[sectionId];
|
||||
|
||||
partialId = partialId || 'index';
|
||||
|
||||
for (var i = 0, ii = pages.length; i < ii; i++) {
|
||||
if (pages[i].id == partialId) {
|
||||
return pages[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
angular.forEach(NG_PAGES, function(page) {
|
||||
page.url = page.section + '/' + page.id;
|
||||
if (page.id == 'angular.Module') {
|
||||
page.partialUrl = 'partials/api/angular.IModule.html';
|
||||
} else {
|
||||
page.partialUrl = 'partials/' + page.url + '.html';
|
||||
}
|
||||
|
||||
sections[page.section].push(page);
|
||||
});
|
||||
|
||||
return sections;
|
||||
};
|
||||
|
||||
|
||||
docsApp.controller.DocsController = function($scope, $location, $window, $cookies, sections) {
|
||||
var OFFLINE_COOKIE_NAME = 'ng-offline',
|
||||
DOCS_PATH = /^\/(api)|(guide)|(cookbook)|(misc)|(tutorial)/,
|
||||
INDEX_PATH = /^(\/|\/index[^\.]*.html)$/,
|
||||
GLOBALS = /^angular\.([^\.]*)$/,
|
||||
MODULE = /^angular\.module\.([^\.]*)$/,
|
||||
MODULE_MOCK = /^angular\.mock\.([^\.]*)$/,
|
||||
MODULE_DIRECTIVE = /^angular\.module\.([^\.]*)(?:\.\$compileProvider)?\.directive\.([^\.]*)$/,
|
||||
MODULE_DIRECTIVE_INPUT = /^angular\.module\.([^\.]*)\.\$compileProvider\.directive\.input\.([^\.]*)$/,
|
||||
MODULE_FILTER = /^angular\.module\.([^\.]*)\.\$?filter\.([^\.]*)$/,
|
||||
MODULE_SERVICE = /^angular\.module\.([^\.]*)\.([^\.]*?)(Provider)?$/,
|
||||
MODULE_TYPE = /^angular\.module\.([^\.]*)\..*\.([A-Z][^\.]*)$/,
|
||||
URL = {
|
||||
module: 'guide/module',
|
||||
directive: 'guide/directive',
|
||||
input: 'api/angular.module.ng.$compileProvider.directive.input',
|
||||
filter: 'guide/dev_guide.templates.filters',
|
||||
service: 'guide/dev_guide.services',
|
||||
type: 'guide/types'
|
||||
};
|
||||
|
||||
|
||||
/**********************************
|
||||
Publish methods
|
||||
***********************************/
|
||||
|
||||
$scope.navClass = function(page1, page2) {
|
||||
return {
|
||||
last: this.$position == 'last',
|
||||
active: page1 && this.currentPage == page1 || page2 && this.currentPage == page2
|
||||
};
|
||||
}
|
||||
|
||||
$scope.submitForm = function() {
|
||||
$scope.bestMatch && $location.path($scope.bestMatch.page.url);
|
||||
};
|
||||
|
||||
$scope.afterPartialLoaded = function() {
|
||||
var currentPageId = $location.path();
|
||||
$scope.partialTitle = $scope.currentPage.shortName;
|
||||
$window._gaq.push(['_trackPageview', currentPageId]);
|
||||
loadDisqus(currentPageId);
|
||||
};
|
||||
|
||||
/** stores a cookie that is used by apache to decide which manifest ot send */
|
||||
$scope.enableOffline = function() {
|
||||
//The cookie will be good for one year!
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime()+(365*24*60*60*1000));
|
||||
var expires = "; expires="+date.toGMTString();
|
||||
var value = angular.version.full;
|
||||
document.cookie = OFFLINE_COOKIE_NAME + "="+value+expires+"; path=" + $location.path;
|
||||
|
||||
//force the page to reload so server can serve new manifest file
|
||||
window.location.reload(true);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**********************************
|
||||
Watches
|
||||
***********************************/
|
||||
|
||||
var SECTION_NAME = {
|
||||
api: 'API Reference',
|
||||
guide: 'Developer Guide',
|
||||
misc: 'Miscellaneous',
|
||||
tutorial: 'Tutorial',
|
||||
cookbook: 'Examples'
|
||||
};
|
||||
$scope.$watch(function() {return $location.path(); }, function(path) {
|
||||
// ignore non-doc links which are used in examples
|
||||
if (DOCS_PATH.test(path)) {
|
||||
var parts = path.split('/'),
|
||||
sectionId = parts[1],
|
||||
partialId = parts[2],
|
||||
sectionName = SECTION_NAME[sectionId] || sectionId,
|
||||
page = sections.getPage(sectionId, partialId);
|
||||
|
||||
$scope.currentPage = sections.getPage(sectionId, partialId);
|
||||
|
||||
if (!$scope.currentPage) {
|
||||
$scope.partialTitle = 'Error: Page Not Found!';
|
||||
}
|
||||
|
||||
updateSearch();
|
||||
|
||||
|
||||
// Update breadcrumbs
|
||||
var breadcrumb = $scope.breadcrumb = [],
|
||||
match;
|
||||
|
||||
if (partialId) {
|
||||
breadcrumb.push({ name: sectionName, url: sectionId });
|
||||
if (partialId == 'angular.Module') {
|
||||
breadcrumb.push({ name: 'angular.Module' });
|
||||
} else if (match = partialId.match(GLOBALS)) {
|
||||
breadcrumb.push({ name: partialId });
|
||||
} else if (match = partialId.match(MODULE)) {
|
||||
breadcrumb.push({ name: match[1] });
|
||||
} else if (match = partialId.match(MODULE_SERVICE)) {
|
||||
breadcrumb.push({ name: match[1], url: sectionId + '/angular.module.' + match[1] });
|
||||
breadcrumb.push({ name: match[2] });
|
||||
} else if (match = partialId.match(MODULE_FILTER)) {
|
||||
breadcrumb.push({ name: match[1], url: sectionId + '/angular.module.' + match[1] });
|
||||
breadcrumb.push({ name: match[2] });
|
||||
} else if (match = partialId.match(MODULE_DIRECTIVE)) {
|
||||
breadcrumb.push({ name: match[1], url: sectionId + '/angular.module.' + match[1] });
|
||||
breadcrumb.push({ name: match[2] });
|
||||
} else if (match = partialId.match(MODULE_DIRECTIVE_INPUT)) {
|
||||
breadcrumb.push({ name: match[1], url: sectionId + '/angular.module.' + match[1] });
|
||||
breadcrumb.push({ name: 'input', url: URL.input });
|
||||
breadcrumb.push({ name: match[2] });
|
||||
} else if (match = partialId.match(MODULE_TYPE)) {
|
||||
breadcrumb.push({ name: match[1], url: sectionId + '/angular.module.' + match[1] });
|
||||
breadcrumb.push({ name: match[2] });
|
||||
} else if (match = partialId.match(MODULE_MOCK)) {
|
||||
breadcrumb.push({ name: 'angular.mock.' + match[1] });
|
||||
} else {
|
||||
breadcrumb.push({ name: page.shortName });
|
||||
}
|
||||
} else {
|
||||
breadcrumb.push({ name: sectionName });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$watch('search', updateSearch);
|
||||
|
||||
|
||||
|
||||
/**********************************
|
||||
Initialize
|
||||
***********************************/
|
||||
|
||||
$scope.versionNumber = angular.version.full;
|
||||
$scope.version = angular.version.full + " " + angular.version.codeName;
|
||||
$scope.subpage = false;
|
||||
$scope.offlineEnabled = ($cookies[OFFLINE_COOKIE_NAME] == angular.version.full);
|
||||
$scope.futurePartialTitle = null;
|
||||
$scope.loading = 0;
|
||||
$scope.URL = URL;
|
||||
$scope.$cookies = $cookies;
|
||||
|
||||
$cookies.platformPreference = $cookies.platformPreference || 'gitUnix';
|
||||
|
||||
if (!$location.path() || INDEX_PATH.test($location.path())) {
|
||||
$location.path('/api').replace();
|
||||
}
|
||||
// bind escape to hash reset callback
|
||||
angular.element(window).bind('keydown', function(e) {
|
||||
if (e.keyCode === 27) {
|
||||
$scope.$apply(function() {
|
||||
$scope.subpage = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**********************************
|
||||
Private methods
|
||||
***********************************/
|
||||
|
||||
function updateSearch() {
|
||||
var cache = {},
|
||||
pages = sections[$location.path().split('/')[1]],
|
||||
modules = $scope.modules = [],
|
||||
otherPages = $scope.pages = [],
|
||||
search = $scope.search,
|
||||
bestMatch = {page: null, rank:0};
|
||||
|
||||
angular.forEach(pages, function(page) {
|
||||
var match,
|
||||
id = page.id;
|
||||
|
||||
if (!(match = rank(page, search))) return;
|
||||
|
||||
if (match.rank > bestMatch.rank) {
|
||||
bestMatch = match;
|
||||
}
|
||||
|
||||
if (id == 'angular.Module') {
|
||||
module('ng').types.push(page);
|
||||
} else if (match = id.match(GLOBALS)) {
|
||||
module('ng').globals.push(page);
|
||||
} else if (match = id.match(MODULE)) {
|
||||
module(match[1]);
|
||||
} else if (match = id.match(MODULE_SERVICE)) {
|
||||
module(match[1]).service(match[2])[match[3] ? 'provider' : 'instance'] = page;
|
||||
} else if (match = id.match(MODULE_FILTER)) {
|
||||
module(match[1]).filters.push(page);
|
||||
} else if (match = id.match(MODULE_DIRECTIVE)) {
|
||||
module(match[1]).directives.push(page);
|
||||
} else if (match = id.match(MODULE_DIRECTIVE_INPUT)) {
|
||||
module(match[1]).directives.push(page);
|
||||
} else if (match = id.match(MODULE_TYPE)) {
|
||||
module(match[1]).types.push(page);
|
||||
} else if (match = id.match(MODULE_MOCK)) {
|
||||
module('ngMock').globals.push(page);
|
||||
} else if (page.section != 'api' && page.id != 'index'){
|
||||
otherPages.push(page);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
$scope.bestMatch = bestMatch;
|
||||
|
||||
/*************/
|
||||
|
||||
function module(name) {
|
||||
var module = cache[name];
|
||||
if (!module) {
|
||||
module = cache[name] = {
|
||||
name: name,
|
||||
url: 'api/angular.module.' + name,
|
||||
globals: [],
|
||||
directives: [],
|
||||
services: [],
|
||||
service: function(name) {
|
||||
var service = cache[this.name + ':' + name];
|
||||
if (!service) {
|
||||
service = {name: name};
|
||||
cache[this.name + ':' + name] = service;
|
||||
this.services.push(service);
|
||||
}
|
||||
return service;
|
||||
},
|
||||
types: [],
|
||||
filters: []
|
||||
}
|
||||
modules.push(module);
|
||||
}
|
||||
return module;
|
||||
}
|
||||
|
||||
function rank(page, terms) {
|
||||
var ranking = {page: page, rank:0},
|
||||
keywords = page.keywords,
|
||||
title = page.shortName.toLowerCase();
|
||||
|
||||
terms && angular.forEach(terms.toLowerCase().split(' '), function(term) {
|
||||
var index;
|
||||
|
||||
if (ranking) {
|
||||
if (keywords.indexOf(term) == -1) {
|
||||
ranking = null;
|
||||
} else {
|
||||
ranking.rank ++; // one point for each term found
|
||||
if ((index = title.indexOf(term)) != -1) {
|
||||
ranking.rank += 20 - index; // ten points if you match title
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return ranking;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function loadDisqus(currentPageId) {
|
||||
// http://docs.disqus.com/help/2/
|
||||
window.disqus_shortname = 'angularjs-next';
|
||||
window.disqus_identifier = currentPageId;
|
||||
window.disqus_url = 'http://docs.angularjs.org' + currentPageId;
|
||||
|
||||
if ($location.host() == 'localhost') {
|
||||
return; // don't display disqus on localhost, comment this out if needed
|
||||
//window.disqus_developer = 1;
|
||||
}
|
||||
|
||||
// http://docs.disqus.com/developers/universal/
|
||||
(function() {
|
||||
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
|
||||
dsq.src = 'http://angularjs.disqus.com/embed.js';
|
||||
(document.getElementsByTagName('head')[0] ||
|
||||
document.getElementsByTagName('body')[0]).appendChild(dsq);
|
||||
})();
|
||||
|
||||
angular.element(document.getElementById('disqus_thread')).html('');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
angular.module('docsApp', ['ngResource', 'ngCookies', 'ngSanitize', 'bootstrap', 'bootstrapPrettify']).
|
||||
config(function($locationProvider) {
|
||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||
}).
|
||||
factory(docsApp.serviceFactory).
|
||||
directive(docsApp.directive).
|
||||
controller(docsApp.controller);
|
||||
@@ -0,0 +1 @@
|
||||
../../../../lib/jquery/jquery.js
|
||||
@@ -0,0 +1 @@
|
||||
../../../../lib/jquery/jquery.min.js
|
||||
@@ -1,52 +0,0 @@
|
||||
/**
|
||||
* SyntaxHighlighter
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter
|
||||
*
|
||||
* SyntaxHighlighter is donationware. If you are using it, please donate.
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
|
||||
*
|
||||
* @version
|
||||
* 3.0.83 (July 02 2010)
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (C) 2004-2010 Alex Gorbatchev.
|
||||
*
|
||||
* @license
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
*/
|
||||
;(function()
|
||||
{
|
||||
// CommonJS
|
||||
typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
|
||||
|
||||
function Brush()
|
||||
{
|
||||
var keywords = 'break case catch continue ' +
|
||||
'default delete do else false ' +
|
||||
'for function if in instanceof ' +
|
||||
'new null return super switch ' +
|
||||
'this throw true try typeof var while with'
|
||||
;
|
||||
|
||||
var r = SyntaxHighlighter.regexLib;
|
||||
|
||||
this.regexList = [
|
||||
{ regex: r.multiLineDoubleQuotedString, css: 'string' }, // double quoted strings
|
||||
{ regex: r.multiLineSingleQuotedString, css: 'string' }, // single quoted strings
|
||||
{ regex: r.singleLineCComments, css: 'comments' }, // one line comments
|
||||
{ regex: r.multiLineCComments, css: 'comments' }, // multiline comments
|
||||
{ regex: /\s*#.*/gm, css: 'preprocessor' }, // preprocessor tags like #region and #endregion
|
||||
{ regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' } // keywords
|
||||
];
|
||||
|
||||
this.forHtmlScript(r.scriptScriptTags);
|
||||
};
|
||||
|
||||
Brush.prototype = new SyntaxHighlighter.Highlighter();
|
||||
Brush.aliases = ['js', 'jscript', 'javascript'];
|
||||
|
||||
SyntaxHighlighter.brushes.JScript = Brush;
|
||||
|
||||
// CommonJS
|
||||
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
|
||||
})();
|
||||
@@ -1,69 +0,0 @@
|
||||
/**
|
||||
* SyntaxHighlighter
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter
|
||||
*
|
||||
* SyntaxHighlighter is donationware. If you are using it, please donate.
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
|
||||
*
|
||||
* @version
|
||||
* 3.0.83 (July 02 2010)
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (C) 2004-2010 Alex Gorbatchev.
|
||||
*
|
||||
* @license
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
*/
|
||||
;(function()
|
||||
{
|
||||
// CommonJS
|
||||
typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
|
||||
|
||||
function Brush()
|
||||
{
|
||||
function process(match, regexInfo)
|
||||
{
|
||||
var constructor = SyntaxHighlighter.Match,
|
||||
code = match[0],
|
||||
tag = new XRegExp('(<|<)[\\s\\/\\?]*(?<name>[:\\w-\\.]+)', 'xg').exec(code),
|
||||
result = []
|
||||
;
|
||||
|
||||
if (match.attributes != null)
|
||||
{
|
||||
var attributes,
|
||||
regex = new XRegExp('(?<name> [\\w:\\-\\.]+)' +
|
||||
'\\s*=\\s*' +
|
||||
'(?<value> ".*?"|\'.*?\'|\\w+)',
|
||||
'xg');
|
||||
|
||||
while ((attributes = regex.exec(code)) != null)
|
||||
{
|
||||
result.push(new constructor(attributes.name, match.index + attributes.index, 'color1'));
|
||||
result.push(new constructor(attributes.value, match.index + attributes.index + attributes[0].indexOf(attributes.value), 'string'));
|
||||
}
|
||||
}
|
||||
|
||||
if (tag != null)
|
||||
result.push(
|
||||
new constructor(tag.name, match.index + tag[0].indexOf(tag.name), 'keyword')
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
this.regexList = [
|
||||
{ regex: new XRegExp('(\\<|<)\\!\\[[\\w\\s]*?\\[(.|\\s)*?\\]\\](\\>|>)', 'gm'), css: 'color2' }, // <![ ... [ ... ]]>
|
||||
{ regex: SyntaxHighlighter.regexLib.xmlComments, css: 'comments' }, // <!-- ... -->
|
||||
{ regex: new XRegExp('(<|<)[\\s\\/\\?]*(\\w+)(?<attributes>.*?)[\\s\\/\\?]*(>|>)', 'sg'), func: process }
|
||||
];
|
||||
};
|
||||
|
||||
Brush.prototype = new SyntaxHighlighter.Highlighter();
|
||||
Brush.aliases = ['xml', 'xhtml', 'xslt', 'html'];
|
||||
|
||||
SyntaxHighlighter.brushes.Xml = Brush;
|
||||
|
||||
// CommonJS
|
||||
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
|
||||
})();
|
||||
@@ -1,226 +0,0 @@
|
||||
/**
|
||||
* SyntaxHighlighter
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter
|
||||
*
|
||||
* SyntaxHighlighter is donationware. If you are using it, please donate.
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
|
||||
*
|
||||
* @version
|
||||
* 3.0.83 (July 02 2010)
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (C) 2004-2010 Alex Gorbatchev.
|
||||
*
|
||||
* @license
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
*/
|
||||
.syntaxhighlighter a,
|
||||
.syntaxhighlighter div,
|
||||
.syntaxhighlighter code,
|
||||
.syntaxhighlighter table,
|
||||
.syntaxhighlighter table td,
|
||||
.syntaxhighlighter table tr,
|
||||
.syntaxhighlighter table tbody,
|
||||
.syntaxhighlighter table thead,
|
||||
.syntaxhighlighter table caption,
|
||||
.syntaxhighlighter textarea {
|
||||
-moz-border-radius: 0 0 0 0 !important;
|
||||
-webkit-border-radius: 0 0 0 0 !important;
|
||||
background: none !important;
|
||||
border: 0 !important;
|
||||
bottom: auto !important;
|
||||
float: none !important;
|
||||
height: auto !important;
|
||||
left: auto !important;
|
||||
line-height: 1.1em !important;
|
||||
margin: 0 !important;
|
||||
outline: 0 !important;
|
||||
overflow: visible !important;
|
||||
padding: 0 !important;
|
||||
position: static !important;
|
||||
right: auto !important;
|
||||
text-align: left !important;
|
||||
top: auto !important;
|
||||
vertical-align: baseline !important;
|
||||
width: auto !important;
|
||||
box-sizing: content-box !important;
|
||||
font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
|
||||
font-weight: normal !important;
|
||||
font-style: normal !important;
|
||||
font-size: 1em !important;
|
||||
min-height: inherit !important;
|
||||
min-height: auto !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter {
|
||||
width: 100% !important;
|
||||
margin: 1em 0 1em 0 !important;
|
||||
position: relative !important;
|
||||
overflow: auto !important;
|
||||
font-size: 1em !important;
|
||||
}
|
||||
.syntaxhighlighter.source {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
.syntaxhighlighter .bold {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
.syntaxhighlighter .italic {
|
||||
font-style: italic !important;
|
||||
}
|
||||
.syntaxhighlighter .line {
|
||||
white-space: pre !important;
|
||||
}
|
||||
.syntaxhighlighter table {
|
||||
width: 100% !important;
|
||||
}
|
||||
.syntaxhighlighter table caption {
|
||||
text-align: left !important;
|
||||
padding: .5em 0 0.5em 1em !important;
|
||||
}
|
||||
.syntaxhighlighter table td.code {
|
||||
width: 100% !important;
|
||||
}
|
||||
.syntaxhighlighter table td.code .container {
|
||||
position: relative !important;
|
||||
}
|
||||
.syntaxhighlighter table td.code .container textarea {
|
||||
box-sizing: border-box !important;
|
||||
position: absolute !important;
|
||||
left: 0 !important;
|
||||
top: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
border: none !important;
|
||||
background: white !important;
|
||||
padding-left: 1em !important;
|
||||
overflow: hidden !important;
|
||||
white-space: pre !important;
|
||||
}
|
||||
.syntaxhighlighter table td.gutter .line {
|
||||
text-align: right !important;
|
||||
padding: 0 0.5em 0 1em !important;
|
||||
}
|
||||
.syntaxhighlighter table td.code .line {
|
||||
padding: 0 1em !important;
|
||||
}
|
||||
.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
|
||||
padding-left: 0em !important;
|
||||
}
|
||||
.syntaxhighlighter.show {
|
||||
display: block !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed table {
|
||||
display: none !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar {
|
||||
padding: 0.1em 0.8em 0em 0.8em !important;
|
||||
font-size: 1em !important;
|
||||
position: static !important;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar span {
|
||||
display: inline !important;
|
||||
margin-right: 1em !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar span a {
|
||||
padding: 0 !important;
|
||||
display: none !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar span a.expandSource {
|
||||
display: inline !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar {
|
||||
position: absolute !important;
|
||||
right: 1px !important;
|
||||
top: 1px !important;
|
||||
width: 11px !important;
|
||||
height: 11px !important;
|
||||
font-size: 10px !important;
|
||||
z-index: 10 !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar span.title {
|
||||
display: inline !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar a {
|
||||
display: block !important;
|
||||
text-align: center !important;
|
||||
text-decoration: none !important;
|
||||
padding-top: 1px !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar a.expandSource {
|
||||
display: none !important;
|
||||
}
|
||||
.syntaxhighlighter.ie {
|
||||
font-size: .9em !important;
|
||||
padding: 1px 0 1px 0 !important;
|
||||
}
|
||||
.syntaxhighlighter.ie .toolbar {
|
||||
line-height: 8px !important;
|
||||
}
|
||||
.syntaxhighlighter.ie .toolbar a {
|
||||
padding-top: 0px !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .line.alt1 .content,
|
||||
.syntaxhighlighter.printing .line.alt2 .content,
|
||||
.syntaxhighlighter.printing .line.highlighted .number,
|
||||
.syntaxhighlighter.printing .line.highlighted.alt1 .content,
|
||||
.syntaxhighlighter.printing .line.highlighted.alt2 .content {
|
||||
background: none !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .line .number {
|
||||
color: #bbbbbb !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .line .content {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .toolbar {
|
||||
display: none !important;
|
||||
}
|
||||
.syntaxhighlighter.printing a {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
|
||||
color: #008200 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
|
||||
color: blue !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .keyword {
|
||||
color: #006699 !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .preprocessor {
|
||||
color: gray !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .variable {
|
||||
color: #aa7700 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .value {
|
||||
color: #009900 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .functions {
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .constants {
|
||||
color: #0066cc !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .script {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
|
||||
color: gray !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
|
||||
color: red !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
|
||||
color: black !important;
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
/**
|
||||
* SyntaxHighlighter
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter
|
||||
*
|
||||
* SyntaxHighlighter is donationware. If you are using it, please donate.
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
|
||||
*
|
||||
* @version
|
||||
* 3.0.83 (July 02 2010)
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (C) 2004-2010 Alex Gorbatchev.
|
||||
*
|
||||
* @license
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
*/
|
||||
.syntaxhighlighter {
|
||||
background-color: white !important;
|
||||
}
|
||||
.syntaxhighlighter .line.alt1 {
|
||||
background-color: white !important;
|
||||
}
|
||||
.syntaxhighlighter .line.alt2 {
|
||||
background-color: white !important;
|
||||
}
|
||||
.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
|
||||
background-color: #e0e0e0 !important;
|
||||
}
|
||||
.syntaxhighlighter .line.highlighted.number {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter table caption {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter .gutter {
|
||||
color: #afafaf !important;
|
||||
}
|
||||
.syntaxhighlighter .gutter .line {
|
||||
border-right: 3px solid #6ce26c !important;
|
||||
}
|
||||
.syntaxhighlighter .gutter .line.highlighted {
|
||||
background-color: #6ce26c !important;
|
||||
color: white !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .line .content {
|
||||
border: none !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed {
|
||||
overflow: visible !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar {
|
||||
color: blue !important;
|
||||
background: white !important;
|
||||
border: 1px solid #6ce26c !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar a {
|
||||
color: blue !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar a:hover {
|
||||
color: red !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar {
|
||||
color: white !important;
|
||||
background: #6ce26c !important;
|
||||
border: none !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar a {
|
||||
color: white !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar a:hover {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
|
||||
color: #008200 !important;
|
||||
}
|
||||
.syntaxhighlighter .string, .syntaxhighlighter .string a {
|
||||
color: blue !important;
|
||||
}
|
||||
.syntaxhighlighter .keyword {
|
||||
color: #006699 !important;
|
||||
}
|
||||
.syntaxhighlighter .preprocessor {
|
||||
color: gray !important;
|
||||
}
|
||||
.syntaxhighlighter .variable {
|
||||
color: #aa7700 !important;
|
||||
}
|
||||
.syntaxhighlighter .value {
|
||||
color: #009900 !important;
|
||||
}
|
||||
.syntaxhighlighter .functions {
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
.syntaxhighlighter .constants {
|
||||
color: #0066cc !important;
|
||||
}
|
||||
.syntaxhighlighter .script {
|
||||
font-weight: bold !important;
|
||||
color: #006699 !important;
|
||||
background-color: none !important;
|
||||
}
|
||||
.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
|
||||
color: gray !important;
|
||||
}
|
||||
.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
|
||||
color: red !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .keyword {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
3.0.83
|
||||
|
||||
@@ -7,7 +7,8 @@ var Q = require('qq');
|
||||
var OUTPUT_DIR = "build/docs/";
|
||||
var fs = require('fs');
|
||||
|
||||
exports.output = function(file, content) {
|
||||
exports.output = output;
|
||||
function output(file, content) {
|
||||
var fullPath = OUTPUT_DIR + file;
|
||||
var dir = parent(fullPath);
|
||||
return Q.when(exports.makeDir(dir), function(error) {
|
||||
@@ -39,7 +40,7 @@ exports.makeDir = function(path) {
|
||||
};
|
||||
|
||||
exports.copyTpl = function(filename) {
|
||||
return exports.copy('docs/src/templates/' + filename, OUTPUT_DIR + filename);
|
||||
return exports.copy('docs/src/templates/' + filename, filename);
|
||||
};
|
||||
|
||||
/* Copy files from one place to another.
|
||||
@@ -56,7 +57,7 @@ exports.copy = function(from, to, transform) {
|
||||
args.unshift(content.toString());
|
||||
content = transform.apply(null, args);
|
||||
}
|
||||
qfs.write(to, content);
|
||||
return output(to, content);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -72,25 +73,15 @@ exports.replace = function(content, replacements) {
|
||||
}
|
||||
|
||||
exports.copyDir = function copyDir(dir) {
|
||||
return qfs.listDirectoryTree('docs/' + dir).then(function(dirs) {
|
||||
var done;
|
||||
dirs.forEach(function(dirToMake) {
|
||||
done = Q.when(done, function() {
|
||||
return exports.makeDir("./build/" + dirToMake);
|
||||
});
|
||||
});
|
||||
return done;
|
||||
}).then(function() {
|
||||
return qfs.listTree('docs/' + dir);
|
||||
}).then(function(files) {
|
||||
files.forEach( function(file) {
|
||||
exports.copy(file,'./build/' + file);
|
||||
return qfs.listTree('docs/' + dir).then(function(files) {
|
||||
files.forEach(function(file) {
|
||||
exports.copy(file, file.replace(/^docs\//, ''));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
exports.merge = function(srcs, to) {
|
||||
return merge(srcs.map(function(src) { return 'docs/src/templates/' + src; }), OUTPUT_DIR + to);
|
||||
return merge(srcs.map(function(src) { return 'docs/src/templates/' + src; }), to);
|
||||
};
|
||||
|
||||
function merge(srcs, to) {
|
||||
@@ -107,7 +98,7 @@ function merge(srcs, to) {
|
||||
// write to file
|
||||
return Q.when(done, function(content) {
|
||||
contents.push(content);
|
||||
qfs.write(to, contents.join('\n'));
|
||||
return output(to, contents.join('\n'));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -130,6 +121,8 @@ exports.toString = function toString(obj) {
|
||||
obj[key] = toString(value);
|
||||
});
|
||||
return obj.join('');
|
||||
} else if (obj.constructor.name == 'Buffer'){
|
||||
// do nothing it is Buffer Object
|
||||
} else {
|
||||
return JSON.stringify(obj);
|
||||
}
|
||||
|
||||
@@ -1,28 +1,31 @@
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org" ng:app>
|
||||
<html ng-app>
|
||||
<head>
|
||||
<title>Personal Log</title>
|
||||
<script type="text/javascript" src="../../src/angular-bootstrap.js"></script>
|
||||
<script type="text/javascript" src="personalLog.js"></script>
|
||||
<script src="../../src/loader.js"></script>
|
||||
<script>
|
||||
setupModuleLoader(window);
|
||||
</script>
|
||||
<script src="personalLog.js"></script>
|
||||
<script src="../../src/angular-bootstrap.js"></script>
|
||||
<script src="../../src/ngCookies/cookies.js"></script>
|
||||
</head>
|
||||
|
||||
|
||||
<!-- TODO: we need to expose $root so that we can delete cookies in the scenario runner, there
|
||||
must be a better way to do this -->
|
||||
<body ng:controller="example.personalLog.LogCtrl">
|
||||
<body ng-controller="LogCtrl">
|
||||
|
||||
<form action="" ng:submit="addLog(newMsg)">
|
||||
<input type="text" ng:model="newMsg" />
|
||||
<input type="submit" value="add" />
|
||||
<input type="button" value="remove all" ng:click="rmLogs()" />
|
||||
<form action="" ng-submit="addLog(newMsg)">
|
||||
<input type="text" ng-model="newMsg">
|
||||
<input type="submit" value="add">
|
||||
<input type="button" value="remove all" ng-click="rmLogs()">
|
||||
</form>
|
||||
|
||||
<hr/>
|
||||
<h2>Logs:</h2>
|
||||
<ul>
|
||||
<li ng:repeat="log in logs | orderBy:'-at'">
|
||||
<li ng-repeat="log in logs | orderBy:'-at'">
|
||||
{{log.at | date:'yy-MM-dd HH:mm'}} {{log.msg}}
|
||||
[<a href="" ng:click="rmLog(log)">x</a>]
|
||||
[<a href="" ng-click="rmLog(log)">x</a>]
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||