Compare commits
78 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 20687aa5f6 | |||
| fc52b81d52 | |||
| ae1aee2b6c | |||
| 423242017e | |||
| 95c5df5958 | |||
| 2cb907a836 | |||
| 2f2fd465a4 | |||
| 6da355c3e1 | |||
| f2106692b1 | |||
| 4557881cf8 | |||
| af0ad6561c | |||
| 35125d2513 | |||
| 87f5c6e5b7 | |||
| a8a750ab05 | |||
| 13a95ae499 | |||
| da9f4dfcf4 | |||
| ac4318a2fa | |||
| bb2fa6f63f | |||
| ba59ef4950 | |||
| 8b93541522 | |||
| 7b22d59b4a | |||
| 798bca62c6 | |||
| 8218c4b60b | |||
| 2430f52bb9 | |||
| 944098a4e0 | |||
| 2ce0485e6f | |||
| a08cbc02e7 | |||
| 55027132f3 | |||
| 09e175f02c | |||
| 5c5b1183c8 | |||
| f04142ea28 | |||
| aaedefb92e | |||
| d54dfecb00 | |||
| 4b8d926062 | |||
| 74c84501ed | |||
| 4581b79bbd | |||
| 2be8847ef6 | |||
| cb2ad9abf2 | |||
| 73c8593077 | |||
| ac75079e21 | |||
| 5390fb37d2 | |||
| 8d7e694849 | |||
| 5fdab52dd7 | |||
| 541bedd1a9 | |||
| 98e18a64aa | |||
| 0a45bff472 | |||
| 263524d381 | |||
| 52c59cf0ce | |||
| c5f8edfe03 | |||
| 69f0aa899d | |||
| e7cd0bcc5a | |||
| ade6c45275 | |||
| 9eafd10fcd | |||
| 3436c027f2 | |||
| 6a8749e65a | |||
| 1a5bebd927 | |||
| 83155e8fbe | |||
| 6d6f875345 | |||
| a4fe51da3b | |||
| ee5a5352fd | |||
| 9cb2195e61 | |||
| 15213ec212 | |||
| 9171c76bb4 | |||
| 64fb1f2620 | |||
| f49eaf8bf2 | |||
| f701ce08f9 | |||
| 1cc0e4173d | |||
| d4ae7988da | |||
| 5ac14f633a | |||
| 9918b748be | |||
| 6ecac8e71a | |||
| 823adb2319 | |||
| 21e74c2d2e | |||
| 6c5a05ad49 | |||
| 192ff61f5d | |||
| 935c1018da | |||
| 78a6291666 | |||
| 53b6f522a5 |
+114
@@ -1,3 +1,117 @@
|
||||
<a name="v1.0.0rc3"></a>
|
||||
# v1.0.0rc3 barefoot-telepathy (2012-03-29)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- properly clone attr.$observers in ng-repeat
|
||||
([f2106692](https://github.com/angular/angular.js/commit/f2106692b1ebf00aa5f8b2accd75f014b6cd4faa))
|
||||
- create new (isolate) scopes for directives on root elements
|
||||
([5390fb37](https://github.com/angular/angular.js/commit/5390fb37d2c01937922613fc57df4986af521787),
|
||||
closes [#817](https://github.com/angular/angular.js/issues/817))
|
||||
- **angular.forEach:** should ignore prototypically inherited properties
|
||||
([8d7e6948](https://github.com/angular/angular.js/commit/8d7e6948496ff26ef1da8854ba02fcb8eebfed61),
|
||||
closes [#813](https://github.com/angular/angular.js/issues/813))
|
||||
- **initialization:** use jQuery#ready for initialization if available
|
||||
([cb2ad9ab](https://github.com/angular/angular.js/commit/cb2ad9abf24e6f855cc749efe3155bd7987ece9d),
|
||||
closes [#818](https://github.com/angular/angular.js/issues/818))
|
||||
- **$q:** resolve all of nothing to nothing
|
||||
([ac75079e](https://github.com/angular/angular.js/commit/ac75079e2113949d5d64adbcf23d56f3cf295d41))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$compile:** do not interpolate boolean attribute directives, rather evaluate them
|
||||
([a08cbc02](https://github.com/angular/angular.js/commit/a08cbc02e78e789a66e9af771c410e8ad1646e25))
|
||||
- **$controller:** support controller registration via $controllerProvider
|
||||
([d54dfecb](https://github.com/angular/angular.js/commit/d54dfecb00fba41455536c5ddd55310592fdaf84))
|
||||
- **$http:**
|
||||
- make the `transformRequest` and `transformResponse` default to an array
|
||||
([a8a750ab](https://github.com/angular/angular.js/commit/a8a750ab05bdff73ba3af0b98f3f284ff8d1e743))
|
||||
- added `params` parameter
|
||||
([73c85930](https://github.com/angular/angular.js/commit/73c8593077155a9f2e8ef42efd4c497eba0bef4f))
|
||||
- **TzDate:** add support for toISOString method
|
||||
([da9f4dfc](https://github.com/angular/angular.js/commit/da9f4dfcf4f3d0c21821d8474ac0bb19a3c51415))
|
||||
- **jqLite:** make injector() and scope() work with the document object
|
||||
([5fdab52d](https://github.com/angular/angular.js/commit/5fdab52dd7c269f99839f4fa6b5854d9548269fa))
|
||||
- **ngValue:** directive that allows radio inputs to have non string values
|
||||
([09e175f0](https://github.com/angular/angular.js/commit/09e175f02cca0f4a295fd0c9b980cd8f432e722b),
|
||||
closes [#816](https://github.com/angular/angular.js/issues/816))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- `$resource`, `$cookies` and `$cookieStore` services are now distributed as separate modules, see
|
||||
`angular-resource.js` and `angular-cookies.js`.
|
||||
([798bca62](https://github.com/angular/angular.js/commit/798bca62c6f64775b85deda3713e7b6bcc7a4b4d),
|
||||
[7b22d59b](https://github.com/angular/angular.js/commit/7b22d59b4a16d5c50c2eee054178ba17f8038880))
|
||||
- angular.fromJson doesn't deserialize date strings into date objects.
|
||||
([ac4318a2](https://github.com/angular/angular.js/commit/ac4318a2fa5c6d306dbc19466246292a81767fca))
|
||||
- angular.toJson always use native JSON.parse and JSON.stringify - this might break code that
|
||||
consumes the output in whitespace-sensitive way
|
||||
([35125d25](https://github.com/angular/angular.js/commit/35125d25137ac2da13ed1ca3e652ec8f2c945053))
|
||||
- IE7 and older have are now required to polyfill the JSON global object
|
||||
([87f5c6e5](https://github.com/angular/angular.js/commit/87f5c6e5b716100e203ec59c5874c3e927f83fa0))
|
||||
- boolean attr directives (ng-disabled, ng-required, etc) are evaluated rather than interpolated
|
||||
([a08cbc02](https://github.com/angular/angular.js/commit/a08cbc02e78e789a66e9af771c410e8ad1646e25))
|
||||
- `ng-bind-attr` directive removed
|
||||
([55027132](https://github.com/angular/angular.js/commit/55027132f3d57e5dcf94683e6e6bd7b0aae0087d))
|
||||
- any app that depends on $sniffer service should use Modernizr instead
|
||||
([aaedefb9](https://github.com/angular/angular.js/commit/aaedefb92e6bec6626e173e5155072c91471596a))
|
||||
|
||||
|
||||
|
||||
<a name="v1.0.0rc2"></a>
|
||||
# v1.0.0rc2 silence-absorption (2012-03-20)
|
||||
|
||||
## Features
|
||||
|
||||
- **$route:** when matching consider trailing slash as optional
|
||||
([a4fe51da](https://github.com/angular/angular.js/commit/a4fe51da3ba0dc297ecd389e230d6664f250c9a6))
|
||||
- **jqLite:** add .controller() method
|
||||
([6c5a05ad](https://github.com/angular/angular.js/commit/6c5a05ad49a1e083570c3dfe331403398f899dbe))
|
||||
- **scope.$eval:** allow passing locals to the expression
|
||||
([192ff61f](https://github.com/angular/angular.js/commit/192ff61f5d61899e667c6dbce4d3e6e399429d8b))
|
||||
- **input[type=radio]:** allow the value attribute to be interpolated
|
||||
([ade6c452](https://github.com/angular/angular.js/commit/ade6c452753145c84884d17027a7865bf4b34b0c))
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$http:** don't send Content-Type header when no data
|
||||
([1a5bebd9](https://github.com/angular/angular.js/commit/1a5bebd927ecd22f9c34617642fdf58fe3f62efb),
|
||||
[#749](https://github.com/angular/angular.js/issues/749))
|
||||
- **$resource:** support escaping of ':' in resource url
|
||||
([6d6f8753](https://github.com/angular/angular.js/commit/6d6f875345e01f2c6c63ef95164f6f39e923da15))
|
||||
- **$compile:**
|
||||
- don't touch static element attributes
|
||||
([9cb2195e](https://github.com/angular/angular.js/commit/9cb2195e61a78e99020ec19d687a221ca88b5900))
|
||||
- merge interpolated css class when replacing an element
|
||||
([f49eaf8b](https://github.com/angular/angular.js/commit/f49eaf8bf2df5f4e0e82d6c89e849a4f82c8d414))
|
||||
- allow transclusion of root elements
|
||||
([9918b748](https://github.com/angular/angular.js/commit/9918b748be01266eb10db39d51b4d3098d54ab66))
|
||||
- **$log:** avoid console.log.apply calls in IE
|
||||
([15213ec2](https://github.com/angular/angular.js/commit/15213ec212769837cb2b7e781ffc5bfd598d27ca),
|
||||
[#805](https://github.com/angular/angular.js/issues/805))
|
||||
- **json:** added support for iso8061 timezone
|
||||
([5ac14f63](https://github.com/angular/angular.js/commit/5ac14f633a69f49973b5512780c6ec7752405967))
|
||||
- **e2e runner:** fix typo that caused errors on IE8
|
||||
([ee5a5352](https://github.com/angular/angular.js/commit/ee5a5352fd4b94cedee6ef20d4bf2d43ce77e00b),
|
||||
[#806](https://github.com/angular/angular.js/issues/806))
|
||||
- **directives:**
|
||||
- **select:** multiselect failes to update view on selection insert
|
||||
([6ecac8e7](https://github.com/angular/angular.js/commit/6ecac8e71a84792a434d21db2c245b3648c55f18))
|
||||
- **ngForm:** alias name||ngForm
|
||||
([823adb23](https://github.com/angular/angular.js/commit/823adb231995e917bc060bfa49453e2a96bac2b6))
|
||||
- **ngView:** publish the controller
|
||||
([21e74c2d](https://github.com/angular/angular.js/commit/21e74c2d2e8e985b23711785287feb59965cbd90))
|
||||
- **ngRepeat:** correct variable reference in error message
|
||||
([935c1018](https://github.com/angular/angular.js/commit/935c1018da05dbf3124b2dd33619c4a3c82d7a2a))
|
||||
- various doc fixes (some contributed by Daniel Zen)
|
||||
|
||||
|
||||
|
||||
<a name="1.0.0rc1"></a>
|
||||
# 1.0.0rc1 moiré-vision (2012-03-13)
|
||||
|
||||
|
||||
@@ -2,7 +2,10 @@ require 'yaml'
|
||||
include FileUtils
|
||||
|
||||
content = File.open('angularFiles.js', 'r') {|f| f.read }
|
||||
files = eval(content.gsub(/\};(\s|\S)*/, '}').gsub(/angularFiles = /, '').gsub(/:/, '=>'));
|
||||
files = eval(content.gsub(/\};(\s|\S)*/, '}').
|
||||
gsub(/angularFiles = /, '').
|
||||
gsub(/:/, '=>').
|
||||
gsub(/\/\//, '#'));
|
||||
|
||||
BUILD_DIR = 'build'
|
||||
|
||||
@@ -35,37 +38,23 @@ end
|
||||
desc 'Compile Scenario'
|
||||
task :compile_scenario => :init do
|
||||
|
||||
deps = [
|
||||
concat_file('angular-scenario.js', [
|
||||
'lib/jquery/jquery.js',
|
||||
'src/scenario/angular.prefix',
|
||||
'src/ngScenario/angular.prefix',
|
||||
files['angularSrc'],
|
||||
files['angularScenario'],
|
||||
'src/scenario/angular.suffix',
|
||||
]
|
||||
|
||||
concat = 'cat ' + deps.flatten.join(' ')
|
||||
|
||||
File.open(path_to('angular-scenario.js'), 'w') do |f|
|
||||
f.write(%x{#{concat}}.gsub('"NG_VERSION_FULL"', NG_VERSION.full))
|
||||
f.write(gen_css('css/angular.css') + "\n")
|
||||
f.write(gen_css('css/angular-scenario.css'))
|
||||
end
|
||||
'src/ngScenario/angular.suffix',
|
||||
], gen_css('css/angular.css') + "\n" + gen_css('css/angular-scenario.css'))
|
||||
end
|
||||
|
||||
desc 'Compile JSTD Scenario Adapter'
|
||||
task :compile_jstd_scenario_adapter => :init do
|
||||
|
||||
deps = [
|
||||
'src/jstd-scenario-adapter/angular.prefix',
|
||||
'src/jstd-scenario-adapter/Adapter.js',
|
||||
'src/jstd-scenario-adapter/angular.suffix',
|
||||
]
|
||||
|
||||
concat = 'cat ' + deps.flatten.join(' ')
|
||||
|
||||
File.open(path_to('jstd-scenario-adapter.js'), 'w') do |f|
|
||||
f.write(%x{#{concat}}.gsub('"NG_VERSION_FULL"', NG_VERSION.full))
|
||||
end
|
||||
concat_file('jstd-scenario-adapter.js', [
|
||||
'src/ngScenario/jstd-scenario-adapter/angular.prefix',
|
||||
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
|
||||
'src/ngScenario/jstd-scenario-adapter/angular.suffix',
|
||||
])
|
||||
|
||||
# TODO(vojta) use jstd configuration when implemented
|
||||
# (instead of including jstd-adapter-config.js)
|
||||
@@ -80,55 +69,28 @@ end
|
||||
desc 'Compile JavaScript'
|
||||
task :compile => [:init, :compile_scenario, :compile_jstd_scenario_adapter] do
|
||||
|
||||
deps = [
|
||||
'src/angular.prefix',
|
||||
files['angularSrc'],
|
||||
'src/angular.suffix',
|
||||
]
|
||||
concat_file('angular.js', [
|
||||
'src/angular.prefix',
|
||||
files['angularSrc'],
|
||||
'src/angular.suffix',
|
||||
], gen_css('css/angular.css', true))
|
||||
|
||||
File.open(path_to('angular.js'), 'w') do |f|
|
||||
concat = 'cat ' + deps.flatten.join(' ')
|
||||
FileUtils.cp_r 'src/ngLocale', path_to('i18n')
|
||||
|
||||
content = %x{#{concat}}.
|
||||
gsub('"NG_VERSION_FULL"', NG_VERSION.full).
|
||||
gsub('"NG_VERSION_MAJOR"', NG_VERSION.major).
|
||||
gsub('"NG_VERSION_MINOR"', NG_VERSION.minor).
|
||||
gsub('"NG_VERSION_DOT"', NG_VERSION.dot).
|
||||
gsub('"NG_VERSION_CODENAME"', NG_VERSION.codename).
|
||||
gsub(/^\s*['"]use strict['"];?\s*$/, ''). # remove all file-specific strict mode flags
|
||||
gsub(/'USE STRICT'/, "'use strict'") # rename the placeholder in angular.prefix
|
||||
|
||||
f.write(content)
|
||||
f.write(gen_css('css/angular.css', true))
|
||||
end
|
||||
|
||||
%x(java -jar lib/closure-compiler/compiler.jar \
|
||||
--compilation_level SIMPLE_OPTIMIZATIONS \
|
||||
--language_in ECMASCRIPT5_STRICT \
|
||||
--js #{path_to('angular.js')} \
|
||||
--js_output_file #{path_to('angular.min.js')})
|
||||
|
||||
FileUtils.cp_r 'i18n/locale', path_to('i18n')
|
||||
|
||||
File.open(path_to('angular-loader.js'), 'w') do |f|
|
||||
concat = 'cat ' + [
|
||||
concat_file('angular-loader.js', [
|
||||
'src/loader.prefix',
|
||||
'src/loader.js',
|
||||
'src/loader.suffix'].flatten.join(' ')
|
||||
'src/loader.suffix'])
|
||||
|
||||
content = %x{#{concat}}.
|
||||
gsub('"NG_VERSION_FULL"', NG_VERSION.full).
|
||||
gsub(/^\s*['"]use strict['"];?\s*$/, '') # remove all file-specific strict mode flags
|
||||
FileUtils.cp 'src/ngMock/angular-mocks.js', path_to('angular-mocks.js')
|
||||
FileUtils.cp 'src/ngResource/resource.js', path_to('angular-resource.js')
|
||||
FileUtils.cp 'src/ngCookies/cookies.js', path_to('angular-cookies.js')
|
||||
|
||||
f.write(content)
|
||||
end
|
||||
|
||||
%x(java -jar lib/closure-compiler/compiler.jar \
|
||||
--compilation_level SIMPLE_OPTIMIZATIONS \
|
||||
--language_in ECMASCRIPT5_STRICT \
|
||||
--js #{path_to('angular-loader.js')} \
|
||||
--js_output_file #{path_to('angular-loader.min.js')})
|
||||
|
||||
closure_compile('angular.js')
|
||||
closure_compile('angular-cookies.js')
|
||||
closure_compile('angular-loader.js')
|
||||
closure_compile('angular-resource.js')
|
||||
|
||||
end
|
||||
|
||||
@@ -153,16 +115,20 @@ task :package => [:clean, :compile, :docs] do
|
||||
FileUtils.rm_r(path_to('pkg'), :force => true)
|
||||
FileUtils.mkdir_p(pkg_dir)
|
||||
|
||||
['src/angular-mocks.js',
|
||||
path_to('angular.js'),
|
||||
path_to('angular-loader.js'),
|
||||
[ path_to('angular.js'),
|
||||
path_to('angular.min.js'),
|
||||
path_to('angular-loader.js'),
|
||||
path_to('angular-loader.min.js'),
|
||||
path_to('angular-mocks.js'),
|
||||
path_to('angular-cookies.js'),
|
||||
path_to('angular-cookies.min.js'),
|
||||
path_to('angular-resource.js'),
|
||||
path_to('angular-resource.min.js'),
|
||||
path_to('angular-scenario.js'),
|
||||
path_to('jstd-scenario-adapter.js'),
|
||||
path_to('jstd-scenario-adapter-config.js'),
|
||||
].each do |src|
|
||||
dest = src.gsub(/^[^\/]+\//, '').gsub(/((\.min)?\.js)$/, "-#{NG_VERSION.full}\\1")
|
||||
dest = src.gsub(/^.*\//, '').gsub(/((\.min)?\.js)$/, "-#{NG_VERSION.full}\\1")
|
||||
FileUtils.cp(src, pkg_dir + '/' + dest)
|
||||
end
|
||||
|
||||
@@ -336,3 +302,32 @@ end
|
||||
def path_to(filename)
|
||||
return File.join(BUILD_DIR, *filename)
|
||||
end
|
||||
|
||||
def closure_compile(filename)
|
||||
puts "Compiling #{filename} ..."
|
||||
%x(java -jar lib/closure-compiler/compiler.jar \
|
||||
--compilation_level SIMPLE_OPTIMIZATIONS \
|
||||
--language_in ECMASCRIPT5_STRICT \
|
||||
--js #{path_to(filename)} \
|
||||
--js_output_file #{path_to(filename.gsub(/\.js$/, '.min.js'))})
|
||||
end
|
||||
|
||||
def concat_file(filename, deps, footer='')
|
||||
puts "Building #{filename} ..."
|
||||
File.open(path_to(filename), 'w') do |f|
|
||||
concat = 'cat ' + deps.flatten.join(' ')
|
||||
|
||||
content = %x{#{concat}}.
|
||||
gsub('"NG_VERSION_FULL"', NG_VERSION.full).
|
||||
gsub('"NG_VERSION_MAJOR"', NG_VERSION.major).
|
||||
gsub('"NG_VERSION_MINOR"', NG_VERSION.minor).
|
||||
gsub('"NG_VERSION_DOT"', NG_VERSION.dot).
|
||||
gsub('"NG_VERSION_CODENAME"', NG_VERSION.codename).
|
||||
gsub(/^\s*['"]use strict['"];?\s*$/, ''). # remove all file-specific strict mode flags
|
||||
gsub(/'USE STRICT'/, "'use strict'") # rename the placeholder in angular.prefix
|
||||
|
||||
f.write(content)
|
||||
f.write(footer)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Vendored
+138
-113
@@ -3,81 +3,102 @@ angularFiles = {
|
||||
'src/Angular.js',
|
||||
'src/loader.js',
|
||||
'src/AngularPublic.js',
|
||||
'src/JSON.js',
|
||||
'src/Injector.js',
|
||||
'src/Resource.js',
|
||||
'src/jqLite.js',
|
||||
'src/apis.js',
|
||||
'src/service/anchorScroll.js',
|
||||
'src/service/browser.js',
|
||||
'src/service/cacheFactory.js',
|
||||
'src/service/compiler.js',
|
||||
'src/service/controller.js',
|
||||
'src/service/cookieStore.js',
|
||||
'src/service/cookies.js',
|
||||
'src/service/defer.js',
|
||||
'src/service/document.js',
|
||||
'src/service/exceptionHandler.js',
|
||||
'src/service/filter.js',
|
||||
'src/service/filter/filter.js',
|
||||
'src/service/filter/filters.js',
|
||||
'src/service/filter/limitTo.js',
|
||||
'src/service/filter/orderBy.js',
|
||||
'src/service/interpolate.js',
|
||||
'src/service/location.js',
|
||||
'src/service/log.js',
|
||||
'src/service/resource.js',
|
||||
'src/service/parse.js',
|
||||
'src/service/q.js',
|
||||
'src/service/route.js',
|
||||
'src/service/routeParams.js',
|
||||
'src/service/scope.js',
|
||||
'src/service/sanitize.js',
|
||||
'src/service/sniffer.js',
|
||||
'src/service/window.js',
|
||||
'src/service/http.js',
|
||||
'src/service/httpBackend.js',
|
||||
'src/service/locale.js',
|
||||
'src/directive/directives.js',
|
||||
'src/directive/a.js',
|
||||
'src/directive/booleanAttrDirs.js',
|
||||
'src/directive/form.js',
|
||||
'src/directive/input.js',
|
||||
'src/directive/ngBind.js',
|
||||
'src/directive/ngClass.js',
|
||||
'src/directive/ngCloak.js',
|
||||
'src/directive/ngController.js',
|
||||
'src/directive/ngEventDirs.js',
|
||||
'src/directive/ngInclude.js',
|
||||
'src/directive/ngInit.js',
|
||||
'src/directive/ngNonBindable.js',
|
||||
'src/directive/ngPluralize.js',
|
||||
'src/directive/ngRepeat.js',
|
||||
'src/directive/ngShowHide.js',
|
||||
'src/directive/ngStyle.js',
|
||||
'src/directive/ngSwitch.js',
|
||||
'src/directive/ngTransclude.js',
|
||||
'src/directive/ngView.js',
|
||||
'src/directive/script.js',
|
||||
'src/directive/select.js',
|
||||
'src/directive/style.js'
|
||||
|
||||
'src/auto/injector.js',
|
||||
|
||||
'src/ng/anchorScroll.js',
|
||||
'src/ng/browser.js',
|
||||
'src/ng/cacheFactory.js',
|
||||
'src/ng/compiler.js',
|
||||
'src/ng/controller.js',
|
||||
'src/ng/defer.js',
|
||||
'src/ng/document.js',
|
||||
'src/ng/exceptionHandler.js',
|
||||
'src/ng/interpolate.js',
|
||||
'src/ng/location.js',
|
||||
'src/ng/log.js',
|
||||
'src/ng/parse.js',
|
||||
'src/ng/q.js',
|
||||
'src/ng/route.js',
|
||||
'src/ng/routeParams.js',
|
||||
'src/ng/rootScope.js',
|
||||
'src/ng/sanitize.js',
|
||||
'src/ng/sniffer.js',
|
||||
'src/ng/window.js',
|
||||
'src/ng/http.js',
|
||||
'src/ng/httpBackend.js',
|
||||
'src/ng/locale.js',
|
||||
|
||||
'src/ng/filter.js',
|
||||
'src/ng/filter/filter.js',
|
||||
'src/ng/filter/filters.js',
|
||||
'src/ng/filter/limitTo.js',
|
||||
'src/ng/filter/orderBy.js',
|
||||
|
||||
'src/ng/directive/directives.js',
|
||||
'src/ng/directive/a.js',
|
||||
'src/ng/directive/booleanAttrDirs.js',
|
||||
'src/ng/directive/form.js',
|
||||
'src/ng/directive/input.js',
|
||||
'src/ng/directive/ngBind.js',
|
||||
'src/ng/directive/ngClass.js',
|
||||
'src/ng/directive/ngCloak.js',
|
||||
'src/ng/directive/ngController.js',
|
||||
'src/ng/directive/ngEventDirs.js',
|
||||
'src/ng/directive/ngInclude.js',
|
||||
'src/ng/directive/ngInit.js',
|
||||
'src/ng/directive/ngNonBindable.js',
|
||||
'src/ng/directive/ngPluralize.js',
|
||||
'src/ng/directive/ngRepeat.js',
|
||||
'src/ng/directive/ngShowHide.js',
|
||||
'src/ng/directive/ngStyle.js',
|
||||
'src/ng/directive/ngSwitch.js',
|
||||
'src/ng/directive/ngTransclude.js',
|
||||
'src/ng/directive/ngView.js',
|
||||
'src/ng/directive/script.js',
|
||||
'src/ng/directive/select.js',
|
||||
'src/ng/directive/style.js'
|
||||
],
|
||||
|
||||
'angularSrcModules': [
|
||||
'src/ngCookies/cookies.js',
|
||||
'src/ngResource/resource.js',
|
||||
'src/ngMock/angular-mocks.js'
|
||||
],
|
||||
|
||||
'angularScenario': [
|
||||
'src/scenario/Scenario.js',
|
||||
'src/scenario/Application.js',
|
||||
'src/scenario/Describe.js',
|
||||
'src/scenario/Future.js',
|
||||
'src/scenario/ObjectModel.js',
|
||||
'src/scenario/Describe.js',
|
||||
'src/scenario/Runner.js',
|
||||
'src/scenario/SpecRunner.js',
|
||||
'src/scenario/dsl.js',
|
||||
'src/scenario/matchers.js',
|
||||
'src/scenario/output/Html.js',
|
||||
'src/scenario/output/Json.js',
|
||||
'src/scenario/output/Xml.js',
|
||||
'src/scenario/output/Object.js'
|
||||
'src/ngScenario/Scenario.js',
|
||||
'src/ngScenario/Application.js',
|
||||
'src/ngScenario/Describe.js',
|
||||
'src/ngScenario/Future.js',
|
||||
'src/ngScenario/ObjectModel.js',
|
||||
'src/ngScenario/Describe.js',
|
||||
'src/ngScenario/Runner.js',
|
||||
'src/ngScenario/SpecRunner.js',
|
||||
'src/ngScenario/dsl.js',
|
||||
'src/ngScenario/matchers.js',
|
||||
'src/ngScenario/output/Html.js',
|
||||
'src/ngScenario/output/Json.js',
|
||||
'src/ngScenario/output/Xml.js',
|
||||
'src/ngScenario/output/Object.js'
|
||||
],
|
||||
|
||||
'angularTest': [
|
||||
'test/testabilityPatch.js',
|
||||
'test/matchers.js',
|
||||
'test/ngScenario/*.js',
|
||||
'test/ngScenario/output/*.js',
|
||||
'test/ngScenario/jstd-scenario-adapter/*.js',
|
||||
'test/*.js',
|
||||
'test/auto/*.js',
|
||||
'test/ng/*.js',
|
||||
'test/ng/directive/*.js',
|
||||
'test/ng/filter/*.js',
|
||||
'test/ngCookies/*.js',
|
||||
'test/ngResource/*.js',
|
||||
'test/ngMock/*.js'
|
||||
],
|
||||
|
||||
'jstd': [
|
||||
@@ -86,28 +107,19 @@ angularFiles = {
|
||||
'lib/jquery/jquery.js',
|
||||
'test/jquery_remove.js',
|
||||
'@angularSrc',
|
||||
'src/publishExternalApis.js',
|
||||
'@angularSrcModules',
|
||||
'@angularScenario',
|
||||
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
|
||||
'@angularTest',
|
||||
'example/personalLog/*.js',
|
||||
'test/testabilityPatch.js',
|
||||
'test/matchers.js',
|
||||
'src/scenario/Scenario.js',
|
||||
'src/scenario/output/*.js',
|
||||
'src/jstd-scenario-adapter/*.js',
|
||||
'src/scenario/*.js',
|
||||
'src/angular-mocks.js',
|
||||
'test/scenario/*.js',
|
||||
'test/scenario/output/*.js',
|
||||
'test/jstd-scenario-adapter/*.js',
|
||||
'test/*.js',
|
||||
'test/service/*.js',
|
||||
'test/service/filter/*.js',
|
||||
'test/directive/*.js',
|
||||
'example/personalLog/test/*.js'
|
||||
],
|
||||
|
||||
'jstdExclude': [
|
||||
'test/jquery_alias.js',
|
||||
'src/angular-bootstrap.js',
|
||||
'src/scenario/angular-bootstrap.js'
|
||||
'src/ngScenario/angular-bootstrap.js'
|
||||
],
|
||||
|
||||
'jstdScenario': [
|
||||
@@ -117,28 +129,33 @@ angularFiles = {
|
||||
'build/docs/docs-scenario.js'
|
||||
],
|
||||
|
||||
'jstdMocks': [
|
||||
"jstdModules": [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'build/angular.js',
|
||||
'src/angular-mocks.js',
|
||||
'src/ngMock/angular-mocks.js',
|
||||
'src/ngCookies/cookies.js',
|
||||
'src/ngResource/resource.js',
|
||||
'test/matchers.js',
|
||||
'test/angular-mocksSpec.js'
|
||||
'test/ngMock/*.js',
|
||||
'test/ngCookies/*.js',
|
||||
'test/ngResource/*.js'
|
||||
],
|
||||
|
||||
'jstdPerf': [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'angularSrc',
|
||||
'src/angular-mocks.js',
|
||||
'@angularSrc',
|
||||
'@angularSrcModules',
|
||||
'src/ngMock/angular-mocks.js',
|
||||
'perf/data/*.js',
|
||||
'perf/testUtils.js',
|
||||
'perf/*.js'
|
||||
],
|
||||
|
||||
'jstdPerfExclude': [
|
||||
'src/angular-bootstrap.js',
|
||||
'src/scenario/angular-bootstrap.js'
|
||||
'src/ng/angular-bootstrap.js',
|
||||
'src/ngScenario/angular-bootstrap.js'
|
||||
],
|
||||
|
||||
'jstdJquery': [
|
||||
@@ -147,41 +164,49 @@ angularFiles = {
|
||||
'lib/jquery/jquery.js',
|
||||
'test/jquery_alias.js',
|
||||
'@angularSrc',
|
||||
'src/publishExternalApis.js',
|
||||
'@angularSrcModules',
|
||||
'@angularScenario',
|
||||
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
|
||||
'@angularTest',
|
||||
'example/personalLog/*.js',
|
||||
'test/testabilityPatch.js',
|
||||
'test/matchers.js',
|
||||
'src/scenario/Scenario.js',
|
||||
'src/scenario/output/*.js',
|
||||
'src/jstd-scenario-adapter/*.js',
|
||||
'src/scenario/*.js',
|
||||
'src/angular-mocks.js',
|
||||
'test/scenario/*.js',
|
||||
'test/scenario/output/*.js',
|
||||
'test/jstd-scenario-adapter/*.js',
|
||||
'test/*.js',
|
||||
'test/service/*.js',
|
||||
'test/directive/*.js',
|
||||
|
||||
'example/personalLog/test/*.js'
|
||||
],
|
||||
|
||||
'jstdJqueryExclude': [
|
||||
'src/angular-bootstrap.js',
|
||||
'src/scenario/angular-bootstrap.js',
|
||||
'src/ngScenario/angular-bootstrap.js',
|
||||
'test/jquery_remove.js'
|
||||
]
|
||||
};
|
||||
|
||||
// Execute only in slim-jim
|
||||
if (typeof JASMINE_ADAPTER !== 'undefined') {
|
||||
// SlimJim config
|
||||
files = [JASMINE, JASMINE_ADAPTER];
|
||||
angularFiles.jstd.forEach(function(pattern) {
|
||||
// replace angular source
|
||||
if (pattern === '@angularSrc') files = files.concat(angularFiles.angularSrc);
|
||||
// ignore jstd and jasmine files
|
||||
else if (!/jstd|jasmine/.test(pattern)) files.push(pattern);
|
||||
// Testacular config
|
||||
var mergedFiles = [];
|
||||
angularFiles.jstd.forEach(function(file) {
|
||||
// replace @ref
|
||||
var match = file.match(/^\@(.*)/);
|
||||
if (match) {
|
||||
var deps = angularFiles[match[1]];
|
||||
if (!deps) {
|
||||
console.log('No dependency:' + file)
|
||||
}
|
||||
mergedFiles = mergedFiles.concat(deps);
|
||||
} else {
|
||||
mergedFiles.push(file);
|
||||
}
|
||||
});
|
||||
|
||||
files = [JASMINE, JASMINE_ADAPTER];
|
||||
|
||||
mergedFiles.forEach(function(file){
|
||||
if (/jstd|jasmine/.test(file)) return;
|
||||
files.push(file);
|
||||
});
|
||||
|
||||
|
||||
exclude = angularFiles.jstdExclude;
|
||||
|
||||
autoWatch = true;
|
||||
|
||||
Executable
+201
@@ -0,0 +1,201 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// TODO(vojta): pre-commit hook for validating messages
|
||||
// TODO(vojta): report errors, currently Q silence everything which really sucks
|
||||
|
||||
var child = require('child_process');
|
||||
var fs = require('fs');
|
||||
var util = require('util');
|
||||
var q = require('qq');
|
||||
|
||||
var GIT_LOG_CMD = 'git log --grep="%s" -E --format=%s %s..HEAD';
|
||||
var GIT_TAG_CMD = 'git describe --tags --abbrev=0';
|
||||
|
||||
var HEADER_TPL = '<a name="%s"></a>\n# %s (%s)\n\n';
|
||||
var LINK_ISSUE = '[#%s](https://github.com/angular/angular.js/issues/%s)';
|
||||
var LINK_COMMIT = '[%s](https://github.com/angular/angular.js/commit/%s)';
|
||||
|
||||
var EMPTY_COMPONENT = '$$';
|
||||
var MAX_SUBJECT_LENGTH = 80;
|
||||
|
||||
|
||||
var warn = function() {
|
||||
console.log('WARNING:', util.format.apply(null, arguments));
|
||||
};
|
||||
|
||||
|
||||
var parseRawCommit = function(raw) {
|
||||
if (!raw) return null;
|
||||
|
||||
var lines = raw.split('\n');
|
||||
var msg = {}, match;
|
||||
|
||||
msg.hash = lines.shift();
|
||||
msg.subject = lines.shift();
|
||||
msg.closes = [];
|
||||
msg.breaks = [];
|
||||
|
||||
lines.forEach(function(line) {
|
||||
match = line.match(/Closes\s#(\d+)/);
|
||||
if (match) msg.closes.push(parseInt(match[1]));
|
||||
|
||||
match = line.match(/Breaks\s(.*)/);
|
||||
if (match) msg.breaks.push(match[1]);
|
||||
});
|
||||
|
||||
msg.body = lines.join('\n');
|
||||
match = msg.subject.match(/^(.*)\((.*)\)\:\s(.*)$/);
|
||||
|
||||
if (!match || !match[1] || !match[3]) {
|
||||
warn('Incorrect message: %s %s', msg.hash, msg.subject);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (match[3].length > MAX_SUBJECT_LENGTH) {
|
||||
warn('Too long subject: %s %s', msg.hash, msg.subject);
|
||||
match[3] = match[3].substr(0, MAX_SUBJECT_LENGTH);
|
||||
}
|
||||
|
||||
msg.type = match[1];
|
||||
msg.component = match[2];
|
||||
msg.subject = match[3];
|
||||
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
var linkToIssue = function(issue) {
|
||||
return util.format(LINK_ISSUE, issue, issue);
|
||||
};
|
||||
|
||||
|
||||
var linkToCommit = function(hash) {
|
||||
return util.format(LINK_COMMIT, hash.substr(0, 8), hash);
|
||||
};
|
||||
|
||||
|
||||
var currentDate = function() {
|
||||
var now = new Date();
|
||||
var pad = function(i) {
|
||||
return ('0' + i).substr(-2);
|
||||
};
|
||||
|
||||
return util.format('%d-%s-%s', now.getFullYear(), pad(now.getMonth() + 1), pad(now.getDate()));
|
||||
};
|
||||
|
||||
|
||||
var printSection = function(stream, title, section) {
|
||||
var NESTED = true;
|
||||
var components = Object.getOwnPropertyNames(section).sort();
|
||||
|
||||
if (!components.length) return;
|
||||
|
||||
stream.write(util.format('\n## %s\n\n', title));
|
||||
|
||||
components.forEach(function(name) {
|
||||
var prefix = '-';
|
||||
|
||||
if (name !== EMPTY_COMPONENT) {
|
||||
if (NESTED) {
|
||||
stream.write(util.format('- **%s:**\n', name));
|
||||
prefix = ' -';
|
||||
} else {
|
||||
prefix = util.format('- **%s:**', name);
|
||||
}
|
||||
}
|
||||
|
||||
section[name].forEach(function(commit) {
|
||||
stream.write(util.format('%s %s (%s', prefix, commit.subject, linkToCommit(commit.hash)));
|
||||
if (commit.closes.length) {
|
||||
stream.write(', closes ' + commit.closes.map(linkToIssue).join(', '));
|
||||
}
|
||||
stream.write(')\n');
|
||||
});
|
||||
});
|
||||
|
||||
stream.write('\n');
|
||||
};
|
||||
|
||||
|
||||
var readGitLog = function(grep, from) {
|
||||
var deffered = q.defer();
|
||||
|
||||
// TODO(vojta): if it's slow, use spawn and stream it instead
|
||||
child.exec(util.format(GIT_LOG_CMD, grep, '%H%n%s%n%b%n==END==', from), function(code, stdout, stderr) {
|
||||
var commits = [];
|
||||
|
||||
stdout.split('\n==END==\n').forEach(function(rawCommit) {
|
||||
var commit = parseRawCommit(rawCommit);
|
||||
if (commit) commits.push(commit);
|
||||
});
|
||||
|
||||
deffered.resolve(commits);
|
||||
});
|
||||
|
||||
return deffered.promise;
|
||||
};
|
||||
|
||||
|
||||
var writeChangelog = function(stream, commits, version) {
|
||||
var sections = {
|
||||
fix: {},
|
||||
feat: {},
|
||||
breaks: {}
|
||||
};
|
||||
|
||||
sections.breaks[EMPTY_COMPONENT] = [];
|
||||
|
||||
commits.forEach(function(commit) {
|
||||
var section = sections[commit.type];
|
||||
var component = commit.component || EMPTY_COMPONENT;
|
||||
|
||||
if (section) {
|
||||
section[component] = section[component] || [];
|
||||
section[component].push(commit);
|
||||
}
|
||||
|
||||
commit.breaks.forEach(function(breakMsg) {
|
||||
sections.breaks[EMPTY_COMPONENT].push({
|
||||
subject: breakMsg,
|
||||
hash: commit.hash,
|
||||
closes: []
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
stream.write(util.format(HEADER_TPL, version, version, currentDate()));
|
||||
printSection(stream, 'Bug Fixes', sections.fix);
|
||||
printSection(stream, 'Features', sections.feat);
|
||||
printSection(stream, 'Breaking Changes', sections.breaks);
|
||||
}
|
||||
|
||||
|
||||
var getPreviousTag = function() {
|
||||
var deffered = q.defer();
|
||||
child.exec(GIT_TAG_CMD, function(code, stdout, stderr) {
|
||||
if (code) deffered.reject('Cannot get the previous tag.');
|
||||
else deffered.resolve(stdout.replace('\n', ''));
|
||||
});
|
||||
return deffered.promise;
|
||||
};
|
||||
|
||||
|
||||
var generate = function(version, file) {
|
||||
getPreviousTag().then(function(tag) {
|
||||
console.log('Reading git log since', tag);
|
||||
readGitLog('^fix|^feat|Breaks', tag).then(function(commits) {
|
||||
console.log('Parsed', commits.length, 'commits');
|
||||
console.log('Generating changelog to', file || 'stdout', '(', version, ')');
|
||||
writeChangelog(file ? fs.createWriteStream(file) : process.stdout, commits, version);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// publish for testing
|
||||
exports.parseRawCommit = parseRawCommit;
|
||||
|
||||
// hacky start if not run by jasmine :-D
|
||||
if (process.argv.join('').indexOf('jasmine-node') === -1) {
|
||||
generate(process.argv[2], process.argv[3]);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
describe('changelog.js', function() {
|
||||
var ch = require('./changelog');
|
||||
|
||||
describe('parseRawCommit', function() {
|
||||
it('should parse raw commit', function() {
|
||||
var msg = ch.parseRawCommit(
|
||||
'9b1aff905b638aa274a5fc8f88662df446d374bd\n' +
|
||||
'feat(scope): broadcast $destroy event on scope destruction\n' +
|
||||
'perf testing shows that in chrome this change adds 5-15% overhead\n' +
|
||||
'when destroying 10k nested scopes where each scope has a $destroy listener\n');
|
||||
|
||||
expect(msg.type).toBe('feat');
|
||||
expect(msg.hash).toBe('9b1aff905b638aa274a5fc8f88662df446d374bd');
|
||||
expect(msg.subject).toBe('broadcast $destroy event on scope destruction');
|
||||
expect(msg.body).toBe('perf testing shows that in chrome this change adds 5-15% overhead\n' +
|
||||
'when destroying 10k nested scopes where each scope has a $destroy listener\n')
|
||||
expect(msg.component).toBe('scope');
|
||||
});
|
||||
|
||||
|
||||
it('should parse closed issues', function() {
|
||||
var msg = ch.parseRawCommit(
|
||||
'13f31602f396bc269076ab4d389cfd8ca94b20ba\n' +
|
||||
'feat(ng-list): Allow custom separator\n' +
|
||||
'bla bla bla\n\n' +
|
||||
'Closes #123\nCloses #25\n');
|
||||
|
||||
expect(msg.closes).toEqual([123, 25]);
|
||||
});
|
||||
|
||||
|
||||
it('should parse breaking changes', function() {
|
||||
var msg = ch.parseRawCommit(
|
||||
'13f31602f396bc269076ab4d389cfd8ca94b20ba\n' +
|
||||
'feat(ng-list): Allow custom separator\n' +
|
||||
'bla bla bla\n\n' +
|
||||
'Breaks first breaking change\nsomething else\n' +
|
||||
'Breaks another breaking change\n');
|
||||
|
||||
expect(msg.breaks).toEqual(['first breaking change', 'another breaking change']);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,80 @@
|
||||
<a name="v1.0.0rc3"></a>
|
||||
# v1.0.0rc3 (2012-03-27)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- create new (isolate) scopes for directives on root elements ([5390fb37](https://github.com/angular/angular.js/commit/5390fb37d2c01937922613fc57df4986af521787), closes [#817](https://github.com/angular/angular.js/issues/817))
|
||||
- don't touch static element attributes ([9cb2195e](https://github.com/angular/angular.js/commit/9cb2195e61a78e99020ec19d687a221ca88b5900))
|
||||
- Merge interpolated css class when replacing an element ([f49eaf8b](https://github.com/angular/angular.js/commit/f49eaf8bf2df5f4e0e82d6c89e849a4f82c8d414))
|
||||
- **$http:**
|
||||
- don't send Content-Type header when no data ([1a5bebd9](https://github.com/angular/angular.js/commit/1a5bebd927ecd22f9c34617642fdf58fe3f62efb), closes [#749](https://github.com/angular/angular.js/issues/749))
|
||||
- **$log:**
|
||||
- avoid console.log.apply calls in IE ([15213ec2](https://github.com/angular/angular.js/commit/15213ec212769837cb2b7e781ffc5bfd598d27ca), closes [#805](https://github.com/angular/angular.js/issues/805))
|
||||
- **$resource:**
|
||||
- support escaping of ':' in resource url ([6d6f8753](https://github.com/angular/angular.js/commit/6d6f875345e01f2c6c63ef95164f6f39e923da15))
|
||||
- **compiler:**
|
||||
- allow transclusion of root elements ([9918b748](https://github.com/angular/angular.js/commit/9918b748be01266eb10db39d51b4d3098d54ab66))
|
||||
- **e2e runner:**
|
||||
- fix typo that caused errors on IE8 ([ee5a5352](https://github.com/angular/angular.js/commit/ee5a5352fd4b94cedee6ef20d4bf2d43ce77e00b), closes [#806](https://github.com/angular/angular.js/issues/806))
|
||||
- **forEach:**
|
||||
- should ignore prototypically inherited properties ([8d7e6948](https://github.com/angular/angular.js/commit/8d7e6948496ff26ef1da8854ba02fcb8eebfed61), closes [#813](https://github.com/angular/angular.js/issues/813))
|
||||
- **forms:**
|
||||
- Remove double registering of form ([1faafa31](https://github.com/angular/angular.js/commit/1faafa31582c4e9413f48dc7d12f5b681f9fe9fd))
|
||||
- Set ng-valid/ng-invalid correctly ([08bfea18](https://github.com/angular/angular.js/commit/08bfea183a850b29da270eac47f80b598cbe600f))
|
||||
- **init:**
|
||||
- use jQuery#ready for init if available ([cb2ad9ab](https://github.com/angular/angular.js/commit/cb2ad9abf24e6f855cc749efe3155bd7987ece9d), closes [#818](https://github.com/angular/angular.js/issues/818))
|
||||
- **json:**
|
||||
- added support for iso8061 timezone ([5ac14f63](https://github.com/angular/angular.js/commit/5ac14f633a69f49973b5512780c6ec7752405967))
|
||||
- **matchers.toHaveClass:**
|
||||
- Correct reference to angular.mock.dump ([f701ce08](https://github.com/angular/angular.js/commit/f701ce08f9d63be05fc3b92f57ad473e1e749b2d))
|
||||
- **ng-switch:**
|
||||
- properly destroy child scopes ([2315d9b3](https://github.com/angular/angular.js/commit/2315d9b3610994b36c44e4a97fb1427d59471ce8))
|
||||
- **ngDocSpec:**
|
||||
- fix broken tests ([53b6f522](https://github.com/angular/angular.js/commit/53b6f522a56eea314cbd084816e08f24b2c7879f))
|
||||
- **ngForm:**
|
||||
- alias name||ngForm ([823adb23](https://github.com/angular/angular.js/commit/823adb231995e917bc060bfa49453e2a96bac2b6))
|
||||
- **ngRepeat:**
|
||||
- correct variable reference in error message ([935c1018](https://github.com/angular/angular.js/commit/935c1018da05dbf3124b2dd33619c4a3c82d7a2a))
|
||||
- **ngView:**
|
||||
- controller not published ([21e74c2d](https://github.com/angular/angular.js/commit/21e74c2d2e8e985b23711785287feb59965cbd90))
|
||||
- **q:**
|
||||
- resolve all of nothing to nothing ([ac75079e](https://github.com/angular/angular.js/commit/ac75079e2113949d5d64adbcf23d56f3cf295d41))
|
||||
- **select:**
|
||||
- multiselect failes to update view on selection insert ([6ecac8e7](https://github.com/angular/angular.js/commit/6ecac8e71a84792a434d21db2c245b3648c55f18))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$compile:**
|
||||
- do not interpolate boolean attributes, rather evaluate them ([a08cbc02](https://github.com/angular/angular.js/commit/a08cbc02e78e789a66e9af771c410e8ad1646e25))
|
||||
- **$controller:**
|
||||
- support controller registration via $controllerProvider ([d54dfecb](https://github.com/angular/angular.js/commit/d54dfecb00fba41455536c5ddd55310592fdaf84))
|
||||
- **$route:**
|
||||
- when matching consider trailing slash as optional ([a4fe51da](https://github.com/angular/angular.js/commit/a4fe51da3ba0dc297ecd389e230d6664f250c9a6), closes [#784](https://github.com/angular/angular.js/issues/784))
|
||||
- **assertArgFn:**
|
||||
- should support array annotated fns ([4b8d9260](https://github.com/angular/angular.js/commit/4b8d926062eb4d4483555bdbdec4656f585ab40b))
|
||||
- **http:**
|
||||
- added params parameter ([73c85930](https://github.com/angular/angular.js/commit/73c8593077155a9f2e8ef42efd4c497eba0bef4f))
|
||||
- **injector:**
|
||||
- infer _foo_ as foo ([f13dd339](https://github.com/angular/angular.js/commit/f13dd3393dfb7a33565c9360342c193bc0bddcb6))
|
||||
- **input.radio:**
|
||||
- Allow value attribute to be interpolated ([ade6c452](https://github.com/angular/angular.js/commit/ade6c452753145c84884d17027a7865bf4b34b0c))
|
||||
- **jqLite:**
|
||||
- make injector() and scope() work with the document object ([5fdab52d](https://github.com/angular/angular.js/commit/5fdab52dd7c269f99839f4fa6b5854d9548269fa))
|
||||
- add .controller() method ([6c5a05ad](https://github.com/angular/angular.js/commit/6c5a05ad49a1e083570c3dfe331403398f899dbe))
|
||||
- **ngValue:**
|
||||
- allow radio inputs to have non string values ([09e175f0](https://github.com/angular/angular.js/commit/09e175f02cca0f4a295fd0c9b980cd8f432e722b), closes [#816](https://github.com/angular/angular.js/issues/816))
|
||||
- **scope:**
|
||||
- broadcast $destroy event on scope destruction ([9b1aff90](https://github.com/angular/angular.js/commit/9b1aff905b638aa274a5fc8f88662df446d374bd))
|
||||
- **scope.$eval:**
|
||||
- Allow passing locals to the expression ([192ff61f](https://github.com/angular/angular.js/commit/192ff61f5d61899e667c6dbce4d3e6e399429d8b))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- boolean attrs are evaluated rather than interpolated ([a08cbc02](https://github.com/angular/angular.js/commit/a08cbc02e78e789a66e9af771c410e8ad1646e25))
|
||||
- ng-bind-attr directive removed ([55027132](https://github.com/angular/angular.js/commit/55027132f3d57e5dcf94683e6e6bd7b0aae0087d))
|
||||
- any app that depends on this service and its fallback to Modernizr, please ([aaedefb9](https://github.com/angular/angular.js/commit/aaedefb92e6bec6626e173e5155072c91471596a))
|
||||
|
||||
@@ -85,8 +85,8 @@ detection, and preventing invalid form submission.
|
||||
<input type="text" ng-model="contact.value" required/>
|
||||
[ <a href="" ng-click="removeContact(contact)">X</a> ]
|
||||
</div>
|
||||
<button ng-click="cancel()" ng-disabled="{{isCancelDisabled()}}">Cancel</button>
|
||||
<button ng-click="save()" ng-disabled="{{isSaveDisabled()}}">Save</button>
|
||||
<button ng-click="cancel()" ng-disabled="isCancelDisabled()">Cancel</button>
|
||||
<button ng-click="save()" ng-disabled="isSaveDisabled()">Save</button>
|
||||
</form>
|
||||
|
||||
<hr/>
|
||||
|
||||
@@ -20,7 +20,7 @@ allow a user to enter data.
|
||||
$scope.zip = /^\d\d\d\d\d$/;
|
||||
|
||||
$scope.addContact = function() {
|
||||
$scope.user.contacts.push({type:'', value:''});
|
||||
$scope.user.contacts.push({type:'email', value:''});
|
||||
};
|
||||
|
||||
$scope.removeContact = function(contact) {
|
||||
@@ -34,16 +34,16 @@ allow a user to enter data.
|
||||
</script>
|
||||
<div ng-controller="FormController" class="example">
|
||||
|
||||
<label>Name:</label><br/>
|
||||
<input type="text" ng-model="user.name" required/> <br/><br/>
|
||||
<label>Name:</label><br>
|
||||
<input type="text" ng-model="user.name" required/> <br><br>
|
||||
|
||||
<label>Address:</label><br/>
|
||||
<input type="text" ng-model="user.address.line1" size="33" required> <br/>
|
||||
<input type="text" ng-model="user.address.city" size="12" required>,
|
||||
<input type="text" ng-model="user.address.state" size="2"
|
||||
ng-pattern="state" required>
|
||||
<label>Address:</label><br>
|
||||
<input type="text" ng-model="user.address.line1" ng-model-instant size="33" required> <br>
|
||||
<input type="text" ng-model="user.address.city" ng-model-instant size="12" required>,
|
||||
<input type="text" ng-model="user.address.state" ng-model-instant
|
||||
ng-pattern="state" size="2" required>
|
||||
<input type="text" ng-model="user.address.zip" size="5"
|
||||
ng-pattern="zip" required><br/><br/>
|
||||
ng-pattern="zip" required><br><br>
|
||||
|
||||
<label>Phone:</label>
|
||||
[ <a href="" ng-click="addContact()">add</a> ]
|
||||
@@ -54,12 +54,12 @@ allow a user to enter data.
|
||||
<option>pager</option>
|
||||
<option>IM</option>
|
||||
</select>
|
||||
<input type="text" ng-model="contact.value" required/>
|
||||
<input type="text" ng-model="contact.value" ng-model-instant required/>
|
||||
[ <a href="" ng-click="removeContact(contact)">X</a> ]
|
||||
</div>
|
||||
<hr/>
|
||||
Debug View:
|
||||
<pre>user={{user}}</pre>
|
||||
<pre>user={{user | json}}</pre>
|
||||
</div>
|
||||
|
||||
</doc:source>
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
@name Developer Guide: Initializing Angular: Automatic Initialization
|
||||
@description
|
||||
|
||||
Angular initializes automatically when you load the angular script into your page that contains an element
|
||||
with `ng-app` directive:
|
||||
For Angular to manage the DOM for your application, it needs to compile some or all of an HTML page. Angular does this initialization automatically when you load the angular.js script into your page and insert an `ng-app` directive (attribute) into one of the page's elements. For example, we can tell Angular to initialize the entire document:
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
@@ -17,30 +16,36 @@ with `ng-app` directive:
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
From a high-level view, this is what happens during angular's automatic initialization process:
|
||||
You can also tell Angular to manage only a portion of a page. You would want to do this if you are using some other framework to manage other parts of the page. You do this by placing the `ng-app` directive on one or more container elements in the document. For example:
|
||||
|
||||
1. The browser loads the page, and then runs the angular script. Angular waits for the
|
||||
`DOMContentLoaded` (or 'Load') event to attempt to bootstrap.
|
||||
<pre>
|
||||
<div ng-app>
|
||||
I can add: {{ 1+2 }}
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
2. Angular looks for the `ng-app` directive. If found it then proceeds to compile the DOM element and its children.
|
||||
Optionally the `ng-app` may specify a {@link api/angular.module module} to load before the compilation. For details on
|
||||
how the compiler works, see {@link dev_guide.compiler Angular HTML Compiler}.
|
||||
You can also ask `ng-app` to load additional {@link api/angular.module modules} containing services, directives or filers that you'll use on the page.
|
||||
|
||||
<pre>
|
||||
<div ng-app="AwesomeModule">
|
||||
...
|
||||
</div>
|
||||
</pre
|
||||
|
||||
|
||||
## Initialization Options
|
||||
From a high-level, here's what Angular does during the initialization process:
|
||||
|
||||
The reason why `ng-app` exists is because angular should not assume that the entire HTML
|
||||
document should be processed just because the `angular.js` script is included. In order to compile
|
||||
only a part of the document set the `ng-app` on the root element of this portion.
|
||||
1. The browser loads the page, and then runs the Angular script. Angular then waits for the
|
||||
`DOMContentLoaded` (or 'Load') event to attempt to initialize.
|
||||
|
||||
## Global Angular Object
|
||||
2. Angular looks for the `ng-app` directive. If found it compilies the DOM element containing `ng-app` and its children.
|
||||
|
||||
The angular script creates a single global variable `angular` in the global namespace. All angular
|
||||
APIs are bound to fields of this global object.
|
||||
3. Angular creates a global variable `angular` and binds all Angular APIs to this object's fields.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.compiler Angular HTML Compiler}
|
||||
* {@link dev_guide.bootstrap Initializing Angular}
|
||||
* {@link dev_guide.bootstrap.manual_bootstrap Manual Initialization}
|
||||
|
||||
|
||||
@@ -2,13 +2,26 @@
|
||||
@name Developer Guide: Initializing Angular: Manual Initialization
|
||||
@description
|
||||
|
||||
Letting angular handle the initialization process (bootstrapping) is a handy way to start using
|
||||
angular, but advanced users who want more control over the initialization process can choose to use
|
||||
the manual bootstrapping method instead.
|
||||
In the vast majority of cases you'll want to let Angular handle initialization automatically.
|
||||
If, however, you need to delay Angular from managing the page right after the DOMContentLoaded
|
||||
event fires, you'll need to control this initialization manually.
|
||||
|
||||
The best way to get started with manual bootstrapping is to look at the what happens when you use
|
||||
{@link api/angular.module.ng.$compileProvider.directive.ng-app ng-app}, by showing each step of the process
|
||||
explicitly.
|
||||
To initialize Angular -- after you've done your own special-purpose initialization -- just call
|
||||
the {@link api/angular.bootstrap bootstrap()} function with the HTML container node that you want
|
||||
Angular to manage. In automatic initialization you'd do this by adding the `ng-app` attribute to
|
||||
the same node. Now, you won't use `ng-app` anywhere in your document.
|
||||
|
||||
To show the contrast of manual vs. automatic initialization, this automatic method:
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html ng-app>
|
||||
<head>
|
||||
<script src="http://code.angularjs.org/angular.js"></script>
|
||||
...
|
||||
</pre
|
||||
|
||||
is the same as this manual method:
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
@@ -21,19 +34,9 @@ explicitly.
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Hello {{'World'}}!
|
||||
</body>
|
||||
</html>
|
||||
...
|
||||
</pre>
|
||||
|
||||
This is the sequence that your code should follow if you bootstrap angular on your own:
|
||||
|
||||
1. After the page is loaded, find the root of the HTML template, which is typically the root of
|
||||
the document.
|
||||
2. Call {@link api/angular.bootstrap} to {@link dev_guide.compiler compile} the template into
|
||||
an executable, bi-directionally bound application.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
|
||||
@@ -6,18 +6,17 @@ The most common place to use dependency injection in angular applications is in
|
||||
dev_guide.mvc.understanding_controller controllers}. Here is a simple example:
|
||||
|
||||
<pre>
|
||||
function MyController($route){
|
||||
// configure the route service
|
||||
$route.when(...);
|
||||
function MyController($location){
|
||||
// do stuff with the $location service
|
||||
}
|
||||
MyController.$inject = ['$route'];
|
||||
MyController.$inject = ['$location'];
|
||||
</pre>
|
||||
|
||||
In this example, the `MyController` constructor function takes one argument, the {@link
|
||||
api/angular.module.ng.$route $route} service. Angular is then responsible for supplying the instance
|
||||
of `$route` to the controller when the constructor is instantiated. There are two ways to cause
|
||||
controller instantiation – by configuring routes with the `$route` service, or by referencing the
|
||||
controller from the HTML template, as follows:
|
||||
api/angular.module.ng.$location $location} service. Angular is then responsible for supplying the
|
||||
instance of `$location` to the controller when the constructor is instantiated. There are two ways
|
||||
to cause controller instantiation – by configuring routes with the `$location` service, or by
|
||||
referencing the controller from the HTML template, as follows:
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
@@ -35,7 +34,7 @@ we have to supply this information to angular in the form of an additional prope
|
||||
controller constructor function called `$inject`. Think of it as annotations for JavaScript.
|
||||
|
||||
<pre>
|
||||
MyController.$inject = ['$route'];
|
||||
MyController.$inject = ['$location'];
|
||||
</pre>
|
||||
|
||||
The information in `$inject` is then used by the {@link api/angular.injector injector} to call the
|
||||
|
||||
@@ -206,19 +206,17 @@ are applied to the scope object.
|
||||
|
||||
## Testing Controllers
|
||||
|
||||
The way to test a controller depends upon how complicated the controller is.
|
||||
|
||||
- If your controller doesn't use DI or scope methods — create the controller with the `new`
|
||||
operator and test away. For example:
|
||||
Although there are many ways to test a controller, one of the best conventions, shown below,
|
||||
involves injecting the `$rootScope` and `$controller`
|
||||
|
||||
Controller Function:
|
||||
<pre>
|
||||
function myController() {
|
||||
this.spices = [{"name":"pasilla", "spiciness":"mild"},
|
||||
function myController($scope) {
|
||||
$scope.spices = [{"name":"pasilla", "spiciness":"mild"},
|
||||
{"name":"jalapeno", "spiceiness":"hot hot hot!"},
|
||||
{"name":"habanero", "spiceness":"LAVA HOT!!"}];
|
||||
|
||||
this.spice = "habanero";
|
||||
$scope.spice = "habanero";
|
||||
}
|
||||
</pre>
|
||||
|
||||
@@ -227,27 +225,51 @@ Controller Test:
|
||||
describe('myController function', function() {
|
||||
|
||||
describe('myController', function() {
|
||||
var ctrl;
|
||||
var scope;
|
||||
|
||||
beforeEach(function() {
|
||||
ctrl = new myController();
|
||||
});
|
||||
beforeEach(inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
var ctrl = $controller(myController, {$scope: scope});
|
||||
}));
|
||||
|
||||
it('should create "spices" model with 3 spices', function() {
|
||||
expect(ctrl.spices.length).toBe(3);
|
||||
expect(scope.spices.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should set the default value of spice', function() {
|
||||
expect(ctrl.spice).toBe('habanero');
|
||||
expect(scope.spice).toBe('habanero');
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
- If your controller does use DI or scope methods — create a root scope, then create the controller
|
||||
in the root scope with `scope.$new(MyController)`. Test the controller using `$eval`, if necessary.
|
||||
- If you need to test a nested controller that depends on its parent's state — create a root scope,
|
||||
create a parent scope, create a child scope, and test the controller using $eval if necessary.
|
||||
|
||||
If you need to test a nested controller one needs to create the same scope hierarchy
|
||||
in your test as exist in the DOM.
|
||||
|
||||
<pre>
|
||||
describe('state', function() {
|
||||
var mainScope, childScope, babyScope;
|
||||
|
||||
beforeEach(inject(function($rootScope, $controller) {
|
||||
mainScope = $rootScope.$new();
|
||||
var mainCtrl = $controller(MainCtrl, {$scope: mainScope});
|
||||
childScope = mainScope.$new();
|
||||
var childCtrl = $controller(ChildCtrl, {$scope: childScope});
|
||||
babyScope = $rootScope.$new();
|
||||
var babyCtrl = $controller(BabyCtrl, {$scope: babyScope});
|
||||
}));
|
||||
|
||||
it('should have over and selected', function() {
|
||||
expect(mainScope.timeOfDay).toBe('morning');
|
||||
expect(mainScope.name).toBe('Nikki');
|
||||
expect(childScope.timeOfDay).toBe('morning');
|
||||
expect(childScope.name).toBe('Mattie');
|
||||
expect(babyScope.timeOfDay).toBe('evening');
|
||||
expect(babyScope.name).toBe('Gingerbreak Baby');
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
@@ -372,13 +372,13 @@ In this examples we use `<base href="/base/index.html" />`
|
||||
<div ng-non-bindable class="html5-hashbang-example">
|
||||
<div id="html5-mode" ng-controller="Html5Cntl">
|
||||
<h3>Browser with History API</h3>
|
||||
<ng-address-bar browser="html5"></ng-address-bar><br /><br />
|
||||
$location.protocol() = {{$location.protocol()}}<br />
|
||||
$location.host() = {{$location.host()}}<br />
|
||||
$location.port() = {{$location.port()}}<br />
|
||||
$location.path() = {{$location.path()}}<br />
|
||||
$location.search() = {{$location.search()}}<br />
|
||||
$location.hash() = {{$location.hash()}}<br />
|
||||
<div ng-address-bar browser="html5"></div><br><br>
|
||||
$location.protocol() = {{$location.protocol()}}<br>
|
||||
$location.host() = {{$location.host()}}<br>
|
||||
$location.port() = {{$location.port()}}<br>
|
||||
$location.path() = {{$location.path()}}<br>
|
||||
$location.search() = {{$location.search()}}<br>
|
||||
$location.hash() = {{$location.hash()}}<br>
|
||||
<a href="/base/first?a=b">/base/first?a=b</a> |
|
||||
<a href="sec/ond?flag#hash">sec/ond?flag#hash</a> |
|
||||
<a href="/base/another?search" ng-ext-link>external</a>
|
||||
@@ -386,13 +386,13 @@ In this examples we use `<base href="/base/index.html" />`
|
||||
|
||||
<div id="hashbang-mode" ng-controller="HashbangCntl">
|
||||
<h3>Browser without History API</h3>
|
||||
<ng-address-bar browser="hashbang"></ng-address-bar><br /><br />
|
||||
$location.protocol() = {{$location.protocol()}}<br />
|
||||
$location.host() = {{$location.host()}}<br />
|
||||
$location.port() = {{$location.port()}}<br />
|
||||
$location.path() = {{$location.path()}}<br />
|
||||
$location.search() = {{$location.search()}}<br />
|
||||
$location.hash() = {{$location.hash()}}<br />
|
||||
<div ng-address-bar browser="hashbang"></div><br><br>
|
||||
$location.protocol() = {{$location.protocol()}}<br>
|
||||
$location.host() = {{$location.host()}}<br>
|
||||
$location.port() = {{$location.port()}}<br>
|
||||
$location.path() = {{$location.path()}}<br>
|
||||
$location.search() = {{$location.search()}}<br>
|
||||
$location.hash() = {{$location.hash()}}<br>
|
||||
<a href="/base/first?a=b">/base/first?a=b</a> |
|
||||
<a href="sec/ond?flag#hash">sec/ond?flag#hash</a> |
|
||||
<a href="/base/another?search" ng-ext-link>external</a>
|
||||
@@ -417,7 +417,6 @@ In this examples we use `<base href="/base/index.html" />`
|
||||
return baseHref;
|
||||
};
|
||||
|
||||
this.hover = angular.noop;
|
||||
this.notifyWhenOutstandingRequests = angular.noop;
|
||||
}
|
||||
|
||||
@@ -426,20 +425,19 @@ In this examples we use `<base href="/base/index.html" />`
|
||||
hashbang: new FakeBrowser('http://www.host.com/base/index.html#!/path?a=b#h', '/base/index.html')
|
||||
};
|
||||
|
||||
function Html5Cntl($location) {
|
||||
this.$location = $location;
|
||||
function Html5Cntl($scope, $location) {
|
||||
$scope.$location = $location;
|
||||
}
|
||||
|
||||
function HashbangCntl($location) {
|
||||
this.$location = $location;
|
||||
function HashbangCntl($scope, $location) {
|
||||
$scope.$location = $location;
|
||||
}
|
||||
|
||||
function initEnv(name) {
|
||||
var root = angular.element(document.getElementById(name + '-mode'));
|
||||
angular.bootstrap(root, [function($compileProvider, $locationProvider, $provide){
|
||||
$locationProvider.html5Mode = true;
|
||||
$locationProvider.hashPrefix = '!';
|
||||
|
||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||
|
||||
$provide.value('$browser', browsers[name]);
|
||||
$provide.value('$document', root);
|
||||
$provide.value('$sniffer', {history: name == 'html5'});
|
||||
|
||||
@@ -92,8 +92,8 @@ State & Singletons}
|
||||
|
||||
The class above is hard to test since we have to change global state:
|
||||
<pre>
|
||||
var oldXHR = glabal.xhr;
|
||||
glabal.xhr = function mockXHR() {};
|
||||
var oldXHR = global.xhr;
|
||||
global.xhr = function mockXHR() {};
|
||||
var myClass = new MyClass();
|
||||
myClass.doWork();
|
||||
// assert that mockXHR got called with the right arguments
|
||||
@@ -126,12 +126,12 @@ there is only one global variable to be reset).
|
||||
|
||||
The class above is hard to test since we have to change global state:
|
||||
<pre>
|
||||
var oldServiceLocator = glabal.serviceLocator;
|
||||
glabal.serviceLocator.set('xhr', function mockXHR() {});
|
||||
var oldServiceLocator = global.serviceLocator;
|
||||
global.serviceLocator.set('xhr', function mockXHR() {});
|
||||
var myClass = new MyClass();
|
||||
myClass.doWork();
|
||||
// assert that mockXHR got called with the right arguments
|
||||
glabal.serviceLocator = oldServiceLocator; // if you forget this bad things will happen
|
||||
global.serviceLocator = oldServiceLocator; // if you forget this bad things will happen
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
@@ -190,7 +190,7 @@ modules require it.
|
||||
Modules are a way of managing $injector configuration, and have nothing to do with loading of
|
||||
scripts into a VM. There are existing projects which deal with script loading, which may be used
|
||||
with Angular. Because modules do nothing at load time they can be loaded into the VM in any order
|
||||
and thus script loaders can take advantage of this property and paralyze the loading process.
|
||||
and thus script loaders can take advantage of this property and parallelize the loading process.
|
||||
|
||||
|
||||
# Unit Testing
|
||||
@@ -232,7 +232,7 @@ describe('myApp', function() {
|
||||
$provide.value('$window', {
|
||||
alert: jasmine.createSpy('alert')
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
// The inject() will create the injector and inject the greet and
|
||||
// $window into the tests. The test need not concern itself with
|
||||
@@ -251,7 +251,7 @@ describe('myApp', function() {
|
||||
});
|
||||
inject(function(greet) {
|
||||
greet('World');
|
||||
expect(alertSpy).toHaveBeenCalledWith('World');
|
||||
expect(alertSpy).toHaveBeenCalledWith('Hello World!');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -65,15 +65,15 @@ This example demonstrates angular's two-way data binding:
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Your name: <input type="text" ng-model="yourname" value="World"/>
|
||||
<hr/>
|
||||
Hello {{yourname}}!
|
||||
Your name: <input type="text" ng-model="yourname" ng-model-instant placeholder="World">
|
||||
<hr>
|
||||
Hello {{yourname || 'World'}}!
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
After the refresh, the page should look something like this:
|
||||
|
||||
<img class="left" src="img/helloworld_2way.png" border="1" />
|
||||
<img class="left" src="img/helloworld_2way.png" border="1" >
|
||||
|
||||
These are some of the important points to note from this example:
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ describe('ngdoc', function() {
|
||||
var d2 = new Doc('@name a.b.ng-c').parse();
|
||||
var d3 = new Doc('@name some text: more text').parse();
|
||||
expect(ngdoc.metadata([d1])[0].shortName).toEqual('c');
|
||||
expect(ngdoc.metadata([d2])[0].shortName).toEqual('ng:c');
|
||||
expect(ngdoc.metadata([d2])[0].shortName).toEqual('ng-c');
|
||||
expect(ngdoc.metadata([d3])[0].shortName).toEqual('more text');
|
||||
});
|
||||
|
||||
|
||||
@@ -677,8 +677,15 @@ var KEYWORD_PRIORITY = {
|
||||
'.angular.module.ngMock': 8,
|
||||
'.dev_guide.overview': 1,
|
||||
'.dev_guide.bootstrap': 2,
|
||||
'.dev_guide.bootstrap.auto_bootstrap': 1,
|
||||
'.dev_guide.bootstrap.manual_bootstrap': 2,
|
||||
'.dev_guide.mvc': 3,
|
||||
'.dev_guide.mvc.understanding_model': 1,
|
||||
'.dev_guide.mvc.understanding_controller': 2,
|
||||
'.dev_guide.mvc.understanding_view': 3,
|
||||
'.dev_guide.scopes': 4,
|
||||
'.dev_guide.scopes.understanding_scopes': 1,
|
||||
'.dev_guide.scopes.internals': 2,
|
||||
'.dev_guide.compiler': 5,
|
||||
'.dev_guide.templates': 6,
|
||||
'.dev_guide.services': 7,
|
||||
|
||||
@@ -2,11 +2,11 @@ angular.module('ngdocs.directives', [], function($compileProvider) {
|
||||
|
||||
var angularJsUrl;
|
||||
var scripts = document.getElementsByTagName("script");
|
||||
var angularJsRegex = /^(|.*\/)angular(-.*?)?(\.min)?.js(\?[^#]*)?(#(.*))?$/;
|
||||
var angularJsRegex = /^(|.*\/)angular(-\d.*?)?(\.min)?.js(\?[^#]*)?(#(.*))?$/;
|
||||
for(var j = 0; j < scripts.length; j++) {
|
||||
var src = scripts[j].src;
|
||||
if (src && src.match(angularJsRegex)) {
|
||||
angularJsUrl = src.replace('docs.angularjs.org', 'code.angularjs.org');
|
||||
angularJsUrl = src.replace(/docs(-next)?\.angularjs\.org/, 'code.angularjs.org');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ function DocsController(scope, $location, $window, $cookies, $filter) {
|
||||
|
||||
function loadDisqus(currentPageId) {
|
||||
// http://docs.disqus.com/help/2/
|
||||
window.disqus_shortname = 'angularjs';
|
||||
window.disqus_shortname = 'angularjs-next';
|
||||
window.disqus_identifier = currentPageId;
|
||||
window.disqus_url = 'http://docs-next.angularjs.org' + currentPageId;
|
||||
|
||||
@@ -146,7 +146,7 @@ function TutorialInstructionsCtrl($cookieStore) {
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('ngdocs', ['ngdocs.directives'], function($locationProvider, $filterProvider, $compileProvider) {
|
||||
angular.module('ngdocs', ['ngdocs.directives', 'ngResource', 'ngCookies'], function($locationProvider, $filterProvider, $compileProvider) {
|
||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||
|
||||
$filterProvider.register('title', function(){
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
baseUrl = location.href.replace(rUrl, indexFile),
|
||||
jQuery = /index-jq[^\.]*\.html$/.test(baseUrl),
|
||||
debug = /index[^\.]*-debug\.html$/.test(baseUrl),
|
||||
angularPath = debug ? '../angular.js' : '../angular.min.js',
|
||||
headEl = document.getElementsByTagName('head')[0],
|
||||
sync = true;
|
||||
|
||||
@@ -28,10 +27,16 @@
|
||||
type: 'text/css'});
|
||||
addTag('script', {src: 'syntaxhighlighter/syntaxhighlighter-combined.js'}, sync);
|
||||
if (jQuery) addTag('script', {src: 'jquery.min.js'});
|
||||
addTag('script', {src: angularPath}, sync);
|
||||
addTag('script', {src: path('angular.js')}, sync);
|
||||
addTag('script', {src: path('angular-resource.js') }, sync);
|
||||
addTag('script', {src: path('angular-cookies.js') }, sync);
|
||||
addTag('script', {src: 'docs-combined.js'}, sync);
|
||||
addTag('script', {src: 'docs-keywords.js'}, sync);
|
||||
|
||||
function path(name) {
|
||||
return '../' + name.replace(/\.js$/, debug ? '.js' : '.min.js');
|
||||
}
|
||||
|
||||
function addTag(name, attributes, sync) {
|
||||
var el = document.createElement(name),
|
||||
attrName;
|
||||
|
||||
@@ -1,28 +1,31 @@
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org" ng:app>
|
||||
<html ng-app>
|
||||
<head>
|
||||
<title>Personal Log</title>
|
||||
<script type="text/javascript" src="../../src/angular-bootstrap.js"></script>
|
||||
<script type="text/javascript" src="personalLog.js"></script>
|
||||
<script src="../../src/loader.js"></script>
|
||||
<script>
|
||||
setupModuleLoader(window);
|
||||
</script>
|
||||
<script src="personalLog.js"></script>
|
||||
<script src="../../src/angular-bootstrap.js"></script>
|
||||
<script src="../../src/ngCookies/cookies.js"></script>
|
||||
</head>
|
||||
|
||||
|
||||
<!-- TODO: we need to expose $root so that we can delete cookies in the scenario runner, there
|
||||
must be a better way to do this -->
|
||||
<body ng:controller="example.personalLog.LogCtrl">
|
||||
<body ng-controller="LogCtrl">
|
||||
|
||||
<form action="" ng:submit="addLog(newMsg)">
|
||||
<input type="text" ng:model="newMsg" />
|
||||
<input type="submit" value="add" />
|
||||
<input type="button" value="remove all" ng:click="rmLogs()" />
|
||||
<form action="" ng-submit="addLog(newMsg)">
|
||||
<input type="text" ng-model="newMsg">
|
||||
<input type="submit" value="add">
|
||||
<input type="button" value="remove all" ng-click="rmLogs()">
|
||||
</form>
|
||||
|
||||
<hr/>
|
||||
<h2>Logs:</h2>
|
||||
<ul>
|
||||
<li ng:repeat="log in logs | orderBy:'-at'">
|
||||
<li ng-repeat="log in logs | orderBy:'-at'">
|
||||
{{log.at | date:'yy-MM-dd HH:mm'}} {{log.msg}}
|
||||
[<a href="" ng:click="rmLog(log)">x</a>]
|
||||
[<a href="" ng-click="rmLog(log)">x</a>]
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -5,28 +5,23 @@
|
||||
* - testability of controllers
|
||||
* - dependency injection for controllers via $inject and constructor function
|
||||
* - $cookieStore for persistent cookie-backed storage
|
||||
* - simple templating constructs such as ng:repeat and {{}}
|
||||
* - simple templating constructs such as ng-repeat and {{}}
|
||||
* - date filter
|
||||
* - and binding onSubmit and onClick events to angular expressions
|
||||
* @author Igor Minar
|
||||
*/
|
||||
|
||||
|
||||
/** @namespace the 'example' namespace */
|
||||
var example = example || {};
|
||||
/** @namespace namespace of the personal log app */
|
||||
example.personalLog = {};
|
||||
|
||||
|
||||
//name space isolating closure
|
||||
(function() {
|
||||
|
||||
var app = angular.module('personalLog', ['ngCookies']);
|
||||
|
||||
var LOGS = 'logs';
|
||||
|
||||
/**
|
||||
* The controller for the personal log app.
|
||||
*/
|
||||
function LogCtrl($cookieStore, $scope) {
|
||||
app.controller('LogCtrl', ['$cookieStore', '$scope', function LogCtrl($cookieStore, $scope) {
|
||||
|
||||
var logs = $scope.logs = $cookieStore.get(LOGS) || []; //main model
|
||||
|
||||
@@ -72,11 +67,6 @@ function LogCtrl($cookieStore, $scope) {
|
||||
logs.splice(0, logs.length);
|
||||
$cookieStore.remove(LOGS);
|
||||
};
|
||||
}
|
||||
}]);
|
||||
|
||||
//inject
|
||||
LogCtrl.$inject = ['$cookieStore', '$scope'];
|
||||
|
||||
//export
|
||||
example.personalLog.LogCtrl = LogCtrl;
|
||||
})();
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
describe('example.personalLog.LogCtrl', function() {
|
||||
var logScope;
|
||||
|
||||
beforeEach(function() {
|
||||
var injector = angular.injector(['ng', 'ngMock']);
|
||||
logScope = injector.get('$rootScope');
|
||||
logScope.$cookies = injector.get('$cookies');
|
||||
injector.instantiate(example.personalLog.LogCtrl, {$scope: logScope});
|
||||
});
|
||||
|
||||
beforeEach(module('personalLog'));
|
||||
|
||||
beforeEach(inject(function($rootScope, $controller) {
|
||||
logScope = $rootScope.$new();
|
||||
$controller('LogCtrl', {$scope: logScope});
|
||||
}));
|
||||
|
||||
|
||||
it('should initialize notes with an empty array', function() {
|
||||
@@ -43,11 +44,11 @@ describe('example.personalLog.LogCtrl', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should store logs in the logs cookie', function() {
|
||||
expect(logScope.$cookies.logs).not.toBeDefined();
|
||||
it('should store logs in the logs cookie', inject(function($cookies) {
|
||||
expect($cookies.logs).not.toBeDefined();
|
||||
logScope.addLog('first log message');
|
||||
expect(logScope.$cookies.logs).toBeTruthy();
|
||||
});
|
||||
expect($cookies.logs).toBeTruthy();
|
||||
}));
|
||||
|
||||
|
||||
it('should do nothing if newMsg is empty', function() {
|
||||
@@ -79,17 +80,17 @@ describe('example.personalLog.LogCtrl', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should update cookies when a log is deleted', function() {
|
||||
expect(logScope.$cookies.logs).toMatch(/\[\{.*?\}(,\{.*?\}){3}\]/);
|
||||
it('should update cookies when a log is deleted', inject(function($cookies) {
|
||||
expect($cookies.logs).toMatch(/\[\{.*?\}(,\{.*?\}){3}\]/);
|
||||
|
||||
logScope.rmLog(logScope.logs[1]);
|
||||
expect(logScope.$cookies.logs).toMatch(/\[\{.*?\}(,\{.*?\}){2}\]/);
|
||||
expect($cookies.logs).toMatch(/\[\{.*?\}(,\{.*?\}){2}\]/);
|
||||
|
||||
logScope.rmLog(logScope.logs[0]);
|
||||
logScope.rmLog(logScope.logs[0]);
|
||||
logScope.rmLog(logScope.logs[0]);
|
||||
expect(logScope.$cookies.logs).toMatch(/\[\]/);
|
||||
});
|
||||
expect($cookies.logs).toMatch(/\[\]/);
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
@@ -110,10 +111,10 @@ describe('example.personalLog.LogCtrl', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should remove logs cookie', function() {
|
||||
expect(logScope.$cookies.logs).toBeTruthy();
|
||||
it('should remove logs cookie', inject(function($cookies) {
|
||||
expect($cookies.logs).toBeTruthy();
|
||||
logScope.rmLogs();
|
||||
expect(logScope.$cookies.logs).not.toBeDefined();
|
||||
});
|
||||
expect($cookies.logs).not.toBeDefined();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
+17
-9
@@ -1,22 +1,30 @@
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org" ng:app>
|
||||
<html ng-app="example">
|
||||
<head>
|
||||
<title>angular dev sandbox</title>
|
||||
<script src="../src/angular-bootstrap.js"></script>
|
||||
<script src="../src/loader.js"></script>
|
||||
<script>
|
||||
angular.module.ng('routeConfig', function($route) {
|
||||
$route.when('/view1', {controller: MyCtrl, template: 'view1.html'});
|
||||
$route.when('/view2', {controller: MyCtrl, template: 'view2.html'});
|
||||
setupModuleLoader(window);
|
||||
angular.module('example', [], function($routeProvider) {
|
||||
$routeProvider.when('/view1', {controller: MyCtrl, template: 'view1.html'});
|
||||
$routeProvider.when('/view2', {controller: MyCtrl, template: 'view2.html'});
|
||||
|
||||
function MyCtrl() {};
|
||||
}, {$inject: ['$route']});
|
||||
function MyCtrl($location, $scope) {
|
||||
$scope.url = function() {
|
||||
return $location.url();
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
<script src="../src/angular-bootstrap.js"></script>
|
||||
</head>
|
||||
<body ng:init="$service('$window').$root = this">
|
||||
<body>
|
||||
<p>
|
||||
<a href="#/view1">view1</a> | <a href="#/view2">view2</a> | <a href="#">blank</a>
|
||||
</p>
|
||||
|
||||
view: <ng:view></ng:view>
|
||||
<hr>
|
||||
|
||||
<div ng-view></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
view1<br>
|
||||
location: {{$service('$location').href}}
|
||||
location: {{url()}}
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
view2<br/>
|
||||
location: {{$service('$location').href}}<br/>
|
||||
location: {{url()}}<br/>
|
||||
|
||||
+4
-2
@@ -16,7 +16,7 @@ fs.readFile('angularFiles.js', function(err, data) {
|
||||
fs.writeFile('./jsTestDriver.conf', prefix + combine(angularFiles.jstd,
|
||||
angularFiles.jstdExclude));
|
||||
|
||||
fs.writeFile('./jsTestDriver-mocks.conf', prefix + combine(angularFiles.jstdMocks));
|
||||
fs.writeFile('./jsTestDriver-modules.conf', prefix + combine(angularFiles.jstdModules));
|
||||
|
||||
fs.writeFile('./jsTestDriver-scenario.conf', prefixScenario +
|
||||
combine(angularFiles.jstdScenario) +
|
||||
@@ -40,6 +40,8 @@ function combine(load, exclude) {
|
||||
if (exclude) fileList += ('\n\nexclude:\n- ' + exclude.join('\n- '));
|
||||
|
||||
//Replace placeholders for src list before returning
|
||||
return fileList.replace(/@angularSrc/g, angularSrc);
|
||||
return fileList.replace(/@(.*)/g, function(all, alias) {
|
||||
return angularFiles[alias].join('\n- ');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+32
-32
@@ -8,7 +8,7 @@ describe("localized filters", function() {
|
||||
expect(binding('input | date:"medium"')).toBe('03/06/1977 18:07:23');
|
||||
expect(binding('input | date:"longDate"')).toBe("3 de junio de 1977");
|
||||
expect(binding('input | number')).toBe('234.234.443.432');
|
||||
expect(binding('input | currency')).toBe('€ 234.234.443.432,00');
|
||||
expect(binding('input | currency')).toBe('€\u00a0234.234.443.432,00');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,8 +20,8 @@ describe("localized filters", function() {
|
||||
it('should check filters for cs locale', function() {
|
||||
expect(binding('input | date:"medium"')).toBe('3.6.1977 18:07:23');
|
||||
expect(binding('input | date:"longDate"')).toBe("3. června 1977");
|
||||
expect(binding('input | number')).toBe('234 234 443 432');
|
||||
expect(binding('input | currency')).toBe('234 234 443 432,00 K\u010d');
|
||||
expect(binding('input | number')).toBe('234\u00a0234\u00a0443\u00a0432');
|
||||
expect(binding('input | currency')).toBe('234\u00a0234\u00a0443\u00a0432,00\u00a0K\u010d');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -34,7 +34,7 @@ describe("localized filters", function() {
|
||||
expect(binding('input | date:"medium"')).toBe('03.06.1977 18:07:23');
|
||||
expect(binding('input | date:"longDate"')).toBe("3. Juni 1977");
|
||||
expect(binding('input | number')).toBe('234.234.443.432');
|
||||
expect(binding('input | currency')).toBe('234.234.443.432,00 €');
|
||||
expect(binding('input | currency')).toBe('234.234.443.432,00\u00a0€');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -53,42 +53,42 @@ describe("localized filters", function() {
|
||||
|
||||
describe('ng:pluralize for en locale', function() {
|
||||
it('should show pluralized strings', function() {
|
||||
expect(element('.ng-pluralize:first').html()).toBe('You have one email!');
|
||||
expect(element('ng-pluralize:first').html()).toBe('You have one email!');
|
||||
|
||||
input('plInput').enter('0');
|
||||
expect(element('.ng-pluralize:first').html()).toBe('You have no email!');
|
||||
expect(element('ng-pluralize:first').html()).toBe('You have no email!');
|
||||
|
||||
input('plInput').enter('3');
|
||||
expect(element('.ng-pluralize:first').html()).toBe('You have 3 emails!');
|
||||
expect(element('ng-pluralize:first').html()).toBe('You have 3 emails!');
|
||||
});
|
||||
|
||||
it('should show pluralized strings with offsets', function() {
|
||||
expect(element('.ng-pluralize:last').html()).toBe('Shanjian is viewing!');
|
||||
expect(element('ng-pluralize:last').html()).toBe('Shanjian is viewing!');
|
||||
|
||||
input('plInput2').enter('0');
|
||||
expect(element('.ng-pluralize:last').html()).toBe('Nobody is viewing!');
|
||||
expect(element('ng-pluralize:last').html()).toBe('Nobody is viewing!');
|
||||
|
||||
input('plInput2').enter('2');
|
||||
expect(element('.ng-pluralize:last').html()).toBe('Shanjian and Di are viewing!');
|
||||
expect(element('ng-pluralize:last').html()).toBe('Shanjian and Di are viewing!');
|
||||
|
||||
input('plInput2').enter('3');
|
||||
expect(element('.ng-pluralize:last').html()).
|
||||
expect(element('ng-pluralize:last').html()).
|
||||
toBe('Shanjian, Di and one other person are viewing!');
|
||||
|
||||
input('plInput2').enter('4');
|
||||
expect(element('.ng-pluralize:last').html()).
|
||||
expect(element('ng-pluralize:last').html()).
|
||||
toBe('Shanjian, Di and 2 other people are viewing!');
|
||||
});
|
||||
|
||||
it('should show pluralized strings with correct data-binding', function() {
|
||||
input('plInput2').enter('2');
|
||||
expect(element('.ng-pluralize:last').html()).toBe('Shanjian and Di are viewing!');
|
||||
expect(element('ng-pluralize:last').html()).toBe('Shanjian and Di are viewing!');
|
||||
|
||||
input('person1').enter('Igor');
|
||||
expect(element('.ng-pluralize:last').html()).toBe('Igor and Di are viewing!');
|
||||
expect(element('ng-pluralize:last').html()).toBe('Igor and Di are viewing!');
|
||||
|
||||
input('person2').enter('Vojta');
|
||||
expect(element('.ng-pluralize:last').html()).toBe('Igor and Vojta are viewing!');
|
||||
expect(element('ng-pluralize:last').html()).toBe('Igor and Vojta are viewing!');
|
||||
});
|
||||
})
|
||||
});
|
||||
@@ -101,26 +101,26 @@ describe("localized filters", function() {
|
||||
it('should check filters for sk locale', function() {
|
||||
expect(binding('input | date:"medium"')).toBe('3.6.1977 18:07:23');
|
||||
expect(binding('input | date:"longDate"')).toBe("3. júna 1977");
|
||||
expect(binding('input | number')).toBe('234 234 443 432');
|
||||
expect(binding('input | currency')).toBe('234 234 443 432,00 Sk');
|
||||
expect(binding('input | number')).toBe('234\u00a0234\u00a0443\u00a0432');
|
||||
expect(binding('input | currency')).toBe('234\u00a0234\u00a0443\u00a0432,00\u00a0Sk');
|
||||
});
|
||||
|
||||
|
||||
describe('ng:pluralize for sk locale', function() {
|
||||
it('should show pluralized strings', function() {
|
||||
expect(element('.ng-pluralize').html()).toBe('Mas jeden email!');
|
||||
expect(element('ng-pluralize').html()).toBe('Mas jeden email!');
|
||||
|
||||
input('plInput').enter('0');
|
||||
expect(element('.ng-pluralize:first').html()).toBe('Mas 0 emailov!');
|
||||
expect(element('ng-pluralize:first').html()).toBe('Mas 0 emailov!');
|
||||
|
||||
input('plInput').enter('3');
|
||||
expect(element('.ng-pluralize:first').html()).toBe('Mas 3 emaily!');
|
||||
expect(element('ng-pluralize:first').html()).toBe('Mas 3 emaily!');
|
||||
|
||||
input('plInput').enter('4');
|
||||
expect(element('.ng-pluralize:first').html()).toBe('Mas 4 emaily!');
|
||||
expect(element('ng-pluralize:first').html()).toBe('Mas 4 emaily!');
|
||||
|
||||
input('plInput').enter('6');
|
||||
expect(element('.ng-pluralize:first').html()).toBe('Mas 6 emailov!');
|
||||
expect(element('ng-pluralize:first').html()).toBe('Mas 6 emailov!');
|
||||
});
|
||||
|
||||
it('should show pluralized strings with offsets', function() {
|
||||
@@ -147,38 +147,38 @@ describe("localized filters", function() {
|
||||
|
||||
describe('ng:pluralize for zh locale', function() {
|
||||
it('should show pluralized strings', function() {
|
||||
expect(element('.ng-pluralize:first').html()).toBe('1人在浏览该文件!');
|
||||
expect(element('ng-pluralize:first').html()).toBe('1人在浏览该文件!');
|
||||
|
||||
input('plInput').enter('0');
|
||||
expect(element('.ng-pluralize:first').html()).toBe('0人在浏览该文件!');
|
||||
expect(element('ng-pluralize:first').html()).toBe('0人在浏览该文件!');
|
||||
|
||||
input('plInput').enter('3');
|
||||
expect(element('.ng-pluralize:first').html()).toBe('3人在浏览该文件!');
|
||||
expect(element('ng-pluralize:first').html()).toBe('3人在浏览该文件!');
|
||||
});
|
||||
|
||||
it('should show pluralized strings with offsets', function() {
|
||||
expect(element('.ng-pluralize:last').html()).toBe('Shanjian 在浏览该文件!');
|
||||
expect(element('ng-pluralize:last').html()).toBe('Shanjian 在浏览该文件!');
|
||||
|
||||
input('plInput2').enter('0');
|
||||
expect(element('.ng-pluralize:last').html()).toBe('没有人在浏览该文件!');
|
||||
expect(element('ng-pluralize:last').html()).toBe('没有人在浏览该文件!');
|
||||
|
||||
input('plInput2').enter('2');
|
||||
expect(element('.ng-pluralize:last').html()).toBe('Shanjian 和 Di 在浏览该文件!');
|
||||
expect(element('ng-pluralize:last').html()).toBe('Shanjian 和 Di 在浏览该文件!');
|
||||
|
||||
input('plInput2').enter('3');
|
||||
expect(element('.ng-pluralize:last').html()).
|
||||
expect(element('ng-pluralize:last').html()).
|
||||
toBe('Shanjian, Di 还有其他1 人在浏览该文件!');
|
||||
});
|
||||
|
||||
it('should show pluralized strings with correct data-binding', function() {
|
||||
input('plInput2').enter('2');
|
||||
expect(element('.ng-pluralize:last').html()).toBe('Shanjian 和 Di 在浏览该文件!');
|
||||
expect(element('ng-pluralize:last').html()).toBe('Shanjian 和 Di 在浏览该文件!');
|
||||
|
||||
input('person1').enter('彭迪');
|
||||
expect(element('.ng-pluralize:last').html()).toBe('彭迪 和 Di 在浏览该文件!');
|
||||
expect(element('ng-pluralize:last').html()).toBe('彭迪 和 Di 在浏览该文件!');
|
||||
|
||||
input('person2').enter('一哥');
|
||||
expect(element('.ng-pluralize:last').html()).toBe('彭迪 和 一哥 在浏览该文件!');
|
||||
expect(element('ng-pluralize:last').html()).toBe('彭迪 和 一哥 在浏览该文件!');
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
<script src="../../build/angular.js"></script>
|
||||
<script src="../../build/i18n/angular-locale_cs.js"></script>
|
||||
<script>
|
||||
function AppCntl(){
|
||||
this.input = 234234443432;
|
||||
function AppCntl($scope){
|
||||
$scope.input = 234234443432;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body ng:controller="AppCntl">
|
||||
<input type="text" ng:model="input"><br>
|
||||
<body ng-controller="AppCntl">
|
||||
<input type="text" ng-model="input"><br>
|
||||
date: {{input | date:"medium"}}<br>
|
||||
date: {{input | date:"longDate"}}<br>
|
||||
number: {{input | number}}<br>
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
<script src="../../build/angular.js"></script>
|
||||
<script src="../../build/i18n/angular-locale_de.js"></script>
|
||||
<script>
|
||||
function AppCntl(){
|
||||
this.input = 234234443432;
|
||||
function AppCntl($scope){
|
||||
$scope.input = 234234443432;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body ng:controller="AppCntl">
|
||||
<input type="text" ng:model="input"><br>
|
||||
<body ng-controller="AppCntl">
|
||||
<input type="text" ng-model="input"><br>
|
||||
date: {{input | date:"medium"}}<br>
|
||||
date: {{input | date:"longDate"}}<br>
|
||||
number: {{input | number}}<br>
|
||||
|
||||
+16
-16
@@ -8,41 +8,41 @@
|
||||
<script src="../../build/i18n/angular-locale_en.js"></script>
|
||||
-->
|
||||
<script>
|
||||
function AppCntl(){
|
||||
this.input = 234234443432;
|
||||
this.plInput = 1;
|
||||
this.person1 = "Shanjian";
|
||||
this.person2 = "Di";
|
||||
this.plInput2 = 1;
|
||||
function AppCntl($scope){
|
||||
$scope.input = 234234443432;
|
||||
$scope.plInput = 1;
|
||||
$scope.person1 = "Shanjian";
|
||||
$scope.person2 = "Di";
|
||||
$scope.plInput2 = 1;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body ng:controller="AppCntl">
|
||||
<body ng-controller="AppCntl">
|
||||
<h3>Datetime/Number/Currency filters demo:</h3>
|
||||
<input type="text" ng:model="input" value="234234443432"><br>
|
||||
<input type="text" ng-model="input" value="234234443432"><br>
|
||||
date(medium): {{input | date:"medium"}}<br>
|
||||
date(longDate): {{input | date:"longDate"}}<br>
|
||||
number: {{input | number}}<br>
|
||||
currency: {{input | currency }}
|
||||
<hr/>
|
||||
<h3>Pluralization demo:</h3>
|
||||
<input type="text" ng:model="plInput"><br>
|
||||
<ng:pluralize count="plInput"
|
||||
<input type="text" ng-model="plInput"><br>
|
||||
<ng-pluralize count="plInput"
|
||||
when= "{ '0': 'You have no email!',
|
||||
'one': 'You have one email!',
|
||||
'other': 'You have {} emails!'}">
|
||||
</ng:pluralize>
|
||||
</ng-pluralize>
|
||||
<hr/>
|
||||
<h3>Pluralization demo with offsets:</h3>
|
||||
Name of person1:<input type="text" ng:model="person1"/><br/>
|
||||
Name of person2:<input type="text" ng:model="person2"/><br/>
|
||||
<input type="text" ng:model="plInput2"><br>
|
||||
<ng:pluralize count="plInput2" offset=2
|
||||
Name of person1:<input type="text" ng-model="person1"/><br/>
|
||||
Name of person2:<input type="text" ng-model="person2"/><br/>
|
||||
<input type="text" ng-model="plInput2"><br>
|
||||
<ng-pluralize count="plInput2" offset=2
|
||||
when= "{'0':'Nobody is viewing!',
|
||||
'1': '{{person1}} is viewing!',
|
||||
'2': '{{person1}} and {{person2}} are viewing!',
|
||||
'3': '{{person1}}, {{person2}} and one other person are viewing!',
|
||||
'other': '{{person1}}, {{person2}} and {} other people are viewing!'}">
|
||||
</ng:pluralize>
|
||||
</ng-pluralize>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
<script src="../../build/angular.js"></script>
|
||||
<script src="../../build/i18n/angular-locale_es.js"></script>
|
||||
<script>
|
||||
function AppCntl(){
|
||||
this.input = 234234443432;
|
||||
function AppCntl($scope){
|
||||
$scope.input = 234234443432;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body ng:controller="AppCntl">
|
||||
<input type="text" ng:model="input" value="234234443432"><br>
|
||||
<body ng-controller="AppCntl">
|
||||
<input type="text" ng-model="input" value="234234443432"><br>
|
||||
date: {{input | date:"medium"}}<br>
|
||||
date: {{input | date:"longDate"}}<br>
|
||||
number: {{input | number}}<br>
|
||||
|
||||
@@ -6,24 +6,24 @@
|
||||
<script src="../../build/angular.js"></script>
|
||||
<script src="../../build/i18n/angular-locale_sk-sk.js"></script>
|
||||
<script>
|
||||
function AppCntl(){
|
||||
this.input = 234234443432;
|
||||
this.plInput = 1;
|
||||
function AppCntl($scope){
|
||||
$scope.input = 234234443432;
|
||||
$scope.plInput = 1;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body ng:controller="AppCntl">
|
||||
<input type="text" ng:model="input" value="234234443432"><br>
|
||||
<body ng-controller="AppCntl">
|
||||
<input type="text" ng-model="input" value="234234443432"><br>
|
||||
date: {{input | date:"medium"}}<br>
|
||||
date: {{input | date:"longDate"}}<br>
|
||||
number: {{input | number}}<br>
|
||||
currency: {{input | currency }}
|
||||
<hr/>
|
||||
<input type="text" ng:model="plInput"><br>
|
||||
<ng:pluralize count="plInput"
|
||||
<input type="text" ng-model="plInput"><br>
|
||||
<ng-pluralize count="plInput"
|
||||
when= "{ 'one': 'Mas jeden email!',
|
||||
'few': 'Mas {} emaily!',
|
||||
'other': 'Mas {} emailov!'}">
|
||||
</ng:pluralize>
|
||||
</ng-pluralize>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+16
-16
@@ -6,38 +6,38 @@
|
||||
<script src="../../build/angular.js"></script>
|
||||
<script src="../../build/i18n/angular-locale_zh-cn.js"></script>
|
||||
<script>
|
||||
function AppCntl(){
|
||||
this.input = 234234443432;
|
||||
this.plInput = 1;
|
||||
this.person1 = "Shanjian";
|
||||
this.person2 = "Di";
|
||||
this.plInput2 = 1;
|
||||
function AppCntl($scope){
|
||||
$scope.input = 234234443432;
|
||||
$scope.plInput = 1;
|
||||
$scope.person1 = "Shanjian";
|
||||
$scope.person2 = "Di";
|
||||
$scope.plInput2 = 1;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body ng:controller="AppCntl">
|
||||
<body ng-controller="AppCntl">
|
||||
<h3>Datetime/Number/Currency filters demo:</h3>
|
||||
<input type="text" ng:model="input"><br>
|
||||
<input type="text" ng-model="input"><br>
|
||||
date(medium): {{input | date:"medium"}}<br>
|
||||
date(longDate): {{input | date:"longDate"}}<br>
|
||||
number: {{input | number}}<br>
|
||||
currency: {{input | currency }}
|
||||
<hr/>
|
||||
<h3>Pluralization demo:</h3>
|
||||
<input type="text" ng:model="plInput"><br>
|
||||
<ng:pluralize count="plInput"
|
||||
<input type="text" ng-model="plInput"><br>
|
||||
<ng-pluralize count="plInput"
|
||||
when= "{'other':'{}人在浏览该文件!'}">
|
||||
</ng:pluralize>
|
||||
</ng-pluralize>
|
||||
<hr/>
|
||||
<h3>Pluralization demo with offsets:</h3>
|
||||
Name of person1:<input type="text" ng:model="person1"/><br/>
|
||||
Name of person2:<input type="text" ng:model="person2"/><br/>
|
||||
<input type="text" ng:model="plInput2"><br>
|
||||
<ng:pluralize count="plInput2" offset=2
|
||||
Name of person1:<input type="text" ng-model="person1"/><br/>
|
||||
Name of person2:<input type="text" ng-model="person2"/><br/>
|
||||
<input type="text" ng-model="plInput2"><br>
|
||||
<ng-pluralize count="plInput2" offset=2
|
||||
when= "{'0':'没有人在浏览该文件!',
|
||||
'1': '{{person1}} 在浏览该文件!',
|
||||
'2': '{{person1}} 和 {{person2}} 在浏览该文件!',
|
||||
'other': '{{person1}}, {{person2}} 还有其他{} 人在浏览该文件!'}">
|
||||
</ng:pluralize>
|
||||
</ng-pluralize>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -12,7 +12,7 @@ var Q = require('qq'),
|
||||
require: function() {},
|
||||
i18n: {currency: {}, pluralRules: {}} };
|
||||
|
||||
createFolder('../locale/').then(function() {
|
||||
createFolder('../../src/ngLocale/').then(function() {
|
||||
var promiseA = Q.defer(),
|
||||
promiseB = Q.defer();
|
||||
|
||||
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
function catch_errors() {
|
||||
echo "ERROR. That's life."
|
||||
exit 1
|
||||
}
|
||||
|
||||
trap catch_errors ERR
|
||||
|
||||
TMP_FILE='changelog.tmp'
|
||||
CHANGELOG_FILE='CHANGELOG.md'
|
||||
|
||||
echo "Getting current version..."
|
||||
VERSION=`./version.js --current`
|
||||
|
||||
echo "Generating changelog..."
|
||||
./changelog.js $VERSION $TMP_FILE
|
||||
|
||||
cat $CHANGELOG_FILE >> $TMP_FILE
|
||||
mv -f $TMP_FILE $CHANGELOG_FILE
|
||||
|
||||
|
||||
echo "Updating version..."
|
||||
./version.js --remove-snapshot
|
||||
|
||||
echo "CONFIRM TO COMMIT"
|
||||
read WHATEVER
|
||||
|
||||
|
||||
echo "Creating commit..."
|
||||
git commit version.yaml CHANGELOG.md -m "chore(relase): cutting the v$VERSION release"
|
||||
|
||||
echo "Creating tag..."
|
||||
git tag "v$VERSION"
|
||||
+77
-13
@@ -61,7 +61,7 @@ var $boolean = 'boolean',
|
||||
$undefined = 'undefined',
|
||||
Error = window.Error,
|
||||
/** holds major version number for IE or NaN for real browsers */
|
||||
msie = parseInt((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1], 10),
|
||||
msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]),
|
||||
jqLite, // delay binding since jQuery could be loaded after us.
|
||||
jQuery, // delay binding
|
||||
slice = [].slice,
|
||||
@@ -73,8 +73,7 @@ var $boolean = 'boolean',
|
||||
angularModule,
|
||||
/** @name angular.module.ng */
|
||||
nodeName_,
|
||||
uid = ['0', '0', '0'],
|
||||
DATE_ISOSTRING_LN = 24;
|
||||
uid = ['0', '0', '0'];
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
@@ -118,8 +117,11 @@ function forEach(obj, iterator, context) {
|
||||
for (key = 0; key < obj.length; key++)
|
||||
iterator.call(context, obj[key], key);
|
||||
} else {
|
||||
for (key in obj)
|
||||
iterator.call(context, obj[key], key);
|
||||
for (key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
iterator.call(context, obj[key], key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
@@ -206,6 +208,10 @@ function extend(dst) {
|
||||
return dst;
|
||||
}
|
||||
|
||||
function int(str) {
|
||||
return parseInt(str, 10);
|
||||
}
|
||||
|
||||
|
||||
function inherit(parent, extra) {
|
||||
return extend(new (extend(function() {}, {prototype:parent}))(), extra);
|
||||
@@ -592,16 +598,16 @@ function copy(source, destination){
|
||||
|
||||
/**
|
||||
* Create a shallow copy of an object
|
||||
* @param src
|
||||
*/
|
||||
function shallowCopy(src) {
|
||||
var dst = {},
|
||||
key;
|
||||
for(key in src) {
|
||||
if (src.hasOwnProperty(key)) {
|
||||
function shallowCopy(src, dst) {
|
||||
dst = dst || {};
|
||||
|
||||
for(var key in src) {
|
||||
if (src.hasOwnProperty(key) && key.substr(0, 2) !== '$$') {
|
||||
dst[key] = src[key];
|
||||
}
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
@@ -721,6 +727,59 @@ function bind(self, fn) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function toJsonReplacer(key, value) {
|
||||
var val = value;
|
||||
|
||||
if (/^\$+/.test(key)) {
|
||||
val = undefined;
|
||||
} else if (isWindow(value)) {
|
||||
val = '$WINDOW';
|
||||
} else if (value && document === value) {
|
||||
val = '$DOCUMENT';
|
||||
} else if (isScope(value)) {
|
||||
val = '$SCOPE';
|
||||
}
|
||||
|
||||
return val;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.toJson
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Serializes input into a JSON-formatted string.
|
||||
*
|
||||
* @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
|
||||
* @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace.
|
||||
* @returns {string} Jsonified string representing `obj`.
|
||||
*/
|
||||
function toJson(obj, pretty) {
|
||||
return JSON.stringify(obj, toJsonReplacer, pretty ? ' ' : null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.fromJson
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Deserializes a JSON string.
|
||||
*
|
||||
* @param {string} json JSON string to deserialize.
|
||||
* @returns {Object|Array|Date|string|number} Deserialized thingy.
|
||||
*/
|
||||
function fromJson(json) {
|
||||
return isString(json)
|
||||
? JSON.parse(json)
|
||||
: json;
|
||||
}
|
||||
|
||||
|
||||
function toBoolean(value) {
|
||||
if (value && value.length !== 0) {
|
||||
var v = lowercase("" + value);
|
||||
@@ -928,6 +987,7 @@ function bindJQuery() {
|
||||
jqLite = jQuery;
|
||||
extend(jQuery.fn, {
|
||||
scope: JQLitePrototype.scope,
|
||||
controller: JQLitePrototype.controller,
|
||||
injector: JQLitePrototype.injector,
|
||||
inheritedData: JQLitePrototype.inheritedData
|
||||
});
|
||||
@@ -935,7 +995,7 @@ function bindJQuery() {
|
||||
JQLitePatchJQueryRemove('empty');
|
||||
JQLitePatchJQueryRemove('html');
|
||||
} else {
|
||||
jqLite = jqLiteWrap;
|
||||
jqLite = JQLite;
|
||||
}
|
||||
angular.element = jqLite;
|
||||
}
|
||||
@@ -952,7 +1012,11 @@ function assertArg(arg, name, reason) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
function assertArgFn(arg, name) {
|
||||
function assertArgFn(arg, name, acceptArrayAnnotation) {
|
||||
if (acceptArrayAnnotation && isArray(arg)) {
|
||||
arg = arg[arg.length - 1];
|
||||
}
|
||||
|
||||
assertArg(isFunction(arg), name, 'not a function, got ' +
|
||||
(arg && typeof arg == 'object' ? arg.constructor.name || 'Object' : typeof arg));
|
||||
return arg;
|
||||
|
||||
@@ -74,7 +74,6 @@ function publishExternalAPI(angular){
|
||||
ngBindHtml: ngBindHtmlDirective,
|
||||
ngBindHtmlUnsafe: ngBindHtmlUnsafeDirective,
|
||||
ngBindTemplate: ngBindTemplateDirective,
|
||||
ngBindAttr: ngBindAttrDirective,
|
||||
ngClass: ngClassDirective,
|
||||
ngClassEven: ngClassEvenDirective,
|
||||
ngClassOdd: ngClassOddDirective,
|
||||
@@ -101,7 +100,8 @@ function publishExternalAPI(angular){
|
||||
ngChange: ngChangeDirective,
|
||||
ngModelInstant: ngModelInstantDirective,
|
||||
required: requiredDirective,
|
||||
ngRequired: requiredDirective
|
||||
ngRequired: requiredDirective,
|
||||
ngValue: ngValueDirective
|
||||
}).
|
||||
directive(ngAttributeAliasDirectives).
|
||||
directive(ngEventDirectives);
|
||||
@@ -110,8 +110,6 @@ function publishExternalAPI(angular){
|
||||
$browser: $BrowserProvider,
|
||||
$cacheFactory: $CacheFactoryProvider,
|
||||
$controller: $ControllerProvider,
|
||||
$cookies: $CookiesProvider,
|
||||
$cookieStore: $CookieStoreProvider,
|
||||
$defer: $DeferProvider,
|
||||
$document: $DocumentProvider,
|
||||
$exceptionHandler: $ExceptionHandlerProvider,
|
||||
@@ -122,7 +120,6 @@ function publishExternalAPI(angular){
|
||||
$location: $LocationProvider,
|
||||
$log: $LogProvider,
|
||||
$parse: $ParseProvider,
|
||||
$resource: $ResourceProvider,
|
||||
$route: $RouteProvider,
|
||||
$routeParams: $RouteParamsProvider,
|
||||
$rootScope: $RootScopeProvider,
|
||||
|
||||
-200
@@ -1,200 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var array = [].constructor;
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.toJson
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Serializes input into a JSON-formatted string.
|
||||
*
|
||||
* @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
|
||||
* @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace.
|
||||
* @returns {string} Jsonified string representing `obj`.
|
||||
*/
|
||||
function toJson(obj, pretty) {
|
||||
var buf = [];
|
||||
toJsonArray(buf, obj, pretty ? "\n " : null, []);
|
||||
return buf.join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.fromJson
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Deserializes a JSON string.
|
||||
*
|
||||
* @param {string} json JSON string to deserialize.
|
||||
* @param {boolean} [useNative=false] Use native JSON parser, if available.
|
||||
* @returns {Object|Array|Date|string|number} Deserialized thingy.
|
||||
*/
|
||||
function fromJson(json, useNative) {
|
||||
if (!isString(json)) return json;
|
||||
|
||||
var obj;
|
||||
|
||||
if (useNative && window.JSON && window.JSON.parse) {
|
||||
obj = JSON.parse(json);
|
||||
} else {
|
||||
obj = parseJson(json, true)();
|
||||
}
|
||||
return transformDates(obj);
|
||||
|
||||
// TODO make forEach optionally recursive and remove this function
|
||||
// TODO(misko): remove this once the $http service is checked in.
|
||||
function transformDates(obj) {
|
||||
if (isString(obj) && obj.length === DATE_ISOSTRING_LN) {
|
||||
return jsonStringToDate(obj);
|
||||
} else if (isArray(obj) || isObject(obj)) {
|
||||
forEach(obj, function(val, name) {
|
||||
obj[name] = transformDates(val);
|
||||
});
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
var R_ISO8061_STR = /^(\d{4})-(\d\d)-(\d\d)(?:T(\d\d)(?:\:(\d\d)(?:\:(\d\d)(?:\.(\d{3}))?)?)?Z)?$/;
|
||||
function jsonStringToDate(string){
|
||||
var match;
|
||||
if (isString(string) && (match = string.match(R_ISO8061_STR))){
|
||||
var date = new Date(0);
|
||||
date.setUTCFullYear(match[1], match[2] - 1, match[3]);
|
||||
date.setUTCHours(match[4]||0, match[5]||0, match[6]||0, match[7]||0);
|
||||
return date;
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
function jsonDateToString(date){
|
||||
if (!date) return date;
|
||||
var isoString = date.toISOString ? date.toISOString() : '';
|
||||
return (isoString.length==24)
|
||||
? isoString
|
||||
: padNumber(date.getUTCFullYear(), 4) + '-' +
|
||||
padNumber(date.getUTCMonth() + 1, 2) + '-' +
|
||||
padNumber(date.getUTCDate(), 2) + 'T' +
|
||||
padNumber(date.getUTCHours(), 2) + ':' +
|
||||
padNumber(date.getUTCMinutes(), 2) + ':' +
|
||||
padNumber(date.getUTCSeconds(), 2) + '.' +
|
||||
padNumber(date.getUTCMilliseconds(), 3) + 'Z';
|
||||
}
|
||||
|
||||
function quoteUnicode(string) {
|
||||
var chars = ['"'];
|
||||
for ( var i = 0; i < string.length; i++) {
|
||||
var code = string.charCodeAt(i);
|
||||
var ch = string.charAt(i);
|
||||
switch(ch) {
|
||||
case '"': chars.push('\\"'); break;
|
||||
case '\\': chars.push('\\\\'); break;
|
||||
case '\n': chars.push('\\n'); break;
|
||||
case '\f': chars.push('\\f'); break;
|
||||
case '\r': chars.push(ch = '\\r'); break;
|
||||
case '\t': chars.push(ch = '\\t'); break;
|
||||
default:
|
||||
if (32 <= code && code <= 126) {
|
||||
chars.push(ch);
|
||||
} else {
|
||||
var encode = "000" + code.toString(16);
|
||||
chars.push("\\u" + encode.substring(encode.length - 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
chars.push('"');
|
||||
return chars.join('');
|
||||
}
|
||||
|
||||
|
||||
function toJsonArray(buf, obj, pretty, stack) {
|
||||
if (isObject(obj)) {
|
||||
if (obj === window) {
|
||||
buf.push('WINDOW');
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj === document) {
|
||||
buf.push('DOCUMENT');
|
||||
return;
|
||||
}
|
||||
|
||||
if (includes(stack, obj)) {
|
||||
buf.push('RECURSION');
|
||||
return;
|
||||
}
|
||||
stack.push(obj);
|
||||
}
|
||||
if (obj === null) {
|
||||
buf.push('null');
|
||||
} else if (obj instanceof RegExp) {
|
||||
buf.push(quoteUnicode(obj.toString()));
|
||||
} else if (isFunction(obj)) {
|
||||
return;
|
||||
} else if (isBoolean(obj)) {
|
||||
buf.push('' + obj);
|
||||
} else if (isNumber(obj)) {
|
||||
if (isNaN(obj)) {
|
||||
buf.push('null');
|
||||
} else {
|
||||
buf.push('' + obj);
|
||||
}
|
||||
} else if (isString(obj)) {
|
||||
return buf.push(quoteUnicode(obj));
|
||||
} else if (isObject(obj)) {
|
||||
if (isArray(obj)) {
|
||||
buf.push("[");
|
||||
var len = obj.length;
|
||||
var sep = false;
|
||||
for(var i=0; i<len; i++) {
|
||||
var item = obj[i];
|
||||
if (sep) buf.push(",");
|
||||
if (!(item instanceof RegExp) && (isFunction(item) || isUndefined(item))) {
|
||||
buf.push('null');
|
||||
} else {
|
||||
toJsonArray(buf, item, pretty, stack);
|
||||
}
|
||||
sep = true;
|
||||
}
|
||||
buf.push("]");
|
||||
} else if (isElement(obj)) {
|
||||
// TODO(misko): maybe in dev mode have a better error reporting?
|
||||
buf.push('DOM_ELEMENT');
|
||||
} else if (isDate(obj)) {
|
||||
buf.push(quoteUnicode(jsonDateToString(obj)));
|
||||
} else {
|
||||
buf.push("{");
|
||||
if (pretty) buf.push(pretty);
|
||||
var comma = false;
|
||||
var childPretty = pretty ? pretty + " " : false;
|
||||
var keys = [];
|
||||
for(var k in obj) {
|
||||
if (k!='this' && k!='$parent' && k.substring(0,2) != '$$' && obj.hasOwnProperty(k) && obj[k] !== undefined) {
|
||||
keys.push(k);
|
||||
}
|
||||
}
|
||||
keys.sort();
|
||||
for ( var keyIndex = 0; keyIndex < keys.length; keyIndex++) {
|
||||
var key = keys[keyIndex];
|
||||
var value = obj[key];
|
||||
if (!isFunction(value)) {
|
||||
if (comma) {
|
||||
buf.push(",");
|
||||
if (pretty) buf.push(pretty);
|
||||
}
|
||||
buf.push(quoteUnicode(key));
|
||||
buf.push(":");
|
||||
toJsonArray(buf, value, childPretty, stack);
|
||||
comma = true;
|
||||
}
|
||||
}
|
||||
buf.push("}");
|
||||
}
|
||||
}
|
||||
if (isObject(obj)) {
|
||||
stack.pop();
|
||||
}
|
||||
}
|
||||
-164
@@ -1,164 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
|
||||
function Route(template, defaults) {
|
||||
this.template = template = template + '#';
|
||||
this.defaults = defaults || {};
|
||||
var urlParams = this.urlParams = {};
|
||||
forEach(template.split(/\W/), function(param){
|
||||
if (param && template.match(new RegExp(":" + param + "\\W"))) {
|
||||
urlParams[param] = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Route.prototype = {
|
||||
url: function(params) {
|
||||
var self = this,
|
||||
url = this.template,
|
||||
encodedVal;
|
||||
|
||||
params = params || {};
|
||||
forEach(this.urlParams, function(_, urlParam){
|
||||
encodedVal = encodeUriSegment(params[urlParam] || self.defaults[urlParam] || "");
|
||||
url = url.replace(new RegExp(":" + urlParam + "(\\W)"), encodedVal + "$1");
|
||||
});
|
||||
url = url.replace(/\/?#$/, '');
|
||||
var query = [];
|
||||
forEachSorted(params, function(value, key){
|
||||
if (!self.urlParams[key]) {
|
||||
query.push(encodeUriQuery(key) + '=' + encodeUriQuery(value));
|
||||
}
|
||||
});
|
||||
url = url.replace(/\/*$/, '');
|
||||
return url + (query.length ? '?' + query.join('&') : '');
|
||||
}
|
||||
};
|
||||
|
||||
function ResourceFactory($http) {
|
||||
this.$http = $http;
|
||||
}
|
||||
|
||||
ResourceFactory.DEFAULT_ACTIONS = {
|
||||
'get': {method:'GET'},
|
||||
'save': {method:'POST'},
|
||||
'query': {method:'GET', isArray:true},
|
||||
'remove': {method:'DELETE'},
|
||||
'delete': {method:'DELETE'}
|
||||
};
|
||||
|
||||
ResourceFactory.prototype = {
|
||||
route: function(url, paramDefaults, actions){
|
||||
var self = this;
|
||||
var route = new Route(url);
|
||||
actions = extend({}, ResourceFactory.DEFAULT_ACTIONS, actions);
|
||||
function extractParams(data){
|
||||
var ids = {};
|
||||
forEach(paramDefaults || {}, function(value, key){
|
||||
ids[key] = value.charAt && value.charAt(0) == '@' ? getter(data, value.substr(1)) : value;
|
||||
});
|
||||
return ids;
|
||||
}
|
||||
|
||||
function Resource(value){
|
||||
copy(value || {}, this);
|
||||
}
|
||||
|
||||
forEach(actions, function(action, name){
|
||||
var isPostOrPut = action.method == 'POST' || action.method == 'PUT';
|
||||
Resource[name] = function(a1, a2, a3, a4) {
|
||||
var params = {};
|
||||
var data;
|
||||
var success = noop;
|
||||
var error = null;
|
||||
switch(arguments.length) {
|
||||
case 4:
|
||||
error = a4;
|
||||
success = a3;
|
||||
//fallthrough
|
||||
case 3:
|
||||
case 2:
|
||||
if (isFunction(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)) success = a1;
|
||||
else if (isPostOrPut) data = a1;
|
||||
else params = a1;
|
||||
break;
|
||||
case 0: break;
|
||||
default:
|
||||
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.$http({
|
||||
method: action.method,
|
||||
url: route.url(extend({}, extractParams(data), action.params || {}, params)),
|
||||
data: data
|
||||
}).then(function(response) {
|
||||
var data = response.data;
|
||||
|
||||
if (data) {
|
||||
if (action.isArray) {
|
||||
value.length = 0;
|
||||
forEach(data, function(item) {
|
||||
value.push(new Resource(item));
|
||||
});
|
||||
} else {
|
||||
copy(data, value);
|
||||
}
|
||||
}
|
||||
(success||noop)(value, response.headers);
|
||||
}, error);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
Resource.bind = function(additionalParamDefaults){
|
||||
return self.route(url, extend({}, paramDefaults, additionalParamDefaults), actions);
|
||||
};
|
||||
|
||||
Resource.prototype['$' + name] = function(a1, a2, a3) {
|
||||
var params = extractParams(this),
|
||||
success = noop,
|
||||
error;
|
||||
|
||||
switch(arguments.length) {
|
||||
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-3 arguments [params, success, error], got " +
|
||||
arguments.length + " arguments.";
|
||||
}
|
||||
var data = isPostOrPut ? this : undefined;
|
||||
Resource[name].call(this, params, data, success, error);
|
||||
};
|
||||
});
|
||||
return Resource;
|
||||
}
|
||||
};
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
|
||||
publishExternalAPI(angular);
|
||||
|
||||
jqLiteWrap(document).ready(function() {
|
||||
jqLite(document).ready(function() {
|
||||
angularInit(document, bootstrap);
|
||||
});
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
* Implicit module which gets automatically added to each {@link angular.module.AUTO.$injector $injector}.
|
||||
*/
|
||||
|
||||
var FN_ARGS = /^function\s*[^\(]*\(([^\)]*)\)/m;
|
||||
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
|
||||
var FN_ARG_SPLIT = /,/;
|
||||
var FN_ARG = /^\s*(_?)(.+?)\1\s*$/;
|
||||
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
|
||||
+44
-25
@@ -60,9 +60,13 @@
|
||||
*
|
||||
* ## In addtion to the above, Angular privides an additional method to both jQuery and jQuery lite:
|
||||
*
|
||||
* - `scope()` - retrieves the current Angular scope of the element.
|
||||
* - `injector()` - retrieves the Angular injector associated with application that the element is
|
||||
* part of.
|
||||
* - `controller(name)` - retrieves the controller of the current element or its parent. By default
|
||||
* retrieves controller associated with the `ng-controller` directive. If `name` is provided as
|
||||
* camelCase directive name, then the controller for this directive will be retrieved (e.g.
|
||||
* `'ngModel'`).
|
||||
* - `injector()` - retrieves the injector of the current element or its parent.
|
||||
* - `scope()` - retrieves the {@link api/angular.module.ng.$rootScope.Scope scope} of the current
|
||||
* element or its parent.
|
||||
* - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
|
||||
* parent element is reached.
|
||||
*
|
||||
@@ -164,17 +168,18 @@ function JQLitePatchJQueryRemove(name, dispatchThis) {
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////
|
||||
function jqLiteWrap(element) {
|
||||
if (isString(element) && element.charAt(0) != '<') {
|
||||
throw new Error('selectors not implemented');
|
||||
}
|
||||
return new JQLite(element);
|
||||
}
|
||||
|
||||
function JQLite(element) {
|
||||
if (element instanceof JQLite) {
|
||||
return element;
|
||||
} else if (isString(element)) {
|
||||
}
|
||||
if (!(this instanceof JQLite)) {
|
||||
if (isString(element) && element.charAt(0) != '<') {
|
||||
throw Error('selectors not implemented');
|
||||
}
|
||||
return new JQLite(element);
|
||||
}
|
||||
|
||||
if (isString(element)) {
|
||||
var div = document.createElement('div');
|
||||
// Read about the NoScope elements here:
|
||||
// http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx
|
||||
@@ -268,6 +273,25 @@ function JQLiteAddNodes(root, elements) {
|
||||
}
|
||||
}
|
||||
|
||||
function JQLiteController(element, name) {
|
||||
return JQLiteInheritedData(element, '$' + (name || 'ngController' ) + 'Controller');
|
||||
}
|
||||
|
||||
function JQLiteInheritedData(element, name, value) {
|
||||
element = jqLite(element);
|
||||
|
||||
// if element is the document object work with the html element instead
|
||||
// this makes $(document).scope() possible
|
||||
if(element[0].nodeType == 9) {
|
||||
element = element.find('html');
|
||||
}
|
||||
|
||||
while (element.length) {
|
||||
if (value = element.data(name)) return value;
|
||||
element = element.parent();
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
// Functions which are declared directly.
|
||||
//////////////////////////////////////////
|
||||
@@ -283,7 +307,7 @@ var JQLitePrototype = JQLite.prototype = {
|
||||
|
||||
this.bind('DOMContentLoaded', trigger); // works for modern browsers and IE9
|
||||
// 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
|
||||
JQLite(window).bind('load', trigger); // fallback to window.onload for others
|
||||
},
|
||||
toString: function() {
|
||||
var value = [];
|
||||
@@ -311,7 +335,7 @@ forEach('multiple,selected,checked,disabled,readOnly,required'.split(','), funct
|
||||
BOOLEAN_ATTR[lowercase(value)] = value;
|
||||
});
|
||||
var BOOLEAN_ELEMENTS = {};
|
||||
forEach('input,select,option,textarea,button'.split(','), function(value) {
|
||||
forEach('input,select,option,textarea,button,form'.split(','), function(value) {
|
||||
BOOLEAN_ELEMENTS[uppercase(value)] = true;
|
||||
});
|
||||
|
||||
@@ -321,20 +345,16 @@ function isBooleanAttr(element, name) {
|
||||
|
||||
forEach({
|
||||
data: JQLiteData,
|
||||
inheritedData: function(element, name, value) {
|
||||
element = jqLite(element);
|
||||
while (element.length) {
|
||||
if (value = element.data(name)) return value;
|
||||
element = element.parent();
|
||||
}
|
||||
},
|
||||
inheritedData: JQLiteInheritedData,
|
||||
|
||||
scope: function(element) {
|
||||
return jqLite(element).inheritedData('$scope');
|
||||
return JQLiteInheritedData(element, '$scope');
|
||||
},
|
||||
|
||||
controller: JQLiteController ,
|
||||
|
||||
injector: function(element) {
|
||||
return jqLite(element).inheritedData('$injector');
|
||||
return JQLiteInheritedData(element, '$injector');
|
||||
},
|
||||
|
||||
removeAttr: function(element,name) {
|
||||
@@ -381,8 +401,7 @@ forEach({
|
||||
}
|
||||
} else {
|
||||
return (element[name] ||
|
||||
element.getAttribute(name) !== null &&
|
||||
(msie < 9 ? element.getAttribute(name) !== '' : true))
|
||||
(element.attributes.getNamedItem(name)|| noop).specified)
|
||||
? lowercasedName
|
||||
: undefined;
|
||||
}
|
||||
@@ -449,7 +468,7 @@ forEach({
|
||||
|
||||
// JQLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
|
||||
// in a way that survives minification.
|
||||
if (((fn.length == 2 && fn !== JQLiteHasClass) ? arg1 : arg2) === undefined) {
|
||||
if (((fn.length == 2 && (fn !== JQLiteHasClass && fn !== JQLiteController)) ? arg1 : arg2) === undefined) {
|
||||
if (isObject(arg1)) {
|
||||
// we are a write, but the object properties are the key/values
|
||||
for(i=0; i < this.length; i++) {
|
||||
|
||||
+12
-1
@@ -167,13 +167,24 @@ function setupModuleLoader(window) {
|
||||
* @ngdoc method
|
||||
* @name angular.Module#filter
|
||||
* @methodOf angular.Module
|
||||
* @param {string} name filter name
|
||||
* @param {string} name Filter name.
|
||||
* @param {Function} filterFactory Factory function for creating new instance of filter.
|
||||
* @description
|
||||
* See {@link angular.module.ng.$filterProvider#register $filterProvider.register()}.
|
||||
*/
|
||||
filter: invokeLater('$filterProvider', 'register'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#controller
|
||||
* @methodOf angular.Module
|
||||
* @param {string} name Controller name.
|
||||
* @param {Function} constructor Controller constructor function.
|
||||
* @description
|
||||
* See {@link angular.module.ng.$controllerProvider#register $controllerProvider.register()}.
|
||||
*/
|
||||
controller: invokeLater('$controllerProvider', 'register'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#directive
|
||||
|
||||
@@ -128,13 +128,7 @@ function $CompileProvider($provide) {
|
||||
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
|
||||
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
|
||||
CONTENT_REGEXP = /\<\<content\>\>/i,
|
||||
HAS_ROOT_ELEMENT = /^\<[\s\S]*\>$/,
|
||||
SIDE_EFFECT_ATTRS = {};
|
||||
|
||||
forEach('src,href,multiple,selected,checked,disabled,readonly,required'.split(','), function(name) {
|
||||
SIDE_EFFECT_ATTRS[name] = name;
|
||||
SIDE_EFFECT_ATTRS[directiveNormalize('ng_' + name)] = name;
|
||||
});
|
||||
HAS_ROOT_ELEMENT = /^\<[\s\S]*\>$/;
|
||||
|
||||
|
||||
this.directive = function registerDirective(name, directiveFactory) {
|
||||
@@ -220,12 +214,88 @@ function $CompileProvider($provide) {
|
||||
}
|
||||
};
|
||||
|
||||
var Attributes = function(element, attr) {
|
||||
this.$$element = element;
|
||||
this.$$observers = {};
|
||||
this.$attr = attr || {};
|
||||
};
|
||||
|
||||
Attributes.prototype = {
|
||||
$normalize: directiveNormalize,
|
||||
|
||||
|
||||
/**
|
||||
* Set a normalized attribute on the element in a way such that all directives
|
||||
* can share the attribute. This function properly handles boolean attributes.
|
||||
* @param {string} key Normalized key. (ie ngAttribute)
|
||||
* @param {string|boolean} value The value to set. If `null` attribute will be deleted.
|
||||
* @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
|
||||
* Defaults to true.
|
||||
* @param {string=} attrName Optional none normalized name. Defaults to key.
|
||||
*/
|
||||
$set: function(key, value, writeAttr, attrName) {
|
||||
var booleanKey = isBooleanAttr(this.$$element[0], key.toLowerCase());
|
||||
|
||||
if (booleanKey) {
|
||||
this.$$element.prop(key, value);
|
||||
attrName = booleanKey;
|
||||
}
|
||||
|
||||
this[key] = value;
|
||||
|
||||
// translate normalized key to actual key
|
||||
if (attrName) {
|
||||
this.$attr[key] = attrName;
|
||||
} else {
|
||||
attrName = this.$attr[key];
|
||||
if (!attrName) {
|
||||
this.$attr[key] = attrName = snake_case(key, '-');
|
||||
}
|
||||
}
|
||||
|
||||
if (writeAttr !== false) {
|
||||
if (value === null || value === undefined) {
|
||||
this.$$element.removeAttr(attrName);
|
||||
} else {
|
||||
this.$$element.attr(attrName, value);
|
||||
}
|
||||
}
|
||||
|
||||
// fire observers
|
||||
forEach(this.$$observers[key], function(fn) {
|
||||
try {
|
||||
fn(value);
|
||||
} catch (e) {
|
||||
$exceptionHandler(e);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Observe an interpolated attribute.
|
||||
* The observer will never be called, if given attribute is not interpolated.
|
||||
*
|
||||
* @param {string} key Normalized key. (ie ngAttribute) .
|
||||
* @param {function(*)} fn Function that will be called whenever the attribute value changes.
|
||||
*/
|
||||
$observe: function(key, fn) {
|
||||
// keep only observers for interpolated attrs
|
||||
if (this.$$observers[key]) {
|
||||
this.$$observers[key].push(fn);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return compile;
|
||||
|
||||
//================================
|
||||
|
||||
function compile(templateElement, transcludeFn, maxPriority) {
|
||||
templateElement = jqLite(templateElement);
|
||||
if (!(templateElement instanceof jqLite)) {
|
||||
// jquery always rewraps, where as we need to preserve the original selector so that we can modify it.
|
||||
templateElement = jqLite(templateElement);
|
||||
}
|
||||
// We can not compile top level text elements since text nodes can be merged and we will
|
||||
// not be able to attach scope data to them, so we will wrap them in <span>
|
||||
forEach(templateElement, function(node, index){
|
||||
@@ -281,13 +351,8 @@ function $CompileProvider($provide) {
|
||||
directiveLinkingFn, childLinkingFn, directives, attrs, linkingFnFound;
|
||||
|
||||
for(var i = 0, ii = nodeList.length; i < ii; i++) {
|
||||
attrs = {
|
||||
$attr: {},
|
||||
$normalize: directiveNormalize,
|
||||
$set: attrSetter,
|
||||
$observe: interpolatedAttrObserve,
|
||||
$observers: {}
|
||||
};
|
||||
attrs = new Attributes();
|
||||
|
||||
// we must always refer to nodeList[i] since the nodes can be replaced underneath us.
|
||||
directives = collectDirectives(nodeList[i], [], attrs, maxPriority);
|
||||
|
||||
@@ -321,7 +386,7 @@ function $CompileProvider($provide) {
|
||||
childLinkingFn = /* nodesetLinkingFn */ linkingFns[i++];
|
||||
|
||||
if (directiveLinkingFn) {
|
||||
if (directiveLinkingFn.scope && !rootElement) {
|
||||
if (directiveLinkingFn.scope) {
|
||||
childScope = scope.$new(isObject(directiveLinkingFn.scope));
|
||||
jqLite(node).data('$scope', childScope);
|
||||
} else {
|
||||
@@ -375,17 +440,19 @@ function $CompileProvider($provide) {
|
||||
for (var attr, name, nName, value, nAttrs = node.attributes,
|
||||
j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
|
||||
attr = nAttrs[j];
|
||||
name = attr.name;
|
||||
nName = directiveNormalize(name.toLowerCase());
|
||||
attrsMap[nName] = name;
|
||||
attrs[nName] = value = trim((msie && name == 'href')
|
||||
if (attr.specified) {
|
||||
name = attr.name;
|
||||
nName = directiveNormalize(name.toLowerCase());
|
||||
attrsMap[nName] = name;
|
||||
attrs[nName] = value = trim((msie && name == 'href')
|
||||
? decodeURIComponent(node.getAttribute(name, 2))
|
||||
: attr.value);
|
||||
if (isBooleanAttr(node, nName)) {
|
||||
attrs[nName] = true; // presence means true
|
||||
if (isBooleanAttr(node, nName)) {
|
||||
attrs[nName] = true; // presence means true
|
||||
}
|
||||
addAttrInterpolateDirective(node, directives, value, nName)
|
||||
addDirective(directives, nName, 'A', maxPriority);
|
||||
}
|
||||
addAttrInterpolateDirective(node, directives, value, nName)
|
||||
addDirective(directives, nName, 'A', maxPriority);
|
||||
}
|
||||
|
||||
// use class as directive
|
||||
@@ -442,7 +509,7 @@ function $CompileProvider($provide) {
|
||||
newIsolatedScopeDirective = null,
|
||||
templateDirective = null,
|
||||
delayedLinkingFn = null,
|
||||
element = templateAttrs.$element = jqLite(templateNode),
|
||||
element = templateAttrs.$$element = jqLite(templateNode),
|
||||
directive,
|
||||
directiveName,
|
||||
template,
|
||||
@@ -486,9 +553,9 @@ function $CompileProvider($provide) {
|
||||
terminalPriority = directive.priority;
|
||||
if (directiveValue == 'element') {
|
||||
template = jqLite(templateNode);
|
||||
templateNode = (element = templateAttrs.$element = jqLite(
|
||||
templateNode = (element = templateAttrs.$$element = jqLite(
|
||||
'<!-- ' + directiveName + ': ' + templateAttrs[directiveName] + ' -->'))[0];
|
||||
template.replaceWith(templateNode);
|
||||
replaceWith(rootElement, jqLite(template[0]), templateNode);
|
||||
childTranscludeFn = compile(template, transcludeFn, terminalPriority);
|
||||
} else {
|
||||
template = jqLite(JQLiteClone(templateNode));
|
||||
@@ -610,10 +677,9 @@ function $CompileProvider($provide) {
|
||||
if (templateNode === linkNode) {
|
||||
attrs = templateAttrs;
|
||||
} else {
|
||||
attrs = shallowCopy(templateAttrs);
|
||||
attrs.$element = jqLite(linkNode);
|
||||
attrs = shallowCopy(templateAttrs, new Attributes(jqLite(linkNode), templateAttrs.$attr));
|
||||
}
|
||||
element = attrs.$element;
|
||||
element = attrs.$$element;
|
||||
|
||||
if (newScopeDirective && isObject(newScopeDirective.scope)) {
|
||||
forEach(newScopeDirective.scope, function(mode, name) {
|
||||
@@ -720,11 +786,14 @@ function $CompileProvider($provide) {
|
||||
function mergeTemplateAttributes(dst, src) {
|
||||
var srcAttr = src.$attr,
|
||||
dstAttr = dst.$attr,
|
||||
element = dst.$element;
|
||||
element = dst.$$element;
|
||||
// reapply the old attributes to the new element
|
||||
forEach(dst, function(value, key) {
|
||||
if (key.charAt(0) != '$') {
|
||||
dst.$set(key, value, srcAttr[key]);
|
||||
if (src[key]) {
|
||||
value += (key === 'style' ? ';' : ' ') + src[key];
|
||||
}
|
||||
dst.$set(key, value, true, srcAttr[key]);
|
||||
}
|
||||
});
|
||||
// copy the new attributes on the old attrs object
|
||||
@@ -854,32 +923,28 @@ function $CompileProvider($provide) {
|
||||
|
||||
function addAttrInterpolateDirective(node, directives, value, name) {
|
||||
var interpolateFn = $interpolate(value, true);
|
||||
if (SIDE_EFFECT_ATTRS[name]) {
|
||||
name = SIDE_EFFECT_ATTRS[name];
|
||||
if (isBooleanAttr(node, name)) {
|
||||
value = true;
|
||||
}
|
||||
} else if (!interpolateFn) {
|
||||
// we are not a side-effect attr, and we have no side-effects -> ignore
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// no interpolation found -> ignore
|
||||
if (!interpolateFn) return;
|
||||
|
||||
directives.push({
|
||||
priority: 100,
|
||||
compile: function(element, attr) {
|
||||
if (interpolateFn) {
|
||||
return function(scope, element, attr) {
|
||||
// we define observers array only for interpolated attrs
|
||||
// and ignore observers for non interpolated attrs to save some memory
|
||||
attr.$observers[name] = [];
|
||||
attr[name] = undefined;
|
||||
scope.$watch(interpolateFn, function(value) {
|
||||
attr.$set(name, value);
|
||||
});
|
||||
};
|
||||
} else {
|
||||
attr.$set(name, value);
|
||||
compile: valueFn(function(scope, element, attr) {
|
||||
if (name === 'class') {
|
||||
// we need to interpolate classes again, in the case the element was replaced
|
||||
// and therefore the two class attrs got merged - we want to interpolate the result
|
||||
interpolateFn = $interpolate(attr[name], true);
|
||||
}
|
||||
}
|
||||
|
||||
// we define observers array only for interpolated attrs
|
||||
// and ignore observers for non interpolated attrs to save some memory
|
||||
attr.$$observers[name] = [];
|
||||
attr[name] = undefined;
|
||||
scope.$watch(interpolateFn, function(value) {
|
||||
attr.$set(name, value);
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@@ -911,68 +976,6 @@ function $CompileProvider($provide) {
|
||||
}
|
||||
element[0] = newNode;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set a normalized attribute on the element in a way such that all directives
|
||||
* can share the attribute. This function properly handles boolean attributes.
|
||||
* @param {string} key Normalized key. (ie ngAttribute)
|
||||
* @param {string|boolean} value The value to set. If `null` attribute will be deleted.
|
||||
* @param {string=} attrName Optional none normalized name. Defaults to key.
|
||||
*/
|
||||
function attrSetter(key, value, attrName) {
|
||||
var booleanKey = isBooleanAttr(this.$element[0], key.toLowerCase());
|
||||
|
||||
if (booleanKey) {
|
||||
value = toBoolean(value);
|
||||
this.$element.prop(key, value);
|
||||
this[key] = value;
|
||||
attrName = key = booleanKey;
|
||||
value = value ? booleanKey : undefined;
|
||||
} else {
|
||||
this[key] = value;
|
||||
}
|
||||
|
||||
// translate normalized key to actual key
|
||||
if (attrName) {
|
||||
this.$attr[key] = attrName;
|
||||
} else {
|
||||
attrName = this.$attr[key];
|
||||
if (!attrName) {
|
||||
this.$attr[key] = attrName = snake_case(key, '-');
|
||||
}
|
||||
}
|
||||
|
||||
if (value === null || value === undefined) {
|
||||
this.$element.removeAttr(attrName);
|
||||
} else {
|
||||
this.$element.attr(attrName, value);
|
||||
}
|
||||
|
||||
// fire observers
|
||||
forEach(this.$observers[key], function(fn) {
|
||||
try {
|
||||
fn(value);
|
||||
} catch (e) {
|
||||
$exceptionHandler(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Observe an interpolated attribute.
|
||||
* The observer will never be called, if given attribute is not interpolated.
|
||||
*
|
||||
* @param {string} key Normalized key. (ie ngAttribute) .
|
||||
* @param {function(*)} fn Function that will be called whenever the attribute value changes.
|
||||
*/
|
||||
function interpolatedAttrObserve(key, fn) {
|
||||
// keep only observers for interpolated attrs
|
||||
if (this.$observers[key]) {
|
||||
this.$observers[key].push(fn);
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name angular.module.ng.$controllerProvider
|
||||
* @description
|
||||
* The {@link angular.module.ng.$controller $controller service} is used by Angular to create new
|
||||
* controllers.
|
||||
*
|
||||
* This provider allows controller registration via the
|
||||
* {@link angular.module.ng.$controllerProvider#register register} method.
|
||||
*/
|
||||
function $ControllerProvider() {
|
||||
var controllers = {};
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.module.ng.$controllerProvider#register
|
||||
* @methodOf angular.module.ng.$controllerProvider
|
||||
* @param {string} name Controller name
|
||||
* @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
|
||||
* annotations in the array notation).
|
||||
*/
|
||||
this.register = function(name, constructor) {
|
||||
controllers[name] = constructor;
|
||||
};
|
||||
|
||||
|
||||
this.$get = ['$injector', '$window', function($injector, $window) {
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.module.ng.$controller
|
||||
* @requires $injector
|
||||
*
|
||||
* @param {Function|string} constructor If called with a function then it's considered to be the
|
||||
* controller constructor function. Otherwise it's considered to be a string which is used
|
||||
* to retrieve the controller constructor using the following steps:
|
||||
*
|
||||
* * check if a controller with given name is registered via `$controllerProvider`
|
||||
* * check if evaluating the string on the current scope returns a constructor
|
||||
* * check `window[constructor]` on the global `window` object
|
||||
*
|
||||
* @param {Object} locals Injection locals for Controller.
|
||||
* @return {Object} Instance of given controller.
|
||||
*
|
||||
* @description
|
||||
* `$controller` service is responsible for instantiating controllers.
|
||||
*
|
||||
* It's just simple call to {@link angular.module.AUTO.$injector $injector}, but extracted into
|
||||
* a service, so that one can override this service with {@link https://gist.github.com/1649788
|
||||
* BC version}.
|
||||
*/
|
||||
return function(constructor, locals) {
|
||||
if(isString(constructor)) {
|
||||
var name = constructor;
|
||||
constructor = controllers.hasOwnProperty(name)
|
||||
? controllers[name]
|
||||
: getter(locals.$scope, name, true) || getter($window, name, true);
|
||||
|
||||
assertArgFn(constructor, name, true);
|
||||
}
|
||||
|
||||
return $injector.instantiate(constructor, locals);
|
||||
};
|
||||
}];
|
||||
}
|
||||
@@ -130,7 +130,7 @@
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Click me to toggle: <input type="checkbox" ng-model="checked"><br/>
|
||||
<button ng-model="button" ng-disabled="{{checked}}">Button</button>
|
||||
<button ng-model="button" ng-disabled="checked">Button</button>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should toggle button', function() {
|
||||
@@ -142,7 +142,7 @@
|
||||
</doc:example>
|
||||
*
|
||||
* @element INPUT
|
||||
* @param {template} ng-disabled any string which can contain '{{}}' markup.
|
||||
* @param {string} expression Angular expression that will be evaluated.
|
||||
*/
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Check me to check both: <input type="checkbox" ng-model="master"><br/>
|
||||
<input id="checkSlave" type="checkbox" ng-checked="{{master}}">
|
||||
<input id="checkSlave" type="checkbox" ng-checked="master">
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should check both checkBoxes', function() {
|
||||
@@ -172,7 +172,7 @@
|
||||
</doc:example>
|
||||
*
|
||||
* @element INPUT
|
||||
* @param {template} ng-checked any string which can contain '{{}}' markup.
|
||||
* @param {string} expression Angular expression that will be evaluated.
|
||||
*/
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Check me check multiple: <input type="checkbox" ng-model="checked"><br/>
|
||||
<select id="select" ng-multiple="{{checked}}">
|
||||
<select id="select" ng-multiple="checked">
|
||||
<option>Misko</option>
|
||||
<option>Igor</option>
|
||||
<option>Vojta</option>
|
||||
@@ -208,7 +208,7 @@
|
||||
</doc:example>
|
||||
*
|
||||
* @element SELECT
|
||||
* @param {template} ng-multiple any string which can contain '{{}}' markup.
|
||||
* @param {string} expression Angular expression that will be evaluated.
|
||||
*/
|
||||
|
||||
|
||||
@@ -226,7 +226,7 @@
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Check me to make text readonly: <input type="checkbox" ng-model="checked"><br/>
|
||||
<input type="text" ng-readonly="{{checked}}" value="I'm Angular"/>
|
||||
<input type="text" ng-readonly="checked" value="I'm Angular"/>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should toggle readonly attr', function() {
|
||||
@@ -238,7 +238,7 @@
|
||||
</doc:example>
|
||||
*
|
||||
* @element INPUT
|
||||
* @param {template} ng-readonly any string which can contain '{{}}' markup.
|
||||
* @param {string} expression Angular expression that will be evaluated.
|
||||
*/
|
||||
|
||||
|
||||
@@ -255,35 +255,62 @@
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Check me to select: <input type="checkbox" ng-model="checked"><br/>
|
||||
Check me to select: <input type="checkbox" ng-model="selected"><br/>
|
||||
<select>
|
||||
<option>Hello!</option>
|
||||
<option id="greet" ng-selected="{{checked}}">Greetings!</option>
|
||||
<option id="greet" ng-selected="selected">Greetings!</option>
|
||||
</select>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should select Greetings!', function() {
|
||||
expect(element('.doc-example-live #greet').prop('selected')).toBeFalsy();
|
||||
input('checked').check();
|
||||
input('selected').check();
|
||||
expect(element('.doc-example-live #greet').prop('selected')).toBeTruthy();
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*
|
||||
* @element OPTION
|
||||
* @param {template} ng-selected any string which can contain '{{}}' markup.
|
||||
* @param {string} expression Angular expression that will be evaluated.
|
||||
*/
|
||||
|
||||
|
||||
function ngAttributeAliasDirective(propName, attrName) {
|
||||
ngAttributeAliasDirectives[directiveNormalize('ng-' + attrName)] = valueFn(
|
||||
function(scope, element, attr) {
|
||||
attr.$observe(directiveNormalize('ng-' + attrName), function(value) {
|
||||
attr.$set(attrName, value);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
var ngAttributeAliasDirectives = {};
|
||||
forEach(BOOLEAN_ATTR, ngAttributeAliasDirective);
|
||||
ngAttributeAliasDirective(null, 'src');
|
||||
|
||||
|
||||
// boolean attrs are evaluated
|
||||
forEach(BOOLEAN_ATTR, function(propName, attrName) {
|
||||
var normalized = directiveNormalize('ng-' + attrName);
|
||||
ngAttributeAliasDirectives[normalized] = function() {
|
||||
return {
|
||||
priority: 100,
|
||||
compile: function(tpl, attr) {
|
||||
return function(scope, element, attr) {
|
||||
attr.$$observers[attrName] = [];
|
||||
scope.$watch(attr[normalized], function(value) {
|
||||
attr.$set(attrName, value);
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
// ng-src, ng-href are interpolated
|
||||
forEach(['src', 'href'], function(attrName) {
|
||||
var normalized = directiveNormalize('ng-' + attrName);
|
||||
ngAttributeAliasDirectives[normalized] = function() {
|
||||
return {
|
||||
priority: 100,
|
||||
compile: function(tpl, attr) {
|
||||
return function(scope, element, attr) {
|
||||
attr.$$observers[attrName] = [];
|
||||
attr.$observe(normalized, function(value) {
|
||||
attr.$set(attrName, value);
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
});
|
||||
@@ -31,10 +31,11 @@ var nullFormCtrl = {
|
||||
* of `FormController`.
|
||||
*
|
||||
*/
|
||||
FormController.$inject = ['name', '$element', '$attrs'];
|
||||
function FormController(name, element, attrs) {
|
||||
//asks for $scope to fool the BC controller module
|
||||
FormController.$inject = ['$element', '$attrs', '$scope'];
|
||||
function FormController(element, attrs) {
|
||||
var form = this,
|
||||
parentForm = element.parent().inheritedData('$formController') || nullFormCtrl,
|
||||
parentForm = element.parent().controller('form') || nullFormCtrl,
|
||||
invalidCount = 0, // used to easily determine if we are valid
|
||||
errors = form.$error = {};
|
||||
|
||||
@@ -45,9 +46,6 @@ function FormController(name, element, attrs) {
|
||||
form.$valid = true;
|
||||
form.$invalid = false;
|
||||
|
||||
// publish the form into scope
|
||||
name(this);
|
||||
|
||||
parentForm.$addControl(form);
|
||||
|
||||
// Setup initial state of the control
|
||||
@@ -130,9 +128,24 @@ function FormController(name, element, attrs) {
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name angular.module.ng.$compileProvider.directive.ng-form
|
||||
* @restrict EAC
|
||||
*
|
||||
* @description
|
||||
* Nestable alias of {@link angular.module.ng.$compileProvider.directive.form `form`} directive. HTML
|
||||
* does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
|
||||
* sub-group of controls needs to be determined.
|
||||
*
|
||||
* @param {string=} ng-form|name Name of the form. If specified, the form controller will be published into
|
||||
* related scope, under this name.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name angular.module.ng.$compileProvider.directive.form
|
||||
* @restrict EA
|
||||
* @restrict E
|
||||
*
|
||||
* @description
|
||||
* Directive that instantiates
|
||||
@@ -141,12 +154,12 @@ function FormController(name, element, attrs) {
|
||||
* If `name` attribute is specified, the form controller is published onto the current scope under
|
||||
* this name.
|
||||
*
|
||||
* # Alias: `ng-form`
|
||||
* # Alias: {@link angular.module.ng.$compileProvider.directive.ng-form `ng-form`}
|
||||
*
|
||||
* In angular forms can be nested. This means that the outer form is valid when all of the child
|
||||
* forms are valid as well. However browsers do not allow nesting of `<form>` elements, for this
|
||||
* reason angular provides `<ng-form>` alias which behaves identical to `<form>` but allows
|
||||
* element nesting.
|
||||
* reason angular provides {@link angular.module.ng.$compileProvider.directive.ng-form `ng-form`} alias
|
||||
* which behaves identical to `<form>` but allows form nesting.
|
||||
*
|
||||
*
|
||||
* # CSS classes
|
||||
@@ -218,25 +231,31 @@ function FormController(name, element, attrs) {
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
var formDirectiveDev = {
|
||||
var formDirectiveDir = {
|
||||
name: 'form',
|
||||
restrict: 'E',
|
||||
inject: {
|
||||
name: 'accessor'
|
||||
},
|
||||
controller: FormController,
|
||||
compile: function() {
|
||||
return {
|
||||
pre: function(scope, formElement, attr, controller) {
|
||||
formElement.bind('submit', function(event) {
|
||||
if (!attr.action) event.preventDefault();
|
||||
});
|
||||
if (!attr.action) {
|
||||
formElement.bind('submit', function(event) {
|
||||
event.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
var parentFormCtrl = formElement.parent().inheritedData('$formController');
|
||||
var parentFormCtrl = formElement.parent().controller('form'),
|
||||
alias = attr.name || attr.ngForm;
|
||||
|
||||
if (alias) {
|
||||
scope[alias] = controller;
|
||||
}
|
||||
if (parentFormCtrl) {
|
||||
formElement.bind('$destroy', function() {
|
||||
parentFormCtrl.$removeControl(controller);
|
||||
if (attr.name) delete scope[attr.name];
|
||||
if (alias) {
|
||||
scope[alias] = undefined;
|
||||
}
|
||||
extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
|
||||
});
|
||||
}
|
||||
@@ -245,5 +264,5 @@ var formDirectiveDev = {
|
||||
}
|
||||
};
|
||||
|
||||
var formDirective = valueFn(formDirectiveDev);
|
||||
var ngFormDirective = valueFn(extend(copy(formDirectiveDev), {restrict:'EAC'}));
|
||||
var formDirective = valueFn(formDirectiveDir);
|
||||
var ngFormDirective = valueFn(extend(copy(formDirectiveDir), {restrict: 'EAC'}));
|
||||
@@ -415,7 +415,7 @@ function textInputType(scope, element, attr, ctrl) {
|
||||
|
||||
// min length validator
|
||||
if (attr.ngMinlength) {
|
||||
var minlength = parseInt(attr.ngMinlength, 10);
|
||||
var minlength = int(attr.ngMinlength);
|
||||
var minLengthValidator = function(value) {
|
||||
if (!isEmpty(value) && value.length < minlength) {
|
||||
ctrl.$setValidity('minlength', false);
|
||||
@@ -432,7 +432,7 @@ function textInputType(scope, element, attr, ctrl) {
|
||||
|
||||
// max length validator
|
||||
if (attr.ngMaxlength) {
|
||||
var maxlength = parseInt(attr.ngMaxlength, 10);
|
||||
var maxlength = int(attr.ngMaxlength);
|
||||
var maxLengthValidator = function(value) {
|
||||
if (!isEmpty(value) && value.length > maxlength) {
|
||||
ctrl.$setValidity('maxlength', false);
|
||||
@@ -558,8 +558,10 @@ function radioInputType(scope, element, attr, ctrl) {
|
||||
|
||||
ctrl.$render = function() {
|
||||
var value = attr.value;
|
||||
element[0].checked = isDefined(value) && (value == ctrl.$viewValue);
|
||||
element[0].checked = (value == ctrl.$viewValue);
|
||||
};
|
||||
|
||||
attr.$observe('value', ctrl.$render);
|
||||
}
|
||||
|
||||
function checkboxInputType(scope, element, attr, ctrl) {
|
||||
@@ -669,7 +671,7 @@ function checkboxInputType(scope, element, attr, ctrl) {
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should initialize to model', function() {
|
||||
expect(binding('user')).toEqual('{"last":"visitor","name":"guest"}');
|
||||
expect(binding('user')).toEqual('{"name":"guest","last":"visitor"}');
|
||||
expect(binding('myForm.userName.$valid')).toEqual('true');
|
||||
expect(binding('myForm.$valid')).toEqual('true');
|
||||
});
|
||||
@@ -683,7 +685,7 @@ function checkboxInputType(scope, element, attr, ctrl) {
|
||||
|
||||
it('should be valid if empty when min length is set', function() {
|
||||
input('user.last').enter('');
|
||||
expect(binding('user')).toEqual('{"last":"","name":"guest"}');
|
||||
expect(binding('user')).toEqual('{"name":"guest","last":""}');
|
||||
expect(binding('myForm.lastName.$valid')).toEqual('true');
|
||||
expect(binding('myForm.$valid')).toEqual('true');
|
||||
});
|
||||
@@ -1166,3 +1168,26 @@ var ngListDirective = function() {
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
|
||||
|
||||
var ngValueDirective = [function() {
|
||||
return {
|
||||
priority: 100,
|
||||
compile: function(tpl, attr) {
|
||||
if (CONSTANT_VALUE_REGEXP.test(attr.ngValue)) {
|
||||
return function(scope) {
|
||||
attr.$set('value', scope.$eval(attr.ngValue));
|
||||
};
|
||||
} else {
|
||||
return function(scope, elm, attr) {
|
||||
attr.$$observers.value = [];
|
||||
scope.$watch(attr.ngValue, function(value) {
|
||||
attr.$set('value', value, false);
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}];
|
||||
@@ -26,7 +26,7 @@
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="Ctrl">
|
||||
Enter name: <input type="text" ng-model="name"> <br/>
|
||||
Enter name: <input type="text" ng-model="name" ng-model-instant><br>
|
||||
Hello <span ng-bind="name"></span>!
|
||||
</div>
|
||||
</doc:source>
|
||||
@@ -122,8 +122,8 @@ var ngBindHtmlDirective = ['$sanitize', function($sanitize) {
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="Ctrl">
|
||||
Salutation: <input type="text" ng-model="salutation"><br/>
|
||||
Name: <input type="text" ng-model="name"><br/>
|
||||
Salutation: <input type="text" ng-model="salutation" ng-model-instant><br>
|
||||
Name: <input type="text" ng-model="name" ng-model-instant><br>
|
||||
<pre ng-bind-template="{{salutation}} {{name}}!"></pre>
|
||||
</div>
|
||||
</doc:source>
|
||||
@@ -153,98 +153,3 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
|
||||
});
|
||||
}
|
||||
}];
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name angular.module.ng.$compileProvider.directive.ng-bind-attr
|
||||
* @restrict A
|
||||
*
|
||||
* @description
|
||||
* The `ng-bind-attr` attribute specifies that a
|
||||
* {@link guide/dev_guide.templates.databinding databinding} should be created between a particular
|
||||
* element attribute and a given expression. Unlike `ng-bind`, the `ng-bind-attr` contains one or
|
||||
* more JSON key value pairs; each pair specifies an attribute and the
|
||||
* {@link guide/dev_guide.expressions expression} to which it will be mapped.
|
||||
*
|
||||
* Instead of writing `ng-bind-attr` statements in your HTML, you can use double-curly markup to
|
||||
* specify an <tt ng-non-bindable>{{expression}}</tt> for the value of an attribute.
|
||||
* At compile time, the attribute is translated into an
|
||||
* `<span ng-bind-attr="{attr:expression}"></span>`.
|
||||
*
|
||||
* The following HTML snippet shows how to specify `ng-bind-attr`:
|
||||
* <pre>
|
||||
* <a ng-bind-attr='{"href":"http://www.google.com/search?q={{query}}"}'>Google</a>
|
||||
* </pre>
|
||||
*
|
||||
* This is cumbersome, so as we mentioned using double-curly markup is a prefered way of creating
|
||||
* this binding:
|
||||
* <pre>
|
||||
* <a href="http://www.google.com/search?q={{query}}">Google</a>
|
||||
* </pre>
|
||||
*
|
||||
* During compilation, the template with attribute markup gets translated to the ng-bind-attr form
|
||||
* mentioned above.
|
||||
*
|
||||
* _Note_: You might want to consider using {@link angular.module.ng.$compileProvider.directive.ng-href ng-href} instead of
|
||||
* `href` if the binding is present in the main application template (`index.html`) and you want to
|
||||
* make sure that a user is not capable of clicking on raw/uncompiled link.
|
||||
*
|
||||
*
|
||||
* @element ANY
|
||||
* @param {string} ng-bind-attr one or more JSON key-value pairs representing
|
||||
* the attributes to replace with expressions. Each key matches an attribute
|
||||
* which needs to be replaced. Each value is a text template of
|
||||
* the attribute with the embedded
|
||||
* <tt ng-non-bindable>{{expression}}</tt>s. Any number of
|
||||
* key-value pairs can be specified.
|
||||
*
|
||||
* @example
|
||||
* Enter a search string in the Live Preview text box and then click "Google". The search executes instantly.
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
function Ctrl($scope) {
|
||||
$scope.query = 'AngularJS';
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="Ctrl">
|
||||
Google for:
|
||||
<input type="text" ng-model="query"/>
|
||||
<a ng-bind-attr='{"href":"http://www.google.com/search?q={{query}}"}'>
|
||||
Google
|
||||
</a> (ng-bind-attr) |
|
||||
<a href="http://www.google.com/search?q={{query}}">Google</a>
|
||||
(curly binding in attribute val)
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should check ng-bind-attr', function() {
|
||||
expect(using('.doc-example-live').element('a').attr('href')).
|
||||
toBe('http://www.google.com/search?q=AngularJS');
|
||||
using('.doc-example-live').input('query').enter('google');
|
||||
expect(using('.doc-example-live').element('a').attr('href')).
|
||||
toBe('http://www.google.com/search?q=google');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
|
||||
var ngBindAttrDirective = ['$interpolate', function($interpolate) {
|
||||
return function(scope, element, attr) {
|
||||
var lastValue = {};
|
||||
var interpolateFns = {};
|
||||
scope.$watch(function() {
|
||||
var values = scope.$eval(attr.ngBindAttr);
|
||||
for(var key in values) {
|
||||
var exp = values[key],
|
||||
fn = (interpolateFns[exp] ||
|
||||
(interpolateFns[values[key]] = $interpolate(exp))),
|
||||
value = fn(scope);
|
||||
if (lastValue[key] !== value) {
|
||||
attr.$set(key, lastValue[key] = value);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}];
|
||||
@@ -95,9 +95,9 @@
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
var ngControllerDirective = ['$controller', '$window', function($controller, $window) {
|
||||
var ngControllerDirective = [function() {
|
||||
return {
|
||||
scope: true,
|
||||
controller: '@'
|
||||
}
|
||||
};
|
||||
}];
|
||||
@@ -73,10 +73,10 @@ var ngRepeatDirective = ngDirective({
|
||||
}
|
||||
lhs = match[1];
|
||||
rhs = match[2];
|
||||
match = lhs.match(/^([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\)$/);
|
||||
match = lhs.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);
|
||||
if (!match) {
|
||||
throw Error("'item' in 'item in collection' should be identifier or (key, value) but got '" +
|
||||
keyValue + "'.");
|
||||
lhs + "'.");
|
||||
}
|
||||
valueIdent = match[3] || match[1];
|
||||
keyIdent = match[2];
|
||||
@@ -149,7 +149,8 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c
|
||||
|
||||
lastScope = current.scope = scope.$new();
|
||||
if (current.controller) {
|
||||
$controller(current.controller, {$scope: lastScope});
|
||||
element.contents().
|
||||
data('$ngControllerController', $controller(current.controller, {$scope: lastScope}));
|
||||
}
|
||||
|
||||
link(lastScope);
|
||||
@@ -174,6 +174,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
}
|
||||
|
||||
function Multiple(scope, selectElement, ctrl) {
|
||||
var lastView;
|
||||
ctrl.$render = function() {
|
||||
var items = new HashMap(ctrl.$viewValue);
|
||||
forEach(selectElement.children(), function(option) {
|
||||
@@ -181,6 +182,15 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
});
|
||||
};
|
||||
|
||||
// we have to do it on each watch since ng-model watches reference, but
|
||||
// we need to work of an array, so we need to see if anything was inserted/removed
|
||||
scope.$watch(function() {
|
||||
if (!equals(lastView, ctrl.$viewValue)) {
|
||||
lastView = copy(ctrl.$viewValue);
|
||||
ctrl.$render();
|
||||
}
|
||||
});
|
||||
|
||||
selectElement.bind('change', function() {
|
||||
scope.$apply(function() {
|
||||
var array = [];
|
||||
@@ -41,7 +41,7 @@
|
||||
{name:'Adam', phone:'555-5678'},
|
||||
{name:'Julie', phone:'555-8765'}]"></div>
|
||||
|
||||
Search: <input ng-model="searchText"/>
|
||||
Search: <input ng-model="searchText" ng-model-instant>
|
||||
<table id="searchTextResults">
|
||||
<tr><th>Name</th><th>Phone</th><tr>
|
||||
<tr ng-repeat="friend in friends | filter:searchText">
|
||||
@@ -50,9 +50,9 @@
|
||||
<tr>
|
||||
</table>
|
||||
<hr>
|
||||
Any: <input ng-model="search.$"/> <br>
|
||||
Name only <input ng-model="search.name"/><br>
|
||||
Phone only <input ng-model="search.phone"/><br>
|
||||
Any: <input ng-model="search.$" ng-model-instant> <br>
|
||||
Name only <input ng-model="search.name" ng-model-instant><br>
|
||||
Phone only <input ng-model="search.phone" ng-model-instant><br>
|
||||
<table id="searchObjResults">
|
||||
<tr><th>Name</th><th>Phone</th><tr>
|
||||
<tr ng-repeat="friend in friends | filter:search">
|
||||
@@ -23,8 +23,8 @@
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="Ctrl">
|
||||
<input type="number" ng-model="amount"/> <br/>
|
||||
default currency symbol ($): {{amount | currency}}<br/>
|
||||
<input type="number" ng-model="amount" ng-model-instant> <br>
|
||||
default currency symbol ($): {{amount | currency}}<br>
|
||||
custom currency identifier (USD$): {{amount | currency:"USD$"}}
|
||||
</div>
|
||||
</doc:source>
|
||||
@@ -74,9 +74,9 @@ function currencyFilter($locale) {
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="Ctrl">
|
||||
Enter number: <input ng-model='val'><br/>
|
||||
Default formatting: {{val | number}}<br/>
|
||||
No fractions: {{val | number:0}}<br/>
|
||||
Enter number: <input ng-model='val' ng-model-instant><br>
|
||||
Default formatting: {{val | number}}<br>
|
||||
No fractions: {{val | number:0}}<br>
|
||||
Negative number: {{-val | number:4}}
|
||||
</div>
|
||||
</doc:source>
|
||||
@@ -288,7 +288,8 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
|
||||
* (e.g. `"h o''clock"`).
|
||||
*
|
||||
* @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).
|
||||
* number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and it's
|
||||
* shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ).
|
||||
* @param {string=} format Formatting rules (see Description). If not specified,
|
||||
* `mediumDate` is used.
|
||||
* @returns {string} Formatted string or the input if input is not recognized as date/millis.
|
||||
@@ -297,11 +298,11 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
|
||||
{{1288323623006 | date:'medium'}}<br/>
|
||||
{{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/>
|
||||
{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}<br>
|
||||
<span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
|
||||
{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}<br/>
|
||||
{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}<br>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should format date', function() {
|
||||
@@ -317,6 +318,27 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
|
||||
*/
|
||||
dateFilter.$inject = ['$locale'];
|
||||
function dateFilter($locale) {
|
||||
|
||||
|
||||
var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
|
||||
function jsonStringToDate(string){
|
||||
var match;
|
||||
if (match = string.match(R_ISO8601_STR)) {
|
||||
var date = new Date(0),
|
||||
tzHour = 0,
|
||||
tzMin = 0;
|
||||
if (match[9]) {
|
||||
tzHour = int(match[9] + match[10]);
|
||||
tzMin = int(match[9] + match[11]);
|
||||
}
|
||||
date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
|
||||
date.setUTCHours(int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0));
|
||||
return date;
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
|
||||
return function(date, format) {
|
||||
var text = '',
|
||||
parts = [],
|
||||
@@ -326,7 +348,7 @@ function dateFilter($locale) {
|
||||
format = $locale.DATETIME_FORMATS[format] || format;
|
||||
if (isString(date)) {
|
||||
if (NUMBER_STRING.test(date)) {
|
||||
date = parseInt(date, 10);
|
||||
date = int(date);
|
||||
} else {
|
||||
date = jsonStringToDate(date);
|
||||
}
|
||||
@@ -385,7 +407,7 @@ function dateFilter($locale) {
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should jsonify filtered objects', function() {
|
||||
expect(binding("{'name':'value'}")).toBe('{\n "name":"value"}');
|
||||
expect(binding("{'name':'value'}")).toMatch(/\{\n "name": ?"value"\n}/);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
@@ -446,7 +468,7 @@ var uppercaseFilter = valueFn(uppercase);
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="Ctrl">
|
||||
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
|
||||
Snippet: <textarea ng-model="snippet" ng-model-instant cols="60" rows="3"></textarea>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Filter</td>
|
||||
@@ -456,7 +478,7 @@ var uppercaseFilter = valueFn(uppercase);
|
||||
<tr id="linky-filter">
|
||||
<td>linky filter</td>
|
||||
<td>
|
||||
<pre><div ng-bind-html="snippet | linky"><br/></div></pre>
|
||||
<pre><div ng-bind-html="snippet | linky"><br></div></pre>
|
||||
</td>
|
||||
<td>
|
||||
<div ng-bind-html="snippet | linky"></div>
|
||||
@@ -464,7 +486,7 @@ var uppercaseFilter = valueFn(uppercase);
|
||||
</tr>
|
||||
<tr id="escaped-html">
|
||||
<td>no filter</td>
|
||||
<td><pre><div ng-bind="snippet"><br/></div></pre></td>
|
||||
<td><pre><div ng-bind="snippet"><br></div></pre></td>
|
||||
<td><div ng-bind="snippet"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -31,24 +31,24 @@
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="Ctrl">
|
||||
Limit {{numbers}} to: <input type="integer" ng-model="limit"/>
|
||||
<p>Output: {{ numbers | limitTo:limit | json }}</p>
|
||||
Limit {{numbers}} to: <input type="integer" ng-model="limit" ng-model-instant>
|
||||
<p>Output: {{ numbers | limitTo:limit }}</p>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should limit the numer array to first three items', function() {
|
||||
expect(element('.doc-example-live input[ng-model=limit]').val()).toBe('3');
|
||||
expect(binding('numbers | limitTo:limit | json')).toEqual('[1,2,3]');
|
||||
expect(binding('numbers | limitTo:limit')).toEqual('[1,2,3]');
|
||||
});
|
||||
|
||||
it('should update the output when -3 is entered', function() {
|
||||
input('limit').enter(-3);
|
||||
expect(binding('numbers | limitTo:limit | json')).toEqual('[7,8,9]');
|
||||
expect(binding('numbers | limitTo:limit')).toEqual('[7,8,9]');
|
||||
});
|
||||
|
||||
it('should not exceed the maximum size of input array', function() {
|
||||
input('limit').enter(100);
|
||||
expect(binding('numbers | limitTo:limit | json')).toEqual('[1,2,3,4,5,6,7,8,9]');
|
||||
expect(binding('numbers | limitTo:limit')).toEqual('[1,2,3,4,5,6,7,8,9]');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
@@ -56,7 +56,7 @@
|
||||
function limitToFilter(){
|
||||
return function(array, limit) {
|
||||
if (!(array instanceof Array)) return array;
|
||||
limit = parseInt(limit, 10);
|
||||
limit = int(limit);
|
||||
var out = [],
|
||||
i, n;
|
||||
|
||||
@@ -90,7 +90,7 @@ function $HttpProvider() {
|
||||
|
||||
var $config = this.defaults = {
|
||||
// transform incoming response data
|
||||
transformResponse: function(data) {
|
||||
transformResponse: [function(data) {
|
||||
if (isString(data)) {
|
||||
// strip json vulnerability protection prefix
|
||||
data = data.replace(PROTECTION_PREFIX, '');
|
||||
@@ -98,12 +98,12 @@ function $HttpProvider() {
|
||||
data = fromJson(data, true);
|
||||
}
|
||||
return data;
|
||||
},
|
||||
}],
|
||||
|
||||
// transform outgoing request data
|
||||
transformRequest: function(d) {
|
||||
transformRequest: [function(d) {
|
||||
return isObject(d) && !isFile(d) ? toJson(d) : d;
|
||||
},
|
||||
}],
|
||||
|
||||
// default headers
|
||||
headers: {
|
||||
@@ -151,7 +151,7 @@ function $HttpProvider() {
|
||||
* For unit testing applications that use `$http` service, see
|
||||
* {@link angular.module.ngMock.$httpBackend $httpBackend mock}.
|
||||
*
|
||||
* For a higher level of abstraction, please check out the {@link angular.module.ng.$resource
|
||||
* For a higher level of abstraction, please check out the {@link angular.module.ngResource.$resource
|
||||
* $resource} service.
|
||||
*
|
||||
* The $http API is based on the {@link angular.module.ng.$q deferred/promise APIs} exposed by
|
||||
@@ -360,6 +360,8 @@ function $HttpProvider() {
|
||||
*
|
||||
* - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
|
||||
* - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
|
||||
* - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be turned to
|
||||
* `?key1=value1&key2=value2` after the url. If the value is not a string, it will be JSONified.
|
||||
* - **data** – `{string|Object}` – Data to be sent as the request message data.
|
||||
* - **headers** – `{Object}` – Map of strings representing HTTP headers to send to the server.
|
||||
* - **transformRequest** – `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
|
||||
@@ -470,6 +472,10 @@ function $HttpProvider() {
|
||||
reqData = transformData(config.data, headersGetter(reqHeaders), reqTransformFn),
|
||||
promise;
|
||||
|
||||
// strip content-type if data is undefined
|
||||
if (isUndefined(config.data)) {
|
||||
delete reqHeaders['Content-Type'];
|
||||
}
|
||||
|
||||
// send request
|
||||
promise = sendReq(config, reqData, reqHeaders);
|
||||
@@ -634,7 +640,8 @@ function $HttpProvider() {
|
||||
var deferred = $q.defer(),
|
||||
promise = deferred.promise,
|
||||
cache,
|
||||
cachedResp;
|
||||
cachedResp,
|
||||
url = buildUrl(config.url, config.params);
|
||||
|
||||
$http.pendingRequests.push(config);
|
||||
promise.then(removePendingReq, removePendingReq);
|
||||
@@ -645,7 +652,7 @@ function $HttpProvider() {
|
||||
}
|
||||
|
||||
if (cache) {
|
||||
cachedResp = cache.get(config.url);
|
||||
cachedResp = cache.get(url);
|
||||
if (cachedResp) {
|
||||
if (cachedResp.then) {
|
||||
// cached request has already been sent, but there is no response yet
|
||||
@@ -661,13 +668,13 @@ function $HttpProvider() {
|
||||
}
|
||||
} else {
|
||||
// put the promise for the non-transformed response into cache as a placeholder
|
||||
cache.put(config.url, promise);
|
||||
cache.put(url, promise);
|
||||
}
|
||||
}
|
||||
|
||||
// if we won't have the response in cache, send the request to the backend
|
||||
if (!cachedResp) {
|
||||
$httpBackend(config.method, config.url, reqData, done, reqHeaders, config.timeout);
|
||||
$httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout);
|
||||
}
|
||||
|
||||
return promise;
|
||||
@@ -682,10 +689,10 @@ function $HttpProvider() {
|
||||
function done(status, response, headersString) {
|
||||
if (cache) {
|
||||
if (isSuccess(status)) {
|
||||
cache.put(config.url, [status, response, parseHeaders(headersString)]);
|
||||
cache.put(url, [status, response, parseHeaders(headersString)]);
|
||||
} else {
|
||||
// remove promise from the cache
|
||||
cache.remove(config.url);
|
||||
cache.remove(url);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -715,5 +722,21 @@ function $HttpProvider() {
|
||||
if (idx !== -1) $http.pendingRequests.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function buildUrl(url, params) {
|
||||
if (!params) return url;
|
||||
var parts = [];
|
||||
forEachSorted(params, function(value, key) {
|
||||
if (value == null || value == undefined) return;
|
||||
if (isObject(value)) {
|
||||
value = toJson(value);
|
||||
}
|
||||
parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
|
||||
});
|
||||
return url + ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
|
||||
}
|
||||
|
||||
|
||||
}];
|
||||
}
|
||||
@@ -18,7 +18,7 @@ var XHR = window.XMLHttpRequest || function() {
|
||||
* XMLHttpRequest object or JSONP and deals with browser incompatibilities.
|
||||
*
|
||||
* You should never need to use this service directly, instead use the higher-level abstractions:
|
||||
* {@link angular.module.ng.$http $http} or {@link angular.module.ng.$resource $resource}.
|
||||
* {@link angular.module.ng.$http $http} or {@link angular.module.ngResource.$resource $resource}.
|
||||
*
|
||||
* During testing this implementation is swapped with {@link angular.module.ngMock.$httpBackend mock
|
||||
* $httpBackend} which can be trained with responses.
|
||||
@@ -30,7 +30,7 @@ function matchUrl(url, obj) {
|
||||
match = {
|
||||
protocol: match[1],
|
||||
host: match[3],
|
||||
port: parseInt(match[5], 10) || DEFAULT_PORTS[match[1]] || null,
|
||||
port: int(match[5]) || DEFAULT_PORTS[match[1]] || null,
|
||||
path: match[6] || '/',
|
||||
search: match[8],
|
||||
hash: match[10]
|
||||
@@ -82,8 +82,9 @@ function $LogProvider(){
|
||||
function formatError(arg) {
|
||||
if (arg instanceof Error) {
|
||||
if (arg.stack) {
|
||||
arg = (arg.message && arg.stack.indexOf(arg.message) === -1) ?
|
||||
'Error: ' + arg.message + '\n' + arg.stack : arg.stack;
|
||||
arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
|
||||
? 'Error: ' + arg.message + '\n' + arg.stack
|
||||
: arg.stack;
|
||||
} else if (arg.sourceURL) {
|
||||
arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
|
||||
}
|
||||
@@ -92,19 +93,23 @@ function $LogProvider(){
|
||||
}
|
||||
|
||||
function consoleLog(type) {
|
||||
var console = $window.console || {};
|
||||
var logFn = console[type] || console.log || noop;
|
||||
var console = $window.console || {},
|
||||
logFn = console[type] || console.log || noop;
|
||||
|
||||
if (logFn.apply) {
|
||||
return function() {
|
||||
var args = [];
|
||||
forEach(arguments, function(arg){
|
||||
forEach(arguments, function(arg) {
|
||||
args.push(formatError(arg));
|
||||
});
|
||||
return logFn.apply(console, args);
|
||||
};
|
||||
} else {
|
||||
// we are IE, in which case there is nothing we can do
|
||||
return logFn;
|
||||
}
|
||||
|
||||
// we are IE which either doesn't have window.console => this is noop and we do nothing,
|
||||
// or we are IE where console.log doesn't have apply so we log at least first 2 args
|
||||
return function(arg1, arg2) {
|
||||
logFn(arg1, arg2);
|
||||
}
|
||||
}
|
||||
}];
|
||||
@@ -752,9 +752,3 @@ function $ParseProvider() {
|
||||
};
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
// This is a special access for JSON parser which bypasses the injector
|
||||
var parseJson = function(json) {
|
||||
return parser(json, true);
|
||||
};
|
||||
@@ -364,16 +364,20 @@ function qFactory(nextTick, exceptionHandler) {
|
||||
counter = promises.length,
|
||||
results = [];
|
||||
|
||||
forEach(promises, function(promise, index) {
|
||||
promise.then(function(value) {
|
||||
if (index in results) return;
|
||||
results[index] = value;
|
||||
if (!(--counter)) deferred.resolve(results);
|
||||
}, function(reason) {
|
||||
if (index in results) return;
|
||||
deferred.reject(reason);
|
||||
if (counter) {
|
||||
forEach(promises, function(promise, index) {
|
||||
ref(promise).then(function(value) {
|
||||
if (index in results) return;
|
||||
results[index] = value;
|
||||
if (!(--counter)) deferred.resolve(results);
|
||||
}, function(reason) {
|
||||
if (index in results) return;
|
||||
deferred.reject(reason);
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
deferred.resolve(results);
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
@@ -437,6 +437,17 @@ function $RootScopeProvider(){
|
||||
this.$root.$$phase = null;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name angular.module.$rootScope.Scope#$destroy
|
||||
* @eventOf angular.module.ng.$rootScope.Scope
|
||||
* @eventType broadcast on scope being destroyed
|
||||
*
|
||||
* @description
|
||||
* Broadcasted when a scope and its children are being destroyed.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.module.ng.$rootScope.Scope#$destroy
|
||||
@@ -445,13 +456,17 @@ function $RootScopeProvider(){
|
||||
*
|
||||
* @description
|
||||
* Remove the current scope (and all of its children) from the parent scope. Removal implies
|
||||
* that calls to {@link angular.module.ng.$rootScope.Scope#$digest $digest()} will no longer propagate to the current
|
||||
* scope and its children. Removal also implies that the current scope is eligible for garbage
|
||||
* collection.
|
||||
* that calls to {@link angular.module.ng.$rootScope.Scope#$digest $digest()} will no longer
|
||||
* propagate to the current scope and its children. Removal also implies that the current
|
||||
* scope is eligible for garbage collection.
|
||||
*
|
||||
* The `$destroy()` is usually used by directives such as
|
||||
* {@link angular.module.ng.$compileProvider.directive.ng-repeat ng-repeat} for managing the unrolling of the loop.
|
||||
* {@link angular.module.ng.$compileProvider.directive.ng-repeat ng-repeat} for managing the
|
||||
* unrolling of the loop.
|
||||
*
|
||||
* Just before a scope is destroyed a `$destroy` event is broadcasted on this scope.
|
||||
* Application code can register a `$destroy` event handler that will give it chance to
|
||||
* perform any necessary cleanup.
|
||||
*/
|
||||
$destroy: function() {
|
||||
if (this.$root == this) return; // we can't remove the root node;
|
||||
@@ -488,12 +503,13 @@ function $RootScopeProvider(){
|
||||
* @param {(string|function())=} expression An angular expression to be executed.
|
||||
*
|
||||
* - `string`: execute using the rules as defined in {@link guide/dev_guide.expressions expression}.
|
||||
* - `function(scope)`: execute the function with the current `scope` parameter.
|
||||
* - `function(scope, locals)`: execute the function with the current `scope` parameter.
|
||||
* @param {Object=} locals Hash object of local variables for the expression.
|
||||
*
|
||||
* @returns {*} The result of evaluating the expression.
|
||||
*/
|
||||
$eval: function(expr) {
|
||||
return $parse(expr)(this);
|
||||
$eval: function(expr, locals) {
|
||||
return $parse(expr)(this, locals);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -18,7 +18,10 @@ function $RouteProvider(){
|
||||
* @name angular.module.ng.$routeProvider#when
|
||||
* @methodOf angular.module.ng.$routeProvider
|
||||
*
|
||||
* @param {string} path Route path (matched against `$location.hash`)
|
||||
* @param {string} path Route path (matched against `$location.path`). If `$location.path`
|
||||
* contains redudant trailing slash or is missing one, the route will still match and the
|
||||
* `$location.path` will be updated to add or drop the trailing slash to exacly match the
|
||||
* route definition.
|
||||
* @param {Object} route Mapping information to be assigned to `$route.current` on route
|
||||
* match.
|
||||
*
|
||||
@@ -57,6 +60,16 @@ function $RouteProvider(){
|
||||
var routeDef = routes[path];
|
||||
if (!routeDef) routeDef = routes[path] = {reloadOnSearch: true};
|
||||
if (route) extend(routeDef, route); // TODO(im): what the heck? merge two route definitions?
|
||||
|
||||
// create redirection for trailing slashes
|
||||
if (path) {
|
||||
var redirectPath = (path[path.length-1] == '/')
|
||||
? path.substr(0, path.length-1)
|
||||
: path +'/';
|
||||
|
||||
routes[redirectPath] = {redirectTo: path};
|
||||
}
|
||||
|
||||
return routeDef;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
* !!! This is an undocumented "private" service !!!
|
||||
*
|
||||
* @name angular.module.ng.$sniffer
|
||||
* @requires $window
|
||||
*
|
||||
@@ -13,8 +14,6 @@
|
||||
*/
|
||||
function $SnifferProvider(){
|
||||
this.$get = ['$window', function($window){
|
||||
if ($window.Modernizr) return $window.Modernizr;
|
||||
|
||||
return {
|
||||
history: !!($window.history && $window.history.pushState),
|
||||
hashchange: 'onhashchange' in $window &&
|
||||
@@ -0,0 +1,163 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name angular.module.ngCookies
|
||||
*/
|
||||
|
||||
|
||||
angular.module('ngCookies', ['ng']).
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name angular.module.ng.$cookies
|
||||
* @requires $browser
|
||||
*
|
||||
* @description
|
||||
* Provides read/write access to browser's cookies.
|
||||
*
|
||||
* Only a simple Object is exposed and by adding or removing properties to/from
|
||||
* this object, new cookies are created/deleted at the end of current $eval.
|
||||
*
|
||||
* @example
|
||||
*/
|
||||
factory('$cookies', ['$rootScope', '$browser', function ($rootScope, $browser) {
|
||||
var cookies = {},
|
||||
lastCookies = {},
|
||||
lastBrowserCookies,
|
||||
runEval = false,
|
||||
copy = angular.copy,
|
||||
isUndefined = angular.isUndefined;
|
||||
|
||||
//creates a poller fn that copies all cookies from the $browser to service & inits the service
|
||||
$browser.addPollFn(function() {
|
||||
var currentCookies = $browser.cookies();
|
||||
if (lastBrowserCookies != currentCookies) { //relies on browser.cookies() impl
|
||||
lastBrowserCookies = currentCookies;
|
||||
copy(currentCookies, lastCookies);
|
||||
copy(currentCookies, cookies);
|
||||
if (runEval) $rootScope.$apply();
|
||||
}
|
||||
})();
|
||||
|
||||
runEval = true;
|
||||
|
||||
//at the end of each eval, push cookies
|
||||
//TODO: this should happen before the "delayed" watches fire, because if some cookies are not
|
||||
// strings or browser refuses to store some cookies, we update the model in the push fn.
|
||||
$rootScope.$watch(push);
|
||||
|
||||
return cookies;
|
||||
|
||||
|
||||
/**
|
||||
* Pushes all the cookies from the service to the browser and verifies if all cookies were stored.
|
||||
*/
|
||||
function push() {
|
||||
var name,
|
||||
value,
|
||||
browserCookies,
|
||||
updated;
|
||||
|
||||
//delete any cookies deleted in $cookies
|
||||
for (name in lastCookies) {
|
||||
if (isUndefined(cookies[name])) {
|
||||
$browser.cookies(name, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
//update all cookies updated in $cookies
|
||||
for(name in cookies) {
|
||||
value = cookies[name];
|
||||
if (!angular.isString(value)) {
|
||||
if (angular.isDefined(lastCookies[name])) {
|
||||
cookies[name] = lastCookies[name];
|
||||
} else {
|
||||
delete cookies[name];
|
||||
}
|
||||
} else if (value !== lastCookies[name]) {
|
||||
$browser.cookies(name, value);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
//verify what was actually stored
|
||||
if (updated){
|
||||
updated = false;
|
||||
browserCookies = $browser.cookies();
|
||||
|
||||
for (name in cookies) {
|
||||
if (cookies[name] !== browserCookies[name]) {
|
||||
//delete or reset all cookies that the browser dropped from $cookies
|
||||
if (isUndefined(browserCookies[name])) {
|
||||
delete cookies[name];
|
||||
} else {
|
||||
cookies[name] = browserCookies[name];
|
||||
}
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}]).
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name angular.module.ng.$cookieStore
|
||||
* @requires $cookies
|
||||
*
|
||||
* @description
|
||||
* Provides a key-value (string-object) storage, that is backed by session cookies.
|
||||
* Objects put or retrieved from this storage are automatically serialized or
|
||||
* deserialized by angular's toJson/fromJson.
|
||||
* @example
|
||||
*/
|
||||
factory('$cookieStore', ['$cookies', function($cookies) {
|
||||
|
||||
return {
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.module.ng.$cookieStore#get
|
||||
* @methodOf angular.module.ng.$cookieStore
|
||||
*
|
||||
* @description
|
||||
* Returns the value of given cookie key
|
||||
*
|
||||
* @param {string} key Id to use for lookup.
|
||||
* @returns {Object} Deserialized cookie value.
|
||||
*/
|
||||
get: function(key) {
|
||||
return angular.fromJson($cookies[key]);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.module.ng.$cookieStore#put
|
||||
* @methodOf angular.module.ng.$cookieStore
|
||||
*
|
||||
* @description
|
||||
* Sets a value for given cookie key
|
||||
*
|
||||
* @param {string} key Id for the `value`.
|
||||
* @param {Object} value Value to be stored.
|
||||
*/
|
||||
put: function(key, value) {
|
||||
$cookies[key] = angular.toJson(value);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.module.ng.$cookieStore#remove
|
||||
* @methodOf angular.module.ng.$cookieStore
|
||||
*
|
||||
* @description
|
||||
* Remove given cookie
|
||||
*
|
||||
* @param {string} key Id of the key-value pair to delete.
|
||||
*/
|
||||
remove: function(key) {
|
||||
delete $cookies[key];
|
||||
}
|
||||
};
|
||||
|
||||
}]);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user