Compare commits
128 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2f128c9619 | |||
| f7a5f1788a | |||
| b3ed7a8a7a | |||
| 62d34e1437 | |||
| 855971c385 | |||
| 2d489ff936 | |||
| fc5cda2f72 | |||
| 05ad1ce90c | |||
| 5703984d4d | |||
| ea8952177e | |||
| 90ca6f983e | |||
| 57030bb6b4 | |||
| 710a27030e | |||
| 19aa16c8d5 | |||
| 4a1972c71b | |||
| 6aa04b1db4 | |||
| ac6e1306ec | |||
| e004378d10 | |||
| 4ec1d8ee86 | |||
| c37bfde9eb | |||
| f6bcbb53f0 | |||
| 53a4580d95 | |||
| 4c8eaa1eb0 | |||
| 4ba35eb97e | |||
| 6fb4bf4c54 | |||
| cc604b6e26 | |||
| 99ee6f275a | |||
| 21c4919a5b | |||
| 714759100c | |||
| ee8e981c47 | |||
| 05e2c3196c | |||
| 718ebf1fcf | |||
| 2f5d17f3b6 | |||
| fd792de9e8 | |||
| a01cf6d39e | |||
| f93e9bfa81 | |||
| 590cd14ae0 | |||
| 74db92cd9f | |||
| aacd5b672e | |||
| 8d64793717 | |||
| 908f59a5df | |||
| a45d383da2 | |||
| c1a681d6f4 | |||
| f4df421b44 | |||
| bdef462ccc | |||
| a79231dea6 | |||
| 3e54a1b18a | |||
| 4b90f65614 | |||
| b5594a773a | |||
| f39420e7d7 | |||
| 72e46548b8 | |||
| 9dea9de449 | |||
| bee78a8492 | |||
| f3e04fbd6a | |||
| 00ea08e0ab | |||
| 31b59efa96 | |||
| 17251372b1 | |||
| f768954f38 | |||
| 3237f8b995 | |||
| 7802c90e13 | |||
| c348f2cad6 | |||
| f3456dc282 | |||
| ee04141a5a | |||
| 66fec10dc3 | |||
| ae75c35746 | |||
| 0cf5535333 | |||
| fdd5d9471f | |||
| 0782422d1f | |||
| 8fa066190a | |||
| e90b741c94 | |||
| 3af1e7ca2e | |||
| 0fbaa2f12a | |||
| ad3b8d7bcf | |||
| 3ea2416f80 | |||
| 9636160332 | |||
| 2b2df4754d | |||
| 120701b9d9 | |||
| fe5240732d | |||
| b98c23274b | |||
| 4c6d26a38f | |||
| c43ce91b25 | |||
| b7cf7f2a79 | |||
| ef7cf60ebd | |||
| 7974e7eb5f | |||
| f9b4c9da64 | |||
| 83ac1193f2 | |||
| 7a3fdda965 | |||
| b4f18fc295 | |||
| da464683aa | |||
| a0b35161a6 | |||
| a8f4d87be5 | |||
| 57ea8156a1 | |||
| 6289d18e61 | |||
| 9e37ebe635 | |||
| 345c01c81b | |||
| 975aef2ad2 | |||
| c863514660 | |||
| 86a6cc7152 | |||
| 8f3276bbcd | |||
| 2428907259 | |||
| 8a8a2cf462 | |||
| 47efe44a1d | |||
| c52e749a6e | |||
| 4ab3596295 | |||
| 106674ac1e | |||
| 330d1a870d | |||
| 7e2e7b07b6 | |||
| ce80576e0b | |||
| 10da625ed9 | |||
| 9ee9ca13da | |||
| bb39d34279 | |||
| e09a78438f | |||
| 1e890863e5 | |||
| 76a500179d | |||
| 7b32c71386 | |||
| 28e84ca167 | |||
| 41250e9cf9 | |||
| 77ba539f63 | |||
| 952225f020 | |||
| 35f9f527d3 | |||
| 30bd04feaa | |||
| 25a77c58c1 | |||
| 75721223b5 | |||
| f606ffed4b | |||
| b49035a8c5 | |||
| 86ff9dee23 | |||
| 6dc22fe575 | |||
| 68ab0f9b02 |
@@ -9,3 +9,4 @@ performance/temp*.html
|
||||
.idea/workspace.xml
|
||||
*~
|
||||
angular.js.tmproj
|
||||
node_modules
|
||||
|
||||
+114
-5
@@ -1,5 +1,109 @@
|
||||
<a name="0.9.19"><a/>
|
||||
# 0.9.19 canine-psychokinesis (2011-08-20) #
|
||||
|
||||
## Features
|
||||
- added error handling support for JSONP requests (see error callback param of the [$xhr] service)
|
||||
([commit](https://github.com/angular/angular.js/commit/05e2c3196c857402a9aa93837b565e0a2736af23))
|
||||
- exposed http response headers in the [$xhr] and [$resource] callbacks
|
||||
([commit](https://github.com/angular/angular.js/commit/4ec1d8ee86e3138fb91543ca0dca28463895c090)
|
||||
contributed by Karl Seamon)
|
||||
- added `reloadOnSearch` [$route] param support to prevent unnecessary controller reloads and
|
||||
resulting flicker
|
||||
([commit](https://github.com/angular/angular.js/commit/e004378d100ce767a1107180102790a9a360644e))
|
||||
|
||||
|
||||
## Fixes
|
||||
- make ng:class-even/odd compatible with ng:class
|
||||
(Issue [#508](https://github.com/angular/angular.js/issues/508))
|
||||
- fixed error handling for resources that didn't work in certain situations
|
||||
([commit](https://github.com/angular/angular.js/commit/c37bfde9eb31556ee1eb146795b0c1f1504a4a26)
|
||||
contributed by Karl Seamon)
|
||||
|
||||
|
||||
## Docs
|
||||
- [jsFiddle](http://jsfiddle.net/) integration for all docs.angularjs.org examples (contributed by
|
||||
Dan Doyon).
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
- removed [jqLite] show/hide support. See the
|
||||
[commit](https://github.com/angular/angular.js/commit/4c8eaa1eb05ba98d30ff83f4420d6fcd69045d99)
|
||||
message for details. Developers should use jquery or jqLite's `css('display', 'none')` and
|
||||
`css('display', 'block'/'inline'/..)` instead
|
||||
|
||||
|
||||
<a name="0.9.18"><a/>
|
||||
# 0.9.18 jiggling-armfat (2011-07-29) #
|
||||
|
||||
### Features
|
||||
- [ECMAScript 5 Strict Mode](https://developer.mozilla.org/en/JavaScript/Strict_mode) compliance
|
||||
- [jqLite]
|
||||
- added `show()`, `hide()` and `eq()` methods to jqlite
|
||||
([commit](https://github.com/angular/angular.js/commit/7a3fdda9650a06792d9278a8cef06d544d49300f))
|
||||
- added $defer.cancel to support cancelation of tasks defered via the [$defer] service
|
||||
- [date] filter
|
||||
- added support for `full`, `long`, `medium` and `short` date-time format flags
|
||||
([commit](https://github.com/angular/angular.js/commit/3af1e7ca2ee8c2acd69e5bcbb3ffc1bf51239285))
|
||||
- added support for `z` flag, which stands for short string timezone identifier, e.g. PST
|
||||
- internal improvements to enable localization of date filter output
|
||||
- [number] filter
|
||||
- internal improvements to enable localization of number filter output
|
||||
- [currency] filter
|
||||
- support for custom currency symbols via an optional param
|
||||
- internal improvements to enable localization of number filter output
|
||||
- added [angular.version] for exposing the version of the loaded angular.js file
|
||||
- updated angular.js and angular.min.js file headers with angular version and shorter & updated
|
||||
license info
|
||||
- [ng:options]
|
||||
- support binding to expression (Issue [#449](https://github.com/angular/angular.js/issues/449))
|
||||
- support iterating over objects (Issue [#448](https://github.com/angular/angular.js/issues/448))
|
||||
- support ng:change (Issue [#463](https://github.com/angular/angular.js/issues/463))
|
||||
- support option groups (`<optgroup>`)
|
||||
(Issue [#450](https://github.com/angular/angular.js/issues/450))
|
||||
- [$xhr] and [$resource] support for per-request error callbacks (Issue
|
||||
[#408](https://github.com/angular/angular.js/issues/408)) (contributed by Karl Seamon)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
- make injector compatible with Rhino (HtmlUnit) (contributed by Mårten Dolk)
|
||||
[commit](https://github.com/angular/angular.js/commit/77ba539f630c57b17d71dbf1e9c5667a7eb603b7)
|
||||
- `ie-compat.js` fixes and improvements related to fetching this file on the fly on legacy browsers
|
||||
- [jqLite]
|
||||
- fix `bind()` when binding to more events separated by space
|
||||
[commit](https://github.com/angular/angular.js/commit/9ee9ca13da3883d06733637f9048a83d94e6f1f8)
|
||||
- non-existing attributes should return undefined just like in jQuery
|
||||
[commit](https://github.com/angular/angular.js/commit/10da625ed93511dbf5d4e61ca4e42f6f2d478959)
|
||||
- set event.target for IE<8
|
||||
[commit](https://github.com/angular/angular.js/commit/ce80576e0b8ac9ed5a5b1f1a4dbc2446434a0002)
|
||||
- improved implementation of [ng:show] and [ng:hide] directives by using jqLite/jQuery hide and
|
||||
show methods
|
||||
- [ng:options]
|
||||
- fix incorrect re-growing of options on datasource change
|
||||
(Issue [#464](https://github.com/angular/angular.js/issues/464))
|
||||
|
||||
|
||||
### Docs
|
||||
- added full offline support for docs (click on the link in the footer of docs.angularjs.org)
|
||||
- many content improvements and corrections across all docs (reference api, tutorial, dev guide)
|
||||
- many small design improvements
|
||||
|
||||
|
||||
### Other
|
||||
- doubled our e2e test suite by running all angular e2e tests with jqLite in addition to jQuery
|
||||
|
||||
|
||||
### Breaking changes
|
||||
- [commit](https://github.com/angular/angular.js/commit/3af1e7ca2ee8c2acd69e5bcbb3ffc1bf51239285)
|
||||
removed support for the `MMMMM` (long month name), use `MMMM` instead. This was done to align
|
||||
Angular with
|
||||
[Unicode Technical Standard #35](http://unicode.org/reports/tr35/#Date_Format_Patterns) used by
|
||||
Closure, as well as, future DOM apis currently being proposed to w3c.
|
||||
- `$xhr.error`'s `request` argument has no `callback` property anymore, use `success` instead
|
||||
|
||||
|
||||
|
||||
<a name="0.9.17"><a/>
|
||||
# <angular/> 0.9.17 vegetable-reanimation (in progress) #
|
||||
# <angular/> 0.9.17 vegetable-reanimation (2011-06-30) #
|
||||
|
||||
### New Features
|
||||
- New [ng:options] directive to better bind a model to `<select>` and `<option>` elements.
|
||||
@@ -25,7 +129,7 @@
|
||||
- $service now has $service.invoke for method injection ($service(self, fn) no longer works)
|
||||
- injection name inference no longer supports method curry and linking functions. Both must be
|
||||
explicitly specified using $inject property.
|
||||
- Dynamic iteration (ng:repeat) on <option> elements is no longer supported. Use ng:options
|
||||
- Dynamic iteration (ng:repeat) on `<option>` elements is no longer supported. Use ng:options
|
||||
- Removal of index formatter (`ng:format="index"`) since its only use was with repeated `<options>`
|
||||
(see above).
|
||||
- Calling [$orderBy] without a predicate now returns the original unsorted array, instead of
|
||||
@@ -74,7 +178,7 @@
|
||||
### Bug Fixes
|
||||
- reverted [ng:view] sync cache fix due to regression in the order of initialization of parent
|
||||
and child controllers. (commits 9bd2c396 and 3d388498)
|
||||
- [$resource] success callback is now executed whenever the http status code is <200,300>
|
||||
- [$resource] success callback is now executed whenever the http status code is `<200,300>`
|
||||
|
||||
|
||||
### Docs
|
||||
@@ -197,8 +301,7 @@
|
||||
- many, but by far not all, docs were updated, improved and cleaned up
|
||||
|
||||
### Features
|
||||
- [`$route`](http://docs.angularjs.org/#!/api/angular.service.$route) service now supports these
|
||||
features:
|
||||
- [$route] service now supports these features:
|
||||
- route not found handling via `#otherwise()`
|
||||
- redirection support via `#when('/foo', {redirectTo: '/bar'})` (including param interpolation)
|
||||
- setting the parent scope for scopes created by the service via `#parent()`
|
||||
@@ -550,17 +653,23 @@ with the `$route` service
|
||||
[ng:checked]: http://docs.angularjs.org/#!/api/angular.directive.ng:checked
|
||||
[ng:multiple]: http://docs.angularjs.org/#!/api/angular.directive.ng:multiple
|
||||
[ng:readonly]: http://docs.angularjs.org/#!/api/angular.directive.ng:readonly
|
||||
[ng:show]: http://docs.angularjs.org/#!/api/angular.directive.ng:show
|
||||
[ng:hide]: http://docs.angularjs.org/#!/api/angular.directive.ng:hide
|
||||
[$defer]: http://docs.angularjs.org/#!/api/angular.service.$defer
|
||||
[$cookies]: http://docs.angularjs.org/#!/api/angular.service.$cookies
|
||||
[$xhr]: http://docs.angularjs.org/#!/api/angular.service.$xhr
|
||||
[$xhr.cache]: http://docs.angularjs.org/#!/api/angular.service.$xhr.cache
|
||||
[$resource]: http://docs.angularjs.org/#!/api/angular.service.$resource
|
||||
[$route]: http://docs.angularjs.org/#!/api/angular.service.$route
|
||||
[$orderBy]: http://docs.angularjs.org/#!/api/angular.Array.orderBy
|
||||
[date]: http://docs.angularjs.org/#!/api/angular.filter.date
|
||||
[number]: http://docs.angularjs.org/#!/api/angular.filter.number
|
||||
[currency]: http://docs.angularjs.org/#!/api/angular.filter.currency
|
||||
[directive]: http://docs.angularjs.org/#!/api/angular.directive
|
||||
[ng:autobind]: http://docs.angularjs.org/#!/api/angular.directive.ng:autobind
|
||||
[guide.di]: http://docs.angularjs.org/#!/guide/dev_guide.di
|
||||
[downloading]: http://docs.angularjs.org/#!/misc/downloading
|
||||
[contribute]: http://docs.angularjs.org/#!/misc/contribute
|
||||
[jqLite]: http://docs.angularjs.org/#!/api/angular.element
|
||||
[angular.version]: http://docs.angularjs.org/#!/api/angular.version
|
||||
[Jstd Scenario Adapter]: https://github.com/angular/angular.js/blob/master/src/jstd-scenario-adapter/Adapter.js
|
||||
|
||||
@@ -64,6 +64,16 @@ task :default => [:compile, :test]
|
||||
desc 'Init the build workspace'
|
||||
task :init do
|
||||
FileUtils.mkdir(BUILD_DIR) unless File.directory?(BUILD_DIR)
|
||||
|
||||
v = YAML::load( File.open( 'version.yaml' ) )
|
||||
match = v['version'].match(/^([^-]*)(-snapshot)?$/)
|
||||
|
||||
NG_VERSION = Struct.new(:full, :major, :minor, :dot, :codename).
|
||||
new(match[1] + (match[2] ? ('-' + %x(git rev-parse HEAD)[0..7]) : ''),
|
||||
match[1].split('.')[0],
|
||||
match[1].split('.')[1],
|
||||
match[1].split('.')[2],
|
||||
v['codename'])
|
||||
end
|
||||
|
||||
|
||||
@@ -88,7 +98,7 @@ task :compile_scenario => :init do
|
||||
concat = 'cat ' + deps.flatten.join(' ')
|
||||
|
||||
File.open(path_to('angular-scenario.js'), 'w') do |f|
|
||||
f.write(%x{#{concat}})
|
||||
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
|
||||
@@ -106,7 +116,7 @@ task :compile_jstd_scenario_adapter => :init do
|
||||
concat = 'cat ' + deps.flatten.join(' ')
|
||||
|
||||
File.open(path_to('jstd-scenario-adapter.js'), 'w') do |f|
|
||||
f.write(%x{#{concat}})
|
||||
f.write(%x{#{concat}}.gsub('"NG_VERSION_FULL"', NG_VERSION.full))
|
||||
end
|
||||
|
||||
# TODO(vojta) use jstd configuration when implemented
|
||||
@@ -157,11 +167,13 @@ task :generate_ie_compat => :init do
|
||||
# generate a javascript closure that contains a function which will append the generated css
|
||||
# string as a stylesheet to the current html document
|
||||
jsString = "(function(){ \r\n" +
|
||||
" var jsUri = document.location.href.replace(/\\/[^\/]+(#.*)?$/, '/') + " +
|
||||
" document.getElementById('ng-ie-compat').src; \r\n" +
|
||||
" var css = '#{cssString}' \r\n" +
|
||||
" var s = document.createElement('style'); \r\n" +
|
||||
" var jsUri = document.location.href.replace(/\\/[^\\\/]+(#.*)?$/, '/') + \r\n" +
|
||||
" document.getElementById('ng-ie-compat').src,\r\n" +
|
||||
" css = '#{cssString}',\r\n" +
|
||||
" s = document.createElement('style'); \r\n" +
|
||||
"\r\n" +
|
||||
" s.setAttribute('type', 'text/css'); \r\n" +
|
||||
"\r\n" +
|
||||
" if (s.styleSheet) { \r\n" +
|
||||
" s.styleSheet.cssText = css; \r\n" +
|
||||
" } else { \r\n" +
|
||||
@@ -186,32 +198,45 @@ task :compile => [:init, :compile_scenario, :compile_jstd_scenario_adapter, :gen
|
||||
|
||||
File.open(path_to('angular.js'), 'w') do |f|
|
||||
concat = 'cat ' + deps.flatten.join(' ')
|
||||
f.write(%x{#{concat}})
|
||||
|
||||
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')})
|
||||
end
|
||||
|
||||
|
||||
desc 'Generate docs'
|
||||
task :docs do
|
||||
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)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
desc 'Create angular distribution'
|
||||
task :package => [:clean, :compile, :docs] do
|
||||
v = YAML::load( File.open( 'version.yaml' ) )['version']
|
||||
match = v.match(/^([^-]*)(-snapshot)?$/)
|
||||
version = match[1] + (match[2] ? ('-' + %x(git rev-parse HEAD)[0..7]) : '')
|
||||
tarball = "angular-#{NG_VERSION.full}.tgz"
|
||||
|
||||
tarball = "angular-#{version}.tgz"
|
||||
|
||||
pkg_dir = path_to("pkg/angular-#{version}")
|
||||
pkg_dir = path_to("pkg/angular-#{NG_VERSION.full}")
|
||||
FileUtils.rm_r(path_to('pkg'), :force => true)
|
||||
FileUtils.mkdir_p(pkg_dir)
|
||||
|
||||
@@ -223,33 +248,71 @@ task :package => [:clean, :compile, :docs] do
|
||||
path_to('jstd-scenario-adapter.js'),
|
||||
path_to('jstd-scenario-adapter-config.js'),
|
||||
].each do |src|
|
||||
dest = src.gsub(/^[^\/]+\//, '').gsub(/((\.min)?\.js)$/, "-#{version}\\1")
|
||||
dest = src.gsub(/^[^\/]+\//, '').gsub(/((\.min)?\.js)$/, "-#{NG_VERSION.full}\\1")
|
||||
FileUtils.cp(src, pkg_dir + '/' + dest)
|
||||
end
|
||||
|
||||
FileUtils.cp_r path_to('docs'), "#{pkg_dir}/docs-#{version}"
|
||||
FileUtils.cp_r path_to('docs'), "#{pkg_dir}/docs-#{NG_VERSION.full}"
|
||||
|
||||
File.open("#{pkg_dir}/docs-#{version}/index.html", File::RDWR) do |f|
|
||||
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-#{version}.min.js")
|
||||
f.write text.sub('angular.min.js', "angular-#{NG_VERSION.full}.min.js")
|
||||
end
|
||||
|
||||
File.open("#{pkg_dir}/docs-#{version}/docs-scenario.html", File::RDWR) do |f|
|
||||
|
||||
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-scenario.js', "angular-scenario-#{version}.js")
|
||||
f.write text.sub('angular.min.js', "angular-#{NG_VERSION.full}.min.js")
|
||||
end
|
||||
|
||||
File.open("#{pkg_dir}/docs-#{version}/app-cache.manifest", File::RDWR) do |f|
|
||||
|
||||
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-#{version}.min.js")
|
||||
f.write text.sub('angular.min.js', "angular-#{NG_VERSION.full}.min.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")
|
||||
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")
|
||||
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")
|
||||
end
|
||||
|
||||
|
||||
%x(tar -czf #{path_to(tarball)} -C #{path_to('pkg')} .)
|
||||
|
||||
FileUtils.cp path_to(tarball), pkg_dir
|
||||
FileUtils.mv pkg_dir, path_to(['pkg', NG_VERSION.full])
|
||||
|
||||
puts "Package created: #{path_to(tarball)}"
|
||||
end
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ to retrieve Buzz activity and comments.
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('fetch buzz and expand', function() {
|
||||
xit('fetch buzz and expand', function() {
|
||||
element(':button:contains(fetch)').click();
|
||||
expect(repeater('div.buzz').count()).toBeGreaterThan(0);
|
||||
element('.buzz a:contains(Expand replies):first').click();
|
||||
|
||||
@@ -93,7 +93,7 @@ no connection between the controller and the view.
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should play a game', function(){
|
||||
xit('should play a game', function(){
|
||||
piece(1, 1);
|
||||
expect(binding('nextMove')).toEqual('O');
|
||||
piece(3, 1);
|
||||
|
||||
@@ -8,7 +8,7 @@ We want this HTML source:
|
||||
|
||||
<pre>
|
||||
<div ng:init="s='Hello'; n='World'">
|
||||
<my:greeter salutation="s" name="n"/>
|
||||
<my:greeter salutation="s" name="n"></my:greeter>
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
@@ -23,9 +23,9 @@ to produce this DOM:
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
That is, the new `<my:greeter/>` tag's `salutation` and `name` attributes should be transformed by
|
||||
the compiler such that two `<span>` tags display the values of the attributes, with CSS classes
|
||||
applied to the output.
|
||||
That is, the new `<my:greeter></my:greeter>` tag's `salutation` and `name` attributes should be
|
||||
transformed by the compiler such that two `<span>` tags display the values of the attributes, with
|
||||
CSS classes applied to the output.
|
||||
|
||||
The following code snippet shows how to write a following widget definition that will be processed
|
||||
by the compiler. Note that you have to declare the {@link dev_guide.bootstrap namespace} `my` in
|
||||
@@ -41,9 +41,9 @@ angular.widget('my:greeter', function(compileElement){
|
||||
var salutationSpan = angular.element('<span class="salutation"></span');
|
||||
var nameSpan = angular.element('<span class="name"></span>');
|
||||
linkElement.append(salutationSpan);
|
||||
linkElement.append(compiler.text(' '));
|
||||
linkElement.append(' ');
|
||||
linkElement.append(nameSpan);
|
||||
linkElement.append(compiler.text('!'));
|
||||
linkElement.append('!');
|
||||
this.$watch(salutationExp, function(value){
|
||||
salutationSpan.text(value);
|
||||
});
|
||||
|
||||
@@ -57,12 +57,9 @@ angular.markup('---', function(text, textNode, parentElement) {
|
||||
var compiler = this;
|
||||
var index = text.indexOf('---');
|
||||
if (index > -1) {
|
||||
var before = compiler.text(text.substring(0, index));
|
||||
var hr = compiler.element('hr');
|
||||
var after = compiler.text(text.substring(index + 3));
|
||||
textNode.after(after);
|
||||
textNode.after(hr);
|
||||
textNode.after(before);
|
||||
textNode.after(text.substring(index + 3));
|
||||
textNode.after(angular.element('<hr>'));
|
||||
textNode.after(text.substring(0, index));
|
||||
textNode.remove();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ expression and `alert()` the user with each new value:
|
||||
|
||||
<pre>
|
||||
// An element widget
|
||||
<my:watch exp="name"/>
|
||||
<my:watch exp="name"></my:watch>
|
||||
</pre>
|
||||
|
||||
You can implement `my:watch` like this:
|
||||
@@ -36,8 +36,8 @@ Let's implement the same widget as in the example in Defining an Element Widget,
|
||||
an attribute that can be added to any existing DOM element:
|
||||
|
||||
<pre>
|
||||
// An attribute widget (my-watch) in a div tag
|
||||
<div my-watch="name">text</div>
|
||||
// An attribute widget (my:watch) in a div tag
|
||||
<div my:watch="name">text</div>
|
||||
</pre>
|
||||
You can implement `my:watch` attribute like this:
|
||||
<pre>
|
||||
@@ -45,7 +45,7 @@ angular.widget('@my:watch', function(expression, compileElement) {
|
||||
var compiler = this;
|
||||
return function(linkElement) {
|
||||
var currentScope = this;
|
||||
currentScope.$watch(expression, function(value){
|
||||
currentScope.$watch(expression, function(value) {
|
||||
alert(value);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -87,14 +87,8 @@ function fnB($window, serviceA_, name){};
|
||||
</pre>
|
||||
|
||||
If angular does not find a `$inject` annotation on the function, then it calls the `.toString()`
|
||||
method and tries to infer what should be injected using the following rules:
|
||||
|
||||
* Any argument starting with `$` is an angular service and will be added to the `$inject` property
|
||||
array
|
||||
* Any argument ending with `_` will be added to the `$inject` property array (angular strips the
|
||||
`_`)
|
||||
* All arguments following an argument which has neither `$` nor `_` , must not have `$` nor `_`
|
||||
(these are free arguments for {@link http://en.wikipedia.org/wiki/Currying currying})
|
||||
method and tries to infer what should be injected by using function argument names as dependency
|
||||
identifiers.
|
||||
|
||||
**IMPORTANT**
|
||||
Minifiers/obfuscators change the names of function arguments and will therefore break the `$inject`
|
||||
|
||||
@@ -39,7 +39,7 @@ only, not recommended for real applications):
|
||||
Angular creates models implicitly (by creating a scope property and assigning it a suitable value)
|
||||
when processing the following template constructs:
|
||||
|
||||
* Form input, select, and textarea elements:
|
||||
* Form input, select, textarea and other form elements:
|
||||
|
||||
<input name="query" value="fluffy cloud">
|
||||
|
||||
|
||||
@@ -43,21 +43,17 @@ easier a web developer's life can if they're using angular:
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<b>Invoice:</b>
|
||||
<br/>
|
||||
<br/>
|
||||
<br />
|
||||
<br />
|
||||
<table>
|
||||
<tr><td> </td><td> </td>
|
||||
<tr><td>Quantity</td><td>Cost</td></tr>
|
||||
<tr>
|
||||
<td><input name="qty" value="1"
|
||||
ng:validate="integer:0"
|
||||
ng:required/></td>
|
||||
<td><input name="cost" value="19.95"
|
||||
ng:validate="number"
|
||||
ng:required/></td>
|
||||
<td><input name="qty" value="1" ng:validate="integer:0" ng:required /></td>
|
||||
<td><input name="cost" value="19.95" ng:validate="number" ng:required /></td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr>
|
||||
<hr />
|
||||
<b>Total:</b> {{qty * cost | currency}}
|
||||
</doc:source>
|
||||
<!--
|
||||
|
||||
@@ -4,39 +4,42 @@
|
||||
@description
|
||||
|
||||
Following is a unit test for the service in the example in {@link
|
||||
dev_guide.services.registering_services Registering Angular Services}. The unit test example uses
|
||||
Jasmine spy (mock) instead of a real browser alert.
|
||||
dev_guide.services.creating_services Creating Angular Services}. The unit test example uses Jasmine
|
||||
spy (mock) instead of a real browser alert.
|
||||
|
||||
<pre>
|
||||
var mock, notify;
|
||||
|
||||
beforeEach(function() {
|
||||
mock = {alert: jasmine.createSpy()};
|
||||
notify = angular.service('notify')(mock);
|
||||
mock = {alert: jasmine.createSpy()};
|
||||
notify = angular.service('notify')(mock);
|
||||
});
|
||||
|
||||
it('should not alert first two notifications', function() {
|
||||
notify('one');
|
||||
notify('two');
|
||||
expect(mock.alert).not.toHaveBeenCalled();
|
||||
notify('one');
|
||||
notify('two');
|
||||
|
||||
expect(mock.alert).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should alert all after third notification', function() {
|
||||
notify('one');
|
||||
notify('two');
|
||||
notify('three');
|
||||
expect(mock.alert).toHaveBeenCalledWith("one\ntwo\nthree");
|
||||
notify('one');
|
||||
notify('two');
|
||||
notify('three');
|
||||
|
||||
expect(mock.alert).toHaveBeenCalledWith("one\ntwo\nthree");
|
||||
});
|
||||
|
||||
it('should clear messages after alert', function() {
|
||||
notify('one');
|
||||
notify('two');
|
||||
notify('third');
|
||||
notify('more');
|
||||
notify('two');
|
||||
notify('third');
|
||||
expect(mock.alert.callCount).toEqual(2);
|
||||
expect(mock.alert.mostRecentCall.args).toEqual(["more\ntwo\nthird"]);
|
||||
notify('one');
|
||||
notify('two');
|
||||
notify('third');
|
||||
notify('more');
|
||||
notify('two');
|
||||
notify('third');
|
||||
|
||||
expect(mock.alert.callCount).toEqual(2);
|
||||
expect(mock.alert.mostRecentCall.args).toEqual(["more\ntwo\nthird"]);
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
@@ -87,6 +87,15 @@ Rake website.
|
||||
development web server. Depending on your system, you can install Node either from source or as a
|
||||
pre-packaged bundle.
|
||||
|
||||
You'll also need npm and the following npm modules:
|
||||
|
||||
* install npm: `curl http://npmjs.org/install.sh | sh`
|
||||
* install q: `npm install q`
|
||||
* install qq: `npm install qq`
|
||||
* install q-fs: `npm install q-fs`
|
||||
|
||||
|
||||
|
||||
* Java: The Java runtime is used to run {@link http://code.google.com/p/js-test-driver
|
||||
JsTestDriver} (JSTD), which we use to run our unit test suite. JSTD binaries are part of the
|
||||
`angular` source base, which means there is no need to install or configure it separately.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
# Hello World!
|
||||
|
||||
A great way for you to get started with `angular` is to create the tradtional
|
||||
A great way for you to get started with AngularJS is to create the tradtional
|
||||
"Hello World!" app:
|
||||
|
||||
1. In your favorite text editor, create an HTML file
|
||||
@@ -27,13 +27,13 @@ Now let's take a closer look at that code, and see what is going on behind
|
||||
the scenes.
|
||||
|
||||
The first line of interest defines the `ng` namespace, which makes
|
||||
`angular` work across all browsers (especially important for IE):
|
||||
AngularJS work across all browsers (especially important for IE):
|
||||
|
||||
<pre>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
</pre>
|
||||
|
||||
The next line downloads the `angular` script, and instructs `angular` to process
|
||||
The next line downloads the angular script, and instructs angular to process
|
||||
the entire HTML page when it is loaded:
|
||||
|
||||
<pre>
|
||||
@@ -41,7 +41,7 @@ the entire HTML page when it is loaded:
|
||||
ng:autobind></script>
|
||||
</pre>
|
||||
|
||||
(For details on what happens when `angular` processes an HTML page,
|
||||
(For details on what happens when angular processes an HTML page,
|
||||
see {@link guide/dev_guide.bootstrap Bootstrap}.)
|
||||
|
||||
Finally, this line in the `<body>` of the page is the template that describes
|
||||
@@ -54,16 +54,16 @@ how to display our greeting in the UI:
|
||||
Note the use of the double curly brace markup (`{{ }}`) to bind the expression to
|
||||
the greeting text. Here the expression is the string literal 'World'.
|
||||
|
||||
Next let's look at a more interesting example, that uses `angular` to
|
||||
Next let's look at a more interesting example, that uses AngularJS to
|
||||
bind a dynamic expression to our greeting text.
|
||||
|
||||
# Hello <angular/> World!
|
||||
# Hello AngularJS World!
|
||||
|
||||
This example demonstrates `angular`'s two-way data binding:
|
||||
This example demonstrates angular's two-way data binding:
|
||||
|
||||
1. Edit the HTML file you created in the "Hello World!" example above.
|
||||
2. Replace the contents of `<body>` with the code from the __Source__ box below.
|
||||
3. Refresh your browswer window.
|
||||
3. Refresh your browser window.
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
@@ -86,7 +86,7 @@ called `yourname`.
|
||||
* You did not need to explicitly register an event listener or define an event handler for events!
|
||||
|
||||
Now try typing your name into the input box, and notice the immediate change to
|
||||
the displayed greeting. This demonstrates the concept of `angular`'s
|
||||
the displayed greeting. This demonstrates the concept of angular's
|
||||
{@link guide/dev_guide.templates.databinding bi-directional data binding}. Any changes to the input
|
||||
field are immediately
|
||||
reflected in the model (one direction), and any changes to the model are
|
||||
@@ -135,8 +135,11 @@ In addition, angular comes with a set of Services, which have the following prop
|
||||
|
||||
# Where To Go Next
|
||||
|
||||
* For explanations and examples of the angular concepts presented on this page, see the {@link
|
||||
guide/index Developer Guide}.
|
||||
* If you like what you've learned so far, you should definitely check out our awesome {@link
|
||||
tutorial/ Tutorial}, which walk you through the process of building real apps with AngularJS.
|
||||
|
||||
* For additional hands-on examples of using `angular`, including more source code that you can
|
||||
copy and paste into your own pages, take a look through the `angular` {@link cookbook/ Cookbook}.
|
||||
* For further explanations and examples of the AngularJS concepts presented on this page, see the
|
||||
{@link guide/index Developer Guide}.
|
||||
|
||||
* For additional hands-on examples of using AngularJS, including more source code that you can
|
||||
copy and paste into your own pages, take a look through the {@link cookbook/ Cookbook}.
|
||||
|
||||
@@ -2,40 +2,40 @@
|
||||
@name Tutorial
|
||||
@description
|
||||
|
||||
A great way to get introduced to angular is to work through this tutorial, which walks you through
|
||||
the construction of an angular 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
|
||||
A great way to get introduced to Angular 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 src="img/tutorial/catalog_screen.png">
|
||||
|
||||
As you work through this tutorial, you will learn how angular makes browsers smarter — without the
|
||||
use of extensions or plugins.
|
||||
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:
|
||||
|
||||
* 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.
|
||||
* You will see how angular creates listeners on your data without the need for DOM manipulation.
|
||||
* You will learn a better, easier way to test your web apps.
|
||||
* You will learn how to use angular services to make common web tasks, such as getting data into
|
||||
your app, easier.
|
||||
* See examples of how to use client-side data binding and dependency injection to build dynamic
|
||||
views of data that change immediately in response to user actions.
|
||||
* See how Angular creates listeners on your data without the need for DOM manipulation.
|
||||
* Learn a better, easier way to test your web apps.
|
||||
* Learn how to use Angular services to make common web tasks, such as getting data into your app,
|
||||
easier.
|
||||
|
||||
And all of this works in any browser without modifications!
|
||||
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 angular
|
||||
* Use the angular-seed project to quickly boot-strap your own projects
|
||||
* Create and run tests
|
||||
* Identify resources for learning more about angular
|
||||
* 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 is will guide you through the process of building a simple application, including
|
||||
writing and running unit and end-to-end tests, and will allow you to experiment with angular and
|
||||
the application through experiments suggested at the end of each step.
|
||||
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 angular, check out the
|
||||
really digging into it. If you're looking for a shorter introduction to AngularJS, check out the
|
||||
{@link misc/started Getting Started} document.
|
||||
|
||||
|
||||
@@ -46,11 +46,11 @@ really digging into it. If you're looking for a shorter introduction to angular,
|
||||
|
||||
# Working with the code
|
||||
|
||||
There are two ways that you can you follow this tutorial and hack on the code, both available on
|
||||
Mac/Linux or Windows environment. The first work flow uses Git versioning system for source code
|
||||
management, the second work flow doesn't depend on any source control system and instead uses
|
||||
scripts to copy snapshots of project files into your workspace (`sandbox`) directory. Choose the
|
||||
one you prefer:
|
||||
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.
|
||||
|
||||
<doc:tutorial-instructions show="true">
|
||||
<doc:tutorial-instruction id="git-mac" title="Git on Mac/Linux">
|
||||
@@ -59,67 +59,68 @@ one you prefer:
|
||||
following command in a terminal window:</p>
|
||||
<pre><code>java -version</code></pre>
|
||||
<p>You will need Java to run unit tests.</p></li>
|
||||
<li><p>Get Git from <a href="http://git-scm.com/download">here</a></p>
|
||||
<p>You can build it from source or use pre-compiled package.</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 this command:</p>
|
||||
href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
|
||||
<pre><code>git clone git://github.com/angular/angular-phonecat.git</code></pre>
|
||||
<p>This will create <code>angular-phonecat</code> directory in current directory.</p></li>
|
||||
<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><code>cd angular-phonecat</code></pre>
|
||||
<p>The tutorial instructions assume you are running all commands from this directory.</p></li>
|
||||
<li><p>You'll also need an http server running on your system. Mac and Linux machines
|
||||
typically have Apache preinstalled.</p>
|
||||
<p>If you don't already have an http server installed, you can <a
|
||||
<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="https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager">install
|
||||
node.js</a> and use it to run <code>scripts/web-server.js</code> — a simple bundled http
|
||||
server.</p></li>
|
||||
node.js</a>. Use <code>node</code> to run <code>scripts/web-server.js</code>, a simple bundled
|
||||
http server.</p></li>
|
||||
</ol>
|
||||
</doc:tutorial-instruction>
|
||||
|
||||
<doc:tutorial-instruction id="git-win" title="Git 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 this command in windows
|
||||
command line:</p>
|
||||
<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><code>java -version</code></pre>
|
||||
<p>You will need Java to run unit tests.</p></li>
|
||||
<li><p>Install msysGit from <a href="http://git-scm.com/download">here</a></p></li>
|
||||
<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 this command:</p>
|
||||
href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
|
||||
<pre><code>git clone git://github.com/angular/angular-phonecat.git</code></pre>
|
||||
<p>This will create angular-phonecat directory in your current directory.</p></li>
|
||||
<li><p>Change your current directory to angular-phonecat:</p>
|
||||
<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><code>cd angular-phonecat</code></pre>
|
||||
<p>The tutorial instructions assume you are running all commands from this directory.</p>
|
||||
<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> that will be
|
||||
introduced soon, should be executed from the windows command line.</li>
|
||||
<li><p>You'll also need an http server running on your system.</p>
|
||||
<p>If you don't already have an http server installed, you can install <a
|
||||
href="http://nodejs.org/">node.js</a>. Just download <a
|
||||
href="http://node-js.prcn.co.cc/">pre-compiled binaries</a>, unzip them and add
|
||||
<code>nodejs\bin</code> into your <code>PATH</code> and use <code>node</code> to run
|
||||
<code>scripts\web-server.js</code> — a simple bundled http server.</p></li>
|
||||
<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. If you don't already have one
|
||||
installed, you can install <a href="http://nodejs.org/">node.js</a>. Download the <a
|
||||
href="http://node-js.prcn.co.cc/">pre-compiled binaries</a>, unzip them, and then add
|
||||
<code>nodejs\bin</code> 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>
|
||||
</doc:tutorial-instruction>
|
||||
|
||||
<doc:tutorial-instruction id="ss-mac" title="Snapshots 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>
|
||||
<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><code>java -version</code></pre>
|
||||
<p>You will need Java to run unit tests.</p></li>
|
||||
<li><p>Download the <a href="http://code.angularjs.org/angular-phonecat/">zip archive</a>
|
||||
with all files and unzip them into [tutorial-dir] directory</p></li>
|
||||
<li><p>Change your current directory to [tutorial-dir]/sanbox:</p>
|
||||
<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><code>cd [tutorial-dir]/sandbox</code></pre>
|
||||
<p>The tutorial instructions assume you are running all commands from this directory.</p></li>
|
||||
<li><p>You'll also need an http server running on your system. Mac and Linux machines
|
||||
typically have Apache preinstalled.</p>
|
||||
<p>If you don't already have an http server installed, you can <a
|
||||
<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="https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager">install
|
||||
node.js</a> and use it to run <code>scripts/web-server.js</code> — a simple bundled http
|
||||
node.js</a> and use it to run <code>scripts/web-server.js</code>, a simple bundled http
|
||||
server.</p></li>
|
||||
</ol>
|
||||
</doc:tutorial-instruction>
|
||||
@@ -127,25 +128,23 @@ server.</p></li>
|
||||
<doc:tutorial-instruction 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 this command in windows
|
||||
command line:</p>
|
||||
<code>java</code> executable is on your <code>PATH</code> by running the following command in the
|
||||
Windows command line:</p>
|
||||
<pre><code>java -version</code></pre>
|
||||
<p>You will need Java to run unit tests.</p></li>
|
||||
<li><p>Download the <a href="http://code.angularjs.org/angular-phonecat/">zip archive</a>
|
||||
with all files and unzip them into [tutorial-dir] directory</p></li>
|
||||
<li><p>Change your current directory to [tutorial-dir]/sanbox:</p>
|
||||
<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><code>cd [tutorial-dir]/sandbox</code></pre>
|
||||
<p>The tutorial instructions assume you are running all commands from this directory.</p></li>
|
||||
<li><p>You'll also need an http server running on your system.</p>
|
||||
<p>If you don't already have an http server installed, you can install <a
|
||||
href="http://nodejs.org/">node.js</a>. Just download <a
|
||||
href="http://node-js.prcn.co.cc/">pre-compiled binaries</a>, unzip them and add
|
||||
<code>nodejs\bin</code> into your <code>PATH</code> and use <code>node</code> to run
|
||||
<code>scripts\web-server.js</code> — a simple bundled http server.</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/">node.js</a>. Download the <a
|
||||
href="http://node-js.prcn.co.cc/">pre-compiled binaries</a>, unzip them, and then add
|
||||
<code>nodejs\bin</code> 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>
|
||||
</doc:tutorial-instruction>
|
||||
</doc:tutorial-instructions>
|
||||
|
||||
For either work flow you'll also need a web browser and your favorite text editor.
|
||||
|
||||
Let's get going with {@link step_00 step 0}.
|
||||
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 going with {@link step_00 step 0}.
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<ul doc:tutorial-nav="0"></ul>
|
||||
|
||||
|
||||
You are now ready to build the phonecat application. In this step, you will become familiar with
|
||||
the most important source code files, learn how to start the development servers bundled with
|
||||
You are now ready to build the Angular phonecat application. 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.
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ directory.</li>
|
||||
|
||||
<doc:tutorial-instruction id="ss-mac" title="Snapshots on Mac/Linux">
|
||||
<ol>
|
||||
<li><p>In angular-phonecat directory, run this command:</p>
|
||||
<li><p>In the angular-phonecat directory, run this command:</p>
|
||||
<pre><code>./goto_step.sh 0</code></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
|
||||
@@ -110,7 +110,7 @@ href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html
|
||||
|
||||
<doc:tutorial-instruction id="ss-win" title="Snapshots on Windows">
|
||||
<ol>
|
||||
<li><p>Open windows command line and run this command (in angular-phonecat directory):</p>
|
||||
<li><p>Open windows command line and run this command (in the angular-phonecat directory):</p>
|
||||
<pre><code>goto_step.bat 0</code></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
|
||||
@@ -144,7 +144,7 @@ href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html
|
||||
You can now see the page in your browser. It's not very exciting, but that's OK.
|
||||
|
||||
The static 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.
|
||||
below. The code contains some key Angular elements that we will need going forward.
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
@@ -172,25 +172,25 @@ __`app/index.html`:__
|
||||
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
|
||||
This `xmlns` declaration for the `ng` namespace must be specified in all angular applications in
|
||||
order to make angular work with XHTML and IE versions older than 9 (regardless of whether you are
|
||||
This `xmlns` declaration for the `ng` namespace must be specified in all Angular applications in
|
||||
order to make Angular work with XHTML and IE versions older than 9 (regardless of whether you are
|
||||
using XHTML or HTML).
|
||||
|
||||
* angular script tag
|
||||
* Angular script tag
|
||||
|
||||
<script src="lib/angular/angular.js" ng:autobind>
|
||||
|
||||
This single line of code is all that is needed to bootstrap an angular application.
|
||||
|
||||
The 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.directive.ng:autobind ng:autobind} attribute. If angular finds
|
||||
browser when the containing HTML page is fully downloaded. When the callback is executed, Angular
|
||||
looks for the {@link api/angular.directive.ng:autobind ng:autobind} attribute. If Angular finds
|
||||
`ng:autobind`, it creates a root scope for the application and associates it with the `<html>`
|
||||
element of the template:
|
||||
|
||||
<img src="img/tutorial/tutorial_00_final.png">
|
||||
|
||||
As you will see shortly, everything in angular is evaluated within a scope. We'll learn more
|
||||
As you will see shortly, everything in Angular is evaluated within a scope. We'll learn more
|
||||
about this in the next steps.
|
||||
|
||||
|
||||
@@ -198,7 +198,7 @@ about this in the next steps.
|
||||
|
||||
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,
|
||||
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:
|
||||
@@ -210,7 +210,7 @@ For the purposes of this tutorial, we modified the angular-seed with the followi
|
||||
|
||||
# Summary
|
||||
|
||||
Now let's go to step 1 and add some content to the web app.
|
||||
Now let's go to {@link step_01 step 1} and add some content to the web app.
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="0"></ul>
|
||||
|
||||
@@ -50,8 +50,8 @@ __`app/index.html`:__
|
||||
|
||||
# Summary
|
||||
|
||||
This addition to your app uses static HTML to display the list. Now, let's go to step 2 to learn
|
||||
how to use angular to dynamically generate the same list.
|
||||
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 angular to dynamically generate the same list.
|
||||
|
||||
|
||||
<ul doc:tutorial-nav="1"></ul>
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
@ngdoc overview
|
||||
@name Tutorial: 2 - Angular Template
|
||||
@name Tutorial: 2 - Angular Templates
|
||||
@description
|
||||
|
||||
<ul doc:tutorial-nav="2"></ul>
|
||||
|
||||
|
||||
Now it's time to make this web page dynamic with angular. We'll also add a test that verifies the
|
||||
Now it's time to make the web page dynamic -- with Angular. 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. With angular, we encourage the use of
|
||||
{@link http://en.wikipedia.org/wiki/Model–View–Controller the MVC design pattern} to decouple the
|
||||
code and separate concerns. With that in mind, let's use a little angular and JavaScript to add
|
||||
model, view, and controller components to our app.
|
||||
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.
|
||||
|
||||
|
||||
<doc:tutorial-instructions step="2"></doc:tutorial-instructions>
|
||||
|
||||
|
||||
The app now contains a list with 3 phones.
|
||||
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}:
|
||||
@@ -25,7 +25,7 @@ https://github.com/angular/angular-phonecat/compare/step-1...step-2 GitHub}:
|
||||
|
||||
## Template for the View
|
||||
|
||||
The __view__ component is constructed by angular from this template:
|
||||
The __view__ component is constructed by Angular from this template:
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
@@ -46,22 +46,24 @@ __`app/index.html`:__
|
||||
</pre>
|
||||
|
||||
We replaced the hard-coded phone list with the {@link api/angular.widget.@ng:repeat ng:repeat
|
||||
widget} and two {@link guide/dev_guide.expressions angular expressions} enclosed in curly braces:
|
||||
widget} 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. It tells
|
||||
angular to create a `<li>` element for each phone in the phones list, using the first `<li>` tag as
|
||||
the template.
|
||||
* 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.
|
||||
|
||||
<img src="img/tutorial/tutorial_02_final.png">
|
||||
|
||||
* The curly braces around `phone.name` and `phone.snippet` are an example of {@link
|
||||
guide/dev_guide.compiler.markup angular markup}. The curly markup is shorthand for the angular
|
||||
directive {@link api/angular.directive.ng:bind ng:bind}. The `ng:bind` directives indicate to
|
||||
angular that these are template binding points. Binding points are locations in the template where
|
||||
angular creates data-binding between the view and the model. 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 curly braces around `phone.name` and `phone.snippet` are examples of {@link
|
||||
guide/dev_guide.compiler.markup Angular markup}. The curly markup is shorthand for the Angular
|
||||
directive {@link api/angular.directive.ng:bind ng:bind}. An `ng:bind` directive indicates a
|
||||
template binding point to Angular. Binding points are locations in a template where Angular creates
|
||||
data-binding between the view and the model.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
## Model and Controller
|
||||
@@ -86,26 +88,27 @@ function PhoneListCtrl() {
|
||||
|
||||
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. Note in the following how we connected the dots between our presentation,
|
||||
data, and logic components:
|
||||
the model and the view. We connected the dots between the presentation, data, and logic components
|
||||
as follows:
|
||||
|
||||
* The name of our controller function (in the JavaScript file `controllers.js`) matches the {@link
|
||||
api/angular.directive.ng:controller ng:controller} directive in the `<body>` tag (`PhoneListCtrl`).
|
||||
* We instantiated our data within the scope of our controller function, and our template binding
|
||||
* The data is instantiated within the *scope* of our controller function; our template binding
|
||||
points are located within the block bounded by the `<body ng:controller="PhoneListCtrl">` tag.
|
||||
|
||||
Angular scopes are a crucial concept in angular; you can think of scopes as the glue that makes the
|
||||
template, model and controller all work together. Angular uses scopes, along with the information
|
||||
contained in the template, data model, and controller, to keep the model and view separated but in
|
||||
sync. Any changes 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.scope
|
||||
angular scope documentation}.
|
||||
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.scope angular scope documentation}.
|
||||
|
||||
|
||||
## Tests
|
||||
|
||||
The "Angular way" makes it easy for us to test as we develop; the unit test for your newly created
|
||||
controller looks as follows:
|
||||
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>
|
||||
@@ -121,14 +124,16 @@ describe('PhoneCat controllers', function() {
|
||||
});
|
||||
</pre>
|
||||
|
||||
Ease of testing is another cornerstone of angular's design philosophy. All we are doing here is
|
||||
showing how easy it is to create a unit test. The test verifies that we have 3 records in the
|
||||
phones array.
|
||||
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 Jasmine is not required by angular, we used it to write all tests in this
|
||||
tutorial. 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}.
|
||||
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:
|
||||
@@ -189,8 +194,9 @@ and kill the script, then repeat the procedure above.
|
||||
|
||||
# Summary
|
||||
|
||||
You now have a dynamic app that features separate model, view, and controller components, and
|
||||
you're testing as you go. Now, let's go to step 3 to learn how to add full text search to the app.
|
||||
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>
|
||||
|
||||
@@ -6,19 +6,19 @@
|
||||
|
||||
|
||||
We did a lot of work in laying a foundation for the app in the last step, so now we'll do something
|
||||
simple, and 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.
|
||||
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.
|
||||
|
||||
|
||||
<doc:tutorial-instructions step="3"></doc:tutorial-instructions>
|
||||
|
||||
|
||||
The app now has a search box. The phone list on the page changes depending on what a user types
|
||||
into the search box.
|
||||
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 changes are listed below. You can see the full diff on {@link
|
||||
https://github.com/angular/angular-phonecat/compare/step-2...step-3
|
||||
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}:
|
||||
|
||||
|
||||
@@ -43,13 +43,13 @@ __`app/index.html`:__
|
||||
...
|
||||
</pre>
|
||||
|
||||
We added a standard HTML `<input>` tag and use angular's {@link api/angular.Array.filter $filter}
|
||||
We added a standard HTML `<input>` tag and used angular's {@link api/angular.Array.filter $filter}
|
||||
function to process the input for the `ng:repeater`.
|
||||
|
||||
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:
|
||||
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
|
||||
* 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
|
||||
@@ -59,7 +59,7 @@ the DOM to reflect the current state of the model.
|
||||
|
||||
<img src="img/tutorial/tutorial_03_final.png">
|
||||
|
||||
* Use of `$filter`. The {@link api/angular.Array.filter $filter} method, uses the `query` value, to
|
||||
* Use of `$filter`. The {@link api/angular.Array.filter $filter} method uses the `query` value to
|
||||
create a new array that contains only those records that match the `query`.
|
||||
|
||||
`ng:repeat` automatically updates the view in response to the changing number of phones returned
|
||||
@@ -67,7 +67,7 @@ by the `$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
|
||||
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.
|
||||
@@ -101,9 +101,9 @@ describe('PhoneCat App', function() {
|
||||
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
|
||||
https://docs.google.com/document/d/11L8htLKrh6c92foV71ytYpiKkeKpM4_a5-9c3HywfIc/edit?hl=en&pli=1#
|
||||
angular's end-to-end test runner}.
|
||||
Angular's end-to-end test runner}.
|
||||
|
||||
To run the end-to-end test, open the following in a new browser tab:
|
||||
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:
|
||||
@@ -111,7 +111,7 @@ To run the end-to-end test, open the following in a new browser tab:
|
||||
* 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
|
||||
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
|
||||
@@ -138,6 +138,14 @@ and title elements:
|
||||
|
||||
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.directive.ng:bind ng:bind} or {@link
|
||||
api/angular.directive.ng:bind-template ng:bind-template} 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>
|
||||
@@ -154,20 +162,20 @@ and title elements:
|
||||
});
|
||||
</pre>
|
||||
|
||||
Refresh the browser tab with end-to-end test runner to see the test fail. Now add a `div` or `p`
|
||||
element with `id` `"status"` and content with the `query` binding into the `index.html` template to
|
||||
make the test pass.
|
||||
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 pausing,
|
||||
giving you the opportunity to explore the state of your application displayed in the browser. The
|
||||
app is live! Change the search query to prove it. This is great for troubleshooting end-to-end
|
||||
tests.
|
||||
* 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
|
||||
|
||||
With full text search under our belt and a test to verify it, let's go to step 4 to learn how to
|
||||
add sorting capability to the phone app.
|
||||
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>
|
||||
|
||||
@@ -16,9 +16,8 @@ the repeater, and letting the data binding magic do the rest of the work.
|
||||
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 changes are listed below. You can see the full diff on {@link
|
||||
https://github.com/angular/angular-phonecat/compare/step-3...step-4
|
||||
GitHub}:
|
||||
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
|
||||
@@ -48,7 +47,7 @@ __`app/index.html`:__
|
||||
...
|
||||
</pre>
|
||||
|
||||
In the `index.html` template we made the following changes:
|
||||
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.
|
||||
@@ -63,7 +62,7 @@ Angular creates a two way data-binding between the select element and the `order
|
||||
`orderProp` is then used as the input for the `$orderBy` method.
|
||||
|
||||
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
|
||||
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!
|
||||
|
||||
@@ -179,7 +178,7 @@ The end-to-end test verifies that the ordering mechanism of the select box is wo
|
||||
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}.
|
||||
Angular's server}.
|
||||
|
||||
# Experiments
|
||||
|
||||
@@ -192,8 +191,8 @@ text.
|
||||
|
||||
# Summary
|
||||
|
||||
Now that you have added list sorting and tested the app, go to step 5 to learn about angular
|
||||
services and how angular uses dependency injection.
|
||||
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>
|
||||
|
||||
@@ -209,8 +209,8 @@ to the first 5 in the list. Use the following code in the xhr callback:
|
||||
# Summary
|
||||
|
||||
Now that you have learned how easy it is to use angular services (thanks to angular's
|
||||
implementation of dependency injection), go to step 6, where you will add some thumbnail images of
|
||||
phones and some links.
|
||||
implementation of 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>
|
||||
|
||||
@@ -58,11 +58,11 @@ __`app/index.html`:__
|
||||
To dynamically generate links that will in the future lead to phone detail pages, we used the
|
||||
now-familiar {@link guide/dev_guide.compiler.markup double-curly brace markup} 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.
|
||||
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.directive.ng:src ng:src} directive. That directive prevents the browser from treating
|
||||
the angular `{{ exppression }}` markup literally, which it would have done if we had only specified
|
||||
the angular `{{ expression }}` markup literally, which it would have done if we had only specified
|
||||
an attribute binding in a regular `src` attribute (`<img src="{{phone.imageUrl}}">`). Using
|
||||
`ng:src` prevents the browser from making an http request to an invalid location.
|
||||
|
||||
@@ -98,8 +98,8 @@ making an extraneous request to `/app/%7B%7Bphone.imageUrl%7D%7D` (or
|
||||
|
||||
# Summary
|
||||
|
||||
Now that you have added phone images and links, go to step 7 to learn about angular layout
|
||||
templates and how angular makes it easy to create applications that have multiple views.
|
||||
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>
|
||||
|
||||
@@ -68,8 +68,8 @@ function PhoneCatCtrl($route) {
|
||||
We created a new controller called `PhoneCatCtrl`. We declared its dependency on the `$route`
|
||||
service and used this service to declare that our application consists of two different views:
|
||||
|
||||
* The phone list view will be shown when the URL hash fragment is `/phone`. To construct this view,
|
||||
angular will use the `phone-list.html` template and the `PhoneListCtrl` controller.
|
||||
* 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
|
||||
@@ -151,7 +151,7 @@ __`app/partials/phone-list.html`:__
|
||||
TBD: detail view for {{params.phoneId}}
|
||||
</pre>
|
||||
|
||||
Note how we are using `params` model defined in the `PhoneCanCtrl` controller.
|
||||
Note how we are using `params` model defined in the `PhoneCatCtrl` controller.
|
||||
|
||||
|
||||
## Test
|
||||
@@ -202,8 +202,8 @@ inheritance and model property shadowing do some wonders.
|
||||
|
||||
# Summary
|
||||
|
||||
With the routing set up and the phone list view implemented, we're ready to go to step 8 to
|
||||
implement the phone details view.
|
||||
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>
|
||||
|
||||
@@ -181,8 +181,8 @@ Nexus S details page.
|
||||
|
||||
# Summary
|
||||
|
||||
Now that the phone details view is in place, proceed to step 9 to learn how to write your own
|
||||
custom display filter.
|
||||
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>
|
||||
|
||||
@@ -114,8 +114,8 @@ the following to index.html:
|
||||
|
||||
# Summary
|
||||
|
||||
Now that you have learned how to write and test a custom filter, go to step 10 to learn how we can
|
||||
use angular to enhance the phone details page further.
|
||||
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>
|
||||
|
||||
@@ -133,8 +133,8 @@ template remains operational.
|
||||
|
||||
# Summary
|
||||
|
||||
With the phone image swapper in place, we're ready for step 11 (the last step!) to learn an even
|
||||
better way to fetch data.
|
||||
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>
|
||||
|
||||
@@ -67,17 +67,17 @@ __`app/js/controllers.js`.__
|
||||
<pre>
|
||||
...
|
||||
|
||||
function PhoneListCtrl(Phone_) {
|
||||
function PhoneListCtrl(Phone) {
|
||||
this.orderProp = 'age';
|
||||
this.phones = Phone_.query();
|
||||
this.phones = Phone.query();
|
||||
}
|
||||
//PhoneListCtrl.$inject = ['Phone'];
|
||||
|
||||
|
||||
function PhoneDetailCtrl(Phone_) {
|
||||
function PhoneDetailCtrl(Phone) {
|
||||
var self = this;
|
||||
|
||||
self.phone = Phone_.get({phoneId: self.params.phoneId}, function(phone) {
|
||||
self.phone = Phone.get({phoneId: self.params.phoneId}, function(phone) {
|
||||
self.mainImageUrl = phone.images[0];
|
||||
});
|
||||
|
||||
@@ -94,7 +94,7 @@ Notice how in `PhoneListCtrl` we replaced:
|
||||
|
||||
with:
|
||||
|
||||
this.phones = Phone_.query();
|
||||
this.phones = Phone.query();
|
||||
|
||||
This is a simple statement that we want to query for all phones.
|
||||
|
||||
@@ -116,7 +116,7 @@ We have modified our unit tests to verify that our new service is issuing HTTP r
|
||||
processing them as expected. The tests also check that our controllers are interacting with the
|
||||
service correctly.
|
||||
|
||||
The {@link api/angular.service.$resource $resource} client augments the response object with
|
||||
The {@link api/angular.service.$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
|
||||
@@ -201,7 +201,8 @@ output.
|
||||
|
||||
# Summary
|
||||
|
||||
There you have it! We have created a web app in a relatively short amount of time.
|
||||
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,2 @@
|
||||
controller: {{name}}<br />
|
||||
Book Id: {{params.bookId}}<br />
|
||||
@@ -0,0 +1,3 @@
|
||||
controller: {{name}}<br />
|
||||
Book Id: {{prams.bookId}}<br />
|
||||
Chapter Id: {{params.chapterId}}
|
||||
@@ -0,0 +1 @@
|
||||
Content of template1.html
|
||||
@@ -0,0 +1 @@
|
||||
Content of template2.html
|
||||
+32
-4
@@ -89,6 +89,14 @@ describe('ngdoc', function(){
|
||||
'<pre class="doc-source">\n<>\n</pre></doc:example><p>after</p>');
|
||||
});
|
||||
|
||||
it('should preserve the jsfiddle attribute', function(){
|
||||
var doc = new Doc('@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('@description before <doc:example>' +
|
||||
'<doc:scenario>\n<>\n</doc:scenario></doc:example> after');
|
||||
@@ -316,8 +324,8 @@ describe('ngdoc', function(){
|
||||
expect(doc.requires).toEqual([
|
||||
{name:'$service', text:'<p>for \n<code>A</code></p>'},
|
||||
{name:'$another', text:'<p>for <code>B</code></p>'}]);
|
||||
expect(doc.html()).toContain('<a href="#!angular.service.$service">$service</a>');
|
||||
expect(doc.html()).toContain('<a href="#!angular.service.$another">$another</a>');
|
||||
expect(doc.html()).toContain('<a href="#!/api/angular.service.$service">$service</a>');
|
||||
expect(doc.html()).toContain('<a href="#!/api/angular.service.$another">$another</a>');
|
||||
expect(doc.html()).toContain('<p>for \n<code>A</code></p>');
|
||||
expect(doc.html()).toContain('<p>for <code>B</code></p>');
|
||||
});
|
||||
@@ -461,8 +469,8 @@ describe('ngdoc', function(){
|
||||
});
|
||||
});
|
||||
|
||||
describe('@depricated', function() {
|
||||
it('should parse @depricated', function() {
|
||||
describe('@deprecated', function() {
|
||||
it('should parse @deprecated', function() {
|
||||
var doc = new Doc('@deprecated Replaced with foo.');
|
||||
doc.parse();
|
||||
expect(doc.deprecated).toBe('Replaced with foo.');
|
||||
@@ -496,6 +504,26 @@ describe('ngdoc', function(){
|
||||
});
|
||||
|
||||
|
||||
describe('function', function(){
|
||||
it('should format', function(){
|
||||
var doc = new Doc({
|
||||
ngdoc:'function',
|
||||
name:'some.name',
|
||||
param: [
|
||||
{name:'a', optional: true},
|
||||
{name:'b', type: 'someType', optional: true, 'default': '"xxx"'},
|
||||
{name:'c', type: 'string', description: 'param desc'}
|
||||
],
|
||||
returns: {type: 'number', description: 'return desc'}
|
||||
});
|
||||
doc.html_usage_function(dom);
|
||||
expect(dom).toContain('some.name([a][, b], c)'); //TODO(i) the comma position here is lame
|
||||
expect(dom).toContain('param desc');
|
||||
expect(dom).toContain('(optional="xxx")');
|
||||
expect(dom).toContain('return desc');
|
||||
});
|
||||
});
|
||||
|
||||
describe('filter', function(){
|
||||
it('should format', function(){
|
||||
var doc = new Doc({
|
||||
|
||||
@@ -12,7 +12,7 @@ describe('sitemap', function(){
|
||||
});
|
||||
|
||||
it('should render ngdoc url', function(){
|
||||
var map = new SiteMap([new Doc({section: 'foo', name: 'a.b.c<>\'"&'})]);
|
||||
var map = new SiteMap([new Doc({section: 'foo', id: 'a.b.c<>\'"&'})]);
|
||||
expect(map.render()).toContain([
|
||||
' <url>',
|
||||
'<loc>http://docs.angularjs.org/#!/foo/a.b.c<>'"&</loc>',
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Generate appCache Manifest file here
|
||||
*/
|
||||
|
||||
exports.appCache = appCache;
|
||||
var fs = require('q-fs');
|
||||
var Q = require('qq');
|
||||
function identity($) {return $;}
|
||||
|
||||
function appCache(path) {
|
||||
if(!path) {
|
||||
return appCacheTemplate();
|
||||
}
|
||||
var blackList = ["build/docs/offline.html",
|
||||
"build/docs/sitemap.xml",
|
||||
"build/docs/robots.txt",
|
||||
"build/docs/docs-scenario.html",
|
||||
"build/docs/docs-scenario.js",
|
||||
"build/docs/appcache.manifest",
|
||||
"build/docs/.htaccess"
|
||||
];
|
||||
|
||||
var result = ["CACHE MANIFEST",
|
||||
"# " + new Date().toISOString(),
|
||||
"",
|
||||
"# cache all of these",
|
||||
"CACHE:",
|
||||
"../angular.min.js"];
|
||||
|
||||
var resultPostfix = ["",
|
||||
"FALLBACK:",
|
||||
"/offline.html",
|
||||
"",
|
||||
"# allow access to google analytics and twitter when we are online",
|
||||
"NETWORK:",
|
||||
"*"];
|
||||
|
||||
var promise = fs.listTree(path).then(function(files){
|
||||
var fileFutures = [];
|
||||
files.forEach(function(file){
|
||||
fileFutures.push(fs.isFile(file).then(function(isFile){
|
||||
if (isFile && blackList.indexOf(file) == -1) {
|
||||
return file.replace('build/docs/','');
|
||||
}
|
||||
}));
|
||||
});
|
||||
return Q.deep(fileFutures);
|
||||
}).then(function(files){
|
||||
return result.concat(files.filter(identity)).concat(resultPostfix).join('\n');
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function appCacheTemplate() {
|
||||
return ["CACHE MANIFEST",
|
||||
"# " + new Date().toISOString(),
|
||||
"",
|
||||
"# cache all of these",
|
||||
"CACHE:",
|
||||
"syntaxhighlighter/syntaxhighlighter-combined.js",
|
||||
"../angular.min.js",
|
||||
"docs-combined.js",
|
||||
"docs-keywords.js",
|
||||
"docs-combined.css",
|
||||
"syntaxhighlighter/syntaxhighlighter-combined.css",
|
||||
"img/texture_1.png",
|
||||
"img/yellow_bkgnd.jpg",
|
||||
"",
|
||||
"FALLBACK:",
|
||||
"/ offline.html",
|
||||
"",
|
||||
"# allow access to google analytics and twitter when we are online",
|
||||
"NETWORK:",
|
||||
"*"].join('\n');
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
function noop(){}
|
||||
|
||||
function chain(delegateFn, explicitDone){
|
||||
var onDoneFn = noop;
|
||||
var onErrorFn = function(e){
|
||||
console.error(e.stack || e);
|
||||
process.exit(-1);
|
||||
};
|
||||
var waitForCount = 1;
|
||||
delegateFn = delegateFn || noop;
|
||||
var stackError = new Error('capture stack');
|
||||
|
||||
function decrementWaitFor() {
|
||||
waitForCount--;
|
||||
if (waitForCount == 0)
|
||||
onDoneFn();
|
||||
}
|
||||
|
||||
function self(){
|
||||
try {
|
||||
return delegateFn.apply(self, arguments);
|
||||
} catch (error) {
|
||||
self.error(error);
|
||||
} finally {
|
||||
if (!explicitDone)
|
||||
decrementWaitFor();
|
||||
}
|
||||
};
|
||||
self.onDone = function(callback){
|
||||
onDoneFn = callback;
|
||||
return self;
|
||||
};
|
||||
self.onError = function(callback){
|
||||
onErrorFn = callback;
|
||||
return self;
|
||||
};
|
||||
self.waitFor = function(callback){
|
||||
if (waitForCount == 0)
|
||||
throw new Error("Can not wait on already called callback.");
|
||||
waitForCount++;
|
||||
return chain(callback).onDone(decrementWaitFor).onError(self.error);
|
||||
};
|
||||
|
||||
self.waitMany = function(callback){
|
||||
if (waitForCount == 0)
|
||||
throw new Error("Can not wait on already called callback.");
|
||||
waitForCount++;
|
||||
return chain(callback, true).onDone(decrementWaitFor).onError(self.error);
|
||||
};
|
||||
|
||||
self.done = function(callback){
|
||||
decrementWaitFor();
|
||||
};
|
||||
|
||||
self.error = function(error) {
|
||||
var stack = stackError.stack.split(/\n\r?/).splice(2);
|
||||
var nakedStack = [];
|
||||
stack.forEach(function(frame){
|
||||
if (!frame.match(/callback\.js:\d+:\d+\)$/))
|
||||
nakedStack.push(frame);
|
||||
});
|
||||
error.stack = error.stack + '\nCalled from:\n' + nakedStack.join('\n');
|
||||
onErrorFn(error);
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
exports.chain = chain;
|
||||
+74
-77
@@ -3,88 +3,85 @@ require.paths.push('lib');
|
||||
var reader = require('reader.js'),
|
||||
ngdoc = require('ngdoc.js'),
|
||||
writer = require('writer.js'),
|
||||
callback = require('callback.js'),
|
||||
SiteMap = require('SiteMap.js').SiteMap;
|
||||
SiteMap = require('SiteMap.js').SiteMap,
|
||||
appCache = require('appCache.js').appCache,
|
||||
Q = require('qq');
|
||||
|
||||
var docs = [];
|
||||
var start;
|
||||
var work = callback.chain(function(){
|
||||
start = now();
|
||||
process.on('uncaughtException', function (err) {
|
||||
console.error(err.stack || err);
|
||||
});
|
||||
|
||||
var start = now();
|
||||
var docs;
|
||||
|
||||
writer.makeDir('build/docs/syntaxhighlighter').then(function() {
|
||||
console.log('Generating Angular Reference Documentation...');
|
||||
reader.collect(work.waitMany(function(text, file, line){
|
||||
var doc = new ngdoc.Doc(text, file, line);
|
||||
docs.push(doc);
|
||||
doc.parse();
|
||||
}));
|
||||
});
|
||||
var writes = callback.chain(function(){
|
||||
return reader.collect();
|
||||
}).then(function generateHtmlDocPartials(docs_) {
|
||||
docs = docs_;
|
||||
ngdoc.merge(docs);
|
||||
var fileFutures = [];
|
||||
docs.forEach(function(doc){
|
||||
writer.output(doc.section + '/' + doc.id + '.html', doc.html(), writes.waitFor());
|
||||
fileFutures.push(writer.output(doc.section + '/' + doc.id + '.html', doc.html()));
|
||||
});
|
||||
var metadata = ngdoc.metadata(docs);
|
||||
writer.output('docs-keywords.js', ['NG_PAGES=', JSON.stringify(metadata).replace(/{/g, '\n{'), ';'], writes.waitFor());
|
||||
writer.copyDir('img', writes.waitFor());
|
||||
writer.copyDir('examples', writes.waitFor());
|
||||
writer.copyTpl('index.html', writes.waitFor());
|
||||
writer.copyTpl('offline.html', writes.waitFor());
|
||||
writer.output('app-cache.manifest',
|
||||
appCacheTemplate().replace(/%TIMESTAMP%/, (new Date()).toISOString()),
|
||||
writes.waitFor());
|
||||
writer.merge(['docs.js',
|
||||
'doc_widgets.js'],
|
||||
'docs-combined.js',
|
||||
writes.waitFor());
|
||||
writer.merge(['docs.css',
|
||||
'doc_widgets.css'],
|
||||
'docs-combined.css',
|
||||
writes.waitFor());
|
||||
writer.copyTpl('docs-scenario.html', writes.waitFor());
|
||||
writer.output('docs-scenario.js', ngdoc.scenarios(docs), writes.waitFor());
|
||||
writer.output('sitemap.xml', new SiteMap(docs).render(), writes.waitFor());
|
||||
writer.output('robots.txt', 'Sitemap: http://docs.angularjs.org/sitemap.xml\n', writes.waitFor());
|
||||
writer.merge(['syntaxhighlighter/shCore.js',
|
||||
'syntaxhighlighter/shBrushJScript.js',
|
||||
'syntaxhighlighter/shBrushXml.js'],
|
||||
'syntaxhighlighter/syntaxhighlighter-combined.js',
|
||||
writes.waitFor());
|
||||
writer.merge(['syntaxhighlighter/shCore.css',
|
||||
'syntaxhighlighter/shThemeDefault.css'],
|
||||
'syntaxhighlighter/syntaxhighlighter-combined.css',
|
||||
writes.waitFor());
|
||||
writer.copyTpl('jquery.min.js', writes.waitFor());
|
||||
});
|
||||
writes.onDone(function(){
|
||||
console.log('DONE. Generated ' + docs.length + ' pages in ' +
|
||||
(now()-start) + 'ms.' );
|
||||
});
|
||||
work.onDone(writes);
|
||||
writer.makeDir('build/docs/syntaxhighlighter', work);
|
||||
|
||||
///////////////////////////////////
|
||||
writeTheRest(fileFutures);
|
||||
|
||||
return Q.deep(fileFutures);
|
||||
}).then(function generateManifestFile() {
|
||||
return appCache('build/docs/').then(function(list) {
|
||||
writer.output('appcache-offline.manifest',list)
|
||||
});
|
||||
}).then(function printStats() {
|
||||
console.log('DONE. Generated ' + docs.length + ' pages in ' + (now()-start) + 'ms.' );
|
||||
}).end();
|
||||
|
||||
|
||||
function writeTheRest(writesFuture) {
|
||||
var metadata = ngdoc.metadata(docs);
|
||||
|
||||
writesFuture.push(writer.copyDir('img'));
|
||||
writesFuture.push(writer.copyDir('examples'));
|
||||
writesFuture.push(writer.copyTpl('index.html'));
|
||||
|
||||
writesFuture.push(writer.copy('docs/src/templates/index.html', 'build/docs/index-jq.html',
|
||||
'<!-- jquery place holder -->', '<script src=\"jquery.min.js\"><\/script>'));
|
||||
|
||||
writesFuture.push(writer.copy('docs/src/templates/index.html', 'build/docs/index-nocache.html',
|
||||
'manifest="appcache.manifest"', ''));
|
||||
|
||||
writesFuture.push(writer.copy('docs/src/templates/index.html', 'build/docs/index-jq-nocache.html',
|
||||
'manifest="appcache.manifest"', '',
|
||||
'<!-- jquery place holder -->', '<script src=\"jquery.min.js\"><\/script>'));
|
||||
|
||||
writesFuture.push(writer.copyTpl('offline.html'));
|
||||
writesFuture.push(writer.copyTpl('docs-scenario.html'));
|
||||
writesFuture.push(writer.copyTpl('jquery.min.js'));
|
||||
|
||||
writesFuture.push(writer.output('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)));
|
||||
writesFuture.push(writer.output('robots.txt', 'Sitemap: http://docs.angularjs.org/sitemap.xml\n'));
|
||||
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'));
|
||||
}
|
||||
|
||||
|
||||
function now(){ return new Date().getTime(); }
|
||||
|
||||
|
||||
function appCacheTemplate() {
|
||||
return ["CACHE MANIFEST",
|
||||
"# %TIMESTAMP%",
|
||||
"",
|
||||
"# cache all of these",
|
||||
"CACHE:",
|
||||
"jquery.min.js",
|
||||
"syntaxhighlighter/syntaxhighlighter-combined.js",
|
||||
"../angular.min.js",
|
||||
"docs-combined.js",
|
||||
"docs-keywords.js",
|
||||
"docs-combined.css",
|
||||
"syntaxhighlighter/syntaxhighlighter-combined.css",
|
||||
"img/texture_1.png",
|
||||
"img/yellow_bkgnd.jpg",
|
||||
"",
|
||||
"FALLBACK:",
|
||||
"/ offline.html",
|
||||
"",
|
||||
"# allow access to google analytics and twitter when we are online",
|
||||
"NETWORK:",
|
||||
"*"].join('\n');
|
||||
}
|
||||
function noop(){};
|
||||
|
||||
+37
-19
@@ -111,9 +111,11 @@ Doc.prototype = {
|
||||
'</pre></div>';
|
||||
});
|
||||
} else if (isDocWidget('example')) {
|
||||
text = text.replace(/(<doc:source>)([\s\S]*)(<\/doc:source>)/mi,
|
||||
function(_, before, content, after){
|
||||
return '<pre class="doc-source">' + htmlEscape(content) + '</pre>';
|
||||
text = text.replace(/<doc:source(\s+jsfiddle="[^"]+")?>([\s\S]*)<\/doc:source>/mi,
|
||||
function(_, jsfiddle, content){
|
||||
return '<pre class="doc-source"' + (jsfiddle || '') +'>' +
|
||||
htmlEscape(content) +
|
||||
'</pre>';
|
||||
});
|
||||
text = text.replace(/(<doc:scenario>)([\s\S]*)(<\/doc:scenario>)/mi,
|
||||
function(_, before, content, after){
|
||||
@@ -236,7 +238,7 @@ Doc.prototype = {
|
||||
}
|
||||
dom.h('Dependencies', self.requires, function(require){
|
||||
dom.tag('code', function(){
|
||||
dom.tag('a', {href:"#!angular.service." + require.name}, require.name);
|
||||
dom.tag('a', {href:"#!/api/angular.service." + require.name}, require.name);
|
||||
});
|
||||
dom.html(require.text);
|
||||
});
|
||||
@@ -309,7 +311,7 @@ Doc.prototype = {
|
||||
var self = this;
|
||||
dom.h('Usage', function(){
|
||||
dom.code(function(){
|
||||
dom.text(self.name);
|
||||
dom.text(self.name.split('service.').pop());
|
||||
dom.text('(');
|
||||
self.parameters(dom, ', ');
|
||||
dom.text(');');
|
||||
@@ -511,6 +513,9 @@ Doc.prototype = {
|
||||
dom.h(method.shortName + '(' + signature.join(', ') + ')', method, function(){
|
||||
dom.html(method.description);
|
||||
method.html_usage_parameters(dom);
|
||||
self.html_usage_this(dom);
|
||||
method.html_usage_returns(dom);
|
||||
|
||||
dom.h('Example', method.example, dom.html);
|
||||
});
|
||||
});
|
||||
@@ -543,22 +548,35 @@ Doc.prototype = {
|
||||
//////////////////////////////////////////////////////////
|
||||
function scenarios(docs){
|
||||
var specs = [];
|
||||
docs.forEach(function(doc){
|
||||
specs.push('describe("' + doc.section + '/' + doc.id + '", function(){');
|
||||
specs.push(' beforeEach(function(){');
|
||||
specs.push(' browser().navigateTo("index.html#!/' + doc.section + '/' + doc.id + '");');
|
||||
specs.push(' });');
|
||||
specs.push('');
|
||||
doc.scenarios.forEach(function(scenario){
|
||||
specs.push(indent(trim(scenario), 2));
|
||||
|
||||
specs.push('describe("angular+jqlite", function() {');
|
||||
appendSpecs('index-nocache.html');
|
||||
specs.push('});');
|
||||
|
||||
specs.push('');
|
||||
specs.push('');
|
||||
|
||||
specs.push('describe("angular+jquery", function() {');
|
||||
appendSpecs('index-jq-nocache.html');
|
||||
specs.push('});');
|
||||
|
||||
return specs.join('\n');
|
||||
|
||||
function appendSpecs(htmlFile) {
|
||||
docs.forEach(function(doc){
|
||||
specs.push(' describe("' + doc.section + '/' + doc.id + '", function(){');
|
||||
specs.push(' beforeEach(function(){');
|
||||
specs.push(' browser().navigateTo("' + htmlFile + '#!/' + doc.section + '/' + doc.id + '");');
|
||||
specs.push(' });');
|
||||
specs.push(' ');
|
||||
doc.scenarios.forEach(function(scenario){
|
||||
specs.push(indent(trim(scenario), 4));
|
||||
specs.push('');
|
||||
});
|
||||
specs.push('});');
|
||||
specs.push('');
|
||||
});
|
||||
specs.push('});');
|
||||
specs.push('');
|
||||
if (doc.scenario) {
|
||||
}
|
||||
});
|
||||
return specs.join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+85
-85
@@ -2,98 +2,98 @@
|
||||
* All reading related code here. This is so that we can separate the async code from sync code
|
||||
* for testability
|
||||
*/
|
||||
|
||||
exports.collect = collect;
|
||||
|
||||
require.paths.push(__dirname);
|
||||
var fs = require('fs'),
|
||||
callback = require('callback');
|
||||
var ngdoc = require('ngdoc.js'),
|
||||
Q = require('qq'),
|
||||
qfs = require('q-fs');
|
||||
|
||||
var NEW_LINE = /\n\r?/;
|
||||
|
||||
function collect(callback){
|
||||
findJsFiles('src', callback.waitMany(function(file) {
|
||||
console.log('reading', file, '...');
|
||||
findNgDocInJsFile(file, callback.waitMany(function(doc, line) {
|
||||
callback('@section api\n' + doc, file, line);
|
||||
}));
|
||||
}));
|
||||
findNgDocInDir('docs/content', callback.waitMany(callback));
|
||||
callback.done();
|
||||
}
|
||||
function collect() {
|
||||
var allDocs = [];
|
||||
|
||||
function findJsFiles(dir, callback){
|
||||
fs.readdir(dir, callback.waitFor(function(err, files){
|
||||
if (err) return this.error(err);
|
||||
files.forEach(function(file){
|
||||
var path = dir + '/' + file;
|
||||
fs.lstat(path, callback.waitFor(function(err, stat){
|
||||
if (err) return this.error(err);
|
||||
if (stat.isDirectory())
|
||||
findJsFiles(path, callback.waitMany(callback));
|
||||
else if (/\.js$/.test(path))
|
||||
callback(path);
|
||||
}));
|
||||
});
|
||||
callback.done();
|
||||
}));
|
||||
}
|
||||
|
||||
function findNgDocInDir(directory, docNotify) {
|
||||
fs.readdir(directory, docNotify.waitFor(function(err, files){
|
||||
if (err) return this.error(err);
|
||||
files.forEach(function(file){
|
||||
fs.stat(directory + '/' + file, docNotify.waitFor(function(err, stats){
|
||||
if (err) return this.error(err);
|
||||
if (stats.isFile()) {
|
||||
if (!file.match(/\.ngdoc$/)) return;
|
||||
console.log('reading', directory + '/' + file, '...');
|
||||
fs.readFile(directory + '/' + file, docNotify.waitFor(function(err, content){
|
||||
if (err) return this.error(err);
|
||||
var section = '@section ' + directory.split('/').pop() + '\n';
|
||||
docNotify(section + content.toString(), directory + '/' +file, 1);
|
||||
}));
|
||||
} else if(stats.isDirectory()) {
|
||||
findNgDocInDir(directory + '/' + file, docNotify.waitFor(docNotify));
|
||||
}
|
||||
}));
|
||||
});
|
||||
docNotify.done();
|
||||
}));
|
||||
}
|
||||
|
||||
function findNgDocInJsFile(file, callback) {
|
||||
fs.readFile(file, callback.waitFor(function(err, content){
|
||||
var lines = content.toString().split(NEW_LINE);
|
||||
var text;
|
||||
var startingLine ;
|
||||
var match;
|
||||
var inDoc = false;
|
||||
lines.forEach(function(line, lineNumber){
|
||||
lineNumber++;
|
||||
// is the comment starting?
|
||||
if (!inDoc && (match = line.match(/^\s*\/\*\*\s*(.*)$/))) {
|
||||
line = match[1];
|
||||
inDoc = true;
|
||||
text = [];
|
||||
startingLine = lineNumber;
|
||||
}
|
||||
// are we done?
|
||||
if (inDoc && line.match(/\*\//)) {
|
||||
text = text.join('\n');
|
||||
text = text.replace(/^\n/, '');
|
||||
if (text.match(/@ngdoc/)){
|
||||
callback(text, startingLine);
|
||||
}
|
||||
doc = null;
|
||||
inDoc = false;
|
||||
}
|
||||
// is the comment add text
|
||||
if (inDoc){
|
||||
text.push(line.replace(/^\s*\*\s?/, ''));
|
||||
//collect docs in JS Files
|
||||
var path = 'src';
|
||||
var promiseA = Q.when(qfs.listTree(path), function(files) {
|
||||
var done;
|
||||
//read all files in parallel.
|
||||
files.forEach(function(file) {
|
||||
var work;
|
||||
if(/\.js$/.test(file)) {
|
||||
console.log("reading " + file + ".......");
|
||||
work = Q.when(qfs.read(file, 'b'), function(content) {
|
||||
processJsFile(content, file).forEach (function(doc) {
|
||||
allDocs.push(doc);
|
||||
});
|
||||
});
|
||||
}
|
||||
done = Q.when(done, function() {
|
||||
return work;
|
||||
});
|
||||
});
|
||||
callback.done();
|
||||
}));
|
||||
return done;
|
||||
});
|
||||
|
||||
//collect all NG Docs in Content Folder
|
||||
var path2 = 'docs/content';
|
||||
var promiseB = Q.when(qfs.listTree(path2), function(files){
|
||||
var done2;
|
||||
files.forEach(function(file) {
|
||||
var work2;
|
||||
if (file.match(/\.ngdoc$/)) {
|
||||
console.log("reading " + file + ".......");
|
||||
work2 = Q.when(qfs.read(file, 'b'), function(content){
|
||||
var section = '@section ' + file.split('/')[2] + '\n';
|
||||
allDocs.push(new ngdoc.Doc(section + content.toString(),file, 1).parse());
|
||||
});
|
||||
}
|
||||
done2 = Q.when(done2, function() {
|
||||
return work2;
|
||||
});
|
||||
});
|
||||
return done2;
|
||||
});
|
||||
|
||||
return Q.join(promiseA, promiseB, function() {
|
||||
return allDocs;
|
||||
});
|
||||
}
|
||||
|
||||
function processJsFile(content, file) {
|
||||
var docs = [];
|
||||
var lines = content.toString().split(NEW_LINE);
|
||||
var text;
|
||||
var startingLine ;
|
||||
var match;
|
||||
var inDoc = false;
|
||||
|
||||
|
||||
exports.collect = collect;
|
||||
lines.forEach(function(line, lineNumber){
|
||||
lineNumber++;
|
||||
// is the comment starting?
|
||||
if (!inDoc && (match = line.match(/^\s*\/\*\*\s*(.*)$/))) {
|
||||
line = match[1];
|
||||
inDoc = true;
|
||||
text = [];
|
||||
startingLine = lineNumber;
|
||||
}
|
||||
// are we done?
|
||||
if (inDoc && line.match(/\*\//)) {
|
||||
text = text.join('\n');
|
||||
text = text.replace(/^\n/, '');
|
||||
if (text.match(/@ngdoc/)){
|
||||
//console.log(file, startingLine)
|
||||
docs.push(new ngdoc.Doc('@section api\n' + text, file, startingLine).parse());
|
||||
}
|
||||
doc = null;
|
||||
inDoc = false;
|
||||
}
|
||||
// is the comment add text
|
||||
if (inDoc){
|
||||
text.push(line.replace(/^\s*\*\s?/, ''));
|
||||
}
|
||||
});
|
||||
return docs;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
## OFFLINE SUPPORT ##
|
||||
|
||||
# These rules tell apache to check if there is a cookie called "offline", with value set to the
|
||||
# current angular version. If this rule matches the appcache-offline.manifest will be served for
|
||||
# requests to appcache.manifest
|
||||
#
|
||||
# This file must be processed by Rake in order to replace %ANGULAR_VERSION% with the actual version.
|
||||
|
||||
RewriteEngine on
|
||||
RewriteCond %{HTTP_COOKIE} ng-offline="NG_VERSION_FULL"
|
||||
RewriteRule appcache.manifest appcache-offline.manifest
|
||||
@@ -1,9 +1,3 @@
|
||||
@namespace doc url("http://docs.angularjs.org/");
|
||||
|
||||
doc\:example {
|
||||
display: none;
|
||||
}
|
||||
|
||||
ul.doc-example {
|
||||
list-style-type: none;
|
||||
position: relative;
|
||||
@@ -25,6 +19,37 @@ ul.doc-example > li.doc-example-heading {
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
|
||||
span.nojsfiddle {
|
||||
float: right;
|
||||
font-size: 14px;
|
||||
margin-right:10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
form.jsfiddle {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
form.jsfiddle button {
|
||||
cursor: pointer;
|
||||
padding: 4px 10px;
|
||||
margin: 10px;
|
||||
background-color: #FFF;
|
||||
font-weight: bold;
|
||||
color: #7989D6;
|
||||
border-color: #7989D6;
|
||||
-moz-border-radius: 8px;
|
||||
-webkit-border-radius:8px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
form.jsfiddle textarea, form.jsfiddle input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
li.doc-example-live {
|
||||
padding: 10px;
|
||||
font-size: 1.2em;
|
||||
|
||||
@@ -23,18 +23,21 @@
|
||||
|
||||
angular.widget('doc:example', function(element){
|
||||
this.descend(true); //compile the example code
|
||||
element.hide();
|
||||
|
||||
var example = element.find('pre.doc-source').eq(0),
|
||||
//jQuery find() methods in this widget contain primitive selectors on purpose so that we can use
|
||||
//jqlite instead. jqlite's find() method currently supports onlt getElementsByTagName!
|
||||
var example = element.find('pre').eq(0), //doc-source
|
||||
exampleSrc = example.text(),
|
||||
scenario = element.find('pre.doc-scenario').eq(0);
|
||||
jsfiddle = example.attr('jsfiddle') || true,
|
||||
scenario = element.find('pre').eq(1); //doc-scenario
|
||||
|
||||
var code = indent(exampleSrc);
|
||||
var tabHtml =
|
||||
'<ul class="doc-example">' +
|
||||
'<li class="doc-example-heading"><h3>Source</h3></li>' +
|
||||
'<li class="doc-example-source" ng:non-bindable>' +
|
||||
'<pre class="brush: js; html-script: true; highlight: [' +
|
||||
jsFiddleButton(jsfiddle) + // may or may not have value
|
||||
'<pre class="brush: js; html-script: true; highlight: [' +
|
||||
code.hilite + ']; toolbar: false;"></pre></li>' +
|
||||
'<li class="doc-example-heading"><h3>Live Preview</h3></li>' +
|
||||
'<li class="doc-example-live">' + exampleSrc +'</li>';
|
||||
@@ -47,18 +50,61 @@
|
||||
'</ul>';
|
||||
var tabs = angular.element(tabHtml);
|
||||
|
||||
tabs.find('li.doc-example-source > pre').text(HTML_TEMPLATE.replace('_HTML_SOURCE_', code.html));
|
||||
tabs.find('li').eq(1).find('pre').text(HTML_TEMPLATE.replace('_HTML_SOURCE_', code.html));
|
||||
|
||||
element.html('');
|
||||
element.append(tabs);
|
||||
element.show();
|
||||
|
||||
var script = (exampleSrc.match(/<script[^\>]*>([\s\S]*)<\/script>/) || [])[1] || '';
|
||||
|
||||
try {
|
||||
eval(script);
|
||||
if (window.execScript) { // IE
|
||||
window.execScript(script || '"stupid IE!"'); // IE complains when evaling empty string
|
||||
} else {
|
||||
window.eval(script);
|
||||
}
|
||||
} catch (e) {
|
||||
alert(e);
|
||||
}
|
||||
|
||||
function jsFiddleButton(jsfiddle) {
|
||||
if (jsfiddle !== 'false') {
|
||||
if(jsfiddle == true) {
|
||||
//dynamically generate a fiddle
|
||||
var fiddleUrl = 'http://jsfiddle.net/api/post/library/pure/',
|
||||
fiddleSrc = exampleSrc,
|
||||
stripIndent = fiddleSrc.match(/^(\s*)/)[1].length;
|
||||
|
||||
//escape closing textarea
|
||||
fiddleSrc = fiddleSrc.replace(/<\/textarea>/gi,'</textarea>')
|
||||
//strip extra indentation
|
||||
fiddleSrc = fiddleSrc.replace(new RegExp('^\\s{' + stripIndent + '}', 'gm'), '');
|
||||
|
||||
return '<form class="jsfiddle" method="post" action="' + fiddleUrl + '" target="_blank">' +
|
||||
'<textarea name="css">' +
|
||||
'body { font-family: Arial,Helvetica,sans-serif; }\n' +
|
||||
'body, td, th { font-size: 14px; margin: 0; }\n' +
|
||||
'table { border-collapse: separate; border-spacing: 2px; display: table; margin-bottom: 0; margin-top: 0; -moz-box-sizing: border-box; text-indent: 0; }\n' +
|
||||
'a:link, a:visited, a:hover { color: #5D6DB6; text-decoration: none; }\n' +
|
||||
'</textarea>' +
|
||||
'<input type="text" name="title" value="AngularJS Live Example">' +
|
||||
'<textarea name="html">' +
|
||||
'<script src="' + angularJsUrl + '" ng:autobind></script>\n\n' +
|
||||
'<!-- AngularJS Example Code: -->\n\n' +
|
||||
fiddleSrc +
|
||||
'</textarea>' +
|
||||
'<button>edit at jsFiddle</button>' +
|
||||
'</form>';
|
||||
} else {
|
||||
//use existing fiddle
|
||||
fiddleUrl = "http://jsfiddle.net" + jsfiddle;
|
||||
return '<form class="jsfiddle" method="get" action="' + fiddleUrl + '" target="_blank">' +
|
||||
'<button>edit at jsFiddle</button>' +
|
||||
'</form>';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
});
|
||||
|
||||
function indent(text) {
|
||||
@@ -86,7 +132,7 @@
|
||||
};
|
||||
|
||||
var HTML_TPL =
|
||||
'<p><a ng:init="showInstructions = {show}" ng:show="!showInstructions" ng:click="showInstructions = true" href>Workspace Reset Instructions</a></p>' +
|
||||
'<p><a ng:init="showInstructions = {show}" ng:show="!showInstructions" ng:click="showInstructions = true" href>Workspace Reset Instructions ➤</a></p>' +
|
||||
'<div ng:controller="TutorialInstructionsCtrl" ng:show="showInstructions">' +
|
||||
'<div class="tabs-nav">' +
|
||||
'<ul>' +
|
||||
@@ -140,12 +186,12 @@
|
||||
'</div>';
|
||||
|
||||
angular.widget('doc:tutorial-instructions', function(element) {
|
||||
element.hide();
|
||||
this.descend(true);
|
||||
|
||||
var tabs = angular.element(HTML_TPL.replace('{show}', element.attr('show') || 'false')),
|
||||
nav = tabs.find('.tabs-nav ul'),
|
||||
content = tabs.find('.tabs-content-inner'),
|
||||
nav = tabs.find('ul'),
|
||||
// use simple selectors because jqLite find() supports getElementsByTagName only
|
||||
content = tabs.find('div').find('div'),
|
||||
children = element.children();
|
||||
|
||||
if (children.length) {
|
||||
@@ -165,7 +211,6 @@
|
||||
|
||||
element.html('');
|
||||
element.append(tabs);
|
||||
element.show();
|
||||
});
|
||||
|
||||
|
||||
|
||||
+114
-5
@@ -29,6 +29,10 @@ p {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
li > p {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 1.5em 0 1em 0;
|
||||
}
|
||||
@@ -76,10 +80,26 @@ li {
|
||||
#footer {
|
||||
clear: both;
|
||||
padding: 2em 4em 1em;
|
||||
text-align: right;
|
||||
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 -----*/
|
||||
@@ -102,6 +122,7 @@ li {
|
||||
#navbar a:link, #navbar a:visited {
|
||||
font-size: 1.2em;
|
||||
color: #FFF;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
@@ -117,7 +138,6 @@ li {
|
||||
|
||||
|
||||
#navbar a.current {
|
||||
font-weight: bold;
|
||||
background-color: #333;
|
||||
|
||||
-webkit-border-radius:10px;
|
||||
@@ -165,10 +185,20 @@ li {
|
||||
}
|
||||
|
||||
|
||||
#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 -1em -1em -0.6em;
|
||||
margin: 0.95em -0.95em -1em -0.6em;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
@@ -190,6 +220,14 @@ li {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
#content-list .level-4 {
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
#content-list .level-5 {
|
||||
margin-left: 3em;
|
||||
}
|
||||
|
||||
|
||||
#content-list a.current {
|
||||
font-weight: bold;
|
||||
@@ -251,7 +289,8 @@ li {
|
||||
}
|
||||
|
||||
|
||||
#content img {
|
||||
#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;
|
||||
@@ -267,7 +306,7 @@ li {
|
||||
}
|
||||
|
||||
|
||||
#content > div > pre {
|
||||
#content pre {
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
|
||||
@@ -285,3 +324,73 @@ li {
|
||||
display: inline;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
var HAS_HASH = /#/;
|
||||
DocsController.$inject = ['$location', '$browser', '$window'];
|
||||
function DocsController($location, $browser, $window) {
|
||||
DocsController.$inject = ['$location', '$browser', '$window', '$cookies'];
|
||||
function DocsController($location, $browser, $window, $cookies) {
|
||||
window.$root = this.$root;
|
||||
var self = this;
|
||||
|
||||
var self = this,
|
||||
OFFLINE_COOKIE_NAME = 'ng-offline',
|
||||
HAS_HASH = /#/;
|
||||
|
||||
this.$location = $location;
|
||||
|
||||
self.versionNumber = angular.version.full;
|
||||
self.version = angular.version.full + " " + angular.version.codeName;
|
||||
self.subpage = false;
|
||||
self.offlineEnabled = ($cookies[OFFLINE_COOKIE_NAME] == angular.version.full);
|
||||
|
||||
if (!HAS_HASH.test($location.href)) {
|
||||
$location.hashPath = '!/api';
|
||||
}
|
||||
@@ -68,6 +76,26 @@ function DocsController($location, $browser, $window) {
|
||||
"body=" + escape("Hi there,\n\nI read " + $location.href + " and wanted to ask ....");
|
||||
};
|
||||
|
||||
/** stores a cookie that is used by apache to decide which manifest ot send */
|
||||
this.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) {
|
||||
self.subpage = false;
|
||||
self.$eval();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// prevent compilation of code
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
<html xmlns:ng="http://angularjs.org/"
|
||||
xmlns:doc="http://docs.angularjs.org/"
|
||||
ng:controller="DocsController"
|
||||
manifest="app-cache.manifest">
|
||||
manifest="appcache.manifest">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title ng:bind-template="<angular/>: {{partialTitle}}"><angular/></title>
|
||||
<title ng:bind-template="AngularJS: {{partialTitle}}">AngularJS</title>
|
||||
<meta name="fragment" content="!">
|
||||
<link rel="stylesheet" href="docs-combined.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="syntaxhighlighter/syntaxhighlighter-combined.css" type="text/css"/>
|
||||
@@ -44,19 +44,19 @@
|
||||
<![endif]-->
|
||||
|
||||
<ul id="navbar">
|
||||
<li><a href="http://angularjs.org/"><angular/></a></li>
|
||||
<li><a href="#!/guide" ng:class="selectedSection('guide')">Developer Guide</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="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 id="sidebar">
|
||||
<input type="text" name="search" id="search-box" placeholder="search the docs"
|
||||
tabindex="1" accesskey="s">
|
||||
|
||||
<ul id="content-list">
|
||||
<ul id="content-list" ng:class="sectionId">
|
||||
<li ng:repeat="page in pages.$filter(search)" ng:class="getClass(page)">
|
||||
<a href="{{getUrl(page)}}" ng:class="selectedPartial(page)"
|
||||
ng:bind="page.shortName"
|
||||
@@ -72,11 +72,37 @@
|
||||
<ng:include id="content" src="getCurrentPartial()" onload="afterPartialLoaded()"></ng:include>
|
||||
</div>
|
||||
|
||||
<div id="footer">© 2010-2011 angular</div>
|
||||
<div id="footer">
|
||||
<a id="version"
|
||||
ng:href="https://github.com/angular/angular.js/blob/master/CHANGELOG.md#{{versionNumber}}"
|
||||
ng:bind-template="v{{version}}">
|
||||
</a>
|
||||
<a ng:hide="offlineEnabled" ng:click ="subpage = true">(enable offline support)</a>
|
||||
<span ng:show="offlineEnabled">(offline support enabled)</span>
|
||||
<span id="copyright">© 2010-2011 AngularJS</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<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
|
||||
necessary files (approximately 3.5MB). The next time you load the docs, the browser will
|
||||
use these cached files.
|
||||
<br><br>
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="jquery.min.js"></script>
|
||||
<script src="syntaxhighlighter/syntaxhighlighter-combined.js"></script>
|
||||
<!-- jquery place holder -->
|
||||
<script src="../angular.min.js" ng:autobind></script>
|
||||
<script src="docs-combined.js"></script>
|
||||
<script src="docs-keywords.js"></script>
|
||||
|
||||
+105
-72
@@ -3,37 +3,126 @@
|
||||
* for testability
|
||||
*/
|
||||
require.paths.push(__dirname);
|
||||
var fs = require('fs');
|
||||
var qfs = require('q-fs');
|
||||
var Q = require('qq');
|
||||
var OUTPUT_DIR = "build/docs/";
|
||||
var fs = require('fs');
|
||||
|
||||
function output(docs, content, callback){
|
||||
callback();
|
||||
exports.output = function(file, content){
|
||||
console.log('writing ', file);
|
||||
var fullPath = OUTPUT_DIR + file;
|
||||
var dir = parent(fullPath);
|
||||
return Q.when(exports.makeDir(dir), function(error) {
|
||||
qfs.write(fullPath,exports.toString(content));
|
||||
});
|
||||
}
|
||||
|
||||
//recursively create directory
|
||||
exports.makeDir = function (path) {
|
||||
var parts = path.split(/\//);
|
||||
var path = ".";
|
||||
//Sequentially create directories
|
||||
var done = Q.defer();
|
||||
(function createPart() {
|
||||
|
||||
if(!parts.length) {
|
||||
done.resolve();
|
||||
} else {
|
||||
path += "/" + parts.shift();
|
||||
qfs.isDirectory(path).then(function(isDir) {
|
||||
if(!isDir) {
|
||||
qfs.makeDirectory(path);
|
||||
}
|
||||
createPart();
|
||||
});
|
||||
}
|
||||
})();
|
||||
return done.promise;
|
||||
};
|
||||
|
||||
exports.copyTpl = function(filename) {
|
||||
return exports.copy('docs/src/templates/' + filename, OUTPUT_DIR + filename);
|
||||
};
|
||||
|
||||
exports.copy = function (from, to) {
|
||||
var args = [].slice.call(arguments);
|
||||
|
||||
args.shift(); // drop 'from'
|
||||
args.shift(); // drop 'to'
|
||||
|
||||
// Have to use rb (read binary), char 'r' is infered by library.
|
||||
return qfs.read(from,'b').then(function(content) {
|
||||
var replacementKey,
|
||||
replacement;
|
||||
|
||||
while (args.length) {
|
||||
replacementKey = args.shift();
|
||||
replacement = args.shift();
|
||||
if(replacementKey != undefined && replacement != undefined) {
|
||||
content = content.toString().replace(replacementKey, replacement);
|
||||
}
|
||||
}
|
||||
|
||||
qfs.write(to, content);
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
exports.merge = function(srcs, to) {
|
||||
return merge(srcs.map(function(src) { return 'docs/src/templates/' + src; }), OUTPUT_DIR + to);
|
||||
};
|
||||
|
||||
function merge(srcs, to) {
|
||||
var contents = [];
|
||||
//Sequentially read file
|
||||
var done;
|
||||
srcs.forEach(function (src) {
|
||||
done = Q.when(done, function(content) {
|
||||
if(content) contents.push(content);
|
||||
return qfs.read(src, 'b');
|
||||
});
|
||||
});
|
||||
|
||||
// write to file
|
||||
return Q.when(done, function(content) {
|
||||
contents.push(content);
|
||||
qfs.write(to, contents.join('\n'));
|
||||
});
|
||||
}
|
||||
|
||||
//----------------------- Synchronous Methods ----------------------------------
|
||||
|
||||
function parent(file) {
|
||||
var parts = file.split('/');
|
||||
parts.pop();
|
||||
return parts.join('/');
|
||||
}
|
||||
|
||||
exports.output = function(file, content, callback){
|
||||
console.log('write', file);
|
||||
exports.makeDir(parent(OUTPUT_DIR + file), callback.waitFor(function(){
|
||||
fs.writeFile(
|
||||
OUTPUT_DIR + file,
|
||||
exports.toString(content),
|
||||
callback);
|
||||
}));
|
||||
};
|
||||
|
||||
|
||||
exports.toString = function toString(obj){
|
||||
exports.toString = function toString(obj) {
|
||||
switch (typeof obj) {
|
||||
case 'string':
|
||||
return obj;
|
||||
case 'object':
|
||||
if (obj instanceof Array) {
|
||||
obj.forEach(function (value, key){
|
||||
obj.forEach(function (value, key) {
|
||||
obj[key] = toString(value);
|
||||
});
|
||||
return obj.join('');
|
||||
@@ -44,61 +133,5 @@ exports.toString = function toString(obj){
|
||||
return obj;
|
||||
};
|
||||
|
||||
exports.makeDir = function (path, callback) {
|
||||
var parts = path.split(/\//);
|
||||
path = '.';
|
||||
(function next(error){
|
||||
if (error && error.code != 'EEXIST') return callback.error(error);
|
||||
if (parts.length) {
|
||||
path += '/' + parts.shift();
|
||||
fs.mkdir(path, 0777, next);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
})();
|
||||
};
|
||||
|
||||
exports.copyTpl = function(filename, callback) {
|
||||
copy('docs/src/templates/' + filename, OUTPUT_DIR + filename, callback);
|
||||
};
|
||||
|
||||
function copy(from, to, callback) {
|
||||
//console.log('writing', to, '...');
|
||||
fs.readFile(from, function(err, content){
|
||||
if (err) return callback.error(err);
|
||||
fs.writeFile(to, content, callback);
|
||||
});
|
||||
}
|
||||
|
||||
exports.copyDir = function copyDir(dir, callback) {
|
||||
exports.makeDir(OUTPUT_DIR + '/' + dir, callback.waitFor(function(){
|
||||
fs.readdir('docs/' + dir, callback.waitFor(function(err, files){
|
||||
if (err) return this.error(err);
|
||||
files.forEach(function(file){
|
||||
var path = 'docs/' + dir + '/' + file;
|
||||
fs.stat(path, callback.waitFor(function(err, stat) {
|
||||
if (err) return this.error(err);
|
||||
if (stat.isDirectory()) {
|
||||
copyDir(dir + '/' + file, callback.waitFor());
|
||||
} else {
|
||||
copy(path, OUTPUT_DIR + '/' + dir + '/' + file, callback.waitFor());
|
||||
}
|
||||
}));
|
||||
});
|
||||
callback();
|
||||
}));
|
||||
}));
|
||||
};
|
||||
|
||||
|
||||
exports.merge = function(srcs, to, callback){
|
||||
merge(srcs.map(function(src) { return 'docs/src/templates/' + src; }), OUTPUT_DIR + to, callback);
|
||||
};
|
||||
|
||||
function merge(srcs, to, callback) {
|
||||
var content = [];
|
||||
srcs.forEach(function (src) {
|
||||
content.push(fs.readFileSync(src));
|
||||
});
|
||||
fs.writeFile(to, content.join('\n'), callback.waitFor());
|
||||
}
|
||||
function noop(){};
|
||||
|
||||
@@ -7,17 +7,49 @@ load:
|
||||
- test/jquery_remove.js
|
||||
- src/Angular.js
|
||||
- src/JSON.js
|
||||
- src/*.js
|
||||
- src/service/*.js
|
||||
- src/Compiler.js
|
||||
- src/Scope.js
|
||||
- src/Injector.js
|
||||
- src/parser.js
|
||||
- src/Resource.js
|
||||
- src/Browser.js
|
||||
- src/sanitizer.js
|
||||
- src/jqLite.js
|
||||
- src/apis.js
|
||||
- src/filters.js
|
||||
- src/formatters.js
|
||||
- src/validators.js
|
||||
- src/service/cookieStore.js
|
||||
- src/service/cookies.js
|
||||
- src/service/defer.js
|
||||
- src/service/document.js
|
||||
- src/service/exceptionHandler.js
|
||||
- src/service/hover.js
|
||||
- src/service/invalidWidgets.js
|
||||
- src/service/location.js
|
||||
- src/service/log.js
|
||||
- src/service/resource.js
|
||||
- src/service/route.js
|
||||
- src/service/updateView.js
|
||||
- src/service/window.js
|
||||
- src/service/xhr.bulk.js
|
||||
- src/service/xhr.cache.js
|
||||
- src/service/xhr.error.js
|
||||
- src/service/xhr.js
|
||||
- src/directives.js
|
||||
- src/markups.js
|
||||
- src/widgets.js
|
||||
- example/personalLog/*.js
|
||||
- test/testabilityPatch.js
|
||||
- src/scenario/Scenario.js
|
||||
- src/scenario/output/*.js
|
||||
- src/jstd-scenario-adapter/*.js
|
||||
- src/scenario/*.js
|
||||
- src/angular-mocks.js
|
||||
- test/mocks.js
|
||||
- test/scenario/*.js
|
||||
- test/scenario/output/*.js
|
||||
- test/jstd-scenario-adapter/*.js
|
||||
- test/*.js
|
||||
- test/service/*.js
|
||||
- example/personalLog/test/*.js
|
||||
|
||||
@@ -7,17 +7,49 @@ load:
|
||||
- test/jquery_alias.js
|
||||
- src/Angular.js
|
||||
- src/JSON.js
|
||||
- src/*.js
|
||||
- src/service/*.js
|
||||
- src/Compiler.js
|
||||
- src/Scope.js
|
||||
- src/Injector.js
|
||||
- src/parser.js
|
||||
- src/Resource.js
|
||||
- src/Browser.js
|
||||
- src/sanitizer.js
|
||||
- src/jqLite.js
|
||||
- src/apis.js
|
||||
- src/filters.js
|
||||
- src/formatters.js
|
||||
- src/validators.js
|
||||
- src/service/cookieStore.js
|
||||
- src/service/cookies.js
|
||||
- src/service/defer.js
|
||||
- src/service/document.js
|
||||
- src/service/exceptionHandler.js
|
||||
- src/service/hover.js
|
||||
- src/service/invalidWidgets.js
|
||||
- src/service/location.js
|
||||
- src/service/log.js
|
||||
- src/service/resource.js
|
||||
- src/service/route.js
|
||||
- src/service/updateView.js
|
||||
- src/service/window.js
|
||||
- src/service/xhr.bulk.js
|
||||
- src/service/xhr.cache.js
|
||||
- src/service/xhr.error.js
|
||||
- src/service/xhr.js
|
||||
- src/directives.js
|
||||
- src/markups.js
|
||||
- src/widgets.js
|
||||
- example/personalLog/*.js
|
||||
- test/testabilityPatch.js
|
||||
- src/scenario/Scenario.js
|
||||
- src/scenario/output/*.js
|
||||
- src/jstd-scenario-adapter/*.js
|
||||
- src/scenario/*.js
|
||||
- src/angular-mocks.js
|
||||
- test/mocks.js
|
||||
- test/scenario/*.js
|
||||
- test/scenario/output/*.js
|
||||
- test/jstd-scenario-adapter/*.js
|
||||
- test/*.js
|
||||
- test/service/*.js
|
||||
- example/personalLog/test/*.js
|
||||
|
||||
+32
-2
@@ -5,8 +5,38 @@ load:
|
||||
- lib/jasmine-jstd-adapter/JasmineAdapter.js
|
||||
- src/Angular.js
|
||||
- src/JSON.js
|
||||
- src/*.js
|
||||
- src/service/*.js
|
||||
- src/Compiler.js
|
||||
- src/Scope.js
|
||||
- src/Injector.js
|
||||
- src/parser.js
|
||||
- src/Resource.js
|
||||
- src/Browser.js
|
||||
- src/sanitizer.js
|
||||
- src/jqLite.js
|
||||
- src/apis.js
|
||||
- src/filters.js
|
||||
- src/formatters.js
|
||||
- src/validators.js
|
||||
- src/service/cookieStore.js
|
||||
- src/service/cookies.js
|
||||
- src/service/defer.js
|
||||
- src/service/document.js
|
||||
- src/service/exceptionHandler.js
|
||||
- src/service/hover.js
|
||||
- src/service/invalidWidgets.js
|
||||
- src/service/location.js
|
||||
- src/service/log.js
|
||||
- src/service/resource.js
|
||||
- src/service/route.js
|
||||
- src/service/updateView.js
|
||||
- src/service/window.js
|
||||
- src/service/xhr.bulk.js
|
||||
- src/service/xhr.cache.js
|
||||
- src/service/xhr.error.js
|
||||
- src/service/xhr.js
|
||||
- src/directives.js
|
||||
- src/markups.js
|
||||
- src/widgets.js
|
||||
- src/angular-mocks.js
|
||||
- perf/data/*.js
|
||||
- perf/testUtils.js
|
||||
|
||||
+32
-4
@@ -7,8 +7,38 @@ load:
|
||||
- test/jquery_remove.js
|
||||
- src/Angular.js
|
||||
- src/JSON.js
|
||||
- src/*.js
|
||||
- src/service/*.js
|
||||
- src/Compiler.js
|
||||
- src/Scope.js
|
||||
- src/Injector.js
|
||||
- src/parser.js
|
||||
- src/Resource.js
|
||||
- src/Browser.js
|
||||
- src/sanitizer.js
|
||||
- src/jqLite.js
|
||||
- src/apis.js
|
||||
- src/filters.js
|
||||
- src/formatters.js
|
||||
- src/validators.js
|
||||
- src/service/cookieStore.js
|
||||
- src/service/cookies.js
|
||||
- src/service/defer.js
|
||||
- src/service/document.js
|
||||
- src/service/exceptionHandler.js
|
||||
- src/service/hover.js
|
||||
- src/service/invalidWidgets.js
|
||||
- src/service/location.js
|
||||
- src/service/log.js
|
||||
- src/service/resource.js
|
||||
- src/service/route.js
|
||||
- src/service/updateView.js
|
||||
- src/service/window.js
|
||||
- src/service/xhr.bulk.js
|
||||
- src/service/xhr.cache.js
|
||||
- src/service/xhr.error.js
|
||||
- src/service/xhr.js
|
||||
- src/directives.js
|
||||
- src/markups.js
|
||||
- src/widgets.js
|
||||
- example/personalLog/*.js
|
||||
- test/testabilityPatch.js
|
||||
- src/scenario/Scenario.js
|
||||
@@ -26,8 +56,6 @@ load:
|
||||
|
||||
exclude:
|
||||
- test/jquery_alias.js
|
||||
- src/angular.prefix
|
||||
- src/angular.suffix
|
||||
- src/angular-bootstrap.js
|
||||
- src/scenario/angular-bootstrap.js
|
||||
- src/AngularPublic.js
|
||||
|
||||
Binary file not shown.
@@ -1 +1 @@
|
||||
20110322
|
||||
20110615
|
||||
|
||||
@@ -2200,7 +2200,8 @@ jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
|
||||
jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;
|
||||
|
||||
jasmine.WaitsForBlock.prototype.execute = function(onComplete) {
|
||||
this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
|
||||
// (i): disabled this log since its annoying
|
||||
//this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
|
||||
var latchFunctionResult;
|
||||
try {
|
||||
latchFunctionResult = this.latchFunction.apply(this.spec);
|
||||
|
||||
+28
-6
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
////////////////////////////////////
|
||||
|
||||
if (typeof document.getAttribute == $undefined)
|
||||
@@ -62,12 +64,10 @@ var _undefined = undefined,
|
||||
$boolean = 'boolean',
|
||||
$console = 'console',
|
||||
$date = 'date',
|
||||
$display = 'display',
|
||||
$element = 'element',
|
||||
$function = 'function',
|
||||
$length = 'length',
|
||||
$name = 'name',
|
||||
$none = 'none',
|
||||
$noop = 'noop',
|
||||
$null = 'null',
|
||||
$number = 'number',
|
||||
@@ -115,7 +115,7 @@ var _undefined = undefined,
|
||||
angularCallbacks = extensionMap(angular, 'callbacks'),
|
||||
nodeName_,
|
||||
rngScript = /^(|.*\/)angular(-.*?)?(\.min)?.js(\?[^#]*)?(#(.*))?$/,
|
||||
uid = ['0', '0', '0'];
|
||||
uid = ['0', '0', '0'],
|
||||
DATE_ISOSTRING_LN = 24;
|
||||
|
||||
/**
|
||||
@@ -144,7 +144,7 @@ var _undefined = undefined,
|
||||
* @param {Object|Array} obj Object to iterate over.
|
||||
* @param {function()} iterator Iterator function.
|
||||
* @param {Object} context Object to become context (`this`) for the iterator function.
|
||||
* @returns {Objet|Array} Reference to `obj`.
|
||||
* @returns {Object|Array} Reference to `obj`.
|
||||
*/
|
||||
function forEach(obj, iterator, context) {
|
||||
var key;
|
||||
@@ -874,7 +874,7 @@ function toKeyValue(obj) {
|
||||
|
||||
|
||||
/**
|
||||
* we need our custom mehtod because encodeURIComponent is too agressive and doesn't follow
|
||||
* We need our custom mehtod because encodeURIComponent is too agressive and doesn't follow
|
||||
* http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
|
||||
* segments:
|
||||
* segment = *pchar
|
||||
@@ -944,7 +944,7 @@ function angularInit(config, document){
|
||||
if (config.css)
|
||||
$browser.addCss(config.base_url + config.css);
|
||||
else if(msie<8)
|
||||
$browser.addJs(config.base_url + config.ie_compat, config.ie_compat_id);
|
||||
$browser.addJs(config.ie_compat, config.ie_compat_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1002,3 +1002,25 @@ function assertArg(arg, name, reason) {
|
||||
function assertArgFn(arg, name) {
|
||||
assertArg(isFunction(arg, name, 'not a function'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name angular.version
|
||||
* @description
|
||||
* Object which contains information about the current AngularJS version. The object has following
|
||||
* properties:
|
||||
*
|
||||
* - `full` – `{string}` – full version string, e.g. "0.9.18"
|
||||
* - `major` – `{number}` – major version number, e.g. 0
|
||||
* - `minor` – `{number}` – minor version number, e.g. 9
|
||||
* - `dot` – `{number}` – dot version number, e.g. 18
|
||||
* - `codeName` – `{string}` – code name of the release, e.g. "jiggling-armfat"
|
||||
*/
|
||||
var version = {
|
||||
full: '"NG_VERSION_FULL"', // all of these placeholder strings will be replaced by rake's
|
||||
major: "NG_VERSION_MAJOR", // compile task
|
||||
minor: "NG_VERSION_MINOR",
|
||||
dot: "NG_VERSION_DOT",
|
||||
codeName: '"NG_VERSION_CODENAME"'
|
||||
};
|
||||
|
||||
+6
-10
@@ -1,13 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
var browserSingleton;
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc service
|
||||
* @name angular.service.$browser
|
||||
* @requires $log
|
||||
*
|
||||
* @description
|
||||
* Represents the browser.
|
||||
*/
|
||||
|
||||
angularService('$browser', function($log){
|
||||
if (!browserSingleton) {
|
||||
browserSingleton = new Browser(window, jqLite(window.document), jqLite(window.document.body),
|
||||
@@ -17,6 +11,7 @@ angularService('$browser', function($log){
|
||||
return browserSingleton;
|
||||
}, {$inject:['$log']});
|
||||
|
||||
|
||||
extend(angular, {
|
||||
// disabled for now until we agree on public name
|
||||
//'annotate': annotate,
|
||||
@@ -39,7 +34,8 @@ extend(angular, {
|
||||
'isFunction': isFunction,
|
||||
'isObject': isObject,
|
||||
'isNumber': isNumber,
|
||||
'isArray': isArray
|
||||
'isArray': isArray,
|
||||
'version': version
|
||||
});
|
||||
|
||||
//try to bind to jquery now so that one can write angular.element().read()
|
||||
|
||||
+137
-51
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
//////////////////////////////
|
||||
// Browser
|
||||
//////////////////////////////
|
||||
@@ -10,8 +12,9 @@ var XHR = window.XMLHttpRequest || function () {
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @name Browser
|
||||
* @ngdoc service
|
||||
* @name angular.service.$browser
|
||||
* @requires $log
|
||||
*
|
||||
* @description
|
||||
* Constructor for the object exposed as $browser service.
|
||||
@@ -21,6 +24,11 @@ var XHR = window.XMLHttpRequest || function () {
|
||||
* - hide all the global state in the browser caused by the window object
|
||||
* - abstract away all the browser specific features and inconsistencies
|
||||
*
|
||||
* For tests we provide {@link angular.mock.service.$browser mock implementation} of the `$browser`
|
||||
* service, which can be used for convenient testing of the application without the interaction with
|
||||
* the real browser apis.
|
||||
*/
|
||||
/**
|
||||
* @param {object} window The global window object.
|
||||
* @param {object} document jQuery wrapped document.
|
||||
* @param {object} body jQuery wrapped document.body.
|
||||
@@ -31,7 +39,10 @@ function Browser(window, document, body, XHR, $log) {
|
||||
var self = this,
|
||||
rawDocument = document[0],
|
||||
location = window.location,
|
||||
setTimeout = window.setTimeout;
|
||||
setTimeout = window.setTimeout,
|
||||
clearTimeout = window.clearTimeout,
|
||||
pendingDeferIds = {},
|
||||
lastLocationUrl;
|
||||
|
||||
self.isMock = false;
|
||||
|
||||
@@ -73,7 +84,9 @@ function Browser(window, document, body, XHR, $log) {
|
||||
* @param {string} method Requested method (get|post|put|delete|head|json)
|
||||
* @param {string} url Requested url
|
||||
* @param {?string} post Post data to send (null if nothing to post)
|
||||
* @param {function(number, string)} callback Function that will be called on response
|
||||
* @param {function(number, string, function([string]))} callback Function that will be called on
|
||||
* response. The third argument is a function that can be called to return a specified response
|
||||
* header or an Object containing all headers (when called with no arguments).
|
||||
* @param {object=} header additional HTTP headers to send with XHR.
|
||||
* Standard headers are:
|
||||
* <ul>
|
||||
@@ -86,17 +99,24 @@ function Browser(window, document, body, XHR, $log) {
|
||||
* Send ajax request
|
||||
*/
|
||||
self.xhr = function(method, url, post, callback, headers) {
|
||||
var parsedHeaders;
|
||||
|
||||
outstandingRequestCount ++;
|
||||
if (lowercase(method) == 'json') {
|
||||
var callbackId = ("angular_" + Math.random() + '_' + (idCounter++)).replace(/\d\./, '');
|
||||
var script = jqLite(rawDocument.createElement('script'))
|
||||
.attr({type: 'text/javascript', src: url.replace('JSON_CALLBACK', callbackId)});
|
||||
window[callbackId] = function(data){
|
||||
window[callbackId] = undefined;
|
||||
script.remove();
|
||||
completeOutstandingRequest(callback, 200, data);
|
||||
window[callbackId] = function(data) {
|
||||
window[callbackId].data = data;
|
||||
};
|
||||
body.append(script);
|
||||
|
||||
var script = self.addJs(url.replace('JSON_CALLBACK', callbackId), null, function() {
|
||||
if (window[callbackId].data) {
|
||||
completeOutstandingRequest(callback, 200, window[callbackId].data);
|
||||
} else {
|
||||
completeOutstandingRequest(callback);
|
||||
}
|
||||
delete window[callbackId];
|
||||
body[0].removeChild(script);
|
||||
});
|
||||
} else {
|
||||
var xhr = new XHR();
|
||||
xhr.open(method, url, true);
|
||||
@@ -106,8 +126,35 @@ function Browser(window, document, body, XHR, $log) {
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState == 4) {
|
||||
// normalize IE bug (http://bugs.jquery.com/ticket/1450)
|
||||
var status = xhr.status == 1223 ? 204 : xhr.status || 200;
|
||||
completeOutstandingRequest(callback, status, xhr.responseText);
|
||||
var status = xhr.status == 1223 ? 204 : xhr.status;
|
||||
completeOutstandingRequest(callback, status, xhr.responseText, function(header) {
|
||||
header = lowercase(header);
|
||||
|
||||
if (header) {
|
||||
return parsedHeaders
|
||||
? parsedHeaders[header] || null
|
||||
: xhr.getResponseHeader(header);
|
||||
} else {
|
||||
// Return an object containing each response header
|
||||
parsedHeaders = {};
|
||||
|
||||
forEach(xhr.getAllResponseHeaders().split('\n'), function(line) {
|
||||
var i = line.indexOf(':'),
|
||||
key = lowercase(trim(line.substr(0, i))),
|
||||
value = trim(line.substr(i + 1));
|
||||
|
||||
if (parsedHeaders[key]) {
|
||||
// Combine repeated headers
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
|
||||
parsedHeaders[key] += ', ' + value;
|
||||
} else {
|
||||
parsedHeaders[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
return parsedHeaders;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
xhr.send(post || '');
|
||||
@@ -115,14 +162,17 @@ function Browser(window, document, body, XHR, $log) {
|
||||
};
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#notifyWhenNoOutstandingRequests
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @private
|
||||
* Note: this method is used only by scenario runner
|
||||
* TODO(vojta): prefix this method with $$ ?
|
||||
* @param {function()} callback Function that will be called when no outstanding request
|
||||
*/
|
||||
self.notifyWhenNoOutstandingRequests = function(callback) {
|
||||
// force browser to execute all pollFns - this is needed so that cookies and other pollers fire
|
||||
// at some deterministic time in respect to the test runner's actions. Leaving things up to the
|
||||
// regular poller would result in flaky tests.
|
||||
forEach(pollFns, function(pollFn){ pollFn(); });
|
||||
|
||||
if (outstandingRequestCount === 0) {
|
||||
callback();
|
||||
} else {
|
||||
@@ -136,16 +186,6 @@ function Browser(window, document, body, XHR, $log) {
|
||||
var pollFns = [],
|
||||
pollTimeout;
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#poll
|
||||
* @methodOf angular.service.$browser
|
||||
*/
|
||||
self.poll = function() {
|
||||
forEach(pollFns, function(pollFn){ pollFn(); });
|
||||
};
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
@@ -161,17 +201,12 @@ function Browser(window, document, body, XHR, $log) {
|
||||
* @returns {function()} the added function
|
||||
*/
|
||||
self.addPollFn = function(fn) {
|
||||
if (!pollTimeout) self.startPoller(100, setTimeout);
|
||||
if (isUndefined(pollTimeout)) startPoller(100, setTimeout);
|
||||
pollFns.push(fn);
|
||||
return fn;
|
||||
};
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#startPoller
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @param {number} interval How often should browser call poll functions (ms)
|
||||
* @param {function()} setTimeout Reference to a real or fake `setTimeout` function.
|
||||
*
|
||||
@@ -179,9 +214,9 @@ function Browser(window, document, body, XHR, $log) {
|
||||
* Configures the poller to run in the specified intervals, using the specified
|
||||
* setTimeout fn and kicks it off.
|
||||
*/
|
||||
self.startPoller = function(interval, setTimeout) {
|
||||
(function check(){
|
||||
self.poll();
|
||||
function startPoller(interval, setTimeout) {
|
||||
(function check() {
|
||||
forEach(pollFns, function(pollFn){ pollFn(); });
|
||||
pollTimeout = setTimeout(check, interval);
|
||||
})();
|
||||
};
|
||||
@@ -202,10 +237,13 @@ function Browser(window, document, body, XHR, $log) {
|
||||
* Sets browser's url
|
||||
*/
|
||||
self.setUrl = function(url) {
|
||||
var existingURL = location.href;
|
||||
|
||||
var existingURL = lastLocationUrl;
|
||||
if (!existingURL.match(/#/)) existingURL += '#';
|
||||
if (!url.match(/#/)) url += '#';
|
||||
location.href = url;
|
||||
if (existingURL != url) {
|
||||
location.href = url;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -220,7 +258,7 @@ function Browser(window, document, body, XHR, $log) {
|
||||
* @returns {string} Browser's url
|
||||
*/
|
||||
self.getUrl = function() {
|
||||
return location.href;
|
||||
return lastLocationUrl = location.href;
|
||||
};
|
||||
|
||||
|
||||
@@ -237,7 +275,7 @@ function Browser(window, document, body, XHR, $log) {
|
||||
* The listener gets called with either HashChangeEvent object or simple object that also contains
|
||||
* `oldURL` and `newURL` properties.
|
||||
*
|
||||
* NOTE: this api is intended for use only by the $location service. Please use the
|
||||
* Note: this api is intended for use only by the $location service. Please use the
|
||||
* {@link angular.service.$location $location service} to monitor hash changes in angular apps.
|
||||
*
|
||||
* @param {function(event)} listener Listener function to be called when url hash changes.
|
||||
@@ -336,20 +374,49 @@ function Browser(window, document, body, XHR, $log) {
|
||||
* @methodOf angular.service.$browser
|
||||
* @param {function()} fn A function, who's execution should be defered.
|
||||
* @param {number=} [delay=0] of milliseconds to defer the function execution.
|
||||
* @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
|
||||
*
|
||||
* @description
|
||||
* Executes a fn asynchroniously via `setTimeout(fn, delay)`.
|
||||
*
|
||||
* Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
|
||||
* `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed via
|
||||
* `$browser.defer.flush()`.
|
||||
* `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
|
||||
* via `$browser.defer.flush()`.
|
||||
*
|
||||
*/
|
||||
self.defer = function(fn, delay) {
|
||||
var timeoutId;
|
||||
outstandingRequestCount++;
|
||||
setTimeout(function() { completeOutstandingRequest(fn); }, delay || 0);
|
||||
timeoutId = setTimeout(function() {
|
||||
delete pendingDeferIds[timeoutId];
|
||||
completeOutstandingRequest(fn);
|
||||
}, delay || 0);
|
||||
pendingDeferIds[timeoutId] = true;
|
||||
return timeoutId;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* THIS DOC IS NOT VISIBLE because ngdocs can't process docs for foo#method.method
|
||||
*
|
||||
* @name angular.service.$browser#defer.cancel
|
||||
* @methodOf angular.service.$browser.defer
|
||||
* @returns {boolean} Returns `true` if the task hasn't executed yet and was successfuly canceled.
|
||||
*
|
||||
* @description
|
||||
* Cancels a defered task identified with `deferId`.
|
||||
*/
|
||||
|
||||
self.defer.cancel = function(deferId) {
|
||||
if (pendingDeferIds[deferId]) {
|
||||
delete pendingDeferIds[deferId];
|
||||
clearTimeout(deferId);
|
||||
completeOutstandingRequest(noop);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Misc API
|
||||
//////////////////////////////////////////////////////////////
|
||||
@@ -416,16 +483,35 @@ function Browser(window, document, body, XHR, $log) {
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @param {string} url Url to js file
|
||||
* @param {string=} dom_id Optional id for the script tag
|
||||
* @param {string=} domId Optional id for the script tag
|
||||
*
|
||||
* @description
|
||||
* Adds a script tag to the head.
|
||||
*/
|
||||
self.addJs = function(url, dom_id) {
|
||||
var script = jqLite(rawDocument.createElement('script'));
|
||||
script.attr('type', 'text/javascript');
|
||||
script.attr('src', url);
|
||||
if (dom_id) script.attr('id', dom_id);
|
||||
body.append(script);
|
||||
self.addJs = function(url, domId, done) {
|
||||
// we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.:
|
||||
// - fetches local scripts via XHR and evals them
|
||||
// - adds and immediately removes script elements from the document
|
||||
//
|
||||
// We need addJs to be able to add angular-ie-compat.js which is very special and must remain
|
||||
// part of the DOM so that the embedded images can reference it. jQuery's append implementation
|
||||
// (v1.4.2) fubars it.
|
||||
var script = rawDocument.createElement('script');
|
||||
|
||||
script.type = 'text/javascript';
|
||||
script.src = url;
|
||||
if (domId) script.id = domId;
|
||||
|
||||
if (msie) {
|
||||
script.onreadystatechange = function() {
|
||||
/loaded|complete/.test(script.readyState) && done && done();
|
||||
};
|
||||
} else {
|
||||
if (done) script.onload = script.onerror = done;
|
||||
}
|
||||
|
||||
body[0].appendChild(script);
|
||||
|
||||
return script;
|
||||
};
|
||||
}
|
||||
|
||||
+13
-11
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Template provides directions an how to bind to a given element.
|
||||
* It contains a list of init functions which need to be called to
|
||||
@@ -87,7 +89,7 @@ Template.prototype = {
|
||||
* The compilation is a process of walking the DOM tree and trying to match DOM elements to
|
||||
* {@link angular.markup markup}, {@link angular.attrMarkup attrMarkup},
|
||||
* {@link angular.widget widgets}, and {@link angular.directive directives}. For each match it
|
||||
* executes coresponding markup, attrMarkup, widget or directive template function and collects the
|
||||
* executes corresponding markup, attrMarkup, widget or directive template function and collects the
|
||||
* instance functions into a single template function which is then returned.
|
||||
*
|
||||
* The template function can then be used once to produce the view or as it is the case with
|
||||
@@ -95,16 +97,16 @@ Template.prototype = {
|
||||
* that is a DOM clone of the original template.
|
||||
*
|
||||
<pre>
|
||||
//copile the entire window.document and give me the scope bound to this template.
|
||||
var rootSscope = angular.compile(window.document)();
|
||||
// compile the entire window.document and give me the scope bound to this template.
|
||||
var rootScope = angular.compile(window.document)();
|
||||
|
||||
//compile a piece of html
|
||||
var rootScope2 = angular.compile(''<div ng:click="clicked = true">click me</div>')();
|
||||
// compile a piece of html
|
||||
var rootScope2 = angular.compile('<div ng:click="clicked = true">click me</div>')();
|
||||
|
||||
//compile a piece of html and retain reference to both the dom and scope
|
||||
// compile a piece of html and retain reference to both the dom and scope
|
||||
var template = angular.element('<div ng:click="clicked = true">click me</div>'),
|
||||
scoope = angular.compile(view)();
|
||||
//at this point template was transformed into a view
|
||||
scope = angular.compile(template)();
|
||||
// at this point template was transformed into a view
|
||||
</pre>
|
||||
*
|
||||
*
|
||||
@@ -116,7 +118,7 @@ Template.prototype = {
|
||||
* root scope is created.
|
||||
* * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
|
||||
* `template` and call the `cloneAttachFn` function allowing the caller to attach the
|
||||
* cloned elements to the DOM document at the approriate place. The `cloneAttachFn` is
|
||||
* cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
|
||||
* called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
|
||||
*
|
||||
* * `clonedElement` - is a clone of the original `element` passed into the compiler.
|
||||
@@ -175,7 +177,7 @@ Template.prototype = {
|
||||
* directives processing state. The compiler will process directives only when directives set to
|
||||
* true.
|
||||
*
|
||||
* For information on how the compiler works, see the
|
||||
* For information on how the compiler works, see the
|
||||
* {@link guide/dev_guide.compiler Angular HTML Compiler} section of the Developer Guide.
|
||||
*/
|
||||
function Compiler(markup, attrMarkup, directives, widgets){
|
||||
@@ -232,7 +234,7 @@ Compiler.prototype = {
|
||||
* not a problem, but under some circumstances the values for data
|
||||
* is not available until after the full view is computed. If such
|
||||
* values are needed before they are computed the order of
|
||||
* evaluation can be change using ng:eval-order
|
||||
* evaluation can be changed using ng:eval-order
|
||||
*
|
||||
* @element ANY
|
||||
* @param {integer|string=} [priority=0] priority integer, or FIRST, LAST constant
|
||||
|
||||
+8
-5
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.injector
|
||||
@@ -29,7 +31,7 @@
|
||||
* * `self` - "`this`" to be used when invoking the function.
|
||||
* * `fn` - the function to be invoked. The function may have the `$inject` property which
|
||||
* lists the set of arguments which should be auto injected
|
||||
* (see {@link guide.di dependency injection}).
|
||||
* (see {@link guide/dev_guide.di dependency injection}).
|
||||
* * `curryArgs(array)` - optional array of arguments to pass to function invocation after the
|
||||
* injection arguments (also known as curry arguments or currying).
|
||||
* * an `eager` property which is used to initialize the eager services.
|
||||
@@ -60,7 +62,7 @@ function createInjector(factoryScope, factories, instanceCache) {
|
||||
instanceCache[value] = invoke(factoryScope, factory);
|
||||
}
|
||||
return instanceCache[value];
|
||||
};
|
||||
}
|
||||
|
||||
function invoke(self, fn, args){
|
||||
args = args || [];
|
||||
@@ -73,8 +75,9 @@ function createInjector(factoryScope, factories, instanceCache) {
|
||||
}
|
||||
}
|
||||
|
||||
/*NOT_PUBLIC_YET
|
||||
* @ngdoc function
|
||||
/**
|
||||
* THIS IS NOT PUBLIC DOC YET!
|
||||
*
|
||||
* @name angular.annotate
|
||||
* @function
|
||||
*
|
||||
@@ -125,7 +128,7 @@ function angularServiceInject(name, fn, inject, eager) {
|
||||
* extracting all arguments which and assuming that they are the
|
||||
* injection names.
|
||||
*/
|
||||
var FN_ARGS = /^function\s*[^\(]*\(([^\)]*)\)/;
|
||||
var FN_ARGS = /^function\s*[^\(]*\(([^\)]*)\)/m;
|
||||
var FN_ARG_SPLIT = /,/;
|
||||
var FN_ARG = /^\s*(.+?)\s*$/;
|
||||
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
var array = [].constructor;
|
||||
|
||||
/**
|
||||
|
||||
+52
-28
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
|
||||
function Route(template, defaults) {
|
||||
@@ -65,53 +67,64 @@ ResourceFactory.prototype = {
|
||||
|
||||
forEach(actions, function(action, name){
|
||||
var isPostOrPut = action.method == 'POST' || action.method == 'PUT';
|
||||
Resource[name] = function (a1, a2, a3) {
|
||||
Resource[name] = function (a1, a2, a3, a4) {
|
||||
var params = {};
|
||||
var data;
|
||||
var callback = noop;
|
||||
var success = noop;
|
||||
var error = null;
|
||||
switch(arguments.length) {
|
||||
case 3: callback = a3;
|
||||
case 4:
|
||||
error = a4;
|
||||
success = a3;
|
||||
//fallthrough
|
||||
case 3:
|
||||
case 2:
|
||||
if (isFunction(a2)) {
|
||||
callback = a2;
|
||||
if (isFunction(a1)) {
|
||||
success = a1;
|
||||
error = a2;
|
||||
break;
|
||||
}
|
||||
|
||||
success = a2;
|
||||
error = a3;
|
||||
//fallthrough
|
||||
} else {
|
||||
params = a1;
|
||||
data = a2;
|
||||
success = a3;
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
if (isFunction(a1)) callback = a1;
|
||||
if (isFunction(a1)) success = a1;
|
||||
else if (isPostOrPut) data = a1;
|
||||
else params = a1;
|
||||
break;
|
||||
case 0: break;
|
||||
default:
|
||||
throw "Expected between 0-3 arguments [params, data, callback], got " + arguments.length + " arguments.";
|
||||
throw "Expected between 0-4 arguments [params, data, success, error], got " +
|
||||
arguments.length + " arguments.";
|
||||
}
|
||||
|
||||
var value = this instanceof Resource ? this : (action.isArray ? [] : new Resource(data));
|
||||
self.xhr(
|
||||
action.method,
|
||||
route.url(extend({}, action.params || {}, extractParams(data), params)),
|
||||
route.url(extend({}, extractParams(data), action.params || {}, params)),
|
||||
data,
|
||||
function(status, response, clear) {
|
||||
if (200 <= status && status < 300) {
|
||||
if (response) {
|
||||
if (action.isArray) {
|
||||
value.length = 0;
|
||||
forEach(response, function(item){
|
||||
value.push(new Resource(item));
|
||||
});
|
||||
} else {
|
||||
copy(response, value);
|
||||
}
|
||||
function(status, response, responseHeaders) {
|
||||
if (response) {
|
||||
if (action.isArray) {
|
||||
value.length = 0;
|
||||
forEach(response, function(item) {
|
||||
value.push(new Resource(item));
|
||||
});
|
||||
} else {
|
||||
copy(response, value);
|
||||
}
|
||||
(callback||noop)(value);
|
||||
} else {
|
||||
throw {status: status, response:response, message: status + ": " + response};
|
||||
}
|
||||
(success||noop)(value, responseHeaders);
|
||||
},
|
||||
error || action.verifyCache,
|
||||
action.verifyCache);
|
||||
return value;
|
||||
};
|
||||
@@ -120,18 +133,29 @@ ResourceFactory.prototype = {
|
||||
return self.route(url, extend({}, paramDefaults, additionalParamDefaults), actions);
|
||||
};
|
||||
|
||||
Resource.prototype['$' + name] = function(a1, a2){
|
||||
var params = extractParams(this);
|
||||
var callback = noop;
|
||||
Resource.prototype['$' + name] = function(a1, a2, a3) {
|
||||
var params = extractParams(this),
|
||||
success = noop,
|
||||
error;
|
||||
|
||||
switch(arguments.length) {
|
||||
case 2: params = a1; callback = a2;
|
||||
case 1: if (typeof a1 == $function) callback = a1; else params = a1;
|
||||
case 3: params = a1; success = a2; error = a3; break;
|
||||
case 2:
|
||||
case 1:
|
||||
if (isFunction(a1)) {
|
||||
success = a1;
|
||||
error = a2;
|
||||
} else {
|
||||
params = a1;
|
||||
success = a2 || noop;
|
||||
}
|
||||
case 0: break;
|
||||
default:
|
||||
throw "Expected between 1-2 arguments [params, callback], got " + arguments.length + " arguments.";
|
||||
throw "Expected between 1-3 arguments [params, success, error], got " +
|
||||
arguments.length + " arguments.";
|
||||
}
|
||||
var data = isPostOrPut ? this : undefined;
|
||||
Resource[name].call(this, params, data, callback);
|
||||
Resource[name].call(this, params, data, success, error);
|
||||
};
|
||||
});
|
||||
return Resource;
|
||||
|
||||
+5
-3
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
function getter(instance, path, unboundFn) {
|
||||
if (!path) return instance;
|
||||
var element = path.split('.');
|
||||
@@ -7,7 +9,7 @@ function getter(instance, path, unboundFn) {
|
||||
for ( var i = 0; i < len; i++) {
|
||||
key = element[i];
|
||||
if (!key.match(/^[\$\w][\$\w\d]*$/))
|
||||
throw "Expression '" + path + "' is not a valid expression for accesing variables.";
|
||||
throw "Expression '" + path + "' is not a valid expression for accessing variables.";
|
||||
if (instance) {
|
||||
lastInstance = instance;
|
||||
instance = instance[key];
|
||||
@@ -200,7 +202,7 @@ function createScope(parent, providers, instanceCache) {
|
||||
* @description
|
||||
* Assigns a value to a property of the current scope specified via `property_chain`. Unlike in
|
||||
* JavaScript, if there are any `undefined` intermediary properties, empty objects are created
|
||||
* and assigned in to them instead of throwing an exception.
|
||||
* and assigned to them instead of throwing an exception.
|
||||
*
|
||||
<pre>
|
||||
var scope = angular.scope();
|
||||
@@ -366,7 +368,7 @@ function createScope(parent, providers, instanceCache) {
|
||||
* parameters, `newValue` and `oldValue`.
|
||||
* @param {(function()|DOMElement)=} [exceptionHanlder=angular.service.$exceptionHandler] Handler
|
||||
* that gets called when `watchExp` or `listener` throws an exception. If a DOMElement is
|
||||
* specified as handler, the element gets decorated by angular with the information about the
|
||||
* specified as a handler, the element gets decorated by angular with the information about the
|
||||
* exception.
|
||||
* @param {boolean=} [initRun=true] Flag that prevents the first execution of the listener upon
|
||||
* registration.
|
||||
|
||||
Vendored
+11
-25
@@ -1,25 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Adam Abrons and Misko Hevery http://getangular.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
* @license AngularJS
|
||||
* (c) 2010-2011 AngularJS http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window) {
|
||||
|
||||
@@ -92,7 +76,7 @@
|
||||
|
||||
// load the js scripts
|
||||
for (i in Array.prototype.slice.call(arguments, 0)) {
|
||||
file = arguments[i];
|
||||
var file = arguments[i];
|
||||
document.write('<script type="text/javascript" src="' + serverPath + file + '" ' +
|
||||
'onload="angularClobberTest(\'' + file + '\')"></script>');
|
||||
}
|
||||
@@ -150,10 +134,12 @@
|
||||
// empty the cache to prevent mem leaks
|
||||
globalVars = {};
|
||||
|
||||
//angular-ie-compat.js needs to be pregenerated for development with IE<8
|
||||
if (msie<8) addScript('../angular-ie-compat.js');
|
||||
var config = angularJsConfig(document);
|
||||
|
||||
angularInit(angularJsConfig(document), document);
|
||||
// angular-ie-compat.js needs to be pregenerated for development with IE<8
|
||||
config.ie_compat = serverPath + '../build/angular-ie-compat.js';
|
||||
|
||||
angularInit(config, document);
|
||||
}
|
||||
|
||||
if (window.addEventListener){
|
||||
|
||||
Vendored
+196
-38
@@ -1,25 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Adam Abrons and Misko Hevery http://getangular.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2011 AngularJS http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
|
||||
@@ -69,8 +53,8 @@
|
||||
* Built-in mocks:
|
||||
*
|
||||
* * {@link angular.mock.service.$browser $browser } - A mock implementation of the browser.
|
||||
* * {@link angular.mock.service.$exceptionHandler $exceptionHandler } - A mock implementation of the
|
||||
* angular service exception handler.
|
||||
* * {@link angular.mock.service.$exceptionHandler $exceptionHandler } - A mock implementation of
|
||||
* the angular service exception handler.
|
||||
* * {@link angular.mock.service.$log $log } - A mock implementation of the angular service log.
|
||||
*/
|
||||
angular.mock = {};
|
||||
@@ -80,6 +64,24 @@ angular.mock = {};
|
||||
* @workInProgress
|
||||
* @ngdoc service
|
||||
* @name angular.mock.service.$browser
|
||||
*
|
||||
* @description
|
||||
* This service is a mock implementation of {@link angular.service.$browser}. It provides fake
|
||||
* implementation for commonly used browser apis that are hard to test, e.g. setTimeout, xhr,
|
||||
* cookies.
|
||||
*
|
||||
* This implementation is automatically available and replaces regular `$browser` service in tests
|
||||
* when `angular-mocks.js` is loaded.
|
||||
*
|
||||
* The api of this service is the same as the real {@link angular.service.$browser $browser}, except
|
||||
* that there are several helper methods available which can be used in tests.
|
||||
*
|
||||
* The following apis can be used in tests:
|
||||
*
|
||||
* - {@link angular.mock.service.$browser.xhr $browser.xhr} — enables testing of code that uses
|
||||
* the {@link angular.service.$xhr $xhr service} to make XmlHttpRequests.
|
||||
* - $browser.defer — enables testing of code that uses
|
||||
* {@link angular.service.$defer $defer service} for executing functions via the `setTimeout` api.
|
||||
*/
|
||||
function MockBrowser() {
|
||||
var self = this,
|
||||
@@ -108,6 +110,33 @@ function MockBrowser() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.mock.service.$browser.xhr
|
||||
*
|
||||
* @description
|
||||
* Generic method for training browser to expect a request in a test and respond to it.
|
||||
*
|
||||
* See also convenience methods for browser training:
|
||||
*
|
||||
* - {@link angular.mock.service.$browser.xhr.expectGET $browser.xhr.expectGET}
|
||||
* - {@link angular.mock.service.$browser.xhr.expectPOST $browser.xhr.expectPOST}
|
||||
* - {@link angular.mock.service.$browser.xhr.expectPUT $browser.xhr.expectPUT}
|
||||
* - {@link angular.mock.service.$browser.xhr.expectDELETE $browser.xhr.expectDELETE}
|
||||
* - {@link angular.mock.service.$browser.xhr.expectJSON $browser.xhr.expectJSON}
|
||||
*
|
||||
* To flush pending requests in tests use
|
||||
* {@link angular.mock.service.$browser.xhr.flush $browser.xhr.flush}.
|
||||
*
|
||||
* @param {string} method Expected HTTP method.
|
||||
* @param {string} url Url path for which a request is expected.
|
||||
* @param {(object|string)=} data Expected body of the (POST) HTTP request.
|
||||
* @param {function(number, *)} callback Callback to call when response is flushed.
|
||||
* @param {object} headers Key-value pairs of expected headers.
|
||||
* @returns {object} Response configuration object. You can call its `respond()` method to
|
||||
* configure what should the browser mock return when the response is
|
||||
* {@link angular.mock.service.$browser.xhr.flush flushed}.
|
||||
*/
|
||||
self.xhr = function(method, url, data, callback, headers) {
|
||||
headers = headers || {};
|
||||
if (data && angular.isObject(data)) data = angular.toJson(data);
|
||||
@@ -123,7 +152,14 @@ function MockBrowser() {
|
||||
throw new Error("Missing HTTP request header: " + key + ": " + value);
|
||||
}
|
||||
});
|
||||
callback(expectation.code, expectation.response);
|
||||
callback(expectation.code, expectation.response, function(header) {
|
||||
if (header) {
|
||||
header = header.toLowerCase();
|
||||
return expectation.responseHeaders && expectation.responseHeaders[header] || null;
|
||||
} else {
|
||||
return expectation.responseHeaders || {};
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
self.xhr.expectations = expectations;
|
||||
@@ -133,20 +169,104 @@ function MockBrowser() {
|
||||
if (data && angular.isString(data)) url += "|" + data;
|
||||
var expect = expectations[method] || (expectations[method] = {});
|
||||
return {
|
||||
respond: function(code, response) {
|
||||
respond: function(code, response, responseHeaders) {
|
||||
if (!angular.isNumber(code)) {
|
||||
responseHeaders = response;
|
||||
response = code;
|
||||
code = 200;
|
||||
}
|
||||
expect[url] = {code:code, response:response, headers: headers || {}};
|
||||
angular.forEach(responseHeaders, function(value, key) {
|
||||
delete responseHeaders[key];
|
||||
responseHeaders[key.toLowerCase()] = value;
|
||||
});
|
||||
expect[url] = {
|
||||
code: code,
|
||||
response: response,
|
||||
headers: headers || {},
|
||||
responseHeaders: responseHeaders || {}
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.mock.service.$browser.xhr.expectGET
|
||||
*
|
||||
* @description
|
||||
* Trains browser to expect a `GET` request and respond to it.
|
||||
*
|
||||
* @param {string} url Url path for which a request is expected.
|
||||
* @returns {object} Response configuration object. You can call its `respond()` method to
|
||||
* configure what should the browser mock return when the response is
|
||||
* {@link angular.mock.service.$browser.xhr.flush flushed}.
|
||||
*/
|
||||
self.xhr.expectGET = angular.bind(self, self.xhr.expect, 'GET');
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.mock.service.$browser.xhr.expectPOST
|
||||
*
|
||||
* @description
|
||||
* Trains browser to expect a `POST` request and respond to it.
|
||||
*
|
||||
* @param {string} url Url path for which a request is expected.
|
||||
* @returns {object} Response configuration object. You can call its `respond()` method to
|
||||
* configure what should the browser mock return when the response is
|
||||
* {@link angular.mock.service.$browser.xhr.flush flushed}.
|
||||
*/
|
||||
self.xhr.expectPOST = angular.bind(self, self.xhr.expect, 'POST');
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.mock.service.$browser.xhr.expectDELETE
|
||||
*
|
||||
* @description
|
||||
* Trains browser to expect a `DELETE` request and respond to it.
|
||||
*
|
||||
* @param {string} url Url path for which a request is expected.
|
||||
* @returns {object} Response configuration object. You can call its `respond()` method to
|
||||
* configure what should the browser mock return when the response is
|
||||
* {@link angular.mock.service.$browser.xhr.flush flushed}.
|
||||
*/
|
||||
self.xhr.expectDELETE = angular.bind(self, self.xhr.expect, 'DELETE');
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.mock.service.$browser.xhr.expectPUT
|
||||
*
|
||||
* @description
|
||||
* Trains browser to expect a `PUT` request and respond to it.
|
||||
*
|
||||
* @param {string} url Url path for which a request is expected.
|
||||
* @returns {object} Response configuration object. You can call its `respond()` method to
|
||||
* configure what should the browser mock return when the response is
|
||||
* {@link angular.mock.service.$browser.xhr.flush flushed}.
|
||||
*/
|
||||
self.xhr.expectPUT = angular.bind(self, self.xhr.expect, 'PUT');
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.mock.service.$browser.xhr.expectJSON
|
||||
*
|
||||
* @description
|
||||
* Trains browser to expect a `JSON` request and respond to it.
|
||||
*
|
||||
* @param {string} url Url path for which a request is expected.
|
||||
* @returns {object} Response configuration object. You can call its `respond()` method to
|
||||
* configure what should the browser mock return when the response is
|
||||
* {@link angular.mock.service.$browser.xhr.flush flushed}.
|
||||
*/
|
||||
self.xhr.expectJSON = angular.bind(self, self.xhr.expect, 'JSON');
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.mock.service.$browser.xhr.flush
|
||||
*
|
||||
* @description
|
||||
* Flushes all pending requests and executes xhr callbacks with the trained response as the
|
||||
* argument.
|
||||
*/
|
||||
self.xhr.flush = function() {
|
||||
if (requests.length == 0) {
|
||||
throw new Error("No xhr requests to be flushed!");
|
||||
@@ -160,17 +280,41 @@ function MockBrowser() {
|
||||
self.cookieHash = {};
|
||||
self.lastCookieHash = {};
|
||||
self.deferredFns = [];
|
||||
self.deferredNextId = 0;
|
||||
|
||||
self.defer = function(fn, delay) {
|
||||
delay = delay || 0;
|
||||
self.deferredFns.push({time:(self.defer.now + delay), fn:fn});
|
||||
self.deferredFns.sort(function(a,b){ return a.time - b.time;});
|
||||
self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId});
|
||||
self.deferredFns.sort(function(a,b){return a.time - b.time;});
|
||||
return self.deferredNextId++;
|
||||
};
|
||||
|
||||
|
||||
self.defer.now = 0;
|
||||
|
||||
self.defer.flush = function(time) {
|
||||
self.defer.now += (time || 0);
|
||||
|
||||
self.defer.cancel = function(deferId) {
|
||||
var fnIndex;
|
||||
|
||||
angular.forEach(self.deferredFns, function(fn, index) {
|
||||
if (fn.id === deferId) fnIndex = index;
|
||||
});
|
||||
|
||||
if (fnIndex !== undefined) {
|
||||
self.deferredFns.splice(fnIndex, 1);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
self.defer.flush = function(delay) {
|
||||
if (angular.isDefined(delay)) {
|
||||
self.defer.now += delay;
|
||||
} else {
|
||||
if (self.deferredFns.length) {
|
||||
self.defer.now = self.deferredFns[self.deferredFns.length-1].time;
|
||||
}
|
||||
}
|
||||
|
||||
while (self.deferredFns.length && self.deferredFns[0].time <= self.defer.now) {
|
||||
self.deferredFns.shift().fn();
|
||||
}
|
||||
@@ -178,6 +322,13 @@ function MockBrowser() {
|
||||
}
|
||||
MockBrowser.prototype = {
|
||||
|
||||
/**
|
||||
* @name angular.mock.service.$browser#poll
|
||||
* @methodOf angular.mock.service.$browser
|
||||
*
|
||||
* @description
|
||||
* run all fns in pollFns
|
||||
*/
|
||||
poll: function poll(){
|
||||
angular.forEach(this.pollFns, function(pollFn){
|
||||
pollFn();
|
||||
@@ -239,7 +390,7 @@ angular.service('$browser', function(){
|
||||
*
|
||||
* See {@link angular.mock} for more info on angular mocks.
|
||||
*/
|
||||
angular.service('$exceptionHandler', function(e) {
|
||||
angular.service('$exceptionHandler', function() {
|
||||
return function(e) {throw e;};
|
||||
});
|
||||
|
||||
@@ -260,10 +411,10 @@ angular.service('$log', MockLogFactory);
|
||||
|
||||
function MockLogFactory() {
|
||||
var $log = {
|
||||
log: function(){ $log.log.logs.push(arguments); },
|
||||
warn: function(){ $log.warn.logs.push(arguments); },
|
||||
info: function(){ $log.info.logs.push(arguments); },
|
||||
error: function(){ $log.error.logs.push(arguments); }
|
||||
log: function(){$log.log.logs.push(arguments);},
|
||||
warn: function(){$log.warn.logs.push(arguments);},
|
||||
info: function(){$log.info.logs.push(arguments);},
|
||||
error: function(){$log.error.logs.push(arguments);}
|
||||
};
|
||||
|
||||
$log.log.logs = [];
|
||||
@@ -305,7 +456,7 @@ function MockLogFactory() {
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
function TzDate(offset, timestamp) {
|
||||
function TzDate(offset, timestamp, toStringVal) {
|
||||
if (angular.isString(timestamp)) {
|
||||
var tsStr = timestamp;
|
||||
|
||||
@@ -329,6 +480,10 @@ function TzDate(offset, timestamp) {
|
||||
return this.date.getTime() - this.offsetDiff;
|
||||
};
|
||||
|
||||
this.toString = function() {
|
||||
return toStringVal;
|
||||
};
|
||||
|
||||
this.toLocaleDateString = function() {
|
||||
return this.date.toLocaleDateString();
|
||||
};
|
||||
@@ -390,7 +545,8 @@ function TzDate(offset, timestamp) {
|
||||
};
|
||||
|
||||
//hide all methods not implemented in this mock that the Date prototype exposes
|
||||
var unimplementedMethods = ['getMilliseconds', 'getTime', 'getUTCDay',
|
||||
var self = this,
|
||||
unimplementedMethods = ['getMilliseconds', 'getUTCDay',
|
||||
'getUTCMilliseconds', 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
|
||||
'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
|
||||
'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
|
||||
@@ -398,7 +554,9 @@ function TzDate(offset, timestamp) {
|
||||
'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
|
||||
|
||||
angular.forEach(unimplementedMethods, function(methodName) {
|
||||
this[methodName] = function() {
|
||||
if (methodName == 'toString' && toStringVal) return;
|
||||
|
||||
self[methodName] = function() {
|
||||
throw {
|
||||
name: "MethodNotImplemented",
|
||||
message: "Method '" + methodName + "' is not implemented in the TzDate mock"
|
||||
|
||||
+4
-21
@@ -1,24 +1,7 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Adam Abrons and Misko Hevery http://getangular.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2011 AngularJS http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
'USE STRICT';
|
||||
(function(window, document, undefined){
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
var angularGlobal = {
|
||||
'typeOf':function(obj){
|
||||
if (obj === null) return $null;
|
||||
|
||||
+15
-13
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc overview
|
||||
@@ -263,7 +265,7 @@ angularDirective("ng:bind", function(expression, element){
|
||||
error = formatError(e);
|
||||
});
|
||||
this.$element = oldElement;
|
||||
// If we are HTML than save the raw HTML data so that we don't
|
||||
// If we are HTML then save the raw HTML data so that we don't
|
||||
// recompute sanitization since it is expensive.
|
||||
// TODO: turn this into a more generic way to compute this
|
||||
if (isHtml = (value instanceof HTML))
|
||||
@@ -339,7 +341,7 @@ function compileBindTemplate(template){
|
||||
* text should be replaced with the template in ng:bind-template.
|
||||
* Unlike ng:bind the ng:bind-template can contain multiple `{{` `}}`
|
||||
* expressions. (This is required since some HTML elements
|
||||
* can not have SPAN elements such as TITLE, or OPTION to name a few.
|
||||
* can not have SPAN elements such as TITLE, or OPTION to name a few.)
|
||||
*
|
||||
* @element ANY
|
||||
* @param {string} template of form
|
||||
@@ -539,19 +541,14 @@ angularDirective("ng:click", function(expression, element){
|
||||
<form ng:submit="list.push(text);text='';" ng:init="list=[]">
|
||||
Enter text and hit enter:
|
||||
<input type="text" name="text" value="hello"/>
|
||||
<input type="submit" id="submit" value="Submit" />
|
||||
</form>
|
||||
<pre>list={{list}}</pre>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should check ng:submit', function(){
|
||||
expect(binding('list')).toBe('list=[]');
|
||||
element('.doc-example-live form input').click();
|
||||
this.addFutureAction('submit from', function($window, $document, done) {
|
||||
$window.angular.element(
|
||||
$document.elements('.doc-example-live form')).
|
||||
trigger('submit');
|
||||
done();
|
||||
});
|
||||
element('.doc-example-live #submit').click();
|
||||
expect(binding('list')).toBe('list=["hello"]');
|
||||
});
|
||||
</doc:scenario>
|
||||
@@ -574,9 +571,14 @@ function ngClass(selector) {
|
||||
var existing = element[0].className + ' ';
|
||||
return function(element){
|
||||
this.$onEval(function(){
|
||||
if (selector(this.$index)) {
|
||||
var value = this.$eval(expression);
|
||||
var scope = this;
|
||||
|
||||
if (selector(scope.$index)) {
|
||||
var ngClassVal = scope.$eval(element.attr('ng:class') || '');
|
||||
if (isArray(ngClassVal)) ngClassVal = ngClassVal.join(' ');
|
||||
var value = scope.$eval(expression);
|
||||
if (isArray(value)) value = value.join(' ');
|
||||
if (ngClassVal && ngClassVal !== value) value = value + ' ' + ngClassVal;
|
||||
element[0].className = trim(existing + value);
|
||||
}
|
||||
}, element);
|
||||
@@ -736,7 +738,7 @@ angularDirective("ng:class-even", ngClass(function(i){return i % 2 === 1;}));
|
||||
angularDirective("ng:show", function(expression, element){
|
||||
return function(element){
|
||||
this.$onEval(function(){
|
||||
element.css($display, toBoolean(this.$eval(expression)) ? '' : $none);
|
||||
element.css('display', toBoolean(this.$eval(expression)) ? '' : 'none');
|
||||
}, element);
|
||||
};
|
||||
});
|
||||
@@ -777,7 +779,7 @@ angularDirective("ng:show", function(expression, element){
|
||||
angularDirective("ng:hide", function(expression, element){
|
||||
return function(element){
|
||||
this.$onEval(function(){
|
||||
element.css($display, toBoolean(this.$eval(expression)) ? $none : '');
|
||||
element.css('display', toBoolean(this.$eval(expression)) ? 'none' : '');
|
||||
}, element);
|
||||
};
|
||||
});
|
||||
|
||||
+164
-62
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc overview
|
||||
@@ -33,36 +35,42 @@
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Formats a number as a currency (ie $1,234.56).
|
||||
* Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
|
||||
* symbol for current locale is used.
|
||||
*
|
||||
* @param {number} amount Input to filter.
|
||||
* @returns {string} Formated number.
|
||||
* @param {string=} symbol Currency symbol or identifier to be displayed.
|
||||
* @returns {string} Formatted number.
|
||||
*
|
||||
* @css ng-format-negative
|
||||
* When the value is negative, this css class is applied to the binding making it by default red.
|
||||
* When the value is negative, this css class is applied to the binding making it (by default) red.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<input type="text" name="amount" value="1234.56"/> <br/>
|
||||
{{amount | currency}}
|
||||
default currency symbol ($): {{amount | currency}}<br/>
|
||||
custom currency identifier (USD$): {{amount | currency:"USD$"}}
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should init with 1234.56', function(){
|
||||
expect(binding('amount | currency')).toBe('$1,234.56');
|
||||
expect(binding('amount | currency:"USD$"')).toBe('USD$1,234.56');
|
||||
});
|
||||
it('should update', function(){
|
||||
input('amount').enter('-1234');
|
||||
expect(binding('amount | currency')).toBe('$-1,234.00');
|
||||
expect(binding('amount | currency')).toBe('($1,234.00)');
|
||||
expect(binding('amount | currency:"USD$"')).toBe('(USD$1,234.00)');
|
||||
expect(element('.doc-example-live .ng-binding').attr('className')).
|
||||
toMatch(/ng-format-negative/);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularFilter.currency = function(amount){
|
||||
angularFilter.currency = function(amount, currencySymbol){
|
||||
this.$element.toggleClass('ng-format-negative', amount < 0);
|
||||
return '$' + angularFilter.number.apply(this, [amount, 2]);
|
||||
if (isUndefined(currencySymbol)) currencySymbol = NUMBER_FORMATS.CURRENCY_SYM;
|
||||
return formatNumber(amount, 2, 1).replace(/\u00A4/g, currencySymbol);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -72,9 +80,9 @@ angularFilter.currency = function(amount){
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Formats a number as text.
|
||||
* Formats a number as text.
|
||||
*
|
||||
* If the input is not a number empty string is returned.
|
||||
* If the input is not a number an empty string is returned.
|
||||
*
|
||||
* @param {number|string} number Number to format.
|
||||
* @param {(number|string)=} [fractionSize=2] Number of decimal places to round the number to.
|
||||
@@ -90,59 +98,104 @@ angularFilter.currency = function(amount){
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should format numbers', function(){
|
||||
expect(binding('val | number')).toBe('1,234.57');
|
||||
expect(binding('val | number')).toBe('1,234.568');
|
||||
expect(binding('val | number:0')).toBe('1,235');
|
||||
expect(binding('-val | number:4')).toBe('-1,234.5679');
|
||||
});
|
||||
|
||||
it('should update', function(){
|
||||
input('val').enter('3374.333');
|
||||
expect(binding('val | number')).toBe('3,374.33');
|
||||
expect(binding('val | number')).toBe('3,374.333');
|
||||
expect(binding('val | number:0')).toBe('3,374');
|
||||
expect(binding('-val | number:4')).toBe('-3,374.3330');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularFilter.number = function(number, fractionSize){
|
||||
if (isNaN(number) || !isFinite(number)) {
|
||||
return '';
|
||||
}
|
||||
fractionSize = isUndefined(fractionSize)? 2 : fractionSize;
|
||||
|
||||
// PATTERNS[0] is an array for Decimal Pattern, PATTERNS[1] is an array Currency Pattern
|
||||
// Following is the order in each pattern array:
|
||||
// 0: minInteger,
|
||||
// 1: minFraction,
|
||||
// 2: maxFraction,
|
||||
// 3: positivePrefix,
|
||||
// 4: positiveSuffix,
|
||||
// 5: negativePrefix,
|
||||
// 6: negativeSuffix,
|
||||
// 7: groupSize,
|
||||
// 8: lastGroupSize
|
||||
var NUMBER_FORMATS = {
|
||||
DECIMAL_SEP: '.',
|
||||
GROUP_SEP: ',',
|
||||
PATTERNS: [[1, 0, 3, '', '', '-', '', 3, 3],[1, 2, 2, '\u00A4', '', '(\u00A4', ')', 3, 3]],
|
||||
CURRENCY_SYM: '$'
|
||||
};
|
||||
var DECIMAL_SEP = '.';
|
||||
|
||||
angularFilter.number = function(number, fractionSize) {
|
||||
if (isNaN(number) || !isFinite(number)) return '';
|
||||
return formatNumber(number, fractionSize, 0);
|
||||
};
|
||||
|
||||
function formatNumber(number, fractionSize, type) {
|
||||
var isNegative = number < 0,
|
||||
pow = Math.pow(10, fractionSize),
|
||||
whole = '' + number,
|
||||
type = type || 0, // 0 is decimal pattern, 1 is currency pattern
|
||||
pattern = NUMBER_FORMATS.PATTERNS[type];
|
||||
|
||||
number = Math.abs(number);
|
||||
var numStr = number + '',
|
||||
formatedText = '',
|
||||
i;
|
||||
parts = [];
|
||||
|
||||
if (whole.indexOf('e') > -1) return whole;
|
||||
if (numStr.indexOf('e') !== -1) {
|
||||
var formatedText = numStr;
|
||||
} else {
|
||||
var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
|
||||
|
||||
number = Math.round(number * pow) / pow;
|
||||
fraction = ('' + number).split('.');
|
||||
whole = fraction[0];
|
||||
fraction = fraction[1] || '';
|
||||
if (isNegative) {
|
||||
formatedText = '-';
|
||||
whole = whole.substring(1);
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < whole.length; i++) {
|
||||
if ((whole.length - i)%3 === 0 && i !== 0) {
|
||||
formatedText += ',';
|
||||
//determine fractionSize if it is not specified
|
||||
if (isUndefined(fractionSize)) {
|
||||
fractionSize = Math.min(Math.max(pattern[1], fractionLen), pattern[2]);
|
||||
}
|
||||
formatedText += whole.charAt(i);
|
||||
}
|
||||
if (fractionSize) {
|
||||
|
||||
var pow = Math.pow(10, fractionSize);
|
||||
number = Math.round(number * pow) / pow;
|
||||
var fraction = ('' + number).split(DECIMAL_SEP);
|
||||
var whole = fraction[0];
|
||||
fraction = fraction[1] || '';
|
||||
|
||||
var pos = 0,
|
||||
lgroup = pattern[8],
|
||||
group = pattern[7];
|
||||
|
||||
if (whole.length >= (lgroup + group)) {
|
||||
pos = whole.length - lgroup;
|
||||
for (var i = 0; i < pos; i++) {
|
||||
if ((pos - i)%group === 0 && i !== 0) {
|
||||
formatedText += NUMBER_FORMATS.GROUP_SEP;
|
||||
}
|
||||
formatedText += whole.charAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = pos; i < whole.length; i++) {
|
||||
if ((whole.length - i)%lgroup === 0 && i !== 0) {
|
||||
formatedText += NUMBER_FORMATS.GROUP_SEP;
|
||||
}
|
||||
formatedText += whole.charAt(i);
|
||||
}
|
||||
|
||||
// format fraction part.
|
||||
while(fraction.length < fractionSize) {
|
||||
fraction += '0';
|
||||
}
|
||||
formatedText += '.' + fraction.substring(0, fractionSize);
|
||||
if (fractionSize) formatedText += NUMBER_FORMATS.DECIMAL_SEP + fraction.substr(0, fractionSize);
|
||||
}
|
||||
return formatedText;
|
||||
};
|
||||
|
||||
parts.push(isNegative ? pattern[5] : pattern[3]);
|
||||
parts.push(formatedText);
|
||||
parts.push(isNegative ? pattern[6] : pattern[4]);
|
||||
return parts.join('');
|
||||
}
|
||||
|
||||
function padNumber(num, digits, trim) {
|
||||
var neg = '';
|
||||
@@ -182,6 +235,17 @@ function dateStrGetter(name, shortForm) {
|
||||
};
|
||||
}
|
||||
|
||||
function timeZoneGetter(numFormat) {
|
||||
return function(date) {
|
||||
var timeZone;
|
||||
if (numFormat || !(timeZone = GET_TIME_ZONE.exec(date.toString()))) {
|
||||
var offset = date.getTimezoneOffset();
|
||||
return padNumber(offset / 60, 2) + padNumber(Math.abs(offset % 60), 2);
|
||||
}
|
||||
return timeZone[0];
|
||||
};
|
||||
}
|
||||
|
||||
var DAY = 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(',');
|
||||
|
||||
var MONTH = 'January,February,March,April,May,June,July,August,September,October,November,December'.
|
||||
@@ -190,7 +254,8 @@ var MONTH = 'January,February,March,April,May,June,July,August,September,October
|
||||
var DATE_FORMATS = {
|
||||
yyyy: dateGetter('FullYear', 4),
|
||||
yy: dateGetter('FullYear', 2, 0, true),
|
||||
MMMMM: dateStrGetter('Month'),
|
||||
y: dateGetter('FullYear', 1),
|
||||
MMMM: dateStrGetter('Month'),
|
||||
MMM: dateStrGetter('Month', true),
|
||||
MM: dateGetter('Month', 2, 1),
|
||||
M: dateGetter('Month', 1, 1),
|
||||
@@ -207,14 +272,26 @@ var DATE_FORMATS = {
|
||||
EEEE: dateStrGetter('Day'),
|
||||
EEE: dateStrGetter('Day', true),
|
||||
a: function(date){return date.getHours() < 12 ? 'am' : 'pm';},
|
||||
Z: function(date){
|
||||
var offset = date.getTimezoneOffset();
|
||||
return padNumber(offset / 60, 2) + padNumber(Math.abs(offset % 60), 2);
|
||||
}
|
||||
z: timeZoneGetter(false),
|
||||
Z: timeZoneGetter(true)
|
||||
};
|
||||
|
||||
var DEFAULT_DATETIME_FORMATS = {
|
||||
long: 'MMMM d, y h:mm:ss a z',
|
||||
medium: 'MMM d, y h:mm:ss a',
|
||||
short: 'M/d/yy h:mm a',
|
||||
fullDate: 'EEEE, MMMM d, y',
|
||||
longDate: 'MMMM d, y',
|
||||
mediumDate: 'MMM d, y',
|
||||
shortDate: 'M/d/yy',
|
||||
longTime: 'h:mm:ss a z',
|
||||
mediumTime: 'h:mm:ss a',
|
||||
shortTime: 'h:mm a'
|
||||
};
|
||||
|
||||
var DATE_FORMATS_SPLIT = /([^yMdHhmsaZE]*)(E+|y+|M+|d+|H+|h+|m+|s+|a|Z)(.*)/;
|
||||
var GET_TIME_ZONE = /[A-Z]{3}(?![+\-])/;
|
||||
var DATE_FORMATS_SPLIT = /([^yMdHhmsazZE]*)(E+|y+|M+|d+|H+|h+|m+|s+|a|Z|z)(.*)/;
|
||||
var OPERA_TOSTRING_PATTERN = /^[\d].*Z$/;
|
||||
var NUMBER_STRING = /^\d+$/;
|
||||
|
||||
|
||||
@@ -229,35 +306,57 @@ var NUMBER_STRING = /^\d+$/;
|
||||
*
|
||||
* `format` string can be composed of the following elements:
|
||||
*
|
||||
* * `'yyyy'`: 4 digit representation of year e.g. 2010
|
||||
* * `'yy'`: 2 digit representation of year, padded (00-99)
|
||||
* * `'MMMMM'`: Month in year (January‒December)
|
||||
* * `'MMM'`: Month in year (Jan - Dec)
|
||||
* * `'MM'`: Month in year, padded (01‒12)
|
||||
* * `'M'`: Month in year (1‒12)
|
||||
* * `'dd'`: Day in month, padded (01‒31)
|
||||
* * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
|
||||
* * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
|
||||
* * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
|
||||
* * `'MMMM'`: Month in year (January-December)
|
||||
* * `'MMM'`: Month in year (Jan-Dec)
|
||||
* * `'MM'`: Month in year, padded (01-12)
|
||||
* * `'M'`: Month in year (1-12)
|
||||
* * `'dd'`: Day in month, padded (01-31)
|
||||
* * `'d'`: Day in month (1-31)
|
||||
* * `'EEEE'`: Day in Week,(Sunday‒Saturday)
|
||||
* * `'EEEE'`: Day in Week,(Sunday-Saturday)
|
||||
* * `'EEE'`: Day in Week, (Sun-Sat)
|
||||
* * `'HH'`: Hour in day, padded (00‒23)
|
||||
* * `'HH'`: Hour in day, padded (00-23)
|
||||
* * `'H'`: Hour in day (0-23)
|
||||
* * `'hh'`: Hour in am/pm, padded (01‒12)
|
||||
* * `'hh'`: Hour in am/pm, padded (01-12)
|
||||
* * `'h'`: Hour in am/pm, (1-12)
|
||||
* * `'mm'`: Minute in hour, padded (00‒59)
|
||||
* * `'mm'`: Minute in hour, padded (00-59)
|
||||
* * `'m'`: Minute in hour (0-59)
|
||||
* * `'ss'`: Second in minute, padded (00‒59)
|
||||
* * `'s'`: Second in minute (0‒59)
|
||||
* * `'ss'`: Second in minute, padded (00-59)
|
||||
* * `'s'`: Second in minute (0-59)
|
||||
* * `'a'`: am/pm marker
|
||||
* * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200‒1200)
|
||||
* * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-1200)
|
||||
* * `'z'`: short form of current timezone name (e.g. PDT)
|
||||
*
|
||||
* `format` string can also be the following default formats for `en_US` locale (support for other
|
||||
* locales will be added in the future versions):
|
||||
*
|
||||
* * `'long'`: equivalent to `'MMMM d, y h:mm:ss a z'` for en_US locale
|
||||
* (e.g. September 3, 2010 12:05:08 pm PDT)
|
||||
* * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
|
||||
* (e.g. Sep 3, 2010 12:05:08 pm)
|
||||
* * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 pm)
|
||||
* * `'fullDate'`: equivalent to `'EEEE, MMMM d,y'` for en_US locale
|
||||
* (e.g. Friday, September 3, 2010)
|
||||
* * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010
|
||||
* * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010)
|
||||
* * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
|
||||
* * `'longTime'`: equivalent to `'h:mm:ss a z'` for en_US locale (e.g. 12:05:08 pm PDT)
|
||||
* * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 pm)
|
||||
* * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 pm)
|
||||
*
|
||||
* @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
|
||||
* number) or ISO 8601 extended datetime string (yyyy-MM-ddTHH:mm:ss.SSSZ).
|
||||
* @param {string=} format Formatting rules. If not specified, Date#toLocaleDateString is used.
|
||||
* @param {string=} format Formatting rules (see Description). If not specified,
|
||||
* Date#toLocaleDateString is used.
|
||||
* @returns {string} Formatted string or the input if input is not recognized as date/millis.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<span ng:non-bindable>{{1288323623006 | date:'medium'}}</span>:
|
||||
{{1288323623006 | date:'medium'}}<br/>
|
||||
<span ng:non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
|
||||
{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}<br/>
|
||||
<span ng:non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
|
||||
@@ -265,6 +364,8 @@ var NUMBER_STRING = /^\d+$/;
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should format date', function(){
|
||||
expect(binding("1288323623006 | date:'medium'")).
|
||||
toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (am|pm)/);
|
||||
expect(binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).
|
||||
toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} \-?\d{4}/);
|
||||
expect(binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).
|
||||
@@ -274,6 +375,7 @@ var NUMBER_STRING = /^\d+$/;
|
||||
</doc:example>
|
||||
*/
|
||||
angularFilter.date = function(date, format) {
|
||||
format = DEFAULT_DATETIME_FORMATS[format] || format;
|
||||
if (isString(date)) {
|
||||
if (NUMBER_STRING.test(date)) {
|
||||
date = parseInt(date, 10);
|
||||
@@ -390,7 +492,7 @@ angularFilter.uppercase = uppercase;
|
||||
*
|
||||
* The input is sanitized by parsing the html into tokens. All safe tokens (from a whitelist) are
|
||||
* then serialized back to properly escaped html string. This means that no unsafe input can make
|
||||
* it into the returned string, however since our parser is more strict than a typical browser
|
||||
* it into the returned string, however, since our parser is more strict than a typical browser
|
||||
* parser, it's possible that some obscure input, which would be recognized as valid HTML by a
|
||||
* browser, won't make it through the sanitizer.
|
||||
*
|
||||
@@ -479,7 +581,7 @@ angularFilter.html = function(html, option){
|
||||
*
|
||||
* @description
|
||||
* Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
|
||||
* plane email address links.
|
||||
* plain email address links.
|
||||
*
|
||||
* @param {string} text Input text.
|
||||
* @returns {string} Html-linkified text.
|
||||
|
||||
+5
-4
@@ -1,20 +1,21 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc overview
|
||||
* @name angular.formatter
|
||||
* @description
|
||||
*
|
||||
* Formatters are used for translating data formats between those used in for display and those used
|
||||
* Formatters are used for translating data formats between those used for display and those used
|
||||
* for storage.
|
||||
*
|
||||
* Following is the list of built-in angular formatters:
|
||||
*
|
||||
* * {@link angular.formatter.boolean boolean} - Formats user input in boolean format
|
||||
* * {@link angular.formatter.index index} - Manages indexing into an HTML select widget
|
||||
* * {@link angular.formatter.json json} - Formats user input in JSON format
|
||||
* * {@link angular.formatter.list list} - Formats user input string as an array
|
||||
* * {@link angular.formatter.number} - Formats user input strings as a number
|
||||
* * {@link angular.formatter.trim} - Trims extras spaces from end of user input
|
||||
* * {@link angular.formatter.number number} - Formats user input strings as a number
|
||||
* * {@link angular.formatter.trim trim} - Trims extras spaces from end of user input
|
||||
*
|
||||
* For more information about how angular formatters work, and how to create your own formatters,
|
||||
* see {@link guide/dev_guide.templates.formatters Understanding Angular Formatters} in the angular
|
||||
|
||||
+19
-24
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
//////////////////////////////////
|
||||
//JQLite
|
||||
//////////////////////////////////
|
||||
@@ -21,7 +23,7 @@
|
||||
* focus on the most commonly needed functionality and minimal footprint. For this reason only a
|
||||
* limited number of jQuery methods, arguments and invocation styles are supported.
|
||||
*
|
||||
* NOTE: All element references in angular are always wrapped with jQuery (lite) and are never
|
||||
* Note: All element references in angular are always wrapped with jQuery (lite) and are never
|
||||
* raw DOM references.
|
||||
*
|
||||
* ## Angular's jQuery lite implements these functions:
|
||||
@@ -44,6 +46,7 @@
|
||||
* - [replaceWith()](http://api.jquery.com/replaceWith/)
|
||||
* - [text()](http://api.jquery.com/text/)
|
||||
* - [trigger()](http://api.jquery.com/trigger/)
|
||||
* - [eq()](http://api.jquery.com/eq/)
|
||||
*
|
||||
* ## Additionally these methods extend the jQuery and are available in both jQuery and jQuery lite
|
||||
* version:
|
||||
@@ -84,23 +87,6 @@ function getStyle(element) {
|
||||
return current;
|
||||
}
|
||||
|
||||
//TODO: delete me! dead code?
|
||||
if (msie) {
|
||||
extend(JQLite.prototype, {
|
||||
text: function(value) {
|
||||
var e = this[0];
|
||||
// NodeType == 3 is text node
|
||||
if (e.nodeType == 3) {
|
||||
if (isDefined(value)) e.nodeValue = value;
|
||||
return e.nodeValue;
|
||||
} else {
|
||||
if (isDefined(value)) e.innerText = value;
|
||||
return e.innerText;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////
|
||||
function jqLiteWrap(element) {
|
||||
if (isString(element) && element.charAt(0) != '<') {
|
||||
@@ -164,7 +150,7 @@ function JQLiteData(element, key, value) {
|
||||
|
||||
function JQLiteHasClass(element, selector, _) {
|
||||
// the argument '_' is important, since it makes the function have 3 arguments, which
|
||||
// is neede for delegate function to realize the this is a getter.
|
||||
// is needed for delegate function to realize the this is a getter.
|
||||
var className = " " + selector + " ";
|
||||
return ((" " + element.className + " ").replace(/[\n\t]/g, " ").indexOf( className ) > -1);
|
||||
}
|
||||
@@ -211,11 +197,16 @@ var JQLitePrototype = JQLite.prototype = {
|
||||
// we can not use jqLite since we are not done loading and jQuery could be loaded later.
|
||||
jqLiteWrap(window).bind('load', trigger); // fallback to window.onload for others
|
||||
},
|
||||
toString: function(){
|
||||
toString: function() {
|
||||
var value = [];
|
||||
forEach(this, function(e){ value.push('' + e);});
|
||||
return '[' + value.join(', ') + ']';
|
||||
},
|
||||
|
||||
eq: function(index) {
|
||||
return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
|
||||
},
|
||||
|
||||
length: 0,
|
||||
push: push,
|
||||
sort: [].sort,
|
||||
@@ -266,7 +257,9 @@ forEach({
|
||||
} else if (element.getAttribute) {
|
||||
// the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
|
||||
// some elements (e.g. Document) don't have get attribute, so return undefined
|
||||
return element.getAttribute(name, 2);
|
||||
var ret = element.getAttribute(name, 2);
|
||||
// normalize non-existing attributes to undefined (as jQuery)
|
||||
return ret === null ? undefined : ret;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -351,11 +344,10 @@ forEach({
|
||||
dealoc: JQLiteDealoc,
|
||||
|
||||
bind: function(element, type, fn){
|
||||
var bind = JQLiteData(element, 'bind'),
|
||||
eventHandler;
|
||||
var bind = JQLiteData(element, 'bind');
|
||||
if (!bind) JQLiteData(element, 'bind', bind = {});
|
||||
forEach(type.split(' '), function(type){
|
||||
eventHandler = bind[type];
|
||||
var eventHandler = bind[type];
|
||||
if (!eventHandler) {
|
||||
bind[type] = eventHandler = function(event) {
|
||||
if (!event.preventDefault) {
|
||||
@@ -368,6 +360,9 @@ forEach({
|
||||
event.cancelBubble = true; //ie
|
||||
};
|
||||
}
|
||||
if (!event.target) {
|
||||
event.target = event.srcElement || document;
|
||||
}
|
||||
forEach(eventHandler.fns, function(fn){
|
||||
fn.call(element, event);
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* JSTestDriver adapter for angular scenario tests
|
||||
*
|
||||
|
||||
@@ -1,24 +1,6 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Adam Abrons and Misko Hevery http://getangular.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2011 AngularJS http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window) {
|
||||
|
||||
+17
-10
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc overview
|
||||
@@ -10,8 +12,8 @@
|
||||
* Markup extensions do not themselves produce linking functions. Think of markup as a way to
|
||||
* produce shorthand for a {@link angular.widget widget} or a {@link angular.directive directive}.
|
||||
*
|
||||
* The most prominent example of an markup in angular is the built-in double curly markup
|
||||
* `{{expression}}`, which is a shorthand for `<span ng:bind="expression"></span>`.
|
||||
* The most prominent example of a markup in angular is the built-in double curly markup
|
||||
* `{{expression}}`, which is shorthand for `<span ng:bind="expression"></span>`.
|
||||
*
|
||||
* Create custom markup like this:
|
||||
*
|
||||
@@ -32,7 +34,7 @@
|
||||
* @description
|
||||
*
|
||||
* Attribute markup extends the angular compiler in a very similar way as {@link angular.markup}
|
||||
* except that it allows you to modify the state of the attribute text rather then the content of a
|
||||
* except that it allows you to modify the state of the attribute text rather than the content of a
|
||||
* node.
|
||||
*
|
||||
* Create custom attribute markup like this:
|
||||
@@ -136,7 +138,7 @@ angularTextMarkup('option', function(text, textNode, parentElement){
|
||||
*
|
||||
* @description
|
||||
* Using <angular/> markup like {{hash}} in an href attribute makes
|
||||
* the page open to a wrong URL, ff the user clicks that link before
|
||||
* the page open to a wrong URL, if the user clicks that link before
|
||||
* angular has a chance to replace the {{hash}} with actual URL, the
|
||||
* link will be broken and will most likely return a 404 error.
|
||||
* The `ng:href` solves this problem by placing the `href` in the
|
||||
@@ -249,7 +251,8 @@ angularTextMarkup('option', function(text, textNode, parentElement){
|
||||
* </div>
|
||||
* </pre>
|
||||
*
|
||||
* the HTML specs do not require browsers preserve the special attributes such as disabled.(The presense of them means true and absense means false)
|
||||
* The HTML specs do not require browsers to preserve the special attributes such as disabled.
|
||||
* (The presence of them means true and absence means false)
|
||||
* This prevents the angular compiler from correctly retrieving the binding expression.
|
||||
* To solve this problem, we introduce ng:disabled.
|
||||
*
|
||||
@@ -279,7 +282,8 @@ angularTextMarkup('option', function(text, textNode, parentElement){
|
||||
* @name angular.directive.ng:checked
|
||||
*
|
||||
* @description
|
||||
* the HTML specs do not require browsers preserve the special attributes such as checked.(The presense of them means true and absense means false)
|
||||
* The HTML specs do not require browsers to preserve the special attributes such as checked.
|
||||
* (The presence of them means true and absence means false)
|
||||
* This prevents the angular compiler from correctly retrieving the binding expression.
|
||||
* To solve this problem, we introduce ng:checked.
|
||||
* @example
|
||||
@@ -308,7 +312,8 @@ angularTextMarkup('option', function(text, textNode, parentElement){
|
||||
* @name angular.directive.ng:multiple
|
||||
*
|
||||
* @description
|
||||
* the HTML specs do not require browsers preserve the special attributes such as multiple.(The presense of them means true and absense means false)
|
||||
* The HTML specs do not require browsers to preserve the special attributes such as multiple.
|
||||
* (The presence of them means true and absence means false)
|
||||
* This prevents the angular compiler from correctly retrieving the binding expression.
|
||||
* To solve this problem, we introduce ng:multiple.
|
||||
*
|
||||
@@ -319,7 +324,7 @@ angularTextMarkup('option', function(text, textNode, parentElement){
|
||||
<select id="select" ng:multiple="{{checked}}">
|
||||
<option>Misko</option>
|
||||
<option>Igor</option>
|
||||
<option>Vojita</option>
|
||||
<option>Vojta</option>
|
||||
<option>Di</option>
|
||||
</select>
|
||||
</doc:source>
|
||||
@@ -343,7 +348,8 @@ angularTextMarkup('option', function(text, textNode, parentElement){
|
||||
* @name angular.directive.ng:readonly
|
||||
*
|
||||
* @description
|
||||
* the HTML specs do not require browsers preserve the special attributes such as readonly.(The presense of them means true and absense means false)
|
||||
* The HTML specs do not require browsers to preserve the special attributes such as readonly.
|
||||
* (The presence of them means true and absence means false)
|
||||
* This prevents the angular compiler from correctly retrieving the binding expression.
|
||||
* To solve this problem, we introduce ng:readonly.
|
||||
* @example
|
||||
@@ -372,7 +378,8 @@ angularTextMarkup('option', function(text, textNode, parentElement){
|
||||
* @name angular.directive.ng:selected
|
||||
*
|
||||
* @description
|
||||
* the HTML specs do not require browsers preserve the special attributes such as selected.(The presense of them means true and absense means false)
|
||||
* The HTML specs do not require browsers to preserve the special attributes such as selected.
|
||||
* (The presence of them means true and absence means false)
|
||||
* This prevents the angular compiler from correctly retrieving the binding expression.
|
||||
* To solve this problem, we introduce ng:selected.
|
||||
* @example
|
||||
|
||||
+2
-2
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
var OPERATORS = {
|
||||
'null':function(self){return null;},
|
||||
'true':function(self){return true;},
|
||||
@@ -290,11 +292,9 @@ function parser(text, json){
|
||||
var token = peek(e1, e2, e3, e4);
|
||||
if (token) {
|
||||
if (json && !token.json) {
|
||||
index = token.index;
|
||||
throwError("is not valid json", token);
|
||||
}
|
||||
tokens.shift();
|
||||
this.currentToken = token;
|
||||
return token;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
* HTML Parser By Misko Hevery (misko@hevery.com)
|
||||
* based on: HTML Parser By John Resig (ejohn.org)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Represents the application currently being tested and abstracts usage
|
||||
* of iframes or separate windows.
|
||||
@@ -89,7 +91,6 @@ angular.scenario.Application.prototype.executeAction = function(action) {
|
||||
return action.call(this, $window, _jQuery($window.document));
|
||||
}
|
||||
var $browser = $window.angular.service.$browser();
|
||||
$browser.poll();
|
||||
$browser.notifyWhenNoOutstandingRequests(function() {
|
||||
action.call(self, $window, _jQuery($window.document));
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* The representation of define blocks. Don't used directly, instead use
|
||||
* define() in your tests.
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* A future action in a spec.
|
||||
*
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Maintains an object tree from the runner events.
|
||||
*
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Runner for scenarios
|
||||
*
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
/**
|
||||
* Setup file for the Scenario.
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* This class is the "this" of the it/beforeEach/afterEach method.
|
||||
* Responsibilities:
|
||||
|
||||
Vendored
+2
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
(function(previousOnLoad){
|
||||
var prefix = (function(){
|
||||
var filename = /(.*\/)angular-bootstrap.js(#(.*))?/;
|
||||
|
||||
@@ -1,25 +1,7 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Adam Abrons and Misko Hevery http://getangular.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2011 AngularJS http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, document){
|
||||
var _jQuery = window.jQuery.noConflict(true);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Shared DSL statements that are useful to all scenarios.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Matchers for implementing specs. Follows the Jasmine spec conventions.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* User Interface for the Scenario Runner.
|
||||
*
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Generates JSON output into a context.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Creates a global value $result with the result of the runner.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Generates XML output into a context.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc service
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc service
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc service
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc service
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc service
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc service
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc service
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
var URL_MATCH = /^(file|ftp|http|https):\/\/(\w+:{0,1}\w*@)?([\w\.-]*)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/,
|
||||
HASH_MATCH = /^([^\?]*)?(\?([^\?]*))?$/,
|
||||
DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp':21};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc service
|
||||
|
||||
+16
-8
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc service
|
||||
@@ -80,9 +82,9 @@
|
||||
* The action methods on the class object or instance object can be invoked with the following
|
||||
* parameters:
|
||||
*
|
||||
* - HTTP GET "class" actions: `Resource.action([parameters], [callback])`
|
||||
* - non-GET "class" actions: `Resource.action(postData, [parameters], [callback])`
|
||||
* - non-GET instance actions: `instance.$action([parameters], [callback])`
|
||||
* - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])`
|
||||
* - non-GET "class" actions: `Resource.action(postData, [parameters], [success], [error])`
|
||||
* - non-GET instance actions: `instance.$action([parameters], [success], [error])`
|
||||
*
|
||||
*
|
||||
* @example
|
||||
@@ -140,14 +142,20 @@
|
||||
});
|
||||
</pre>
|
||||
*
|
||||
* It's worth noting that the callback for `get`, `query` and other method gets passed in the
|
||||
* response that came from the server, so one could rewrite the above example as:
|
||||
* It's worth noting that the success callback for `get`, `query` and other method gets passed
|
||||
* in the response that came from the server, so one could rewrite the above example as:
|
||||
*
|
||||
<pre>
|
||||
var User = $resource('/user/:userId', {userId:'@id'});
|
||||
User.get({userId:123}, function(u){
|
||||
User.get({userId:123}, function(u, responseHeaders){
|
||||
u.abc = true;
|
||||
u.$save();
|
||||
u.$save(function(u, responseHeaders) {
|
||||
// Get an Object containing all response headers
|
||||
var allHeaders = responseHeaders();
|
||||
|
||||
// Get a specific response header
|
||||
u.newId = responseHeaders('Location');
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
@@ -155,7 +163,7 @@
|
||||
|
||||
Let's look at what a buzz client created with the `$resource` service looks like:
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<doc:source jsfiddle="false">
|
||||
<script>
|
||||
function BuzzController($resource) {
|
||||
this.Activity = $resource(
|
||||
|
||||
+53
-22
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc service
|
||||
@@ -20,15 +22,18 @@
|
||||
Try changing the URL in the input box to see changes.
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<doc:source jsfiddle="false">
|
||||
<script>
|
||||
angular.service('myApp', function($route) {
|
||||
$route.when('/Book/:bookId', {template:'rsrc/book.html', controller:BookCntl});
|
||||
$route.when('/Book/:bookId/ch/:chapterId', {template:'rsrc/chapter.html', controller:ChapterCntl});
|
||||
function MainCntl($route, $location) {
|
||||
this.$route = $route;
|
||||
this.$location = $location;
|
||||
|
||||
$route.when('/Book/:bookId', {template: 'examples/book.html', controller: BookCntl});
|
||||
$route.when('/Book/:bookId/ch/:chapterId', {template: 'examples/chapter.html', controller: ChapterCntl});
|
||||
$route.onChange(function() {
|
||||
$route.current.scope.params = $route.current.params;
|
||||
});
|
||||
}, {$inject: ['$route']});
|
||||
}
|
||||
|
||||
function BookCntl() {
|
||||
this.name = "BookCntl";
|
||||
@@ -39,18 +44,19 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
Chose:
|
||||
<a href="#/Book/Moby">Moby</a> |
|
||||
<a href="#/Book/Moby/ch/1">Moby: Ch1</a> |
|
||||
<a href="#/Book/Gatsby">Gatsby</a> |
|
||||
<a href="#/Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a><br/>
|
||||
<input type="text" name="$location.hashPath" size="80" />
|
||||
<pre>$location={{$location}}</pre>
|
||||
<pre>$route.current.template={{$route.current.template}}</pre>
|
||||
<pre>$route.current.params={{$route.current.params}}</pre>
|
||||
<pre>$route.current.scope.name={{$route.current.scope.name}}</pre>
|
||||
<hr/>
|
||||
<ng:include src="$route.current.template" scope="$route.current.scope"/>
|
||||
<div ng:controller="MainCntl">
|
||||
Choose:
|
||||
<a href="#/Book/Moby">Moby</a> |
|
||||
<a href="#/Book/Moby/ch/1">Moby: Ch1</a> |
|
||||
<a href="#/Book/Gatsby">Gatsby</a> |
|
||||
<a href="#/Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a><br/>
|
||||
$location.hashPath: <input type="text" name="$location.hashPath" size="80" />
|
||||
<pre>$route.current.template = {{$route.current.template}}</pre>
|
||||
<pre>$route.current.params = {{$route.current.params}}</pre>
|
||||
<pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
|
||||
<hr />
|
||||
<ng:view></ng:view>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
</doc:scenario>
|
||||
@@ -62,6 +68,8 @@ angularServiceInject('$route', function(location, $updateView) {
|
||||
matcher = switchRouteMatcher,
|
||||
parentScope = this,
|
||||
dirty = 0,
|
||||
lastHashPath,
|
||||
lastRouteParams,
|
||||
$route = {
|
||||
routes: routes,
|
||||
|
||||
@@ -130,6 +138,18 @@ angularServiceInject('$route', function(location, $updateView) {
|
||||
* The custom `redirectTo` function is expected to return a string which will be used
|
||||
* to update `$location.hash`.
|
||||
*
|
||||
* - `[reloadOnSearch=true]` - {boolean=} - reload route when $location.hashSearch
|
||||
* changes. If this option is disabled, you should set up a $watch to be notified of
|
||||
* param (hashSearch) changes as follows:
|
||||
*
|
||||
* function MyCtrl($route) {
|
||||
* this.$watch(function() {
|
||||
* return $route.current.params.myHashSearchParam;
|
||||
* }, function(params) {
|
||||
* //do stuff with params
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* @returns {Object} route object
|
||||
*
|
||||
* @description
|
||||
@@ -138,8 +158,8 @@ angularServiceInject('$route', function(location, $updateView) {
|
||||
when:function (path, params) {
|
||||
if (isUndefined(path)) return routes; //TODO(im): remove - not needed!
|
||||
var route = routes[path];
|
||||
if (!route) route = routes[path] = {};
|
||||
if (params) extend(route, params);
|
||||
if (!route) route = routes[path] = {reloadOnSearch: true};
|
||||
if (params) extend(route, params); //TODO(im): what the heck? merge two route definitions?
|
||||
dirty++;
|
||||
return route;
|
||||
},
|
||||
@@ -177,14 +197,16 @@ angularServiceInject('$route', function(location, $updateView) {
|
||||
|
||||
|
||||
function switchRouteMatcher(on, when, dstName) {
|
||||
var regex = '^' + when.replace(/[\.\\\(\)\^\$]/g, "\$1") + '$',
|
||||
// TODO(i): this code is convoluted and inefficient, we should construct the route matching
|
||||
// regex only once and then reuse it
|
||||
var regex = '^' + when.replace(/([\.\\\(\)\^\$])/g, "\\$1") + '$',
|
||||
params = [],
|
||||
dst = {};
|
||||
forEach(when.split(/\W/), function(param){
|
||||
if (param) {
|
||||
var paramRegExp = new RegExp(":" + param + "([\\W])");
|
||||
if (regex.match(paramRegExp)) {
|
||||
regex = regex.replace(paramRegExp, "([^\/]*)$1");
|
||||
regex = regex.replace(paramRegExp, "([^\\/]*)$1");
|
||||
params.push(param);
|
||||
}
|
||||
}
|
||||
@@ -203,6 +225,14 @@ angularServiceInject('$route', function(location, $updateView) {
|
||||
function updateRoute(){
|
||||
var childScope, routeParams, pathParams, segmentMatch, key, redir;
|
||||
|
||||
if ($route.current) {
|
||||
if (!$route.current.reloadOnSearch && (lastHashPath == location.hashPath)) {
|
||||
$route.current.params = extend({}, location.hashSearch, lastRouteParams);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
lastHashPath = location.hashPath;
|
||||
$route.current = null;
|
||||
forEach(routes, function(rParams, rPath) {
|
||||
if (!pathParams) {
|
||||
@@ -249,6 +279,7 @@ angularServiceInject('$route', function(location, $updateView) {
|
||||
scope: childScope,
|
||||
params: extend({}, location.hashSearch, pathParams)
|
||||
});
|
||||
lastRouteParams = pathParams;
|
||||
}
|
||||
|
||||
//fire onChange callbacks
|
||||
@@ -260,7 +291,7 @@ angularServiceInject('$route', function(location, $updateView) {
|
||||
}
|
||||
|
||||
|
||||
this.$watch(function(){return dirty + location.hash;}, updateRoute);
|
||||
this.$watch(function(){ return dirty + location.hash; }, updateRoute);
|
||||
|
||||
return $route;
|
||||
}, ['$location', '$updateView']);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc service
|
||||
@@ -32,7 +34,7 @@
|
||||
* or 'XHR' (instead of {@link angular.service.$xhr}) then you may be changing the model
|
||||
* without angular knowledge and you may need to call '$updateView()' directly.
|
||||
*
|
||||
* NOTE: if you wish to update the view immediately (without delay), you can do so by calling
|
||||
* Note: if you wish to update the view immediately (without delay), you can do so by calling
|
||||
* {@link angular.scope.$eval} at any time from your code:
|
||||
* <pre>scope.$root.$eval()</pre>
|
||||
*
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user