Compare commits
151 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5e3ee0a99b | |||
| 308a59bf44 | |||
| 19f1801379 | |||
| 303df9dafe | |||
| fec4ef3881 | |||
| ecdf119a76 | |||
| 820253f670 | |||
| 6bca948323 | |||
| 556f9cc35e | |||
| 2845dd1590 | |||
| 0b6f1ce5f8 | |||
| 4bfb66ce0b | |||
| 85c31e0688 | |||
| c2e215fab6 | |||
| 61f2767ce6 | |||
| 5eb968553a | |||
| 04cc1d2890 | |||
| 7d4a3210f0 | |||
| 8dca0561e8 | |||
| 53abd3fba7 | |||
| 06ada222c2 | |||
| 9480136d9f | |||
| 4ae46814ff | |||
| 5c735eb4ab | |||
| 364e597499 | |||
| 23abb990f1 | |||
| 0c72708a2b | |||
| fec2909f3a | |||
| 4efda14b49 | |||
| 821d2fddb7 | |||
| ef76afdf80 | |||
| 564963dc27 | |||
| fe4f0ea262 | |||
| 3a81dd8bdd | |||
| df9bff13b2 | |||
| f197e391c1 | |||
| 1c1cd4fdf6 | |||
| 79049b9fee | |||
| 9428338e97 | |||
| 707c65d5a2 | |||
| d1b49e25f1 | |||
| 5fd39e050b | |||
| 9befe37014 | |||
| e88d6179c3 | |||
| 90ba9aadc6 | |||
| 96b13bbee1 | |||
| a248d5a32d | |||
| 69ef17cce9 | |||
| f20646bce5 | |||
| 65e57a7c3d | |||
| 6f71e80914 | |||
| cb5ce981fb | |||
| 99f3b70b2d | |||
| 603fe0d196 | |||
| 485f104099 | |||
| d38d8448e8 | |||
| 8a96393179 | |||
| 49128cc100 | |||
| 79b51d5b57 | |||
| fe8d893b83 | |||
| eb53423a41 | |||
| 5e18a15fb0 | |||
| 60f1f099fc | |||
| cf17c6af47 | |||
| 86d191ed4a | |||
| c38c1c5030 | |||
| d4fe383b7b | |||
| 6397860831 | |||
| 398691beb3 | |||
| 7ddbde8c1c | |||
| 753fc9e58d | |||
| 6a612df7de | |||
| ac899d0da5 | |||
| 30162b769c | |||
| 7d18d0ae26 | |||
| e27bb6eb13 | |||
| 7b236b29aa | |||
| c36933d38f | |||
| bec614fd90 | |||
| 509ec745fd | |||
| d44ca19da7 | |||
| 2508b47c1a | |||
| de6d4ca1da | |||
| 776dfc678b | |||
| 9532234bf1 | |||
| 5f5d4feadb | |||
| 791804bdbf | |||
| 7eafbb98c6 | |||
| bb8448c011 | |||
| 2ed53087d7 | |||
| 0af172040e | |||
| e19b04c9ec | |||
| 37e8b12265 | |||
| 1ace5eb396 | |||
| 3c2aee01b0 | |||
| 37bdcc984a | |||
| 027f20be1f | |||
| 9b7c1d0f7c | |||
| 5548328b67 | |||
| 7c6b1e06e8 | |||
| 288b69a314 | |||
| 2a2123441c | |||
| 1d7a95df56 | |||
| 1ed638582d | |||
| 3b14092135 | |||
| ef268196b9 | |||
| 12ba6cec4f | |||
| b7e1fb0515 | |||
| 755beb2b66 | |||
| 6d70ff5c8d | |||
| ace54ff08c | |||
| f5835963d5 | |||
| f3231b9447 | |||
| a8a3efb5f0 | |||
| e47f8d2b96 | |||
| dba6bc73e8 | |||
| c0a0781425 | |||
| 035e0130f3 | |||
| 9e57ce0c7a | |||
| 42a5033c56 | |||
| 6b19e7d527 | |||
| 28273b7f1e | |||
| ec54712ff3 | |||
| 23dd78f8a4 | |||
| d46fe3c23f | |||
| 92ca7efaa4 | |||
| 7090924515 | |||
| df744f3af4 | |||
| 8155c3a29e | |||
| b001c8ece5 | |||
| bec4435945 | |||
| 6fb1054ce6 | |||
| a83eced974 | |||
| 7cc4063303 | |||
| d8e242418d | |||
| 6518369f25 | |||
| 649b892205 | |||
| e0295cfec4 | |||
| 17fc6a70fe | |||
| 5d0f9ce4c7 | |||
| 250aec71f3 | |||
| 3b317c5dcb | |||
| e4cfb9d938 | |||
| 87ba8221ec | |||
| e34519e93b | |||
| 69be39fccf | |||
| deac80a6e8 | |||
| 0539611bac | |||
| d2177ae312 | |||
| f3bff27460 | |||
| 4df45b20d4 |
+4
-4
@@ -5,9 +5,9 @@ node_js:
|
||||
before_script:
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
- npm install -g testacular@canary
|
||||
- rake package
|
||||
- ./nodeserver.sh > /dev/null &
|
||||
- npm install -g grunt-cli
|
||||
- grunt package
|
||||
- grunt webserver > /dev/null &
|
||||
|
||||
script:
|
||||
- rake test[Firefox,"--reporters=dots"]
|
||||
- grunt test --browsers Firefox --reporters=dots
|
||||
|
||||
+122
-2
@@ -1,3 +1,123 @@
|
||||
<a name="1.1.3"></a>
|
||||
# 1.1.3 radioactive-gargle (2013-02-20)
|
||||
|
||||
_Note: 1.1.x releases are [considered unstable](http://blog.angularjs.org/2012/07/angularjs-10-12-roadmap.html).
|
||||
They pass all tests but we reserve the right to change new features/apis in between minor releases. Check them
|
||||
out and please give us feedback._
|
||||
|
||||
_Note: This release also contains all bug fixes available in [1.0.5](#1.0.5)._
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- initialize interpolated attributes before directive linking
|
||||
([bb8448c0](https://github.com/angular/angular.js/commit/bb8448c011127306df08c7479b66e5afe7a0fa94))
|
||||
- interpolate @ locals before the link function runs
|
||||
([2ed53087](https://github.com/angular/angular.js/commit/2ed53087d7dd06d728e333a449265f7685275548))
|
||||
- **$http:**
|
||||
- do not encode special characters `@$:,` in params
|
||||
([288b69a3](https://github.com/angular/angular.js/commit/288b69a314e9bd14458b6647532eb62aad5c5cdf))
|
||||
- **$resource:**
|
||||
- params should expand array values properly
|
||||
([2a212344](https://github.com/angular/angular.js/commit/2a2123441c2b749b8f316a24c3ca3f77a9132a01))
|
||||
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$http:** allow overriding the XSRF header and cookie name
|
||||
([8155c3a2](https://github.com/angular/angular.js/commit/8155c3a29ea0eb14806913b8ac08ba7727e1969c))
|
||||
- **$parse:** added `constant` and `literal` properties
|
||||
([1ed63858](https://github.com/angular/angular.js/commit/1ed638582d2f2c7f89384d9712f4cfac52cc5b70))
|
||||
- **$resource:** expose promise based api via $then and $resolved
|
||||
([dba6bc73](https://github.com/angular/angular.js/commit/dba6bc73e802fdae685a9f351d3e23c7efa8568a))
|
||||
- **$routeProvider:** add support to catch-all parameters in routes
|
||||
([7eafbb98](https://github.com/angular/angular.js/commit/7eafbb98c64c0dc079d7d3ec589f1270b7f6fea5))
|
||||
- **Scope:**
|
||||
- expose transcluded and isolate scope info for batarang
|
||||
([649b8922](https://github.com/angular/angular.js/commit/649b892205615a144dafff9984c0e6ab10ed341d))
|
||||
- only evaluate constant $watch expressions once
|
||||
([1d7a95df](https://github.com/angular/angular.js/commit/1d7a95df565192fc02a18b0b297b39dd615eaeb5))
|
||||
- **angular.noConflict:** added api to restore previous angular namespace reference
|
||||
([12ba6cec](https://github.com/angular/angular.js/commit/12ba6cec4fb79521101744e02a7e09f9fbb591c4))
|
||||
- **Directives:**
|
||||
- **ngSwitch:** support multiple matches on ngSwitchWhen and ngSwitchDefault
|
||||
([0af17204](https://github.com/angular/angular.js/commit/0af172040e03811c59d01682968241e3df226774),
|
||||
[#1074](https://github.com/angular/angular.js/issues/1074))
|
||||
- **Filters:**
|
||||
- **date:** add `[.,]sss` formatter for milliseconds
|
||||
([df744f3a](https://github.com/angular/angular.js/commit/df744f3af46fc227a934f16cb63c7a6038e7133b))
|
||||
- **filter:** add comparison function to filter
|
||||
([ace54ff0](https://github.com/angular/angular.js/commit/ace54ff08c4593195b49eadb04d258e6409d969e))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **$http:** due to [288b69a3](https://github.com/angular/angular.js/commit/288b69a314e9bd14458b6647532eb62aad5c5cdf),
|
||||
$http now follows RFC3986 and does not encode special characters like `$@,:` in params.
|
||||
If your application needs to encode these characters, encode them manually, before sending the request.
|
||||
- **$resource:** due to [2a212344](https://github.com/angular/angular.js/commit/2a2123441c2b749b8f316a24c3ca3f77a9132a01),
|
||||
if the server relied on the buggy behavior of serializing arrays as http query arguments then
|
||||
either the backend should be fixed or a simple serialization of the array should be done
|
||||
on the client before calling the resource service.
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.5"></a>
|
||||
# 1.0.5 flatulent-propulsion (2013-02-20)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- sanitize values bound to `a[href]`
|
||||
([9532234b](https://github.com/angular/angular.js/commit/9532234bf1c408af9a6fd2c4743fdb585b920531))
|
||||
- rename $compileNote to compileNode
|
||||
([92ca7efa](https://github.com/angular/angular.js/commit/92ca7efaa4bc4f37da3008b234e19343a1fa4207),
|
||||
[#1941](https://github.com/angular/angular.js/issues/1941))
|
||||
- should not leak memory when there are top level empty text nodes
|
||||
([791804bd](https://github.com/angular/angular.js/commit/791804bdbfa6da7a39283623bd05628a01cd8720))
|
||||
- allow startingTag method to handle text / comment nodes
|
||||
([755beb2b](https://github.com/angular/angular.js/commit/755beb2b66ce9f9f9a218f2355bbaf96d94fbc15))
|
||||
- **$cookies:** set cookies on Safari&IE when `base[href]` is undefined
|
||||
([70909245](https://github.com/angular/angular.js/commit/7090924515214752b919b0c5630b3ea5e7c77223),
|
||||
[#1190](https://github.com/angular/angular.js/issues/1190))
|
||||
- **$http:**
|
||||
- patch for Firefox bug w/ CORS and response headers
|
||||
([e19b04c9](https://github.com/angular/angular.js/commit/e19b04c9ec985821edf1269c628cfa261f81d631),
|
||||
[#1468](https://github.com/angular/angular.js/issues/1468))
|
||||
- **$resource:**
|
||||
- update RegExp to allow urlParams with out leading slash
|
||||
([b7e1fb05](https://github.com/angular/angular.js/commit/b7e1fb0515798e1b4f3f2426f6b050951bee2617))
|
||||
- **Directives:**
|
||||
- **a:** workaround IE bug affecting mailto urls
|
||||
([37e8b122](https://github.com/angular/angular.js/commit/37e8b12265291918396bfee65d444a8f63697b73),
|
||||
[#1949](https://github.com/angular/angular.js/issues/1949))
|
||||
- **ngClass:** keep track of old ngClass value manually
|
||||
([5f5d4fea](https://github.com/angular/angular.js/commit/5f5d4feadbfa9d8ecc8150041dfd2bca2b2e9fea),
|
||||
[#1637](https://github.com/angular/angular.js/issues/1637))
|
||||
- **ngSwitch:** make ngSwitch compatible with controller backwards-compatiblity module
|
||||
([9b7c1d0f](https://github.com/angular/angular.js/commit/9b7c1d0f7ce442d4ad2ec587e66d2d335e64fa4e))
|
||||
- **Filters:**
|
||||
- **date:** invert timezone sign and always display sign
|
||||
([b001c8ec](https://github.com/angular/angular.js/commit/b001c8ece5472626bf49cf82753e8ac1aafd2513),
|
||||
[#1261](https://github.com/angular/angular.js/issues/1261))
|
||||
- **number:** fix formatting when "0" passed as fractionSize
|
||||
([f5835963](https://github.com/angular/angular.js/commit/f5835963d5982003a713dd354eefd376ed39ac02))
|
||||
- **scenario runner:** include error messages in XML output
|
||||
([d46fe3c2](https://github.com/angular/angular.js/commit/d46fe3c23fa269dcc10249148f2af14f3db6b066))
|
||||
- **Misc:**
|
||||
- don't use instanceof to detect arrays
|
||||
([3c2aee01](https://github.com/angular/angular.js/commit/3c2aee01b0b299995eb92f4255159585b0f53c10),
|
||||
[#1966](https://github.com/angular/angular.js/issues/1966))
|
||||
- angular.forEach should correctly iterate over objects with length prop
|
||||
([ec54712f](https://github.com/angular/angular.js/commit/ec54712ff3dab1ade44f94fa82d67edeffa79a1d),
|
||||
[#1840](https://github.com/angular/angular.js/issues/1840))
|
||||
|
||||
|
||||
|
||||
<a name="1.1.2"></a>
|
||||
# 1.1.2 tofu-animation (2013-01-22)
|
||||
|
||||
@@ -73,6 +193,8 @@ _Note: This release also contains all bug fixes available in [1.0.4](#1.0.4)._
|
||||
- HTTP method should be case-insensitive
|
||||
([8991680d](https://github.com/angular/angular.js/commit/8991680d8ab632dda60cd70c780868c803c74509),
|
||||
[#1403](https://github.com/angular/angular.js/issues/1403))
|
||||
- correct leading slash removal in resource URLs
|
||||
([b2f46251](https://github.com/angular/angular.js/commit/b2f46251aca76c8568ee7d4bab54edbc9d7a186a))
|
||||
- **$route:**
|
||||
- support route params not separated with slashes.
|
||||
([c6392616](https://github.com/angular/angular.js/commit/c6392616ea5245bd0d2f77dded0b948d9e2637c8))
|
||||
@@ -100,8 +222,6 @@ _Note: This release also contains all bug fixes available in [1.0.4](#1.0.4)._
|
||||
- **ngRepeat:** correctly apply $last if repeating over object
|
||||
([7e746015](https://github.com/angular/angular.js/commit/7e746015ea7dec3e9eb81bc4678fa9b6a83bc47c),
|
||||
[#1789](https://github.com/angular/angular.js/issues/1789))
|
||||
- **ngResource:** correct leading slash removal.
|
||||
([b2f46251](https://github.com/angular/angular.js/commit/b2f46251aca76c8568ee7d4bab54edbc9d7a186a))
|
||||
- **ngSwitch:** don't leak when destroyed while not attached
|
||||
([a26234f7](https://github.com/angular/angular.js/commit/a26234f7183013e2fcc9b35377e181ad96dc9917),
|
||||
[#1621](https://github.com/angular/angular.js/issues/1621))
|
||||
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
var files = require('./angularFiles').files;
|
||||
var util = require('./lib/grunt/utils.js');
|
||||
|
||||
module.exports = function(grunt) {
|
||||
//grunt plugins
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-contrib-connect');
|
||||
grunt.loadNpmTasks('grunt-contrib-compress');
|
||||
grunt.loadTasks('lib/grunt');
|
||||
|
||||
var NG_VERSION = util.getVersion();
|
||||
var dist = 'angular-'+ NG_VERSION.full;
|
||||
|
||||
|
||||
//global beforeEach
|
||||
util.init();
|
||||
|
||||
|
||||
//config
|
||||
grunt.initConfig({
|
||||
NG_VERSION: NG_VERSION,
|
||||
|
||||
connect: {
|
||||
devserver: {
|
||||
options: {
|
||||
port: 8000,
|
||||
hostname: '0.0.0.0',
|
||||
base: '.',
|
||||
keepalive: true,
|
||||
middleware: function(connect, options){
|
||||
return [
|
||||
//uncomment to enable CSP
|
||||
// util.csp(),
|
||||
util.rewrite(),
|
||||
connect.favicon('images/favicon.ico'),
|
||||
connect.static(options.base),
|
||||
connect.directory(options.base)
|
||||
];
|
||||
}
|
||||
}
|
||||
},
|
||||
testserver: {}
|
||||
},
|
||||
|
||||
|
||||
test: {
|
||||
jqlite: 'karma-jqlite.conf.js',
|
||||
jquery: 'karma-jquery.conf.js',
|
||||
modules: 'karma-modules.conf.js',
|
||||
//NOTE run grunt test:e2e instead and it will start a webserver for you
|
||||
end2end: 'karma-e2e.conf.js'
|
||||
},
|
||||
|
||||
|
||||
autotest: {
|
||||
jqlite: 'karma-jqlite.conf.js',
|
||||
jquery: 'karma-jquery.conf.js'
|
||||
},
|
||||
|
||||
|
||||
clean: {build: ['build']},
|
||||
|
||||
|
||||
build: {
|
||||
scenario: {
|
||||
dest: 'build/angular-scenario.js',
|
||||
src: [
|
||||
'lib/jquery/jquery.js',
|
||||
util.wrap([files['angularSrc'], files['angularScenario']], 'ngScenario/angular')
|
||||
],
|
||||
styles: {
|
||||
css: ['css/angular.css', 'css/angular-scenario.css']
|
||||
}
|
||||
},
|
||||
angular: {
|
||||
dest: 'build/angular.js',
|
||||
src: util.wrap([files['angularSrc']], 'angular'),
|
||||
styles: {
|
||||
css: ['css/angular.css'],
|
||||
minify: true
|
||||
}
|
||||
},
|
||||
loader: {
|
||||
dest: 'build/angular-loader.js',
|
||||
src: util.wrap(['src/loader.js'], 'loader')
|
||||
},
|
||||
mobile: {
|
||||
dest: 'build/angular-mobile.js',
|
||||
src: util.wrap([
|
||||
'src/ngMobile/mobile.js',
|
||||
'src/ngMobile/directive/ngClick.js'
|
||||
], 'module')
|
||||
},
|
||||
mocks: {
|
||||
dest: 'build/angular-mocks.js',
|
||||
src: ['src/ngMock/angular-mocks.js'],
|
||||
strict: false
|
||||
},
|
||||
sanitize: {
|
||||
dest: 'build/angular-sanitize.js',
|
||||
src: util.wrap([
|
||||
'src/ngSanitize/sanitize.js',
|
||||
'src/ngSanitize/directive/ngBindHtml.js',
|
||||
'src/ngSanitize/filter/linky.js',
|
||||
], 'module')
|
||||
},
|
||||
resource: {
|
||||
dest: 'build/angular-resource.js',
|
||||
src: util.wrap(['src/ngResource/resource.js'], 'module')
|
||||
},
|
||||
cookies: {
|
||||
dest: 'build/angular-cookies.js',
|
||||
src: util.wrap(['src/ngCookies/cookies.js'], 'module')
|
||||
},
|
||||
bootstrap: {
|
||||
dest: 'build/angular-bootstrap.js',
|
||||
src: util.wrap(['src/bootstrap/bootstrap.js'], 'module')
|
||||
},
|
||||
bootstrapPrettify: {
|
||||
dest: 'build/angular-bootstrap-prettify.js',
|
||||
src: util.wrap(['src/bootstrap/bootstrap-prettify.js', 'src/bootstrap/google-prettify/prettify.js'], 'module'),
|
||||
styles: {
|
||||
css: ['src/bootstrap/google-prettify/prettify.css'],
|
||||
minify: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
min: {
|
||||
angular: 'build/angular.js',
|
||||
cookies: 'build/angular-cookies.js',
|
||||
loader: 'build/angular-loader.js',
|
||||
mobile: 'build/angular-mobile.js',
|
||||
resource: 'build/angular-resource.js',
|
||||
sanitize: 'build/angular-sanitize.js',
|
||||
bootstrap: 'build/angular-bootstrap.js',
|
||||
bootstrapPrettify: 'build/angular-bootstrap-prettify.js',
|
||||
},
|
||||
|
||||
|
||||
docs: {
|
||||
process: ['build/docs/*.html', 'build/docs/.htaccess']
|
||||
},
|
||||
|
||||
|
||||
copy: {
|
||||
i18n: {
|
||||
files: [
|
||||
{ src: 'src/ngLocale/**', dest: 'build/i18n/', expand: true, flatten: true }
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
compress: {
|
||||
build: {
|
||||
options: {archive: 'build/' + dist +'.zip'},
|
||||
src: ['**'], cwd: 'build', expand: true, dot: true, dest: dist + '/'
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
write: {
|
||||
versionTXT: {file: 'build/version.txt', val: NG_VERSION.full},
|
||||
versionJSON: {file: 'build/version.json', val: JSON.stringify(NG_VERSION)}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
//alias tasks
|
||||
grunt.registerTask('test:unit', ['test:jqlite', 'test:jquery', 'test:modules']);
|
||||
grunt.registerTask('minify', ['clean', 'build', 'minall']);
|
||||
grunt.registerTask('test:e2e', ['connect:testserver', 'test:end2end']);
|
||||
grunt.registerTask('webserver', ['connect:devserver']);
|
||||
grunt.registerTask('package', ['clean', 'buildall', 'minall', 'docs', 'copy', 'write', 'compress']);
|
||||
grunt.registerTask('default', ['package']);
|
||||
};
|
||||
@@ -21,25 +21,19 @@ Building AngularJS
|
||||
---------
|
||||
[Once you have your environment setup](http://docs.angularjs.org/misc/contribute) just run:
|
||||
|
||||
rake package
|
||||
grunt package
|
||||
|
||||
|
||||
Running Tests
|
||||
-------------
|
||||
Running tests requires installation of [Testacular](http://vojtajina.github.com/testacular):
|
||||
|
||||
sudo npm install -g testacular
|
||||
|
||||
To execute all unit tests, use:
|
||||
|
||||
rake test:unit
|
||||
grunt test:unit
|
||||
|
||||
To execute end-to-end (e2e) tests, use:
|
||||
|
||||
rake package
|
||||
rake webserver &
|
||||
rake test:e2e
|
||||
grunt package
|
||||
grunt test:e2e
|
||||
|
||||
To learn more about the rake tasks, run `rake -T` and also read our
|
||||
[contribution guidelines](http://docs.angularjs.org/misc/contribute) and instructions in this
|
||||
[commit message](https://github.com/angular/angular.js/commit/9d168f058f9c6d7eeae0daa7cb72ea4e02a0003a).
|
||||
To learn more about the grunt tasks, run `grunt --help` and also read our
|
||||
[contribution guidelines](http://docs.angularjs.org/misc/contribute).
|
||||
|
||||
@@ -1,355 +0,0 @@
|
||||
require 'yaml'
|
||||
include FileUtils
|
||||
|
||||
|
||||
## High level flow of the build:
|
||||
##
|
||||
## clean -> init -> concat -> minify -> package
|
||||
##
|
||||
|
||||
|
||||
content = File.open('angularFiles.js', 'r') {|f| f.read }
|
||||
files = eval(content.gsub(/\};(\s|\S)*/, '}').
|
||||
gsub(/angularFiles = /, '').
|
||||
gsub(/:/, '=>').
|
||||
gsub(/\/\//, '#'));
|
||||
|
||||
BUILD_DIR = 'build'
|
||||
|
||||
task :default => [:package]
|
||||
|
||||
|
||||
desc 'Init the build workspace'
|
||||
task :init do
|
||||
FileUtils.mkdir(BUILD_DIR) unless File.directory?(BUILD_DIR)
|
||||
|
||||
v = YAML::load( File.open( 'version.yaml' ) )
|
||||
match = v['version'].match(/^([^-]*)(-snapshot)?$/)
|
||||
|
||||
NG_VERSION = Struct.new(:full, :major, :minor, :dot, :codename, :stable).
|
||||
new(match[1] + (match[2] ? ('-' + %x(git rev-parse HEAD)[0..7]) : ''),
|
||||
match[1].split('.')[0],
|
||||
match[1].split('.')[1],
|
||||
match[1].split('.')[2].sub(/\D+.*$/, ''),
|
||||
v['codename'],
|
||||
v['stable'])
|
||||
end
|
||||
|
||||
|
||||
desc 'Clean Generated Files'
|
||||
task :clean do
|
||||
FileUtils.rm_r(BUILD_DIR, :force => true)
|
||||
FileUtils.mkdir(BUILD_DIR)
|
||||
FileUtils.rm_r('test_out', :force => true)
|
||||
end
|
||||
|
||||
|
||||
desc 'Concat Scenario'
|
||||
task :concat_scenario => :init do
|
||||
|
||||
concat_file('angular-scenario.js', [
|
||||
'lib/jquery/jquery.js',
|
||||
'src/ngScenario/angular.prefix',
|
||||
files['angularSrc'],
|
||||
files['angularScenario'],
|
||||
'src/ngScenario/angular.suffix',
|
||||
], gen_css('css/angular.css') + "\n" + gen_css('css/angular-scenario.css'))
|
||||
end
|
||||
|
||||
|
||||
|
||||
desc 'Concat AngularJS files'
|
||||
task :concat => :init do
|
||||
concat_file('angular.js', [
|
||||
'src/angular.prefix',
|
||||
files['angularSrc'],
|
||||
'src/angular.suffix',
|
||||
], gen_css('css/angular.css', true))
|
||||
|
||||
FileUtils.cp_r 'src/ngLocale', path_to('i18n')
|
||||
|
||||
concat_file('angular-loader.js', [
|
||||
'src/loader.prefix',
|
||||
'src/loader.js',
|
||||
'src/loader.suffix'])
|
||||
|
||||
|
||||
concat_module('sanitize', [
|
||||
'src/ngSanitize/sanitize.js',
|
||||
'src/ngSanitize/directive/ngBindHtml.js',
|
||||
'src/ngSanitize/filter/linky.js'])
|
||||
|
||||
concat_module('resource', ['src/ngResource/resource.js'])
|
||||
concat_module('cookies', ['src/ngCookies/cookies.js'])
|
||||
concat_module('bootstrap', ['src/bootstrap/bootstrap.js'])
|
||||
concat_module('bootstrap-prettify', ['src/bootstrap/bootstrap-prettify.js',
|
||||
'src/bootstrap/google-prettify/prettify.js'],
|
||||
gen_css('src/bootstrap/google-prettify/prettify.css', true))
|
||||
|
||||
|
||||
FileUtils.cp 'src/ngMock/angular-mocks.js', path_to('angular-mocks.js')
|
||||
|
||||
rewrite_file(path_to('angular-mocks.js')) do |content|
|
||||
content.sub!('"NG_VERSION_FULL"', NG_VERSION.full)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
desc 'Minify JavaScript'
|
||||
task :minify => [:init, :concat, :concat_scenario] do
|
||||
[ 'angular.js',
|
||||
'angular-cookies.js',
|
||||
'angular-loader.js',
|
||||
'angular-resource.js',
|
||||
'angular-sanitize.js',
|
||||
'angular-bootstrap.js',
|
||||
'angular-bootstrap-prettify.js'
|
||||
].each do |file|
|
||||
unless ENV['TRAVIS']
|
||||
fork { closure_compile(file) }
|
||||
else
|
||||
closure_compile(file)
|
||||
end
|
||||
end
|
||||
|
||||
Process.waitall
|
||||
end
|
||||
|
||||
|
||||
desc 'Generate version.txt and version.json files'
|
||||
task :version => [:init] do
|
||||
`echo #{NG_VERSION.full} > #{path_to('version.txt')}`
|
||||
`echo '{
|
||||
"full": "#{NG_VERSION.full}",
|
||||
"major": "#{NG_VERSION.major}",
|
||||
"minor": "#{NG_VERSION.minor}",
|
||||
"dot": "#{NG_VERSION.dot}",
|
||||
"codename": "#{NG_VERSION.codename}"\n}' > #{path_to('version.json')}`
|
||||
end
|
||||
|
||||
|
||||
desc 'Generate docs'
|
||||
task :docs => [:init] do
|
||||
`node docs/src/gen-docs.js`
|
||||
|
||||
[ path_to('docs/.htaccess'),
|
||||
path_to('docs/index.html'),
|
||||
path_to('docs/index-debug.html'),
|
||||
path_to('docs/index-nocache.html'),
|
||||
path_to('docs/index-jq.html'),
|
||||
path_to('docs/index-jq-debug.html'),
|
||||
path_to('docs/index-jq-nocache.html'),
|
||||
path_to('docs/docs-scenario.html')
|
||||
].each do |src|
|
||||
rewrite_file(src) do |content|
|
||||
content.sub!('"NG_VERSION_FULL"', NG_VERSION.full).
|
||||
sub('"NG_VERSION_STABLE"', NG_VERSION.stable)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
desc 'Create angular distribution'
|
||||
task :package => [:clean, :minify, :version, :docs] do
|
||||
zip_dir = "angular-#{NG_VERSION.full}"
|
||||
zip_file = "#{zip_dir}.zip"
|
||||
|
||||
FileUtils.ln_s BUILD_DIR, zip_dir
|
||||
%x(zip -r #{zip_file} #{zip_dir})
|
||||
FileUtils.rm zip_dir
|
||||
|
||||
FileUtils.mv zip_file, path_to(zip_file)
|
||||
|
||||
puts "Package created: #{path_to(zip_file)}"
|
||||
end
|
||||
|
||||
|
||||
desc 'Start development webserver'
|
||||
task :webserver, :port do |t, args|
|
||||
exec "node lib/nodeserver/server.js #{args[:port]}"
|
||||
end
|
||||
|
||||
|
||||
desc 'Run all AngularJS tests'
|
||||
task :test, :browsers, :misc_options do |t, args|
|
||||
[ 'test:jqlite',
|
||||
'test:jquery',
|
||||
'test:modules',
|
||||
'test:e2e'
|
||||
].each do |task|
|
||||
Rake::Task[task].invoke(args[:browsers], args[:misc_options])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
namespace :test do
|
||||
|
||||
desc 'Run all unit tests (single run)'
|
||||
task :unit, :browsers, :misc_options do |t, args|
|
||||
[ 'test:jqlite',
|
||||
'test:jquery',
|
||||
'test:modules'
|
||||
].each do |task|
|
||||
Rake::Task[task].invoke(args[:browsers], args[:misc_options])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
desc 'Run jqLite-based unit test suite (single run)'
|
||||
task :jqlite, :browsers, :misc_options do |t, args|
|
||||
start_testacular('testacular-jqlite.conf.js', true, args[:browsers], args[:misc_options])
|
||||
end
|
||||
|
||||
|
||||
desc 'Run jQuery-based unit test suite (single run)'
|
||||
task :jquery, :browsers, :misc_options do |t, args|
|
||||
start_testacular('testacular-jquery.conf.js', true, args[:browsers], args[:misc_options])
|
||||
end
|
||||
|
||||
|
||||
desc 'Run bundled modules unit test suite (single run)'
|
||||
task :modules, :browsers, :misc_options do |t, args|
|
||||
start_testacular('testacular-modules.conf.js', true, args[:browsers], args[:misc_options])
|
||||
end
|
||||
|
||||
|
||||
desc 'Run e2e test suite (single run)'
|
||||
task :e2e, :browsers, :misc_options do |t, args|
|
||||
start_testacular('testacular-e2e.conf.js', true, args[:browsers], args[:misc_options])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
namespace :autotest do
|
||||
|
||||
desc 'Run jqLite-based unit test suite (autowatch)'
|
||||
task :jqlite, :browsers, :misc_options do |t, args|
|
||||
start_testacular('testacular-jqlite.conf.js', false, args[:browsers], args[:misc_options])
|
||||
end
|
||||
|
||||
|
||||
desc 'Run jQuery-based unit test suite (autowatch)'
|
||||
task :jquery, :browsers, :misc_options do |t, args|
|
||||
start_testacular('testacular-jquery.conf.js', false, args[:browsers], args[:misc_options])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
###################
|
||||
# utility methods #
|
||||
###################
|
||||
|
||||
|
||||
##
|
||||
# generates css snippet from a given files and optionally applies simple minification rules
|
||||
#
|
||||
def gen_css(cssFile, minify = false)
|
||||
css = ''
|
||||
File.open(cssFile, 'r') do |f|
|
||||
css = f.read
|
||||
end
|
||||
|
||||
if minify
|
||||
css.gsub! /\n/, ''
|
||||
css.gsub! /\/\*.*?\*\//, ''
|
||||
css.gsub! /:\s+/, ':'
|
||||
css.gsub! /\s*\{\s*/, '{'
|
||||
css.gsub! /\s*\}\s*/, '}'
|
||||
css.gsub! /\s*\,\s*/, ','
|
||||
css.gsub! /\s*\;\s*/, ';'
|
||||
end
|
||||
|
||||
#escape for js
|
||||
css.gsub! /\\/, "\\\\\\"
|
||||
css.gsub! /'/, "\\\\'"
|
||||
css.gsub! /\n/, "\\n"
|
||||
|
||||
return %Q{angular.element(document).find('head').append('<style type="text/css">#{css}</style>');}
|
||||
end
|
||||
|
||||
|
||||
##
|
||||
# returns path to the file in the build directory
|
||||
#
|
||||
def path_to(filename)
|
||||
return File.join(BUILD_DIR, *filename)
|
||||
end
|
||||
|
||||
|
||||
##
|
||||
# returns the 32-bit mode force flags for java compiler if supported, this makes the build much
|
||||
# faster
|
||||
#
|
||||
def java32flags
|
||||
return '-d32 -client' unless Rake::Win32.windows? || `java -version -d32 2>&1`.match(/Error/i)
|
||||
end
|
||||
|
||||
|
||||
def closure_compile(filename)
|
||||
puts "Minifying #{filename} ..."
|
||||
|
||||
min_path = path_to(filename.gsub(/\.js$/, '.min.js'))
|
||||
|
||||
%x(java \
|
||||
#{java32flags()} \
|
||||
-jar lib/closure-compiler/compiler.jar \
|
||||
--compilation_level SIMPLE_OPTIMIZATIONS \
|
||||
--language_in ECMASCRIPT5_STRICT \
|
||||
--js #{path_to(filename)} \
|
||||
--js_output_file #{min_path})
|
||||
|
||||
rewrite_file(min_path) do |content|
|
||||
content.sub!("'use strict';", "").
|
||||
sub!(/\(function\([^)]*\)\{/, "\\0'use strict';")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def concat_file(filename, deps, footer='')
|
||||
puts "Creating #{filename} ..."
|
||||
File.open(path_to(filename), 'w') do |f|
|
||||
concat = 'cat ' + deps.flatten.join(' ')
|
||||
|
||||
content = %x{#{concat}}.
|
||||
gsub('"NG_VERSION_FULL"', NG_VERSION.full).
|
||||
gsub('"NG_VERSION_MAJOR"', NG_VERSION.major).
|
||||
gsub('"NG_VERSION_MINOR"', NG_VERSION.minor).
|
||||
gsub('"NG_VERSION_DOT"', NG_VERSION.dot).
|
||||
gsub('"NG_VERSION_CODENAME"', NG_VERSION.codename).
|
||||
gsub(/^\s*['"]use strict['"];?\s*$/, ''). # remove all file-specific strict mode flags
|
||||
sub(/\(function\([^)]*\)\s*\{/, "\\0\n'use strict';") # add single strict mode flag
|
||||
|
||||
f.write(content)
|
||||
f.write(footer)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def concat_module(name, files, footer='')
|
||||
concat_file('angular-' + name + '.js', ['src/module.prefix'] + files + ['src/module.suffix'], footer)
|
||||
end
|
||||
|
||||
|
||||
def rewrite_file(filename)
|
||||
File.open(filename, File::RDWR) do |f|
|
||||
content = f.read
|
||||
|
||||
content = yield content
|
||||
|
||||
raise "File rewrite failed - No content!" unless content
|
||||
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write content
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def start_testacular(config, singleRun, browsers, misc_options)
|
||||
sh "./node_modules/testacular/bin/testacular start " +
|
||||
"#{config} " +
|
||||
"#{'--single-run=true' if singleRun} " +
|
||||
"#{'--browsers=' + browsers.gsub('+', ',') if browsers} " +
|
||||
"#{(misc_options || '').gsub('+', ',')}"
|
||||
end
|
||||
Vendored
+12
-4
@@ -9,6 +9,8 @@ angularFiles = {
|
||||
'src/auto/injector.js',
|
||||
|
||||
'src/ng/anchorScroll.js',
|
||||
'src/ng/animation.js',
|
||||
'src/ng/animator.js',
|
||||
'src/ng/browser.js',
|
||||
'src/ng/cacheFactory.js',
|
||||
'src/ng/compile.js',
|
||||
@@ -69,7 +71,8 @@ angularFiles = {
|
||||
'src/ngSanitize/directive/ngBindHtml.js',
|
||||
'src/ngSanitize/filter/linky.js',
|
||||
'src/ngMock/angular-mocks.js',
|
||||
|
||||
'src/ngMobile/mobile.js',
|
||||
'src/ngMobile/directive/ngClick.js',
|
||||
'src/bootstrap/bootstrap.js'
|
||||
],
|
||||
|
||||
@@ -106,7 +109,8 @@ angularFiles = {
|
||||
'test/ngSanitize/*.js',
|
||||
'test/ngSanitize/directive/*.js',
|
||||
'test/ngSanitize/filter/*.js',
|
||||
'test/ngMock/*.js'
|
||||
'test/ngMock/*.js',
|
||||
'test/ngMobile/directive/*.js'
|
||||
],
|
||||
|
||||
'jstd': [
|
||||
@@ -141,9 +145,12 @@ angularFiles = {
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'build/angular.js',
|
||||
'build/angular-scenario.js',
|
||||
'src/ngMock/angular-mocks.js',
|
||||
'src/ngCookies/cookies.js',
|
||||
'src/ngResource/resource.js',
|
||||
'src/ngMobile/mobile.js',
|
||||
'src/ngMobile/directive/ngClick.js',
|
||||
'src/ngSanitize/sanitize.js',
|
||||
'src/ngSanitize/directive/ngBindHtml.js',
|
||||
'src/ngSanitize/filter/linky.js',
|
||||
@@ -153,7 +160,8 @@ angularFiles = {
|
||||
'test/ngResource/*.js',
|
||||
'test/ngSanitize/*.js',
|
||||
'test/ngSanitize/directive/*.js',
|
||||
'test/ngSanitize/filter/*.js'
|
||||
'test/ngSanitize/filter/*.js',
|
||||
'test/ngMobile/directive/*.js'
|
||||
],
|
||||
|
||||
'jstdPerf': [
|
||||
@@ -201,7 +209,7 @@ if (exports) {
|
||||
var files = [];
|
||||
|
||||
[].splice.call(arguments, 0).forEach(function(file) {
|
||||
if (file.match(/testacular/)) {
|
||||
if (file.match(/karma/)) {
|
||||
files.push(file);
|
||||
} else {
|
||||
angularFiles[file].forEach(function(f) {
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
rake minify
|
||||
grunt minify
|
||||
gzip -c < build/angular.min.js > build/angular.min.js.gzip
|
||||
ls -l build/angular.min.*
|
||||
|
||||
@@ -48,11 +48,10 @@ initialization.
|
||||
|
||||
# Automatic Initialization
|
||||
|
||||
Angular initializes automatically upon `DOMContentLoaded` event, at which point Angular looks for
|
||||
the {@link api/ng.directive:ngApp `ng-app`} directive which
|
||||
designates your application root. If the {@link
|
||||
api/ng.directive:ngApp `ng-app`} directive is found then Angular
|
||||
will:
|
||||
Angular initializes automatically upon `DOMContentLoaded` event or when the `angular.js` script is
|
||||
evaluated if at that time `document.readyState` is set to `'complete'`. At this point Angular looks
|
||||
for the {@link api/ng.directive:ngApp `ng-app`} directive which designates your application root.
|
||||
If the {@link api/ng.directive:ngApp `ng-app`} directive is found then Angular will:
|
||||
|
||||
* load the {@link guide/module module} associated with the directive.
|
||||
* create the application {@link api/AUTO.$injector injector}
|
||||
|
||||
@@ -234,7 +234,7 @@ The separation of the controller and the view is important because:
|
||||
|
||||
The model is the data which is used merged with the template to produce the view. To be able to
|
||||
render the model into the view, the model has to be able to be referenced from the scope. Unlike many
|
||||
other frameworks Angular makes no restrictions or requirements an the model. There are no classes
|
||||
other frameworks Angular makes no restrictions or requirements on the model. There are no classes
|
||||
to inherit from or special accessor methods for accessing or changing the model. The model can be
|
||||
primitive, object hash, or a full object Type. In short the model is a plain JavaScript object.
|
||||
|
||||
@@ -248,9 +248,9 @@ primitive, object hash, or a full object Type. In short the model is a plain Jav
|
||||
|
||||
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-view.png">
|
||||
|
||||
The view is what the users sees. The view begins its life as a template, it is merged with the
|
||||
The view is what the user sees. The view begins its life as a template, is merged with the
|
||||
model and finally rendered into the browser DOM. Angular takes a very different approach to
|
||||
rendering the view, compared to most other templating systems.
|
||||
rendering the view compared to most other templating systems.
|
||||
|
||||
* **Others** - Most templating systems begin as an HTML string with special templating markup.
|
||||
Often the template markup breaks the HTML syntax which means that the template can not be
|
||||
|
||||
@@ -178,8 +178,9 @@ have a look at an example:
|
||||
<body ng-controller="MainCtrl">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
<div ng-controller="ChildCtrl">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
<p ng-controller="BabyCtrl">Good {{timeOfDay}}, {{name}}!</p>
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
<p ng-controller="BabyCtrl">Good {{timeOfDay}}, {{name}}!</p>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
function MainCtrl($scope) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@name Developer Guide: Templates: Data Binding in Angular
|
||||
@description
|
||||
|
||||
Data-binding in Angular web apps is the automatic syncronization of data between the model and view
|
||||
Data-binding in Angular web apps is the automatic synchronization of data between the model and view
|
||||
components. The way that Angular implements data-binding lets you treat the model as the
|
||||
single-source-of-truth in your application. The view is a projection of the model at all times.
|
||||
When the model changes, the view reflects the change, and vice versa.
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
@name Developer Guide: Templates: Understanding Angular Filters
|
||||
@description
|
||||
|
||||
Angular filters format data for display to the user. In addition to formatting data, filters can
|
||||
also modify the DOM. This allows filters to handle tasks such as conditionally applying CSS styles
|
||||
to filtered output.
|
||||
Angular filters format data for display to the user.
|
||||
|
||||
For example, you might have a data object that needs to be formatted according to the locale before
|
||||
displaying it to the user. You can pass expressions through a chain of filters like this:
|
||||
|
||||
@@ -213,7 +213,7 @@ services, and filters. The factory methods are registered with the module, and t
|
||||
of declaring factories is:
|
||||
|
||||
<pre>
|
||||
angualar.module('myModule', []).
|
||||
angular.module('myModule', []).
|
||||
config(['depProvider', function(depProvider){
|
||||
...
|
||||
}]).
|
||||
|
||||
@@ -39,11 +39,11 @@ the following example.
|
||||
</script>
|
||||
<div ng-controller="Ctrl1">
|
||||
Hello <input ng-model='name'> <hr/>
|
||||
<span ng:bind="name"> <span ng:bind="name"></span> <br/>
|
||||
<span ng_bind="name"> <span ng_bind="name"></span> <br/>
|
||||
<span ng-bind="name"> <span ng-bind="name"></span> <br/>
|
||||
<span data-ng-bind="name"> <span data-ng-bind="name"></span> <br/>
|
||||
<span x-ng-bind="name"> <span x-ng-bind="name"></span> <br/>
|
||||
<span ng:bind="name"> <span ng:bind="name"></span> <br/>
|
||||
<span ng_bind="name"> <span ng_bind="name"></span> <br/>
|
||||
<span ng-bind="name"> <span ng-bind="name"></span> <br/>
|
||||
<span data-ng-bind="name"> <span data-ng-bind="name"></span> <br/>
|
||||
<span x-ng-bind="name"> <span x-ng-bind="name"></span> <br/>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
@@ -53,7 +53,7 @@ the following example.
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
# String interpolation
|
||||
# Text and attribute bindings
|
||||
|
||||
During the compilation process the {@link api/ng.$compile compiler} matches text and
|
||||
attributes using the {@link api/ng.$interpolate $interpolate} service to see if they
|
||||
@@ -66,6 +66,31 @@ here:
|
||||
<a href="img/{{username}}.jpg">Hello {{username}}!</a>
|
||||
</pre>
|
||||
|
||||
|
||||
# ngAttr attribute bindings
|
||||
|
||||
If an attribute with a binding is prefixed with `ngAttr` prefix (denormalized prefix: 'ng-attr-',
|
||||
'ng:attr-') then during the compilation the prefix will be removed and the binding will be applied
|
||||
to an unprefixed attribute. This allows binding to attributes that would otherwise be eagerly
|
||||
processed by browsers in their uncompiled form (e.g. `img[src]` or svg's `circle[cx]` attributes).
|
||||
|
||||
For example, considering template:
|
||||
|
||||
<svg>
|
||||
<circle ng-attr-cx="{{cx}}"></circle>
|
||||
</svg>
|
||||
|
||||
and model cx set to 5, will result in rendering this dom:
|
||||
|
||||
<svg>
|
||||
<circle cx="5"></circle>
|
||||
</svg>
|
||||
|
||||
If you were to bind `{{cx}}` directly to the `cx` attribute, you'd get the following error:
|
||||
`Error: Invalid value for attribute cx="{{cx}}"`. With `ng-attr-cx` you can work around this
|
||||
problem.
|
||||
|
||||
|
||||
# Compilation process, and directive matching
|
||||
|
||||
Compilation of HTML happens in three phases:
|
||||
@@ -95,7 +120,7 @@ Compilation of HTML happens in three phases:
|
||||
var $compile = ...; // injected into your code
|
||||
var scope = ...;
|
||||
|
||||
var html = '<div ng-bind='exp'></div>';
|
||||
var html = '<div ng-bind="exp"></div>';
|
||||
|
||||
// Step 1: parse HTML into DOM element
|
||||
var template = angular.element(html);
|
||||
@@ -206,7 +231,7 @@ In this example we will build a directive that displays the current time.
|
||||
}
|
||||
|
||||
// listen on DOM destroy (removal) event, and cancel the next UI update
|
||||
// to prevent updating time ofter the DOM element was removed.
|
||||
// to prevent updating time after the DOM element was removed.
|
||||
element.bind('$destroy', function() {
|
||||
$timeout.cancel(timeoutId);
|
||||
});
|
||||
@@ -255,7 +280,7 @@ In most cases you will not need such fine control and so the above can be simpli
|
||||
different parts of this skeleton are explained in following sections. In this section we are
|
||||
interested only in some of this skeleton.
|
||||
|
||||
The first step in simplyfing the code is to rely on the default values. Therefore the above can be
|
||||
The first step in simplifying the code is to rely on the default values. Therefore the above can be
|
||||
simplified as:
|
||||
|
||||
<pre>
|
||||
@@ -336,7 +361,9 @@ compiler}. The attributes are:
|
||||
Given `<widget my-attr="parentModel">` and widget definition of
|
||||
`scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
|
||||
value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
|
||||
in `localModel` and any changes in `localModel` will reflect in `parentModel`.
|
||||
in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
|
||||
scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
|
||||
can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional.
|
||||
|
||||
* `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
|
||||
If no `attr` name is specified then the attribute name is assumed to be the same as the
|
||||
@@ -355,7 +382,7 @@ compiler}. The attributes are:
|
||||
|
||||
* `$scope` - Current scope associated with the element
|
||||
* `$element` - Current element
|
||||
* `$attrs` - Current attributes obeject for the element
|
||||
* `$attrs` - Current attributes object for the element
|
||||
* `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
|
||||
`function(cloneLinkingFn)`.
|
||||
|
||||
@@ -376,13 +403,21 @@ compiler}. The attributes are:
|
||||
* `M` - Comment: `<!-- directive: my-directive exp -->`
|
||||
|
||||
* `template` - replace the current element with the contents of the HTML. The replacement process
|
||||
migrates all of the attributes / classes from the old element to the new one. See Creating
|
||||
Widgets section below for more information.
|
||||
migrates all of the attributes / classes from the old element to the new one. See the
|
||||
{@link guide/directive#Components Creating Components} section below for more information.
|
||||
|
||||
You can specify `template` as a string representing the template or as a function which takes
|
||||
two arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
|
||||
a string value representing the template.
|
||||
|
||||
* `templateUrl` - Same as `template` but the template is loaded from the specified URL. Because
|
||||
the template loading is asynchronous the compilation/linking is suspended until the template
|
||||
is loaded.
|
||||
|
||||
You can specify `templateUrl` as a string representing the URL or as a function which takes two
|
||||
arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
|
||||
a string value representing the url.
|
||||
|
||||
* `replace` - if set to `true` then the template will replace the current element, rather than
|
||||
append the template to the element.
|
||||
|
||||
@@ -601,6 +636,7 @@ restrict: 'E',
|
||||
replace: true
|
||||
</pre>
|
||||
|
||||
<a name="Components"></a>
|
||||
# Creating Components
|
||||
|
||||
It is often desirable to replace a single directive with a more complex DOM structure. This
|
||||
|
||||
@@ -278,7 +278,7 @@ However, if you need more flexibility, you can write your own form control as a
|
||||
|
||||
In order for custom control to work with `ngModel` and to achieve two-way data-binding it needs to:
|
||||
|
||||
- implement `render` method, which is responsible for rendering the data after it passed the {@link api/ng.directive:ngModel.NgModelController#$formatters NgModelController#$formatters},
|
||||
- implement `$render` method, which is responsible for rendering the data after it passed the {@link api/ng.directive:ngModel.NgModelController#$formatters NgModelController#$formatters},
|
||||
- call `$setViewValue` method, whenever the user interacts with the control and model needs to be updated. This is usually done inside a DOM Event listener.
|
||||
|
||||
See {@link guide/directive $compileProvider.directive} for more info.
|
||||
|
||||
@@ -158,9 +158,9 @@ angular.module('myModule', []).
|
||||
|
||||
angular.module('myModule', []).
|
||||
config(function($provide, $compileProvider, $filterProvider) {
|
||||
$provide.value('a', 123)
|
||||
$provide.factory('a', function() { return 123; })
|
||||
$compileProvider.directive('directiveName', ...).
|
||||
$provide.value('a', 123);
|
||||
$provide.factory('a', function() { return 123; });
|
||||
$compileProvider.directive('directiveName', ...);
|
||||
$filterProvider.register('filterName', ...);
|
||||
});
|
||||
</pre>
|
||||
|
||||
@@ -81,15 +81,11 @@ Several steps are needed to check out and build AngularJS:
|
||||
Before you can build AngularJS, you must install or configure the following dependencies on your
|
||||
machine:
|
||||
|
||||
* {@link http://rake.rubyforge.org Rake}: We use Rake as our build system, which is pre-installed
|
||||
on most Macintosh and Linux machines. If that is not true in your case, you can grab it from the
|
||||
Rake website.
|
||||
|
||||
* Git: The {@link http://help.github.com/mac-git-installation Github Guide to Installing Git} is
|
||||
quite a good source for information on Git.
|
||||
|
||||
* {@link http://nodejs.org Node.js}: We use Node to generate the documentation and to run a
|
||||
development web server. Depending on your system, you can install Node either from source or as a
|
||||
* {@link http://nodejs.org Node.js}: We use Node to generate the documentation, run a
|
||||
development web server, run tests, and generate a build. Depending on your system, you can install Node either from source or as a
|
||||
pre-packaged bundle.
|
||||
|
||||
Once installed, you'll also need several npms (node packages), which you can install once you checked out a local copy
|
||||
@@ -98,6 +94,10 @@ pre-packaged bundle.
|
||||
* `cd angular.js`
|
||||
* `npm install`
|
||||
|
||||
* {@link http://gruntjs.com Grunt}: We use Grunt as our build system. Install the grunt command-line tool globally with:
|
||||
|
||||
* `sudo npm install -g grunt-cli`
|
||||
|
||||
|
||||
## Creating a Github Account and Forking Angular
|
||||
|
||||
@@ -108,7 +108,7 @@ https://github.com/angular/angular.js main angular repository}.
|
||||
|
||||
## Building AngularJS
|
||||
|
||||
To build AngularJS, you check out the source code and use Rake to generate the non-minified and
|
||||
To build AngularJS, you check out the source code and use Grunt to generate the non-minified and
|
||||
minified AngularJS files:
|
||||
|
||||
1. To clone your Github repository, run:
|
||||
@@ -129,7 +129,11 @@ minified AngularJS files:
|
||||
|
||||
5. To build AngularJS, run:
|
||||
|
||||
rake package
|
||||
grunt package
|
||||
|
||||
NOTE: If you're using Windows you must run your command line with administrative privileges (right click, run as
|
||||
Administrator).
|
||||
|
||||
|
||||
The build output can be located under the `build` directory. It consists of the following files and
|
||||
directories:
|
||||
@@ -158,7 +162,7 @@ made available a local web server based on Node.js.
|
||||
|
||||
1. To start the web server, run:
|
||||
|
||||
rake webserver
|
||||
grunt webserver
|
||||
|
||||
2. To access the local server, go to this website:
|
||||
|
||||
@@ -173,18 +177,20 @@ made available a local web server based on Node.js.
|
||||
Our unit and integration tests are written with Jasmine and executed with Testacular. To run all of the
|
||||
tests once on Chrome run:
|
||||
|
||||
rake test:unit
|
||||
grunt test:unit
|
||||
|
||||
To run the tests on other browsers (Chrome, ChromeCanary, Firefox, Opera and Safari are pre-configured) use:
|
||||
|
||||
rake test:unit[Opera+Firefox]
|
||||
grunt test:unit --browsers Opera,Firefox
|
||||
|
||||
Note there should be _no spaces between browsers_. `Opera, Firefox` is INVALID.
|
||||
|
||||
During development it's however more productive to continuously run unit tests every time the source or test files
|
||||
change. To execute tests in this mode run:
|
||||
|
||||
1. To start the Testacular server, capture Chrome browser and run unit tests, run:
|
||||
|
||||
rake autotest:jqlite
|
||||
grunt autotest:jqlite
|
||||
|
||||
2. To capture more browsers, open this url in the desired browser (url might be different if you have multiple instance
|
||||
of Testacular running, read Testacular's console output for the correct url):
|
||||
@@ -194,9 +200,9 @@ change. To execute tests in this mode run:
|
||||
3. To re-run tests just change any source or test file.
|
||||
|
||||
|
||||
To learn more about all of the preconfigured Rake tasks run:
|
||||
To learn more about all of the preconfigured Grunt tasks run:
|
||||
|
||||
rake -T
|
||||
grunt --help
|
||||
|
||||
|
||||
## Running the end-to-end Test Suite
|
||||
@@ -205,7 +211,7 @@ To run the E2E test suite:
|
||||
|
||||
1. Start the local web server if it's not running already.
|
||||
|
||||
rake webserver
|
||||
grunt webserver
|
||||
|
||||
2. In a browser, go to:
|
||||
|
||||
@@ -213,7 +219,13 @@ To run the E2E test suite:
|
||||
|
||||
or in terminal run:
|
||||
|
||||
rake test:e2e
|
||||
grunt test:end2end
|
||||
|
||||
For convenience you can also simply run:
|
||||
|
||||
grunt test:e2e
|
||||
|
||||
This will start the webserver for you and run the tests.
|
||||
|
||||
|
||||
|
||||
@@ -222,7 +234,8 @@ To run the E2E test suite:
|
||||
|
||||
To create and submit a change:
|
||||
|
||||
1. Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code changes to be
|
||||
1. <a name="CLA"></a>
|
||||
Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code changes to be
|
||||
accepted, the CLA must be signed. It's a quick process, we promise!
|
||||
|
||||
For individuals we have a [simple click-through form](http://code.google.com/legal/individual-cla-v1.0.html). For
|
||||
|
||||
@@ -64,8 +64,8 @@ testing angular apps even easier. Your unit/integration test harness should load
|
||||
to write and execute end-to-end tests for angular applications.
|
||||
|
||||
* __`angular-loader.min.js`__ — Module loader for Angular modules. If you are loading multiple script files containing
|
||||
Angular modules, you can load them asynchronosuly and in any order as long as you load this file first. Often the
|
||||
contents of this file are copy&pasted into the `index.html` to avoid even the inial request to `angular-loader.min.js`.
|
||||
Angular modules, you can load them asynchronously and in any order as long as you load this file first. Often the
|
||||
contents of this file are copy&pasted into the `index.html` to avoid even the initial request to `angular-loader.min.js`.
|
||||
See [angular-seed](https://github.com/angular/angular-seed/blob/master/app/index-async.html) for an example of usage.
|
||||
|
||||
* __`angular-resource.js`__, __`angular-cookies.js`__, etc - extra Angular modules with additional functionality.
|
||||
|
||||
@@ -109,7 +109,7 @@ __`app/index.html`:__
|
||||
|
||||
<html ng-app>
|
||||
|
||||
The `ng-app` attribute is represents an Angular directive (named `ngApp`; Angular uses
|
||||
The `ng-app` attribute represents an Angular directive (named `ngApp`; Angular uses
|
||||
`name-with-dashes` for attribute names and `camelCase` for the corresponding directive name)
|
||||
used to flag an element which Angular should consider to be the root element of our application.
|
||||
This gives application developers the freedom to tell Angular if the entire html page or only a
|
||||
@@ -127,7 +127,7 @@ being the element on which the `ngApp` directive was defined.
|
||||
|
||||
* Double-curly binding with an expression:
|
||||
|
||||
Nothing here {{'yet' + '!'}}`
|
||||
Nothing here {{'yet' + '!'}}
|
||||
|
||||
This line demonstrates the core feature of Angular's templating capabilities – a binding, denoted
|
||||
by double-curlies `{{ }}` as well as a simple expression `'yet' + '!'` used in this binding.
|
||||
|
||||
+39
-5
@@ -55,12 +55,15 @@ describe('ngdoc', function() {
|
||||
'@name a\n' +
|
||||
'@param {*} a short\n' +
|
||||
'@param {Type} b med\n' +
|
||||
'@param {Class=} [c=2] long\nline');
|
||||
'@param {Class=} [c=2] long\nline\n' +
|
||||
'@param {function(number, string=)} d fn with optional arguments');
|
||||
doc.parse();
|
||||
expect(doc.param).toEqual([
|
||||
{name:'a', description:'<p>short</p>', type:'*', optional:false, 'default':undefined},
|
||||
{name:'b', description:'<p>med</p>', type:'Type', optional:false, 'default':undefined},
|
||||
{name:'c', description:'<p>long\nline</p>', type:'Class', optional:true, 'default':'2'}
|
||||
{name:'c', description:'<p>long\nline</p>', type:'Class', optional:true, 'default':'2'},
|
||||
{name:'d', description:'<p>fn with optional arguments</p>',
|
||||
type: 'function(number, string=)', optional: false, 'default':undefined}
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -318,9 +321,9 @@ describe('ngdoc', function() {
|
||||
});
|
||||
|
||||
it('should not parse @property without a type', function() {
|
||||
var doc = new Doc("@property fake");
|
||||
var doc = new Doc("@property fake", 'test.js', '44');
|
||||
expect(function() { doc.parse(); }).
|
||||
toThrow(new Error("Not a valid 'property' format: fake"));
|
||||
toThrow(new Error("Not a valid 'property' format: fake (found in: test.js:44)"));
|
||||
});
|
||||
|
||||
it('should parse @property with type', function() {
|
||||
@@ -350,15 +353,30 @@ describe('ngdoc', function() {
|
||||
describe('@returns', function() {
|
||||
it('should not parse @returns without type', function() {
|
||||
var doc = new Doc("@returns lala");
|
||||
expect(doc.parse).toThrow();
|
||||
expect(function() { doc.parse(); }).
|
||||
toThrow();
|
||||
});
|
||||
|
||||
|
||||
it('should not parse @returns with invalid type', function() {
|
||||
var doc = new Doc("@returns {xx}x} lala", 'test.js', 34);
|
||||
expect(function() { doc.parse(); }).
|
||||
toThrow(new Error("Not a valid 'returns' format: {xx}x} lala (found in: test.js:34)"));
|
||||
});
|
||||
|
||||
|
||||
it('should parse @returns with type and description', function() {
|
||||
var doc = new Doc("@name a\n@returns {string} descrip tion");
|
||||
doc.parse();
|
||||
expect(doc.returns).toEqual({type: 'string', description: '<p>descrip tion</p>'});
|
||||
});
|
||||
|
||||
it('should parse @returns with complex type and description', function() {
|
||||
var doc = new Doc("@name a\n@returns {function(string, number=)} description");
|
||||
doc.parse();
|
||||
expect(doc.returns).toEqual({type: 'function(string, number=)', description: '<p>description</p>'});
|
||||
});
|
||||
|
||||
it('should transform description of @returns with markdown', function() {
|
||||
var doc = new Doc("@name a\n@returns {string} descrip *tion*");
|
||||
doc.parse();
|
||||
@@ -457,6 +475,22 @@ describe('ngdoc', function() {
|
||||
'<div><p>I am self.</p></div>');
|
||||
});
|
||||
});
|
||||
|
||||
describe('@animations', function() {
|
||||
it('should render @this', function() {
|
||||
var doc = new Doc('@name a\n@animations\nenter - Add text\nleave - Remove text\n');
|
||||
doc.ngdoc = 'filter';
|
||||
doc.parse();
|
||||
expect(doc.html()).toContain(
|
||||
'<h3 id="Animations">Animations</h3>\n' +
|
||||
'<div class="animations">' +
|
||||
'<ul>' +
|
||||
'<li>enter - Add text</li>' +
|
||||
'<li>leave - Remove text</li>' +
|
||||
'</ul>' +
|
||||
'</div>');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('usage', function() {
|
||||
|
||||
+3
-1
@@ -89,7 +89,9 @@ DOM.prototype = {
|
||||
replace(/-+/gm, '-').
|
||||
replace(/-*$/gm, '');
|
||||
anchor = {'id': id};
|
||||
className = {'class': id.toLowerCase().replace(/[._]/mg, '-')};
|
||||
var classNameValue = id.toLowerCase().replace(/[._]/mg, '-');
|
||||
if(classNameValue == 'hide') classNameValue = '';
|
||||
className = {'class': classNameValue};
|
||||
}
|
||||
this.tag('h' + this.headingDepth, anchor, heading);
|
||||
if (content instanceof Array) {
|
||||
|
||||
+24
-6
@@ -57,12 +57,27 @@ exports.Example.prototype.addSource = function(name, content) {
|
||||
}
|
||||
};
|
||||
|
||||
exports.Example.prototype.enableAnimations = function() {
|
||||
this.animations = true;
|
||||
};
|
||||
|
||||
exports.Example.prototype.disableAnimations = function() {
|
||||
this.animations = false;
|
||||
};
|
||||
|
||||
exports.Example.prototype.toHtml = function() {
|
||||
return '<h2>Source</h2>\n' +
|
||||
this.toHtmlEdit() +
|
||||
this.toHtmlTabs() +
|
||||
'<h2>Demo</h2>\n' +
|
||||
this.toHtmlEmbed();
|
||||
var html = "<h2>Source</h2>\n";
|
||||
html += this.toHtmlEdit();
|
||||
html += this.toHtmlTabs();
|
||||
if(this.animations) {
|
||||
html += '<div class="pull-right">';
|
||||
html += ' <button class="btn btn-primary" ng-click="animationsOff=true" ng-hide="animationsOff">Animations on</button>';
|
||||
html += ' <button class="btn btn-primary disabled" ng-click="animationsOff=false" ng-show="animationsOff">Animations off</button>';
|
||||
html += '</div>';
|
||||
}
|
||||
html += "<h2>Demo</h2>\n";
|
||||
html += this.toHtmlEmbed();
|
||||
return html;
|
||||
};
|
||||
|
||||
|
||||
@@ -116,7 +131,10 @@ exports.Example.prototype.toHtmlTabs = function() {
|
||||
|
||||
exports.Example.prototype.toHtmlEmbed = function() {
|
||||
var out = [];
|
||||
out.push('<div class="well doc-example-live"');
|
||||
out.push('<div class="well doc-example-live animator-container"');
|
||||
if(this.animations) {
|
||||
out.push(" ng-class=\"{'animations-off':animationsOff == true}\"");
|
||||
}
|
||||
out.push(' ng-embed-app="' + this.module + '"');
|
||||
out.push(' ng-set-html="' + this.html[0].id + '"');
|
||||
out.push(' ng-eval-javascript="' + ids(this.js) + '">');
|
||||
|
||||
@@ -5,10 +5,6 @@ var reader = require('./reader.js'),
|
||||
appCache = require('./appCache.js').appCache,
|
||||
Q = require('qq');
|
||||
|
||||
process.on('uncaughtException', function(err) {
|
||||
console.error(err.stack || err);
|
||||
});
|
||||
|
||||
var start = now();
|
||||
var docs;
|
||||
|
||||
@@ -36,16 +32,16 @@ writer.makeDir('build/docs/', true).then(function() {
|
||||
});
|
||||
}).then(function printStats() {
|
||||
console.log('DONE. Generated ' + docs.length + ' pages in ' + (now()-start) + 'ms.' );
|
||||
}).done();
|
||||
});
|
||||
|
||||
|
||||
function writeTheRest(writesFuture) {
|
||||
var metadata = ngdoc.metadata(docs);
|
||||
|
||||
writesFuture.push(writer.symlinkTemplate('css'));
|
||||
writesFuture.push(writer.symlinkTemplate('font'));
|
||||
writesFuture.push(writer.symlink('../../docs/img', 'build/docs/img'));
|
||||
writesFuture.push(writer.symlinkTemplate('js'));
|
||||
writesFuture.push(writer.symlinkTemplate('css', 'dir'));
|
||||
writesFuture.push(writer.symlinkTemplate('font', 'dir'));
|
||||
writesFuture.push(writer.symlink('../../docs/img', 'build/docs/img', 'dir'));
|
||||
writesFuture.push(writer.symlinkTemplate('js', 'dir'));
|
||||
|
||||
var manifest = 'manifest="/build/docs/appcache.manifest"';
|
||||
|
||||
|
||||
+158
-44
@@ -8,6 +8,8 @@ var htmlEscape = require('./dom.js').htmlEscape;
|
||||
var Example = require('./example.js').Example;
|
||||
var NEW_LINE = /\n\r?/;
|
||||
var globalID = 0;
|
||||
var fs = require('fs');
|
||||
var fspath = require('path');
|
||||
|
||||
exports.trim = trim;
|
||||
exports.metadata = metadata;
|
||||
@@ -113,18 +115,58 @@ Doc.prototype = {
|
||||
return id;
|
||||
}
|
||||
|
||||
function extractInlineDocCode(text, tag) {
|
||||
if(tag == 'all') {
|
||||
//use a greedy operator to match the last </docs> tag
|
||||
regex = /\/\/<docs.*?>([.\s\S]+)\/\/<\/docs>/im;
|
||||
}
|
||||
else {
|
||||
//use a non-greedy operator to match the next </docs> tag
|
||||
regex = new RegExp("\/\/<docs\\s*tag=\"" + tag + "\".*?>([.\\s\\S]+?)\/\/<\/docs>","im");
|
||||
}
|
||||
var matches = regex.exec(text.toString());
|
||||
return matches && matches.length > 1 ? matches[1] : "";
|
||||
}
|
||||
|
||||
parts.forEach(function(text, i) {
|
||||
parts[i] = (text || '').
|
||||
replace(/<example(?:\s+module="([^"]*)")?(?:\s+deps="([^"]*)")?>([\s\S]*?)<\/example>/gmi, function(_, module, deps, content) {
|
||||
replace(/<example(?:\s+module="([^"]*)")?(?:\s+deps="([^"]*)")?(\s+animations="true")?>([\s\S]*?)<\/example>/gmi,
|
||||
function(_, module, deps, animations, content) {
|
||||
|
||||
var example = new Example(self.scenarios);
|
||||
if(animations) {
|
||||
example.enableAnimations();
|
||||
}
|
||||
|
||||
example.setModule(module);
|
||||
example.addDeps(deps);
|
||||
content.replace(/<file\s+name="([^"]*)"\s*>([\s\S]*?)<\/file>/gmi, function(_, name, content) {
|
||||
example.addSource(name, content);
|
||||
});
|
||||
content.replace(/<file\s+src="([^"]+)"(?:\s+tag="([^"]+)")?(?:\s+name="([^"]+)")?\s*\/?>/gmi, function(_, file, tag, name) {
|
||||
if(fspath.existsSync(file)) {
|
||||
var content = fs.readFileSync(file, 'utf8');
|
||||
if(content && content.length > 0) {
|
||||
if(tag && tag.length > 0) {
|
||||
content = extractInlineDocCode(content, tag);
|
||||
}
|
||||
name = name && name.length > 0 ? name : fspath.basename(file);
|
||||
example.addSource(name, content);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
})
|
||||
return placeholder(example.toHtml());
|
||||
}).
|
||||
replace(/(?:\*\s+)?<file.+?src="([^"]+)"(?:\s+tag="([^"]+)")?\s*\/?>/i, function(_, file, tag) {
|
||||
if(fspath.existsSync(file)) {
|
||||
var content = fs.readFileSync(file, 'utf8');
|
||||
if(tag && tag.length > 0) {
|
||||
content = extractInlineDocCode(content, tag);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
}).
|
||||
replace(/^<doc:example(\s+[^>]*)?>([\s\S]*)<\/doc:example>/mi, function(_, attrs, content) {
|
||||
var html, script, scenario,
|
||||
example = new Example(self.scenarios);
|
||||
@@ -203,7 +245,7 @@ Doc.prototype = {
|
||||
flush();
|
||||
this.shortName = this.name.split(/[\.:#]/).pop().trim();
|
||||
this.id = this.id || // if we have an id just use it
|
||||
(((this.file||'').match(/.*\/([^\/]*)\.ngdoc/)||{})[1]) || // try to extract it from file name
|
||||
(((this.file||'').match(/.*(\/|\\)([^(\/|\\)]*)\.ngdoc/)||{})[2]) || // try to extract it from file name
|
||||
this.name; // default to name
|
||||
this.description = this.markdown(this.description);
|
||||
this.example = this.markdown(this.example);
|
||||
@@ -214,23 +256,25 @@ Doc.prototype = {
|
||||
if (atName) {
|
||||
var text = trim(atText.join('\n')), match;
|
||||
if (atName == 'param') {
|
||||
match = text.match(/^\{([^}=]+)(=)?\}\s+(([^\s=]+)|\[(\S+)=([^\]]+)\])\s+(.*)/);
|
||||
// 1 12 2 34 4 5 5 6 6 3 7 7
|
||||
match = text.match(/^\{([^}]+)\}\s+(([^\s=]+)|\[(\S+)=([^\]]+)\])\s+(.*)/);
|
||||
// 1 1 23 3 4 4 5 5 2 6 6
|
||||
if (!match) {
|
||||
throw new Error("Not a valid 'param' format: " + text);
|
||||
throw new Error("Not a valid 'param' format: " + text + ' (found in: ' + self.file + ':' + self.line + ')');
|
||||
}
|
||||
|
||||
var optional = (match[1].slice(-1) === '=');
|
||||
var param = {
|
||||
name: match[5] || match[4],
|
||||
description:self.markdown(text.replace(match[0], match[7])),
|
||||
type: match[1],
|
||||
optional: !!match[2],
|
||||
'default':match[6]
|
||||
name: match[4] || match[3],
|
||||
description:self.markdown(text.replace(match[0], match[6])),
|
||||
type: optional ? match[1].substring(0, match[1].length-1) : match[1],
|
||||
optional: optional,
|
||||
'default':match[5]
|
||||
};
|
||||
self.param.push(param);
|
||||
} else if (atName == 'returns' || atName == 'return') {
|
||||
match = text.match(/^\{([^}=]+)\}\s+(.*)/);
|
||||
match = text.match(/^\{([^}]+)\}\s+(.*)/);
|
||||
if (!match) {
|
||||
throw new Error("Not a valid 'returns' format: " + text + ' in ' + self.file + ':' + self.line);
|
||||
throw new Error("Not a valid 'returns' format: " + text + ' (found in: ' + self.file + ':' + self.line + ')');
|
||||
}
|
||||
self.returns = {
|
||||
type: match[1],
|
||||
@@ -245,7 +289,7 @@ Doc.prototype = {
|
||||
} else if(atName == 'property') {
|
||||
match = text.match(/^\{(\S+)\}\s+(\S+)(\s+(.*))?/);
|
||||
if (!match) {
|
||||
throw new Error("Not a valid 'property' format: " + text);
|
||||
throw new Error("Not a valid 'property' format: " + text + ' (found in: ' + self.file + ':' + self.line + ')');
|
||||
}
|
||||
var property = new Doc({
|
||||
type: match[1],
|
||||
@@ -270,8 +314,9 @@ Doc.prototype = {
|
||||
self = this;
|
||||
|
||||
dom.h(title(this.name), function() {
|
||||
notice('deprecated', 'Deprecated API', self.deprecated);
|
||||
|
||||
notice('deprecated', 'Deprecated API', self.deprecated);
|
||||
dom.tag('a', {href: 'http://github.com/angular/angular.js/edit/master/' + self.file, class: 'improve-docs btn btn-primary'}, 'Improve this doc');
|
||||
if (self.ngdoc != 'overview') {
|
||||
dom.h('Description', self.description, dom.html);
|
||||
}
|
||||
@@ -325,6 +370,18 @@ Doc.prototype = {
|
||||
});
|
||||
dom.html(param.description);
|
||||
});
|
||||
if(this.animations) {
|
||||
dom.h('Animations', this.animations, function(animations){
|
||||
dom.html('<ul>');
|
||||
var animations = animations.split("\n");
|
||||
animations.forEach(function(ani) {
|
||||
dom.html('<li>');
|
||||
dom.text(ani);
|
||||
dom.html('</li>');
|
||||
});
|
||||
dom.html('</ul>');
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
html_usage_returns: function(dom) {
|
||||
@@ -383,40 +440,97 @@ Doc.prototype = {
|
||||
var self = this;
|
||||
dom.h('Usage', function() {
|
||||
var restrict = self.restrict || 'AC';
|
||||
|
||||
if (restrict.match(/E/)) {
|
||||
dom.text('as element (see ');
|
||||
dom.html('<p>');
|
||||
dom.text('This directive can be used as custom element, but we aware of ');
|
||||
dom.tag('a', {href:'guide/ie'}, 'IE restrictions');
|
||||
dom.text(')');
|
||||
dom.code(function() {
|
||||
dom.text('<');
|
||||
dom.text(dashCase(self.shortName));
|
||||
renderParams('\n ', '="', '"');
|
||||
dom.text('>\n</');
|
||||
dom.text(dashCase(self.shortName));
|
||||
dom.text('>');
|
||||
});
|
||||
dom.text('.');
|
||||
dom.html('</p>');
|
||||
}
|
||||
if (restrict.match(/A/)) {
|
||||
var element = self.element || 'ANY';
|
||||
dom.text('as attribute');
|
||||
dom.code(function() {
|
||||
dom.text('<' + element + ' ');
|
||||
dom.text(dashCase(self.shortName));
|
||||
renderParams('\n ', '="', '"', true);
|
||||
dom.text('>\n ...\n');
|
||||
dom.text('</' + element + '>');
|
||||
});
|
||||
}
|
||||
if (restrict.match(/C/)) {
|
||||
dom.text('as class');
|
||||
var element = self.element || 'ANY';
|
||||
dom.code(function() {
|
||||
dom.text('<' + element + ' class="');
|
||||
dom.text(dashCase(self.shortName));
|
||||
renderParams(' ', ': ', ';', true);
|
||||
dom.text('">\n ...\n');
|
||||
dom.text('</' + element + '>');
|
||||
|
||||
if (self.usage) {
|
||||
dom.tag('pre', function() {
|
||||
dom.tag('code', function() {
|
||||
dom.text(self.usage);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
if (restrict.match(/E/)) {
|
||||
dom.text('as element:');
|
||||
dom.code(function() {
|
||||
dom.text('<');
|
||||
dom.text(dashCase(self.shortName));
|
||||
renderParams('\n ', '="', '"');
|
||||
dom.text('>\n</');
|
||||
dom.text(dashCase(self.shortName));
|
||||
dom.text('>');
|
||||
});
|
||||
}
|
||||
if (restrict.match(/A/)) {
|
||||
var element = self.element || 'ANY';
|
||||
dom.text('as attribute');
|
||||
dom.code(function() {
|
||||
dom.text('<' + element + ' ');
|
||||
dom.text(dashCase(self.shortName));
|
||||
renderParams('\n ', '="', '"', true);
|
||||
dom.text('>\n ...\n');
|
||||
dom.text('</' + element + '>');
|
||||
});
|
||||
}
|
||||
if (restrict.match(/C/)) {
|
||||
dom.text('as class');
|
||||
var element = self.element || 'ANY';
|
||||
dom.code(function() {
|
||||
dom.text('<' + element + ' class="');
|
||||
dom.text(dashCase(self.shortName));
|
||||
renderParams(' ', ': ', ';', true);
|
||||
dom.text('">\n ...\n');
|
||||
dom.text('</' + element + '>');
|
||||
});
|
||||
}
|
||||
if(self.animations) {
|
||||
var animations = [], matches = self.animations.split("\n");
|
||||
matches.forEach(function(ani) {
|
||||
var name = ani.match(/^\s*(.+?)\s*-/)[1];
|
||||
animations.push(name);
|
||||
});
|
||||
|
||||
dom.html('with <span id="animations">animations</span>');
|
||||
var comment;
|
||||
if(animations.length == 1) {
|
||||
comment = 'The ' + animations[0] + ' animation is supported';
|
||||
}
|
||||
else {
|
||||
var rhs = animations[animations.length-1];
|
||||
var lhs = '';
|
||||
for(var i=0;i<animations.length-1;i++) {
|
||||
if(i>0) {
|
||||
lhs += ', ';
|
||||
}
|
||||
lhs += animations[i];
|
||||
}
|
||||
comment = 'The ' + lhs + ' and ' + rhs + ' animations are supported';
|
||||
}
|
||||
var element = self.element || 'ANY';
|
||||
dom.code(function() {
|
||||
dom.text('//' + comment + "\n");
|
||||
dom.text('<' + element + ' ');
|
||||
dom.text(dashCase(self.shortName));
|
||||
renderParams('\n ', '="', '"', true);
|
||||
dom.text(' ng-animate="{');
|
||||
animations.forEach(function(ani, index) {
|
||||
if (index) {
|
||||
dom.text(', ');
|
||||
}
|
||||
dom.text(ani + ': \'' + ani + '-animation\'');
|
||||
});
|
||||
dom.text('}">\n ...\n');
|
||||
dom.text('</' + element + '>');
|
||||
});
|
||||
|
||||
dom.html('<a href="api/ng.$animator#Methods">Click here</a> to learn more about the steps involved in the animation.');
|
||||
}
|
||||
}
|
||||
self.html_usage_directiveInfo(dom);
|
||||
self.html_usage_parameters(dom);
|
||||
|
||||
+3
-2
@@ -7,7 +7,8 @@ exports.collect = collect;
|
||||
|
||||
var ngdoc = require('./ngdoc.js'),
|
||||
Q = require('qq'),
|
||||
qfs = require('q-fs');
|
||||
qfs = require('q-fs'),
|
||||
PATH = require('path');
|
||||
|
||||
var NEW_LINE = /\n\r?/;
|
||||
|
||||
@@ -43,7 +44,7 @@ function collect() {
|
||||
var work2;
|
||||
if (file.match(/\.ngdoc$/)) {
|
||||
work2 = Q.when(qfs.read(file, 'b'), function(content){
|
||||
var section = '@section ' + file.split('/')[2] + '\n';
|
||||
var section = '@section ' + file.split(PATH.sep)[2] + '\n';
|
||||
allDocs.push(new ngdoc.Doc(section + content.toString(),file, 1).parse());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# current angular version. If this rule matches the appcache-offline.manifest will be served for
|
||||
# requests to appcache.manifest
|
||||
#
|
||||
# This file must be processed by Rake in order to replace %ANGULAR_VERSION% with the actual version.
|
||||
# This file must be processed by Grunt in order to replace %ANGULAR_VERSION% with the actual version.
|
||||
|
||||
Options -Indexes
|
||||
RewriteEngine on
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
.reveal-setup {
|
||||
-webkit-transition:1s linear all;
|
||||
-moz-transition:1s linear all;
|
||||
-ms-transition:1s linear all;
|
||||
-o-transition:1s linear all;
|
||||
transition:1s linear all;
|
||||
|
||||
opacity:0;
|
||||
}
|
||||
.reveal-setup.reveal-start {
|
||||
opacity:1;
|
||||
}
|
||||
|
||||
.slide-reveal-setup {
|
||||
-webkit-transition:0.5s linear all;
|
||||
-moz-transition:0.5s linear all;
|
||||
-ms-transition:0.5s linear all;
|
||||
-o-transition:0.5s linear all;
|
||||
transition:0.5s linear all;
|
||||
opacity:0.5;
|
||||
}
|
||||
.slide-reveal-setup.slide-reveal-start {
|
||||
opacity:1;
|
||||
}
|
||||
|
||||
.slide-enter-setup {
|
||||
-webkit-transition:0.5s linear all;
|
||||
-moz-transition:0.5s linear all;
|
||||
-ms-transition:0.5s linear all;
|
||||
-o-transition:0.5s linear all;
|
||||
transition:0.5s linear all;
|
||||
|
||||
position:relative;
|
||||
left:10px;
|
||||
opacity:0;
|
||||
}
|
||||
.slide-enter-setup.slide-enter-start {
|
||||
left:0;
|
||||
opacity:1;
|
||||
}
|
||||
|
||||
.slide-leave-setup {
|
||||
-webkit-transition:0.5s linear all;
|
||||
-moz-transition:0.5s linear all;
|
||||
-ms-transition:0.5s linear all;
|
||||
-o-transition:0.5s linear all;
|
||||
transition:0.5s linear all;
|
||||
|
||||
opacity:1;
|
||||
}
|
||||
.slide-leave-setup.slide-leave-start {
|
||||
opacity:0;
|
||||
}
|
||||
|
||||
.example-animate-container {
|
||||
position:relative;
|
||||
background:white;
|
||||
border:1px solid black;
|
||||
height:40px;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
.example-animate-container > div {
|
||||
padding:1em;
|
||||
}
|
||||
|
||||
.animator-container.animations-off * {
|
||||
-webkit-transition: none;
|
||||
-moz-transition: none;
|
||||
-ms-transition: none;
|
||||
-o-transition: color 0 ease-in; /* opera is special :) */
|
||||
transition: none;
|
||||
}
|
||||
@@ -3,6 +3,15 @@ img.AngularJS-small {
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
/* this is here to avoid the display=block shuffling of ngShow */
|
||||
.breadcrumb li > * {
|
||||
float:left;
|
||||
margin:0 2px 0 0;
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
padding-bottom:2px;
|
||||
}
|
||||
|
||||
.clear-navbar {
|
||||
margin-top: 60px;
|
||||
@@ -86,6 +95,10 @@ img.AngularJS-small {
|
||||
/* Content */
|
||||
/* =============================== */
|
||||
|
||||
.improve-docs {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.hint {
|
||||
font-size: .7em;
|
||||
color: #c0c0c0;
|
||||
|
||||
@@ -34,11 +34,13 @@
|
||||
addTag('link', {rel: 'stylesheet', href: 'css/bootstrap.min.css', type: 'text/css'});
|
||||
addTag('link', {rel: 'stylesheet', href: 'css/font-awesome.css', type: 'text/css'});
|
||||
addTag('link', {rel: 'stylesheet', href: 'css/docs.css', type: 'text/css'});
|
||||
addTag('link', {rel: 'stylesheet', href: 'css/animations.css', type: 'text/css'});
|
||||
if (jQuery) addTag('script', {src: debug ? 'js/jquery.js' : 'js/jquery.min.js'});
|
||||
addTag('script', {src: path('angular.js')}, sync);
|
||||
addTag('script', {src: path('angular-resource.js') }, sync);
|
||||
addTag('script', {src: path('angular-cookies.js') }, sync);
|
||||
addTag('script', {src: path('angular-sanitize.js') }, sync);
|
||||
addTag('script', {src: path('angular-mobile.js') }, sync);
|
||||
addTag('script', {src: path('angular-bootstrap.js') }, sync);
|
||||
addTag('script', {src: path('angular-bootstrap-prettify.js') }, sync);
|
||||
addTag('script', {src: 'js/docs.js'}, sync);
|
||||
@@ -230,21 +232,21 @@
|
||||
<li class="nav-header section" ng-show="module.directives">
|
||||
<a href="{{URL.directive}}" class="guide">directive</a>
|
||||
</li>
|
||||
<li ng-repeat="page in module.directives" ng-class="navClass(page)">
|
||||
<li ng-repeat="page in module.directives" ng-class="navClass(page)" ng-animate="'slide'">
|
||||
<a href="{{page.url}}" tabindex="2">{{page.shortName}}</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-header section" ng-show="module.filters">
|
||||
<a href="{{URL.filter}}" class="guide">filter</a>
|
||||
</li>
|
||||
<li ng-repeat="page in module.filters" ng-class="navClass(page)">
|
||||
<li ng-repeat="page in module.filters" ng-class="navClass(page)" ng-animate="'slide'">
|
||||
<a href="{{page.url}}" tabindex="2">{{page.shortName}}</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-header section" ng-show="module.services">
|
||||
<a href="{{URL.service}}" class="guide">service</a>
|
||||
</li>
|
||||
<li ng-repeat="service in module.services" ng-class="navClass(service.instance, service.provider)">
|
||||
<li ng-repeat="service in module.services" ng-animate="'slide'" ng-class="navClass(service.instance, service.provider)">
|
||||
<a ng-show="service.provider" class="pull-right" href="{{service.provider.url}}" tabindex="2"><i class="icon-cog"></i></a>
|
||||
<a href="{{service.instance.url}}" tabindex="2">{{service.name}}</a>
|
||||
</li>
|
||||
@@ -252,7 +254,7 @@
|
||||
<li class="nav-header section" ng-show="module.types">
|
||||
<a href="{{URL.type}}" class="guide">Types</a>
|
||||
</li>
|
||||
<li ng-repeat="page in module.types" ng-class="navClass(page)">
|
||||
<li ng-repeat="page in module.types" ng-class="navClass(page)" ng-animate="'slide'">
|
||||
<a href="{{page.url}}" tabindex="2">{{page.shortName}}</a>
|
||||
</li>
|
||||
|
||||
@@ -280,7 +282,7 @@
|
||||
|
||||
<div id="loading" ng-show="loading">Loading...</div>
|
||||
|
||||
<div ng-hide="loading" ng-include src="currentPage.partialUrl" onload="afterPartialLoaded()" autoscroll class="content"></div>
|
||||
<div ng-hide="loading" ng-include src="currentPage.partialUrl" onload="afterPartialLoaded()" autoscroll class="content" ng-animate="{enter: 'slide-reveal'}" ></div>
|
||||
|
||||
<div id="disqus" class="disqus">
|
||||
<h2>Discussion</h2>
|
||||
|
||||
+4
-5
@@ -61,22 +61,21 @@ exports.copy = function(from, to, transform) {
|
||||
|
||||
|
||||
exports.symlink = symlink;
|
||||
function symlink(from, to) {
|
||||
function symlink(from, to, type) {
|
||||
return qfs.exists(to).then(function(exists) {
|
||||
if (!exists) {
|
||||
return qfs.symbolicLink(to, from);
|
||||
return qfs.symbolicLink(to, from, type);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
exports.symlinkTemplate = symlinkTemplate;
|
||||
function symlinkTemplate(filename) {
|
||||
function symlinkTemplate(filename, type) {
|
||||
var dest = OUTPUT_DIR + filename,
|
||||
dirDepth = dest.split('/').length,
|
||||
src = Array(dirDepth).join('../') + 'docs/src/templates/' + filename;
|
||||
|
||||
return symlink(src, dest);
|
||||
return symlink(src, dest, type);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+158
-121
@@ -16,14 +16,14 @@
|
||||
/**
|
||||
* @fileoverview A utility to get better currency format pattern.
|
||||
*
|
||||
* This module implement a new currency format representation model. It
|
||||
* This module implements a new currency format representation model. It
|
||||
* provides 3 currency representation forms: global, portable and local. Local
|
||||
* format is the most popular format people use to represent currency in its
|
||||
* circulating country without worrying about how it should be distinguished
|
||||
* from other currencies. Global format is a formal representation in context
|
||||
* of multiple currencies in same page, it is ISO 4217 currency code. Portable
|
||||
* format is a compromise between global and local. It looks similar to how
|
||||
* people would like to see how their currencies is being represented in other
|
||||
* people would like to see how their currency is being represented in other
|
||||
* media. While at the same time, it should be distinguishable to world's
|
||||
* popular currencies (like USD, EUR) and currencies somewhat relevant in the
|
||||
* area (like CNY in HK, though native currency is HKD). There is no guarantee
|
||||
@@ -43,15 +43,14 @@ goog.i18n.currency.PRECISION_MASK_ = 0x07;
|
||||
|
||||
|
||||
/**
|
||||
* If this flag is set, it means the currency sign should position before
|
||||
* number.
|
||||
* Whether the currency sign should be positioned after the number.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.currency.POSITION_FLAG_ = 0x08;
|
||||
|
||||
|
||||
/**
|
||||
* Should a space to inserted between number and currency sign.
|
||||
* Whether a space should be inserted between the number and currency sign.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.currency.SPACE_FLAG_ = 0x20;
|
||||
@@ -59,8 +58,8 @@ goog.i18n.currency.SPACE_FLAG_ = 0x20;
|
||||
|
||||
/**
|
||||
* This function will add tier2 currency support. Be default, only tier1
|
||||
* (most popular currencies) are supportted. If an application really need
|
||||
* to support some of the rarely used currency, it should call this function
|
||||
* (most popular currencies) are supported. If an application really needs
|
||||
* to support some of the rarely used currencies, it should call this function
|
||||
* before any other functions in this namespace.
|
||||
*/
|
||||
goog.i18n.currency.addTier2Support = function() {
|
||||
@@ -75,8 +74,8 @@ goog.i18n.currency.addTier2Support = function() {
|
||||
* Global currency pattern always uses ISO-4217 currency code as prefix. Local
|
||||
* currency sign is added if it is different from currency code. Each currency
|
||||
* is unique in this form. The negative side is that ISO code looks weird in
|
||||
* some countries as poeple normally do not use it. Local currency sign
|
||||
* alleviate the problem, but also make it a little verbose.
|
||||
* some countries as people normally do not use it. Local currency sign
|
||||
* alleviates the problem, but also makes it a little verbose.
|
||||
*
|
||||
* @param {string} currencyCode ISO-4217 3-letter currency code.
|
||||
* @return {string} Global currency pattern string for given currency.
|
||||
@@ -85,9 +84,6 @@ goog.i18n.currency.getGlobalCurrencyPattern = function(currencyCode) {
|
||||
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
|
||||
var patternNum = info[0];
|
||||
if (currencyCode == info[1]) {
|
||||
if ((patternNum & goog.i18n.currency.POSITION_FLAG_) == 0) {
|
||||
patternNum |= goog.i18n.currency.SPACE_FLAG_;
|
||||
}
|
||||
return goog.i18n.currency.getCurrencyPattern_(patternNum, info[1]);
|
||||
}
|
||||
return currencyCode + ' ' +
|
||||
@@ -104,10 +100,8 @@ goog.i18n.currency.getGlobalCurrencyPattern = function(currencyCode) {
|
||||
*/
|
||||
goog.i18n.currency.getGlobalCurrencySign = function(currencyCode) {
|
||||
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
|
||||
if (currencyCode == info[1]) {
|
||||
return currencyCode;
|
||||
}
|
||||
return currencyCode + ' ' + info[1];
|
||||
return (currencyCode == info[1]) ? currencyCode :
|
||||
currencyCode + ' ' + info[1];
|
||||
};
|
||||
|
||||
|
||||
@@ -128,6 +122,7 @@ goog.i18n.currency.getLocalCurrencyPattern = function(currencyCode) {
|
||||
/**
|
||||
* Returns local currency sign string for those applications that need to
|
||||
* handle currency sign separately.
|
||||
*
|
||||
* @param {string} currencyCode ISO-4217 3-letter currency code.
|
||||
* @return {string} Local currency sign for given currency.
|
||||
*/
|
||||
@@ -156,6 +151,7 @@ goog.i18n.currency.getPortableCurrencyPattern = function(currencyCode) {
|
||||
/**
|
||||
* Return portable currency sign string for those applications that need to
|
||||
* handle currency sign themselves.
|
||||
*
|
||||
* @param {string} currencyCode ISO-4217 3-letter currency code.
|
||||
* @return {string} Portable currency sign for given currency.
|
||||
*/
|
||||
@@ -165,10 +161,13 @@ goog.i18n.currency.getPortableCurrencySign = function(currencyCode) {
|
||||
|
||||
|
||||
/**
|
||||
* This function returns the default currency sign position. Some application
|
||||
* This function returns the default currency sign position. Some applications
|
||||
* may want to handle currency sign and currency amount separately. This
|
||||
* function can be used in such situation to position the currency sign
|
||||
* relative to amount field correctly.
|
||||
* function can be used in such situations to correctly position the currency
|
||||
* sign relative to the amount.
|
||||
*
|
||||
* To match the behavior of ICU, position is not determined by display locale.
|
||||
*
|
||||
* @param {string} currencyCode ISO-4217 3-letter currency code.
|
||||
* @return {boolean} true if currency should be positioned before amount field.
|
||||
*/
|
||||
@@ -179,13 +178,12 @@ goog.i18n.currency.isPrefixSignPosition = function(currencyCode) {
|
||||
|
||||
|
||||
/**
|
||||
* This function construct the currency pattern. Currency sign is provided. The
|
||||
* This function constructs the currency pattern. Currency sign is provided. The
|
||||
* pattern information is encoded in patternNum.
|
||||
*
|
||||
* @param {number} patternNum Encoded pattern number that has
|
||||
* currency pattern information.
|
||||
* @param {string} sign the currency sign that will be used in pattern.
|
||||
*
|
||||
* @param {string} sign The currency sign that will be used in pattern.
|
||||
* @return {string} currency pattern string.
|
||||
* @private
|
||||
*/
|
||||
@@ -211,56 +209,97 @@ goog.i18n.currency.getCurrencyPattern_ = function(patternNum, sign) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Modify currency pattern string by adjusting precision for given currency.
|
||||
* Standard currency pattern will have 2 digit after decimal point.
|
||||
* Examples:
|
||||
* $#,##0.00 -> $#,##0 (precision == 0)
|
||||
* $#,##0.00 -> $#,##0.0 (precision == 1)
|
||||
* $#,##0.00 -> $#,##0.000 (precision == 3)
|
||||
*
|
||||
* @param {string} pattern currency pattern string.
|
||||
* @param {string} currencyCode 3-letter currency code.
|
||||
* @return {string} modified currency pattern string.
|
||||
*/
|
||||
goog.i18n.currency.adjustPrecision = function(pattern, currencyCode) {
|
||||
var strParts = ['0'];
|
||||
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
|
||||
var precision = info[0] & goog.i18n.currency.PRECISION_MASK_;
|
||||
if (precision > 0) {
|
||||
strParts.push('.');
|
||||
for (var i = 0; i < precision; i++) {
|
||||
strParts.push('0');
|
||||
}
|
||||
}
|
||||
return pattern.replace(/0.00/g, strParts.join(''));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Tier 1 currency information.
|
||||
*
|
||||
* The first number in the array is a combination of the precision mask and
|
||||
* other flags. The precision mask indicates how many decimal places to show for
|
||||
* the currency. Valid values are [0..7]. The position flag indicates whether
|
||||
* the currency sign should be positioned after the number. Valid values are 0
|
||||
* (before the number) or 16 (after the number). The space flag indicates
|
||||
* whether a space should be inserted between the currency sign and number.
|
||||
* Valid values are 0 (no space) and 24 (space).
|
||||
*
|
||||
* The number in the array is calculated by adding together the mask and flag
|
||||
* values. For example:
|
||||
*
|
||||
* 0: no precision (0), currency sign first (0), no space (0)
|
||||
* 2: two decimals precision (2), currency sign first (0), no space (0)
|
||||
* 18: two decimals precision (2), currency sign last (16), no space (0)
|
||||
* 42: two decimals precision (2), currency sign last (16), space (24)
|
||||
*
|
||||
* @type {!Object.<!Array>}
|
||||
*/
|
||||
goog.i18n.currency.CurrencyInfo = {
|
||||
'AED': [2, '\u062F\u002e\u0625', 'DH'],
|
||||
'ARS': [2, '$', 'AR$'],
|
||||
'AED': [2, 'dh', '\u062f.\u0625.', 'DH'],
|
||||
'AUD': [2, '$', 'AU$'],
|
||||
'BDT': [2, '\u09F3', 'Tk'],
|
||||
'BRL': [2, 'R$', 'R$'],
|
||||
'CAD': [2, '$', 'C$'],
|
||||
'CHF': [2, 'Fr.', 'CHF'],
|
||||
'CHF': [2, 'CHF', 'CHF'],
|
||||
'CLP': [0, '$', 'CL$'],
|
||||
'CNY': [2, '¥', 'RMB¥'],
|
||||
'COP': [2, '$', 'COL$'],
|
||||
'CRC': [2, '\u20a1', 'CR₡'],
|
||||
'CUP': [2, '$', '$MN'],
|
||||
'CZK': [10, 'Kč', 'Kč'],
|
||||
'DKK': [26, 'kr', 'kr'],
|
||||
'COP': [0, '$', 'COL$'],
|
||||
'CRC': [0, '\u20a1', 'CR\u20a1'],
|
||||
'CZK': [2, 'K\u010d', 'K\u010d'],
|
||||
'DKK': [18, 'kr', 'kr'],
|
||||
'DOP': [2, '$', 'RD$'],
|
||||
'EGP': [2, '£', 'LE'],
|
||||
'EUR': [26, '€', '€'],
|
||||
'EUR': [18, '€', '€'],
|
||||
'GBP': [2, '£', 'GB£'],
|
||||
'HKD': [2, '$', 'HK$'],
|
||||
'ILS': [10, '\u20AA', 'IL₪'],
|
||||
'INR': [2, 'Rs', 'Rs'],
|
||||
'ISK': [10, 'kr', 'kr'],
|
||||
'ILS': [2, '\u20AA', 'IL\u20AA'],
|
||||
'INR': [2, '\u20B9', 'Rs'],
|
||||
'ISK': [0, 'kr', 'kr'],
|
||||
'JMD': [2, '$', 'JA$'],
|
||||
'JPY': [0, '¥', 'JP¥'],
|
||||
'KRW': [0, '\u20A9', 'KR₩'],
|
||||
'LKR': [2, 'Rs', 'SLRs'],
|
||||
'MNT': [2, '\u20AE', 'MN₮'],
|
||||
'MNT': [0, '\u20AE', 'MN₮'],
|
||||
'MXN': [2, '$', 'Mex$'],
|
||||
'MYR': [2, 'RM', 'RM'],
|
||||
'NOK': [26, 'kr', 'NOkr'],
|
||||
'NOK': [18, 'kr', 'NOkr'],
|
||||
'PAB': [2, 'B/.', 'B/.'],
|
||||
'PEN': [2, 'S/.', 'S/.'],
|
||||
'PHP': [2, 'P', 'PHP'],
|
||||
'PKR': [2, 'Rs.', 'PKRs.'],
|
||||
'RUB': [10, 'руб', 'руб'],
|
||||
'SAR': [2, '\u0633\u002E\u0631', 'SR'],
|
||||
'SEK': [10, 'kr', 'kr'],
|
||||
'PHP': [2, '\u20B1', 'Php'],
|
||||
'PKR': [0, 'Rs', 'PKRs.'],
|
||||
'RUB': [42, 'руб.', 'руб.'],
|
||||
'SAR': [2, 'Rial', 'Rial'],
|
||||
'SEK': [2, 'kr', 'kr'],
|
||||
'SGD': [2, '$', 'S$'],
|
||||
'THB': [2, '\u0e3f', 'THB'],
|
||||
'TRY': [2, 'YTL', 'YTL'],
|
||||
'TRY': [2, 'TL', 'YTL'],
|
||||
'TWD': [2, 'NT$', 'NT$'],
|
||||
'USD': [2, '$', 'US$'],
|
||||
'UYU': [2, '$', 'UY$'],
|
||||
'VND': [10, '\u20AB', 'VN₫'],
|
||||
'YER': [2, 'YER', 'YER'],
|
||||
'VND': [0, '\u20AB', 'VN\u20AB'],
|
||||
'YER': [0, 'Rial', 'Rial'],
|
||||
'ZAR': [2, 'R', 'ZAR']
|
||||
};
|
||||
|
||||
@@ -270,116 +309,114 @@ goog.i18n.currency.CurrencyInfo = {
|
||||
* @type {!Object.<!Array>}
|
||||
*/
|
||||
goog.i18n.currency.CurrencyInfoTier2 = {
|
||||
'AFN': [18, '\u060b', 'AFN'],
|
||||
'ALL': [2, 'Lek', 'Lek'],
|
||||
'AMD': [10, '\u0564\u0580\u002e', 'dram'],
|
||||
'ANG': [2, '\u0083', 'NAƒ'],
|
||||
'AFN': [16, 'Af.', 'AFN'],
|
||||
'ALL': [0, 'Lek', 'Lek'],
|
||||
'AMD': [0, 'Dram', 'dram'],
|
||||
'AOA': [2, 'Kz', 'Kz'],
|
||||
'AWG': [2, 'ƒ', 'Afl.'],
|
||||
'AZN': [2, 'm', 'man'],
|
||||
'BAM': [18, 'КМ', 'KM'],
|
||||
'ARS': [2, '$', 'AR$'],
|
||||
'AWG': [2, 'Afl.', 'Afl.'],
|
||||
'AZN': [2, 'man.', 'man.'],
|
||||
'BAM': [18, 'KM', 'KM'],
|
||||
'BBD': [2, '$', 'Bds$'],
|
||||
'BGN': [10, '\u043b\u0432', 'лв'],
|
||||
'BHD': [3, '\u0628\u002e\u062f\u002e', 'BD'],
|
||||
'BGN': [2, 'lev', 'lev'],
|
||||
'BHD': [3, 'din', 'din'],
|
||||
'BIF': [0, 'FBu', 'FBu'],
|
||||
'BMD': [2, '$', 'BD$'],
|
||||
'BND': [2, '$', 'B$'],
|
||||
'BOB': [2, 'B$', 'B$'],
|
||||
'BSD': [2, '$', 'B$'],
|
||||
'BOB': [2, 'Bs', 'Bs'],
|
||||
'BSD': [2, '$', 'BS$'],
|
||||
'BTN': [2, 'Nu.', 'Nu.'],
|
||||
'BWP': [2, 'P', 'pula'],
|
||||
'BYR': [0, 'Br', 'Br'],
|
||||
'BYR': [0, 'BYR', 'BYR'],
|
||||
'BZD': [2, '$', 'BZ$'],
|
||||
'CDF': [2, 'F', 'CDF'],
|
||||
'CVE': [2, '$', 'Esc'],
|
||||
'CDF': [2, 'FrCD', 'CDF'],
|
||||
'CUC': [1, '$', 'CUC$'],
|
||||
'CUP': [2, '$', 'CU$'],
|
||||
'CVE': [2, 'CVE', 'Esc'],
|
||||
'DJF': [0, 'Fdj', 'Fdj'],
|
||||
'DZD': [2, '\u062f\u062C', 'DA'],
|
||||
'EEK': [10, 'EEK', 'EEK'],
|
||||
'DZD': [2, 'din', 'din'],
|
||||
'ERN': [2, 'Nfk', 'Nfk'],
|
||||
'ETB': [2, 'Br', 'Br'],
|
||||
'ETB': [2, 'Birr', 'Birr'],
|
||||
'FJD': [2, '$', 'FJ$'],
|
||||
'FKP': [2, '£', 'FK£'],
|
||||
'GEL': [2, 'GEL', 'GEL'],
|
||||
'GHS': [2, '\u20B5', 'GHS¢'],
|
||||
'GHS': [2, 'GHS', 'GHS'],
|
||||
'GIP': [2, '£', 'GI£'],
|
||||
'GMD': [2, 'D', 'GMD'],
|
||||
'GMD': [2, 'GMD', 'GMD'],
|
||||
'GNF': [0, 'FG', 'FG'],
|
||||
'GTQ': [2, 'Q', 'GTQ'],
|
||||
'GYD': [2, '$', 'GY$'],
|
||||
'GYD': [0, '$', 'GY$'],
|
||||
'HNL': [2, 'L', 'HNL'],
|
||||
'HRK': [2, 'kn', 'kn'],
|
||||
'HTG': [2, 'G', 'HTG'],
|
||||
'HUF': [10, 'Ft', 'Ft'],
|
||||
'IDR': [2, 'Rp', 'Rp'],
|
||||
'IQD': [3, '\u0639\u062F', 'IQD'],
|
||||
'IRR': [2, '\ufdfc', 'IRR'],
|
||||
'JOD': [3, 'JOD', 'JOD'],
|
||||
'KES': [2, 'KSh', 'KSh'],
|
||||
'KGS': [2, 'som', 'som'],
|
||||
'KHR': [10, '\u17DB', 'KHR'],
|
||||
'KMF': [0, 'KMF', 'KMF'],
|
||||
'KPW': [2, '\u20A9', 'KPW'],
|
||||
'KWD': [3, '\u062F\u002e\u0643', 'KWD'],
|
||||
'KYD': [2, '$', 'CI$'],
|
||||
'KZT': [10, 'KZT', 'KZT'],
|
||||
'LAK': [2, '\u20AD', 'LA₭'],
|
||||
'LBP': [2, '\u0644\u002e\u0644', 'LBP'],
|
||||
'HTG': [2, 'HTG', 'HTG'],
|
||||
'HUF': [0, 'Ft', 'Ft'],
|
||||
'IDR': [0, 'Rp', 'Rp'],
|
||||
'IQD': [0, 'din', 'IQD'],
|
||||
'IRR': [0, 'Rial', 'IRR'],
|
||||
'JOD': [3, 'din', 'JOD'],
|
||||
'KES': [2, 'Ksh', 'Ksh'],
|
||||
'KGS': [2, 'KGS', 'KGS'],
|
||||
'KHR': [2, 'Riel', 'KHR'],
|
||||
'KMF': [0, 'CF', 'KMF'],
|
||||
'KPW': [0, '\u20A9KP', 'KPW'],
|
||||
'KWD': [3, 'din', 'KWD'],
|
||||
'KYD': [2, '$', 'KY$'],
|
||||
'KZT': [2, '\u20B8', 'KZT'],
|
||||
'LAK': [0, '\u20AD', '\u20AD'],
|
||||
'LBP': [0, 'L£', 'LBP'],
|
||||
'LRD': [2, '$', 'L$'],
|
||||
'LSL': [2, 'L', 'LSL'],
|
||||
'LTL': [10, 'Lt', 'Lt'],
|
||||
'LVL': [10, 'Ls', 'Ls'],
|
||||
'LYD': [3, '\u0644\u002e\u062F', 'LD'],
|
||||
'MAD': [2, '\u0645\u002E\u062F\u002E', 'MAD'],
|
||||
'LSL': [2, 'LSL', 'LSL'],
|
||||
'LTL': [2, 'Lt', 'Lt'],
|
||||
'LVL': [2, 'Ls', 'Ls'],
|
||||
'LYD': [3, 'din', 'LD'],
|
||||
'MAD': [2, 'dh', 'MAD'],
|
||||
'MDL': [2, 'MDL', 'MDL'],
|
||||
'MGA': [1, 'MGA', 'MGA'],
|
||||
'MKD': [2, 'MKD', 'MKD'],
|
||||
'MMK': [2, 'K', 'MMK'],
|
||||
'MOP': [2, 'MOP$', 'MOP$'],
|
||||
'MRO': [1, 'UM', 'UM'],
|
||||
'MUR': [2, 'Rs', 'MURs'],
|
||||
'MVR': [2, 'Rf', 'MRF'],
|
||||
'MWK': [2, 'MK', 'MK'],
|
||||
'MGA': [0, 'Ar', 'MGA'],
|
||||
'MKD': [2, 'din', 'MKD'],
|
||||
'MMK': [0, 'K', 'MMK'],
|
||||
'MOP': [2, 'MOP', 'MOP$'],
|
||||
'MRO': [0, 'MRO', 'MRO'],
|
||||
'MUR': [0, 'MURs', 'MURs'],
|
||||
'MWK': [2, 'MWK', 'MWK'],
|
||||
'MZN': [2, 'MTn', 'MTn'],
|
||||
'NAD': [2, '$', 'N$'],
|
||||
'NGN': [2, '\u20A6', 'NG₦'],
|
||||
'NGN': [2, '\u20A6', 'NG\u20A6'],
|
||||
'NIO': [2, 'C$', 'C$'],
|
||||
'NPR': [2, 'Rs', 'NPRs'],
|
||||
'NZD': [2, '$', 'NZ$'],
|
||||
'OMR': [3, '\u0639\u002E\u062F\u002E', 'OMR'],
|
||||
'PGK': [2, 'K', 'PGK'],
|
||||
'PLN': [10, 'zł', 'zł'],
|
||||
'PYG': [0, '\u20b2', 'PYG'],
|
||||
'QAR': [2, '\u0642\u002E\u0631', 'QR'],
|
||||
'RON': [2, 'L', 'RON'],
|
||||
'RSD': [2, 'РС\u0414', 'RSD'],
|
||||
'OMR': [3, 'Rial', 'OMR'],
|
||||
'PGK': [2, 'PGK', 'PGK'],
|
||||
'PLN': [2, 'z\u0142', 'z\u0142'],
|
||||
'PYG': [0, 'Gs', 'PYG'],
|
||||
'QAR': [2, 'Rial', 'QR'],
|
||||
'RON': [2, 'RON', 'RON'],
|
||||
'RSD': [0, 'din', 'RSD'],
|
||||
'RWF': [0, 'RF', 'RF'],
|
||||
'SBD': [2, '$', 'SI$'],
|
||||
'SCR': [2, 'SR', 'SCR'],
|
||||
'SCR': [2, 'SCR', 'SCR'],
|
||||
'SDG': [2, 'SDG', 'SDG'],
|
||||
'SHP': [2, '£', 'SH£'],
|
||||
'SKK': [10, 'Sk', 'Sk'],
|
||||
'SLL': [2, 'Le', 'Le'],
|
||||
'SOS': [2, 'So. Sh.', 'So. Sh.'],
|
||||
'SLL': [0, 'SLL', 'SLL'],
|
||||
'SOS': [0, 'SOS', 'SOS'],
|
||||
'SRD': [2, '$', 'SR$'],
|
||||
'STD': [2, 'Db', 'Db'],
|
||||
'SYP': [18, 'SYP', 'SYP'],
|
||||
'SZL': [2, 'L', 'SZL'],
|
||||
'TJS': [2, 'TJS', 'TJS'],
|
||||
'TMM': [2, 'm', 'TMM'],
|
||||
'TND': [3, '\u062F\u002e\u062A ', 'DT'],
|
||||
'STD': [0, 'Db', 'Db'],
|
||||
'SYP': [16, '£', 'SY£'],
|
||||
'SZL': [2, 'SZL', 'SZL'],
|
||||
'TJS': [2, 'Som', 'TJS'],
|
||||
'TND': [3, 'din', 'DT'],
|
||||
'TOP': [2, 'T$', 'T$'],
|
||||
'TTD': [2, '$', 'TT$'],
|
||||
'TZS': [10, 'TZS', 'TZS'],
|
||||
'UAH': [10, '\u20B4', 'грн'],
|
||||
'UGX': [2, 'USh', 'USh'],
|
||||
'UZS': [2, 'UZS', 'UZS'],
|
||||
'VEF': [2, 'Bs.F', 'Bs.F'],
|
||||
'VUV': [0, 'Vt', 'Vt'],
|
||||
'WST': [2, 'WS$', 'WS$'],
|
||||
'TZS': [0, 'TSh', 'TSh'],
|
||||
'UAH': [2, '\u20B4', 'UAH'],
|
||||
'UGX': [0, 'UGX', 'UGX'],
|
||||
'UYU': [1, '$', '$U'],
|
||||
'UZS': [0, 'so\u02bcm', 'UZS'],
|
||||
'VEF': [2, 'Bs', 'Bs'],
|
||||
'VUV': [0, 'VUV', 'VUV'],
|
||||
'WST': [2, 'WST', 'WST'],
|
||||
'XAF': [0, 'FCFA', 'FCFA'],
|
||||
'XCD': [2, '$', 'EC$'],
|
||||
'XOF': [0, 'CFA', 'CFA'],
|
||||
'XPF': [0, 'F', 'XPF'],
|
||||
'ZMK': [2, 'ZK', 'ZK'],
|
||||
'ZWL': [2, '$', 'ZW$']
|
||||
'XPF': [0, 'FCFP', 'FCFP'],
|
||||
'ZMK': [0, 'ZMK', 'ZMK']
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+497
-296
File diff suppressed because it is too large
Load Diff
+358
-181
File diff suppressed because it is too large
Load Diff
+213
-146
@@ -1,4 +1,4 @@
|
||||
// Copyright 2011 The Closure Library Authors. All Rights Reserved
|
||||
// Copyright 2012 The Closure Library Authors. All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -7,35 +7,24 @@
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations under
|
||||
// the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Plural rules.
|
||||
*
|
||||
* This file is autogenerated by script:
|
||||
* http://go/generate_pluralrules.py
|
||||
* using the --for_closure flag.
|
||||
* http://go/generate_pluralrules.py
|
||||
*
|
||||
* To reduce the file size (which may cause issues in some JS
|
||||
* developing environments), this file will only contain locales
|
||||
* that are usually supported by google products. This is defined as
|
||||
* closure_tier1_locales and will change (most likely addition)
|
||||
* over time. Rest of the data can be found in another file named
|
||||
* "pluralrulesext.js", which will be generated at the
|
||||
* same time together with this file.
|
||||
*
|
||||
* Before checkin, this file could have been manually edited. This is
|
||||
* to incorporate changes before we could fix CLDR. All manual
|
||||
* modification must be documented in this section, and should be
|
||||
* removed after those changes land to CLDR.
|
||||
* Before check in, this file could have been manually edited. This is to
|
||||
* incorporate changes before we could fix CLDR. All manual modification must be
|
||||
* documented in this section, and should be removed after those changes land to
|
||||
* CLDR.
|
||||
*/
|
||||
|
||||
goog.provide('goog.i18n.pluralRules');
|
||||
|
||||
|
||||
/**
|
||||
* Plural pattern keyword
|
||||
* @enum {string}
|
||||
@@ -53,7 +42,7 @@ goog.i18n.pluralRules.Keyword = {
|
||||
/**
|
||||
* Default plural select rule.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Default plural value.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Default value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.defaultSelect_ = function(n) {
|
||||
@@ -64,25 +53,25 @@ goog.i18n.pluralRules.defaultSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for ar locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.arSelect_ = function(n) {
|
||||
if (n == 0) {
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
}
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 2) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
}
|
||||
if ((n % 100) >= 3 && (n % 100) <= 10 && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == (n | 0) && n % 100 >= 3 && n % 100 <= 10) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
if ((n % 100) >= 11 && (n % 100) <= 99 && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
if (n == (n | 0) && n % 100 >= 11 && n % 100 <= 99) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -91,13 +80,13 @@ goog.i18n.pluralRules.arSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for en locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.enSelect_ = function(n) {
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -106,13 +95,13 @@ goog.i18n.pluralRules.enSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for fil locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.filSelect_ = function(n) {
|
||||
if (n == 0 || n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -121,13 +110,13 @@ goog.i18n.pluralRules.filSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for fr locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.frSelect_ = function(n) {
|
||||
if (n >= 0 && n < 2) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
if (n >= 0 && n <= 2 && n != 2) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -136,16 +125,34 @@ goog.i18n.pluralRules.frSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for lv locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.lvSelect_ = function(n) {
|
||||
if (n == 0) {
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
}
|
||||
if ((n % 10) == 1 && (n % 100) != 11) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
if (n % 10 == 1 && n % 100 != 11) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Plural select rules for iu locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.iuSelect_ = function(n) {
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 2) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -154,16 +161,22 @@ goog.i18n.pluralRules.lvSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for ga locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.gaSelect_ = function(n) {
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 2) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
}
|
||||
if (n == (n | 0) && n >= 3 && n <= 6) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
if (n == (n | 0) && n >= 7 && n <= 10) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -172,17 +185,16 @@ goog.i18n.pluralRules.gaSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for ro locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.roSelect_ = function(n) {
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 0 || n != 1 && (n % 100) >= 1 &&
|
||||
(n % 100) <= 19 && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == 0 || n != 1 && n == (n | 0) && n % 100 >= 1 && n % 100 <= 19) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -191,40 +203,37 @@ goog.i18n.pluralRules.roSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for lt locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.ltSelect_ = function(n) {
|
||||
if ((n % 10) == 1 && ((n % 100) < 11 || (n % 100) > 19)) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
if (n % 10 == 1 && (n % 100 < 11 || n % 100 > 19)) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if ((n % 10) >= 2 && (n % 10) <= 9 &&
|
||||
((n % 100) < 11 || (n % 100) > 19) && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == (n | 0) && n % 10 >= 2 && n % 10 <= 9 && (n % 100 < 11 || n % 100 > 19)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Plural select rules for hr locale
|
||||
* Plural select rules for be locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.hrSelect_ = function(n) {
|
||||
if ((n % 10) == 1 && (n % 100) != 11) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
goog.i18n.pluralRules.beSelect_ = function(n) {
|
||||
if (n % 10 == 1 && n % 100 != 11) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if ((n % 10) >= 2 && (n % 10) <= 4 &&
|
||||
((n % 100) < 12 || (n % 100) > 14) && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == (n | 0) && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
if ((n % 10) == 0 || ((n % 10) >= 5 && (n % 10) <= 9) ||
|
||||
((n % 100) >= 11 && (n % 100) <= 14) && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
if (n % 10 == 0 || n == (n | 0) && n % 10 >= 5 && n % 10 <= 9 || n == (n | 0) && n % 100 >= 11 && n % 100 <= 14) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -233,16 +242,16 @@ goog.i18n.pluralRules.hrSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for cs locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.csSelect_ = function(n) {
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 2 || n == 3 || n == 4) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == (n | 0) && n >= 2 && n <= 4) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -251,22 +260,19 @@ goog.i18n.pluralRules.csSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for pl locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.plSelect_ = function(n) {
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if ((n % 10) >= 2 && (n % 10) <= 4 &&
|
||||
((n % 100) < 12 || (n % 100) > 14) && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == (n | 0) && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
if ((n % 10) == 0 || n != 1 && (n % 10) == 1 ||
|
||||
((n % 10) >= 5 && (n % 10) <= 9 || (n % 100) >= 12 && (n % 100) <= 14) &&
|
||||
n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
if (n != 1 && (n % 10 == 0 || n % 10 == 1) || n == (n | 0) && n % 10 >= 5 && n % 10 <= 9 || n == (n | 0) && n % 100 >= 12 && n % 100 <= 14) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -275,19 +281,19 @@ goog.i18n.pluralRules.plSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for sl locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.slSelect_ = function(n) {
|
||||
if ((n % 100) == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
if (n % 100 == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if ((n % 100) == 2) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
if (n % 100 == 2) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
}
|
||||
if ((n % 100) == 3 || (n % 100) == 4) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n % 100 == 3 || n % 100 == 4) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -296,19 +302,19 @@ goog.i18n.pluralRules.slSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for mt locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.mtSelect_ = function(n) {
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 0 || ((n % 100) >= 2 && (n % 100) <= 4 && n == Math.floor(n))) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == 0 || n == (n | 0) && n % 100 >= 2 && n % 100 <= 10) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
if ((n % 100) >= 11 && (n % 100) <= 19 && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
if (n == (n | 0) && n % 100 >= 11 && n % 100 <= 19) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -317,13 +323,13 @@ goog.i18n.pluralRules.mtSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for mk locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.mkSelect_ = function(n) {
|
||||
if ((n % 10) == 1 && n != 11) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
if (n % 10 == 1 && n != 11) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -332,25 +338,25 @@ goog.i18n.pluralRules.mkSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for cy locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.cySelect_ = function(n) {
|
||||
if (n == 0) {
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
}
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 2) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
}
|
||||
if (n == 3) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
if (n == 6) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -359,16 +365,16 @@ goog.i18n.pluralRules.cySelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for lag locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.lagSelect_ = function(n) {
|
||||
if (n == 0) {
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
}
|
||||
if (n > 0 && n < 2) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
if (n >= 0 && n <= 2 && n != 0 && n != 2) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -377,16 +383,16 @@ goog.i18n.pluralRules.lagSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for shi locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.shiSelect_ = function(n) {
|
||||
if (n >= 0 && n <= 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n >= 2 && n <= 10 && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == (n | 0) && n >= 2 && n <= 10) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -395,25 +401,91 @@ goog.i18n.pluralRules.shiSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for br locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.brSelect_ = function(n) {
|
||||
if (n % 10 == 1 && n % 100 != 11 && n % 100 != 71 && n % 100 != 91) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n % 10 == 2 && n % 100 != 12 && n % 100 != 72 && n % 100 != 92) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
}
|
||||
if ((n % 10 == 3 || n % 10 == 4 || n % 10 == 9) && ((n % 100 < 10 || n % 100 > 19) && (n % 100 < 70 || n % 100 > 79) && (n % 100 < 90 || n % 100 > 99))) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
if (n % 1000000 == 0 && n != 0) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Plural select rules for ksh locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.kshSelect_ = function(n) {
|
||||
if (n == 0) {
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
}
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 2) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Plural select rules for tzm locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.tzmSelect_ = function(n) {
|
||||
if (n == 0 || n == 1 || n == (n | 0) && n >= 11 && n <= 99) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 3) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Plural select rules for gv locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.gvSelect_ = function(n) {
|
||||
if (n % 10 == 1 || n % 10 == 2 || n % 20 == 0) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 6) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Plural select rules for gd locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.gdSelect_ = function(n) {
|
||||
if (n == 1 || n == 11) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 2 || n == 12) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
}
|
||||
if (n == (n | 0) && (n >= 3 && n <= 10 || n >= 13 && n <= 19)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -423,7 +495,6 @@ goog.i18n.pluralRules.brSelect_ = function(n) {
|
||||
* Selected plural rules by locale.
|
||||
*/
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
|
||||
|
||||
if (goog.LOCALE == 'am') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.filSelect_;
|
||||
}
|
||||
@@ -557,7 +628,7 @@ if (goog.LOCALE == 'hi') {
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'hr') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.hrSelect_;
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.beSelect_;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'hu') {
|
||||
@@ -581,7 +652,7 @@ if (goog.LOCALE == 'it') {
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'iw') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'ja') {
|
||||
@@ -612,10 +683,6 @@ if (goog.LOCALE == 'ml') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'mo') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.roSelect_;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'mr') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
|
||||
}
|
||||
@@ -661,7 +728,7 @@ if (goog.LOCALE == 'ro') {
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'ru') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.hrSelect_;
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.beSelect_;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'sk') {
|
||||
@@ -677,7 +744,7 @@ if (goog.LOCALE == 'sq') {
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'sr') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.hrSelect_;
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.beSelect_;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'sv') {
|
||||
@@ -709,7 +776,7 @@ if (goog.LOCALE == 'tr') {
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'uk') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.hrSelect_;
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.beSelect_;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'ur') {
|
||||
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
PARENT_DIR="$(dirname "$0")"
|
||||
jasmine-node "$PARENT_DIR"/spec/
|
||||
@@ -0,0 +1,250 @@
|
||||
var closureI18nExtractor = require('../src/closureI18nExtractor.js');
|
||||
var converter = require('../src/converter.js');
|
||||
findLocaleId = closureI18nExtractor.findLocaleId;
|
||||
extractNumberSymbols = closureI18nExtractor.extractNumberSymbols;
|
||||
extractCurrencySymbols = closureI18nExtractor.extractCurrencySymbols;
|
||||
extractDateTimeSymbols = closureI18nExtractor.extractDateTimeSymbols;
|
||||
|
||||
|
||||
function newTestLocaleInfo() {
|
||||
return { fr_CA: {
|
||||
DATETIME_FORMATS: {
|
||||
MONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre',
|
||||
'octobre', 'novembre', 'décembre'],
|
||||
SHORTMONTH: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.',
|
||||
'nov.', 'déc.'],
|
||||
DAY: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
|
||||
SHORTDAY: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
|
||||
AMPMS: ['AM', 'PM'],
|
||||
medium: 'yyyy-MM-dd HH:mm:ss',
|
||||
short: 'yy-MM-dd HH:mm',
|
||||
fullDate: 'EEEE d MMMM y',
|
||||
longDate: 'd MMMM y',
|
||||
mediumDate: 'yyyy-MM-dd',
|
||||
shortDate: 'yy-MM-dd',
|
||||
mediumTime: 'HH:mm:ss',
|
||||
shortTime: 'HH:mm'
|
||||
},
|
||||
NUMBER_FORMATS: {
|
||||
"DECIMAL_SEP": ".",
|
||||
"GROUP_SEP": ",",
|
||||
"PATTERNS": [{
|
||||
"minInt": 1,
|
||||
"minFrac": 0,
|
||||
"macFrac": 0,
|
||||
"posPre": "",
|
||||
"posSuf": "",
|
||||
"negPre": "-",
|
||||
"negSuf": "",
|
||||
"gSize": 3,
|
||||
"lgSize": 3,
|
||||
"maxFrac": 3
|
||||
}, {
|
||||
"minInt": 1,
|
||||
"minFrac": 2,
|
||||
"macFrac": 0,
|
||||
"posPre": "¤",
|
||||
"posSuf": "",
|
||||
"negPre": "¤-",
|
||||
"negSuf": "",
|
||||
"gSize": 3,
|
||||
"lgSize": 3,
|
||||
"maxFrac": 2
|
||||
}],
|
||||
"CURRENCY_SYM": "£"
|
||||
}}};
|
||||
}
|
||||
|
||||
|
||||
describe("findLocaleId", function () {
|
||||
it("should find the id from numbers", function() {
|
||||
expect(findLocaleId("NumberFormatSymbols_en_GB", "num")).toEqual("en_GB");
|
||||
});
|
||||
|
||||
|
||||
it("should find the id from datetime", function() {
|
||||
expect(findLocaleId("DateTimeSymbols_en_ISO", "datetime")).toEqual("en_ISO");
|
||||
});
|
||||
|
||||
|
||||
it("should throw an error otherwise", function() {
|
||||
expect(function() {
|
||||
findLocaleId("str", "otherwise")
|
||||
}).toThrow("unknown type in findLocaleId: otherwise");
|
||||
});
|
||||
});
|
||||
|
||||
describe("extractNumberSymbols", function () {
|
||||
it("should extract number data", function() {
|
||||
var CONTENT = [
|
||||
"goog.provide('goog.i18n.NumberFormatSymbols_en_GB');",
|
||||
"goog.i18n.NumberFormatSymbols_en_GB = {",
|
||||
"DECIMAL_SEP: '.',",
|
||||
"GROUP_SEP: ',',",
|
||||
"PERCENT: '%',",
|
||||
"ZERO_DIGIT: '0',",
|
||||
"PLUS_SIGN: '+',",
|
||||
"MINUS_SIGN: '-',",
|
||||
"EXP_SYMBOL: 'E',",
|
||||
"PERMILL: '\u2030',",
|
||||
"INFINITY: '\u221E',",
|
||||
"NAN: 'NaN',",
|
||||
"DECIMAL_PATTERN: '#,##0.###',",
|
||||
"SCIENTIFIC_PATTERN: '#E0',",
|
||||
"PERCENT_PATTERN: '#,##0%',",
|
||||
"CURRENCY_PATTERN: '\u00A4#,##0.00',",
|
||||
"DEF_CURRENCY_CODE: 'GBP' };"
|
||||
].join('\n');
|
||||
|
||||
var currencySymbols = {'GBP':[2, '£', 'GB£']};
|
||||
|
||||
var expectedNumberFormats = converter.convertNumberData(
|
||||
{
|
||||
DECIMAL_SEP:'.',
|
||||
GROUP_SEP:',',
|
||||
DECIMAL_PATTERN:'#,##0.###',
|
||||
CURRENCY_PATTERN:'\u00A4#,##0.00',
|
||||
DEF_CURRENCY_CODE: 'GBP'
|
||||
}, currencySymbols
|
||||
);
|
||||
|
||||
var localeInfo = {};
|
||||
extractNumberSymbols(CONTENT, localeInfo, currencySymbols);
|
||||
|
||||
expect(localeInfo).toEqual({
|
||||
'en_GB': { NUMBER_FORMATS: expectedNumberFormats }
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
describe("extractCurrencySymbols", function () {
|
||||
it("should extract currency data", function() {
|
||||
var CONTENT = [
|
||||
"goog.i18n.currency.CurrencyInfo = {",
|
||||
" 'GBP':[2, '£', 'GB£'],",
|
||||
"};",
|
||||
"goog.i18n.currency.CurrencyInfoTier2 = {",
|
||||
" 'AOA':[2, 'Kz', 'Kz'],",
|
||||
"};"
|
||||
].join('\n');
|
||||
|
||||
var localeInfo = {};
|
||||
expect(extractCurrencySymbols(CONTENT)).toEqual({
|
||||
'GBP':[2, '£', 'GB£'],
|
||||
'AOA':[2, 'Kz', 'Kz']
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("extractDateTimeSymbols", function () {
|
||||
it("should extract date time data", function() {
|
||||
var CONTENT = [
|
||||
"goog.i18n.DateTimeSymbols_fr_CA = {",
|
||||
" ERAS: ['av. J.-C.', 'ap. J.-C.'],",
|
||||
" ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],",
|
||||
" NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],",
|
||||
" STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',",
|
||||
" 'N', 'D'],",
|
||||
" MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',",
|
||||
" 'août', 'septembre', 'octobre', 'novembre', 'décembre'],",
|
||||
" STANDALONEMONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin',",
|
||||
" 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],",
|
||||
" SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',",
|
||||
" 'août', 'sept.', 'oct.', 'nov.', 'déc.'],",
|
||||
" STANDALONESHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin',",
|
||||
" 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'],",
|
||||
" WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',",
|
||||
" 'samedi'],",
|
||||
" STANDALONEWEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi',",
|
||||
" 'vendredi', 'samedi'],",
|
||||
" SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],",
|
||||
" STANDALONESHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.',",
|
||||
" 'sam.'],",
|
||||
" NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],",
|
||||
" STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],",
|
||||
" SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],",
|
||||
" QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],",
|
||||
" AMPMS: ['AM', 'PM'],",
|
||||
" DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'yyyy-MM-dd', 'yy-MM-dd'],",
|
||||
" TIMEFORMATS: ['HH \\'h\\' mm \\'min\\' ss \\'s\\' zzzz', 'HH:mm:ss z',",
|
||||
" 'HH:mm:ss', 'HH:mm'],",
|
||||
" FIRSTDAYOFWEEK: 6,",
|
||||
" WEEKENDRANGE: [5, 6],",
|
||||
" FIRSTWEEKCUTOFFDAY: 2",
|
||||
"};"
|
||||
].join('\n');
|
||||
var localeInfo = {};
|
||||
var expectedLocaleInfo = {
|
||||
fr_CA: {
|
||||
DATETIME_FORMATS: {
|
||||
MONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre',
|
||||
'octobre', 'novembre', 'décembre'],
|
||||
SHORTMONTH: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.',
|
||||
'nov.', 'déc.'],
|
||||
DAY: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
|
||||
SHORTDAY: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
|
||||
AMPMS: ['AM', 'PM'],
|
||||
medium: 'yyyy-MM-dd HH:mm:ss',
|
||||
short: 'yy-MM-dd HH:mm',
|
||||
fullDate: 'EEEE d MMMM y',
|
||||
longDate: 'd MMMM y',
|
||||
mediumDate: 'yyyy-MM-dd',
|
||||
shortDate: 'yy-MM-dd',
|
||||
mediumTime: 'HH:mm:ss',
|
||||
shortTime: 'HH:mm'
|
||||
}
|
||||
}
|
||||
};
|
||||
extractDateTimeSymbols(CONTENT, localeInfo);
|
||||
expect(localeInfo).toEqual(expectedLocaleInfo);
|
||||
})
|
||||
});
|
||||
|
||||
describe("pluralExtractor", function() {
|
||||
it("should output PLURAL_CAT in the output string code", function() {
|
||||
var localeIds = ["fr_CA"];
|
||||
var content = (
|
||||
"goog.provide('goog.i18n.pluralRules');\n" +
|
||||
"\n" +
|
||||
"goog.i18n.pluralRules.Keyword = {\n" +
|
||||
" ZERO: 'zero',\n" +
|
||||
" ONE: 'one',\n" +
|
||||
" TWO: 'two',\n" +
|
||||
" FEW: 'few',\n" +
|
||||
" MANY: 'many',\n" +
|
||||
" OTHER: 'other'\n" +
|
||||
"};\n" +
|
||||
"\n" +
|
||||
"goog.i18n.pluralRules.frSelect_ = function(n) {\n" +
|
||||
" if (n >= 0 && n < 2) {\n" +
|
||||
" return goog.i18n.pluralRules.Keyword.ONE;\n" +
|
||||
" }\n" +
|
||||
" return goog.i18n.pluralRules.Keyword.OTHER;\n" +
|
||||
"};\n" +
|
||||
"\n" +
|
||||
"if (goog.LOCALE == 'fr') {\n" +
|
||||
" goog.i18n.pluralRules.select = goog.i18n.pluralRules.frSelect_;\n" +
|
||||
"}"
|
||||
);
|
||||
var localeInfo = newTestLocaleInfo();
|
||||
closureI18nExtractor.pluralExtractor(content, localeInfo);
|
||||
var pluralCat = localeInfo["fr_CA"].pluralCat;
|
||||
expect(pluralCat).toBeDefined();
|
||||
// pluralCat is the source text for the pluralCat and contains @@
|
||||
// placeholders that need to be stripped before evaluation.
|
||||
// Ref: closureI18nExtractor.pluralExtractor.
|
||||
pluralCat = pluralCat.replace(/^@@|@@$/g, '');
|
||||
// pluralCat requires these constants to exist.
|
||||
var PLURAL_CATEGORY = {
|
||||
ZERO: "zero", ONE: "one", TWO: "two",
|
||||
FEW: "few", MANY: "many", OTHER: "other"
|
||||
};
|
||||
// Obtain the function by evaluating the source text.
|
||||
pluralCat = eval("(" + pluralCat + ")");
|
||||
// Confirm some expectations for pluralCat in fr_CA.
|
||||
expect(pluralCat(0)).toEqual("one");
|
||||
expect(pluralCat(3)).toEqual("other");
|
||||
})
|
||||
});
|
||||
|
||||
@@ -24,6 +24,8 @@ describe('parsePattern', function() {
|
||||
parseAndExpect('#,##,##0.###', '', '-', '', '', 1, 0, 3, 2, 3);
|
||||
parseAndExpect("#,##0.###;\'\u202A\'-#,##0.###\'\u202C\'",
|
||||
'', '\u202A-', '', '\u202C', 1, 0, 3, 3, 3);
|
||||
parseAndExpect('#0.###;#0.###-', '', '', '', '-', 1, 0, 3, 0, 0);
|
||||
|
||||
});
|
||||
|
||||
it('should parse CURRENCY patterns', function() {
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
'use strict';
|
||||
|
||||
var converter = require('./converter.js');
|
||||
|
||||
exports.extractNumberSymbols = extractNumberSymbols;
|
||||
exports.extractCurrencySymbols = extractCurrencySymbols;
|
||||
exports.extractDateTimeSymbols = extractDateTimeSymbols;
|
||||
exports.pluralExtractor = pluralExtractor;
|
||||
exports.outputLocale = outputLocale;
|
||||
exports.correctedLocaleId = correctedLocaleId;
|
||||
exports.findLocaleId = findLocaleId;
|
||||
|
||||
var goog = { provide: function() {},
|
||||
require: function() {},
|
||||
i18n: {currency: {}, pluralRules: {}} };
|
||||
|
||||
function findLocaleId(str, type) {
|
||||
if (type === 'num') {
|
||||
return (str.match(/^NumberFormatSymbols_(.+)$/) || [])[1];
|
||||
}
|
||||
|
||||
if (type != 'datetime') { throw new Error('unknown type in findLocaleId: ' + type); }
|
||||
|
||||
return (str.match(/^DateTimeSymbols_(.+)$/) || [])[1];
|
||||
}
|
||||
|
||||
|
||||
function getInfoForLocale(localeInfo, localeID) {
|
||||
if (!localeInfo[localeID]) {
|
||||
localeInfo[localeID] = {};
|
||||
//localeIds.push(localeID);
|
||||
}
|
||||
return localeInfo[localeID];
|
||||
}
|
||||
|
||||
function extractNumberSymbols(content, localeInfo, currencySymbols) {
|
||||
//eval script in the current context so that we get access to all the symbols
|
||||
eval(content.toString());
|
||||
for (var propName in goog.i18n) {
|
||||
var localeID = findLocaleId(propName, 'num');
|
||||
if (localeID) {
|
||||
var info = getInfoForLocale(localeInfo, localeID);
|
||||
info.NUMBER_FORMATS =
|
||||
converter.convertNumberData(goog.i18n[propName], currencySymbols);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function extractCurrencySymbols(content) {
|
||||
//eval script in the current context so that we get access to all the symbols
|
||||
eval(content.toString());
|
||||
var currencySymbols = goog.i18n.currency.CurrencyInfo;
|
||||
currencySymbols.__proto__ = goog.i18n.currency.CurrencyInfoTier2;
|
||||
|
||||
return currencySymbols;
|
||||
}
|
||||
|
||||
function extractDateTimeSymbols(content, localeInfo) {
|
||||
//eval script in the current context so that we get access to all the symbols
|
||||
eval(content.toString());
|
||||
for (var propName in goog.i18n) {
|
||||
var localeID = findLocaleId(propName, 'datetime');
|
||||
if (localeID) {
|
||||
var info = getInfoForLocale(localeInfo, localeID);
|
||||
localeInfo[localeID].DATETIME_FORMATS =
|
||||
converter.convertDatetimeData(goog.i18n[propName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pluralExtractor(content, localeInfo) {
|
||||
var contentText = content.toString();
|
||||
var localeIds = Object.keys(localeInfo);
|
||||
for (var i = 0; i < localeIds.length; i++) {
|
||||
//We don't need to care about country ID because the plural rules in more specific id are
|
||||
//always the same as those in its language ID.
|
||||
// e.g. plural rules for en_SG is the same as those for en.
|
||||
goog.LOCALE = localeIds[i].match(/[^_]+/)[0];
|
||||
try {
|
||||
eval(contentText);
|
||||
} catch(e) {
|
||||
console.log("Error in eval(contentText): " + e.stack);
|
||||
}
|
||||
if (!goog.i18n.pluralRules.select) {
|
||||
console.log('No select for lang [' + goog.LOCALE + ']');
|
||||
continue;
|
||||
}
|
||||
var temp = goog.i18n.pluralRules.select.toString().
|
||||
replace(/goog.i18n.pluralRules.Keyword/g, 'PLURAL_CATEGORY').replace(/\n/g, '');
|
||||
|
||||
///@@ is a crazy place holder to be replaced before writing to file
|
||||
localeInfo[localeIds[i]].pluralCat = "@@" + temp + "@@";
|
||||
}
|
||||
}
|
||||
|
||||
function correctedLocaleId(localeID) {
|
||||
// e.g. from zh_CN to zh-CN, from en_US to en-US
|
||||
return localeID.replace(/_/g, '-').toLowerCase();
|
||||
}
|
||||
|
||||
function canonicalizeForJsonStringify(unused_key, object) {
|
||||
// This function is intended to be called as the 2nd argument to
|
||||
// JSON.stringify. The goal here is to ensure that the generated JSON has
|
||||
// objects with their keys in ascending order. Without this, it's much
|
||||
// harder to diff the generated files in src/ngLocale as the order isn't
|
||||
// exactly consistent. We've gotten lucky in the past.
|
||||
//
|
||||
// Iteration order, for string keys, ends up being the same as insertion
|
||||
// order. Refer :-
|
||||
// 1. http://ejohn.org/blog/javascript-in-chrome/
|
||||
// (search for "for loop order").
|
||||
// Currently all major browsers loop over the properties of an object
|
||||
// in the order in which they were defined.
|
||||
// - John Resig
|
||||
// 2. https://code.google.com/p/v8/issues/detail?id=164
|
||||
// ECMA-262 does not specify enumeration order. The de facto standard
|
||||
// is to match insertion order, which V8 also does ...
|
||||
if (typeof object != "object") {
|
||||
return object;
|
||||
}
|
||||
var result = {};
|
||||
Object.keys(object).sort().forEach(function(key) {
|
||||
result[key] = object[key];
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function outputLocale(localeInfo, localeID) {
|
||||
var fallBackID = localeID.match(/[A-Za-z]+/)[0],
|
||||
localeObj = localeInfo[localeID],
|
||||
fallBackObj = localeInfo[fallBackID];
|
||||
|
||||
// fallBack to language formats when country format is missing
|
||||
// e.g. if NUMBER_FORMATS of en_xyz is not present, use the NUMBER_FORMATS of en instead
|
||||
if (!localeObj.NUMBER_FORMATS) {
|
||||
localeObj.NUMBER_FORMATS = fallBackObj.NUMBER_FORMATS;
|
||||
}
|
||||
|
||||
// datetimesymbolsext.js provides more top level locales than the other
|
||||
// files. We process datetimesymbolsext.js because we want the country
|
||||
// specific formats that are missing from datetimesymbols.js. However, we
|
||||
// don't want to write locale files that only have dateformat (i.e. missing
|
||||
// number formats.) So we skip them.
|
||||
if (!localeObj.NUMBER_FORMATS) {
|
||||
console.log("Skipping locale %j: Don't have any number formats", localeID);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!localeObj.DATETIME_FORMATS) {
|
||||
localeObj.DATETIME_FORMATS = fallBackObj.DATETIME_FORMATS;
|
||||
}
|
||||
localeObj.id = correctedLocaleId(localeID);
|
||||
|
||||
var prefix =
|
||||
'angular.module("ngLocale", [], ["$provide", function($provide) {\n' +
|
||||
'var PLURAL_CATEGORY = {' +
|
||||
'ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"' +
|
||||
'};\n' +
|
||||
'$provide.value("$locale", ';
|
||||
|
||||
var suffix = ');\n}]);';
|
||||
|
||||
localeObj = {
|
||||
DATETIME_FORMATS: localeObj.DATETIME_FORMATS,
|
||||
NUMBER_FORMATS: localeObj.NUMBER_FORMATS,
|
||||
pluralCat: localeObj.pluralCat,
|
||||
id: localeObj.id
|
||||
};
|
||||
|
||||
var content = JSON.stringify(localeInfo[localeID], canonicalizeForJsonStringify, ' ')
|
||||
.replace(/\¤/g, '\\u00A4')
|
||||
.replace(/"@@|@@"/g, '');
|
||||
|
||||
return prefix + content + suffix;
|
||||
}
|
||||
+57
-102
@@ -1,130 +1,85 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
var Q = require('qq'),
|
||||
var Q = require('q'),
|
||||
qfs = require('q-fs'),
|
||||
converter = require('./converter.js'),
|
||||
util = require('./util.js'),
|
||||
closureI18nExtractor = require('./closureI18nExtractor.js'),
|
||||
localeInfo = {},
|
||||
localeIds = [],
|
||||
currencySymbols,
|
||||
goog = { provide: function() {},
|
||||
require: function() {},
|
||||
i18n: {currency: {}, pluralRules: {}} };
|
||||
|
||||
createFolder('../../src/ngLocale/').then(function() {
|
||||
var promiseA = Q.defer(),
|
||||
promiseB = Q.defer();
|
||||
|
||||
qfs.read(__dirname + '/../closure/currencySymbols.js', 'b').then(function(content) {
|
||||
eval(content.toString());
|
||||
currencySymbols = goog.i18n.currency.CurrencyInfo;
|
||||
currencySymbols.__proto__ = goog.i18n.currency.CurrencyInfoTier2;
|
||||
var NG_LOCALE_DIR = '../src/ngLocale/';
|
||||
|
||||
qfs.read(__dirname + '/../closure/numberSymbols.js', 'b').then(function(content) {
|
||||
//eval script in the current context so that we get access to all the symbols
|
||||
eval(content.toString());
|
||||
for (var propName in goog.i18n) {
|
||||
var localeID = util.findLocaleId(propName, 'num');
|
||||
if (localeID) {
|
||||
if (!localeInfo[localeID]) {
|
||||
localeInfo[localeID] = {};
|
||||
localeIds.push(localeID);
|
||||
}
|
||||
var convertedData = converter.convertNumberData(goog.i18n[propName], currencySymbols);
|
||||
localeInfo[localeID].NUMBER_FORMATS = convertedData;
|
||||
}
|
||||
}
|
||||
|
||||
promiseA.resolve();
|
||||
function readSymbols() {
|
||||
console.log("Processing currency and number symbols ...");
|
||||
var numericStagePromise = qfs.read(__dirname + '/../closure/currencySymbols.js', 'b')
|
||||
.then(function(content) {
|
||||
var currencySymbols = closureI18nExtractor.extractCurrencySymbols(content);
|
||||
return qfs.read(__dirname + '/../closure/numberSymbols.js', 'b').then(function(content) {
|
||||
closureI18nExtractor.extractNumberSymbols(content, localeInfo, currencySymbols);
|
||||
});
|
||||
});
|
||||
|
||||
console.log("Processing datetime symbols ...");
|
||||
var datetimeStagePromise = qfs.read(__dirname + '/../closure/datetimeSymbols.js', 'b')
|
||||
.then(function(content) {
|
||||
closureI18nExtractor.extractDateTimeSymbols(content, localeInfo);
|
||||
return qfs.read(__dirname + '/../closure/datetimeSymbolsExt.js', 'b').then(function(content) {
|
||||
closureI18nExtractor.extractDateTimeSymbols(content, localeInfo);
|
||||
});
|
||||
});
|
||||
|
||||
return Q.all([numericStagePromise, datetimeStagePromise]);
|
||||
}
|
||||
|
||||
function extractPlurals() {
|
||||
console.log('Extracting Plurals ...');
|
||||
return qfs.read(__dirname + '/../closure/pluralRules.js').then(function(content) {
|
||||
closureI18nExtractor.pluralExtractor(content, localeInfo);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
qfs.read(__dirname + '/../closure/datetimeSymbols.js', 'b').then(function(content) {
|
||||
eval(content.toString());
|
||||
for (var propName in goog.i18n) {
|
||||
var localeID = util.findLocaleId(propName, 'datetime');
|
||||
if (localeID) {
|
||||
if (!localeInfo[localeID]) {
|
||||
localeInfo[localeID] = {};
|
||||
localeIds.push(localeID);
|
||||
}
|
||||
var convertedData = converter.convertDatetimeData(goog.i18n[propName]);
|
||||
localeInfo[localeID].DATETIME_FORMATS = convertedData;
|
||||
}
|
||||
}
|
||||
|
||||
promiseB.resolve();
|
||||
});
|
||||
|
||||
return Q.join(promiseA.promise, promiseB.promise, noop);
|
||||
}).then(function() {
|
||||
var promise = Q.defer();
|
||||
|
||||
qfs.read(__dirname + '/../closure/pluralRules.js').then(function(content) {
|
||||
for(var i = 0; i < localeIds.length; i++) {
|
||||
//We don't need to care about country ID because the plural rules in more specific id are
|
||||
//always the same as those in its language ID.
|
||||
// e.g. plural rules for en_SG is the same as those for en.
|
||||
goog.LOCALE = localeIds[i].match(/[^_]+/)[0];
|
||||
eval(content);
|
||||
var temp = goog.i18n.pluralRules.select.toString().
|
||||
replace(/goog.i18n.pluralRules.Keyword/g, 'PLURAL_CATEGORY').replace(/\n/g, '');
|
||||
|
||||
///@@ is a crazy place holder to be replaced before writing to file
|
||||
localeInfo[localeIds[i]].pluralCat = "@@" + temp + "@@";
|
||||
}
|
||||
promise.resolve();
|
||||
});
|
||||
|
||||
return promise.promise;
|
||||
}).then(function() {
|
||||
function writeLocaleFiles() {
|
||||
console.log('Final stage: Writing angular locale files to directory: %j', NG_LOCALE_DIR);
|
||||
var writePromises = [];
|
||||
var localeIds = Object.keys(localeInfo);
|
||||
var num_files = 0;
|
||||
localeIds.forEach(function(localeID) {
|
||||
var fallBackID = localeID.match(/[A-Za-z]+/)[0],
|
||||
localeObj = localeInfo[localeID],
|
||||
fallBackObj = localeInfo[fallBackID];
|
||||
|
||||
// fallBack to language formats when country format is missing
|
||||
// e.g. if NUMBER_FORMATS of en_xyz is not present, use the NUMBER_FORMATS of en instead
|
||||
if (!localeObj.NUMBER_FORMATS) {
|
||||
localeObj.NUMBER_FORMATS = fallBackObj.NUMBER_FORMATS;
|
||||
}
|
||||
|
||||
if (!localeObj.DATETIME_FORMATS) {
|
||||
localeObj.DATETIME_FORMATS = fallBackObj.DATETIME_FORMATS;
|
||||
}
|
||||
|
||||
// e.g. from zh_CN to zh-CN, from en_US to en-US
|
||||
var correctedLocaleId = localeID.replace(/_/g, '-').toLowerCase();
|
||||
localeObj.id = correctedLocaleId;
|
||||
|
||||
var prefix =
|
||||
'angular.module("ngLocale", [], ["$provide", function($provide) {\n' +
|
||||
'var PLURAL_CATEGORY = {' +
|
||||
'ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"' +
|
||||
'};\n' +
|
||||
'$provide.value("$locale", ';
|
||||
|
||||
var suffix = ');\n}]);';
|
||||
|
||||
var content = JSON.stringify(localeInfo[localeID]).replace(/\¤/g,'\\u00A4').
|
||||
replace(/"@@|@@"/g, '');
|
||||
|
||||
var toWrite = prefix + content + suffix;
|
||||
qfs.write(__dirname + '/../locale/' + 'angular-locale_' + correctedLocaleId + '.js', toWrite);
|
||||
var content = closureI18nExtractor.outputLocale(localeInfo, localeID);
|
||||
if (!content) return;
|
||||
var correctedLocaleId = closureI18nExtractor.correctedLocaleId(localeID);
|
||||
var filename = NG_LOCALE_DIR + 'angular-locale_' + correctedLocaleId + '.js'
|
||||
writePromises.push(
|
||||
qfs.write(filename, content)
|
||||
.then(function () {
|
||||
console.log('Wrote ' + filename);
|
||||
++num_files;
|
||||
}));
|
||||
console.log('Writing ' + filename);
|
||||
});
|
||||
console.log('Generated ' + localeIds.length + ' locale files!');
|
||||
}).end();
|
||||
|
||||
function noop() {};
|
||||
console.log('Generated %j locale files.', localeIds.length);
|
||||
return Q.all(writePromises).then(function() { return num_files });
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a folder under current directory.
|
||||
* @param folder {string} name of the folder to be made
|
||||
*/
|
||||
function createFolder(folder) {
|
||||
return qfs.isDirectory(__dirname + '/' + folder).then(function(isDir) {
|
||||
if (!isDir) return qfs.makeDirectory(__dirname + '/' + folder);
|
||||
return qfs.isDirectory(folder).then(function(isDir) {
|
||||
if (!isDir) return qfs.makeDirectory(folder).then(function() {
|
||||
console.log('Created directory %j', folder); });
|
||||
});
|
||||
}
|
||||
|
||||
createFolder(NG_LOCALE_DIR)
|
||||
.then(readSymbols)
|
||||
.then(extractPlurals)
|
||||
.then(writeLocaleFiles)
|
||||
.done(function(num_files) { console.log("Wrote %j files.\nAll Done!", num_files); });
|
||||
|
||||
+2
-2
@@ -45,8 +45,8 @@ function parsePattern(pattern) {
|
||||
}
|
||||
|
||||
var groups = integer.split(GROUP_SEP);
|
||||
p.gSize = groups[1].length;
|
||||
p.lgSize = (groups[2] || groups[1]).length;
|
||||
p.gSize = groups[1] ? groups[1].length : 0;
|
||||
p.lgSize = (groups[2] || groups[1]) ? (groups[2] || groups[1]).length : 0;
|
||||
|
||||
if (negative) {
|
||||
var trunkLen = positive.length - p.posPre.length - p.posSuf.length,
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e # Exit on error.
|
||||
|
||||
BASE_DIR=`dirname $0`
|
||||
cd $BASE_DIR
|
||||
|
||||
set -x # Trace commands as they're executed.
|
||||
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/currency.js > closure/currencySymbols.js
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/datetimesymbols.js > closure/datetimeSymbols.js
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/datetimesymbolsext.js > closure/datetimeSymbolsExt.js
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/numberformatsymbols.js > closure/numberSymbols.js
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/pluralrules.js > closure/pluralRules.js
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,61 @@
|
||||
var util = require('./utils.js');
|
||||
var spawn = require('child_process').spawn;
|
||||
|
||||
module.exports = function(grunt) {
|
||||
|
||||
grunt.registerMultiTask('min', 'minify JS files', function(){
|
||||
util.min.call(util, this.data, this.async());
|
||||
});
|
||||
|
||||
|
||||
grunt.registerTask('minall', 'minify all the JS files in parallel', function(){
|
||||
var files = grunt.config('min');
|
||||
files = Object.keys(files).map(function(key){ return files[key]; });
|
||||
grunt.util.async.forEach(files, util.min.bind(util), this.async());
|
||||
});
|
||||
|
||||
|
||||
grunt.registerMultiTask('build', 'build JS files', function(){
|
||||
util.build.call(util, this.data, this.async());
|
||||
});
|
||||
|
||||
|
||||
grunt.registerTask('buildall', 'build all the JS files in parallel', function(){
|
||||
var builds = grunt.config('build');
|
||||
builds = Object.keys(builds).map(function(key){ return builds[key]; });
|
||||
grunt.util.async.forEach(builds, util.build.bind(util), this.async());
|
||||
});
|
||||
|
||||
|
||||
grunt.registerMultiTask('write', 'write content to a file', function(){
|
||||
grunt.file.write(this.data.file, this.data.val);
|
||||
grunt.log.ok('wrote to ' + this.data.file);
|
||||
});
|
||||
|
||||
|
||||
grunt.registerMultiTask('docs', 'create angular docs', function(){
|
||||
var done = this.async();
|
||||
var files = this.data;
|
||||
var docs = spawn('node', ['docs/src/gen-docs.js']);
|
||||
docs.stdout.pipe(process.stdout);
|
||||
docs.stderr.pipe(process.stderr);
|
||||
docs.on('exit', function(code){
|
||||
if(code !== 0) grunt.fail.warn('Error creating docs');
|
||||
grunt.file.expand(files).forEach(function(file){
|
||||
grunt.file.write(file, util.process(grunt.file.read(file), grunt.config('NG_VERSION'), false));
|
||||
});
|
||||
grunt.log.ok('docs created');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
grunt.registerMultiTask('test', 'Run the unit tests with Karma', function(){
|
||||
util.startKarma.call(util, this.data, true, this.async());
|
||||
});
|
||||
|
||||
|
||||
grunt.registerMultiTask('autotest', 'Run and watch the unit tests with Karma', function(){
|
||||
util.startKarma.call(util, this.data, false, this.async());
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,175 @@
|
||||
var fs = require('fs');
|
||||
var shell = require('shelljs');
|
||||
var yaml = require('yaml-js');
|
||||
var grunt = require('grunt');
|
||||
var spawn = require('child_process').spawn;
|
||||
|
||||
module.exports = {
|
||||
|
||||
init: function() {
|
||||
shell.exec('npm install');
|
||||
},
|
||||
|
||||
|
||||
getVersion: function(){
|
||||
var versionYaml = yaml.load(fs.readFileSync('version.yaml', 'UTF-8'));
|
||||
var match = versionYaml.version.match(/^([^\-]*)(-snapshot)?$/);
|
||||
var semver = match[1].split('.');
|
||||
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
|
||||
|
||||
var version = {
|
||||
full: (match[1] + (match[2] ? '-' + hash : '')),
|
||||
major: semver[0],
|
||||
minor: semver[1],
|
||||
dot: semver[2],
|
||||
codename: versionYaml.codename,
|
||||
stable: versionYaml.stable
|
||||
};
|
||||
|
||||
return version;
|
||||
},
|
||||
|
||||
|
||||
startKarma: function(config, singleRun, done){
|
||||
var browsers = grunt.option('browsers');
|
||||
var reporters = grunt.option('reporters');
|
||||
var noColor = grunt.option('no-colors');
|
||||
var p = spawn('node', ['node_modules/karma/bin/karma', 'start', config,
|
||||
singleRun ? '--single-run=true' : '',
|
||||
reporters ? '--reporters=' + reporters : '',
|
||||
browsers ? '--browsers=' + browsers : '',
|
||||
noColor ? '--no-colors' : ''
|
||||
]);
|
||||
p.stdout.pipe(process.stdout);
|
||||
p.stderr.pipe(process.stderr);
|
||||
p.on('exit', function(code){
|
||||
if(code !== 0) grunt.fail.warn("Test(s) failed");
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
wrap: function(src, name){
|
||||
src.unshift('src/' + name + '.prefix');
|
||||
src.push('src/' + name + '.suffix');
|
||||
return src;
|
||||
},
|
||||
|
||||
|
||||
addStyle: function(src, styles, minify){
|
||||
styles = styles.map(processCSS.bind(this)).join('\n');
|
||||
src += styles;
|
||||
return src;
|
||||
|
||||
function processCSS(file){
|
||||
var css = fs.readFileSync(file).toString();
|
||||
if(minify){
|
||||
css = css
|
||||
.replace(/\n/g, '')
|
||||
.replace(/\/\*.*?\*\//g, '')
|
||||
.replace(/:\s+/g, ':')
|
||||
.replace(/\s*\{\s*/g, '{')
|
||||
.replace(/\s*\}\s*/g, '}')
|
||||
.replace(/\s*\,\s*/g, ',')
|
||||
.replace(/\s*\;\s*/g, ';');
|
||||
}
|
||||
//espace for js
|
||||
css = css
|
||||
.replace(/\\/g, '\\\\')
|
||||
.replace(/'/g, "\\'")
|
||||
.replace(/\n/g, '\\n');
|
||||
return "angular.element(document).find('head').append('<style type=\"text/css\">" + css + "</style>');";
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
process: function(src, NG_VERSION, strict){
|
||||
var processed = src
|
||||
.replace(/"NG_VERSION_FULL"/g, NG_VERSION.full)
|
||||
.replace(/"NG_VERSION_MAJOR"/, NG_VERSION.major)
|
||||
.replace(/"NG_VERSION_MINOR"/, NG_VERSION.minor)
|
||||
.replace(/"NG_VERSION_DOT"/, NG_VERSION.dot)
|
||||
.replace(/"NG_VERSION_STABLE"/, NG_VERSION.stable)
|
||||
.replace(/"NG_VERSION_CODENAME"/, NG_VERSION.codename);
|
||||
if (strict !== false) processed = this.singleStrict(processed, '\n\n', true);
|
||||
return processed;
|
||||
},
|
||||
|
||||
|
||||
build: function(config, fn){
|
||||
var files = grunt.file.expand(config.src);
|
||||
var styles = config.styles;
|
||||
//concat
|
||||
var src = files.map(function(filepath){
|
||||
return grunt.file.read(filepath);
|
||||
}).join(grunt.util.normalizelf('\n'));
|
||||
//process
|
||||
var processed = this.process(src, grunt.config('NG_VERSION'), config.strict);
|
||||
if (styles) processed = this.addStyle(processed, styles.css, styles.minify);
|
||||
//write
|
||||
grunt.file.write(config.dest, processed);
|
||||
grunt.log.ok('File ' + config.dest + ' created.');
|
||||
fn();
|
||||
},
|
||||
|
||||
|
||||
singleStrict: function(src, insert, newline){
|
||||
var useStrict = newline ? "$1\n'use strict';" : "$1'use strict';";
|
||||
return src
|
||||
.replace(/\s*("|')use strict("|');\s*/g, insert) // remove all file-specific strict mode flags
|
||||
.replace(/(\(function\([^)]*\)\s*\{)/, useStrict); // add single strict mode flag
|
||||
},
|
||||
|
||||
|
||||
min: function(file, done) {
|
||||
var minFile = file.replace(/\.js$/, '.min.js');
|
||||
shell.exec(
|
||||
'java ' +
|
||||
this.java32flags() + ' ' +
|
||||
'-jar lib/closure-compiler/compiler.jar ' +
|
||||
'--compilation_level SIMPLE_OPTIMIZATIONS ' +
|
||||
'--language_in ECMASCRIPT5_STRICT ' +
|
||||
'--js ' + file + ' ' +
|
||||
'--js_output_file ' + minFile,
|
||||
function(code) {
|
||||
if (code !== 0) grunt.fail.warn('Error minifying ' + file);
|
||||
grunt.file.write(minFile, this.singleStrict(grunt.file.read(minFile), '\n'));
|
||||
grunt.log.ok(file + ' minified into ' + minFile);
|
||||
done();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
|
||||
//returns the 32-bit mode force flags for java compiler if supported, this makes the build much faster
|
||||
java32flags: function(){
|
||||
if (process.platform === "win32") return '';
|
||||
if (shell.exec('java -version -d32 2>&1', {silent: true}).code !== 0) return '';
|
||||
return ' -d32 -client';
|
||||
},
|
||||
|
||||
|
||||
//csp connect middleware
|
||||
csp: function(){
|
||||
return function(req, res, next){
|
||||
res.setHeader("X-WebKit-CSP", "default-src 'self';");
|
||||
res.setHeader("X-Content-Security-Policy", "default-src 'self'");
|
||||
next();
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
//rewrite connect middleware
|
||||
rewrite: function(){
|
||||
return function(req, res, next){
|
||||
var REWRITE = /\/(guide|api|cookbook|misc|tutorial).*$/,
|
||||
IGNORED = /(\.(css|js|png|jpg)$|partials\/.*\.html$)/,
|
||||
match;
|
||||
|
||||
if (!IGNORED.test(req.url) && (match = req.url.match(REWRITE))) {
|
||||
console.log('rewriting', req.url);
|
||||
req.url = req.url.replace(match[0], '/index.html');
|
||||
}
|
||||
next();
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -1,273 +0,0 @@
|
||||
var sys = require('sys'),
|
||||
http = require('http'),
|
||||
fs = require('fs'),
|
||||
url = require('url'),
|
||||
events = require('events');
|
||||
|
||||
var DEFAULT_PORT = 8000;
|
||||
|
||||
function main(argv) {
|
||||
new HttpServer({
|
||||
'GET': createServlet(StaticServlet),
|
||||
'HEAD': createServlet(StaticServlet)
|
||||
}).start(Number(argv[2]) || DEFAULT_PORT);
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
return value.toString().
|
||||
replace('<', '<').
|
||||
replace('>', '>').
|
||||
replace('"', '"');
|
||||
}
|
||||
|
||||
function createServlet(Class) {
|
||||
var servlet = new Class();
|
||||
return servlet.handleRequest.bind(servlet);
|
||||
}
|
||||
|
||||
/**
|
||||
* An Http server implementation that uses a map of methods to decide
|
||||
* action routing.
|
||||
*
|
||||
* @param {Object} Map of method => Handler function
|
||||
*/
|
||||
function HttpServer(handlers) {
|
||||
this.handlers = handlers;
|
||||
this.server = http.createServer(this.handleRequest_.bind(this));
|
||||
}
|
||||
|
||||
HttpServer.prototype.start = function(port) {
|
||||
this.port = port;
|
||||
this.server.listen(port);
|
||||
sys.puts('Http Server running at http://127.0.0.1:' + port + '/');
|
||||
};
|
||||
|
||||
HttpServer.prototype.parseUrl_ = function(urlString) {
|
||||
var parsed = url.parse(urlString);
|
||||
parsed.pathname = url.resolve('/', parsed.pathname);
|
||||
return url.parse(url.format(parsed), true);
|
||||
};
|
||||
|
||||
HttpServer.prototype.handleRequest_ = function(req, res) {
|
||||
var logEntry = req.method + ' ' + req.url;
|
||||
if (req.headers['user-agent']) {
|
||||
logEntry += ' ' + req.headers['user-agent'];
|
||||
}
|
||||
sys.puts(logEntry);
|
||||
req.url = this.parseUrl_(req.url);
|
||||
var handler = this.handlers[req.method];
|
||||
if (!handler) {
|
||||
res.writeHead(501);
|
||||
res.end();
|
||||
} else {
|
||||
handler.call(this, req, res);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles static content.
|
||||
*/
|
||||
function StaticServlet() {}
|
||||
|
||||
StaticServlet.MimeMap = {
|
||||
'txt': 'text/plain',
|
||||
'html': 'text/html',
|
||||
'css': 'text/css',
|
||||
'xml': 'application/xml',
|
||||
'json': 'application/json',
|
||||
'js': 'application/javascript',
|
||||
'jpg': 'image/jpeg',
|
||||
'jpeg': 'image/jpeg',
|
||||
'gif': 'image/gif',
|
||||
'png': 'image/png',
|
||||
'manifest': 'text/cache-manifest',
|
||||
// it should be application/font-woff
|
||||
// but only this silences chrome warnings
|
||||
'woff': 'font/opentype'
|
||||
};
|
||||
|
||||
StaticServlet.prototype.handleRequest = function(req, res) {
|
||||
var self = this;
|
||||
var path = ('./' + req.url.pathname).replace('//','/').replace(/%(..)/g, function(match, hex){
|
||||
return String.fromCharCode(parseInt(hex, 16));
|
||||
});
|
||||
var parts = path.split('/');
|
||||
if (parts[parts.length-1].charAt(0) === '.')
|
||||
return self.sendForbidden_(req, res, path);
|
||||
|
||||
// favicon rewriting
|
||||
if (path === './favicon.ico')
|
||||
return self.sendFile_(req, res, './lib/nodeserver/favicon.ico');
|
||||
|
||||
// docs rewriting
|
||||
var REWRITE = /\/(guide|api|cookbook|misc|tutorial).*$/,
|
||||
IGNORED = /(\.(css|js|png|jpg)$|partials\/.*\.html$)/,
|
||||
match;
|
||||
|
||||
if (!IGNORED.test(path) && (match = path.match(REWRITE))) {
|
||||
path = path.replace(match[0], '/index.html');
|
||||
sys.puts('Rewrite to ' + path);
|
||||
}
|
||||
|
||||
// end of docs rewriting
|
||||
|
||||
fs.stat(path, function(err, stat) {
|
||||
if (err)
|
||||
return self.sendMissing_(req, res, path);
|
||||
if (stat.isDirectory())
|
||||
return fs.stat(path + 'index.html', function(err, stat) {
|
||||
// send index.html if exists
|
||||
if (!err)
|
||||
return self.sendFile_(req, res, path + 'index.html');
|
||||
|
||||
// list files otherwise
|
||||
return self.sendDirectory_(req, res, path);
|
||||
});
|
||||
|
||||
return self.sendFile_(req, res, path);
|
||||
});
|
||||
};
|
||||
|
||||
StaticServlet.prototype.sendError_ = function(req, res, error) {
|
||||
res.writeHead(500, {
|
||||
'Content-Type': 'text/html'
|
||||
});
|
||||
res.write('<!doctype html>\n');
|
||||
res.write('<title>Internal Server Error</title>\n');
|
||||
res.write('<h1>Internal Server Error</h1>');
|
||||
res.write('<pre>' + escapeHtml(sys.inspect(error)) + '</pre>');
|
||||
sys.puts('500 Internal Server Error');
|
||||
sys.puts(sys.inspect(error));
|
||||
};
|
||||
|
||||
StaticServlet.prototype.sendMissing_ = function(req, res, path) {
|
||||
path = path.substring(1);
|
||||
res.writeHead(404, {
|
||||
'Content-Type': 'text/html'
|
||||
});
|
||||
res.write('<!doctype html>\n');
|
||||
res.write('<title>404 Not Found</title>\n');
|
||||
res.write('<h1>Not Found</h1>');
|
||||
res.write(
|
||||
'<p>The requested URL ' +
|
||||
escapeHtml(path) +
|
||||
' was not found on this server.</p>'
|
||||
);
|
||||
res.end();
|
||||
sys.puts('404 Not Found: ' + path);
|
||||
};
|
||||
|
||||
StaticServlet.prototype.sendForbidden_ = function(req, res, path) {
|
||||
path = path.substring(1);
|
||||
res.writeHead(403, {
|
||||
'Content-Type': 'text/html'
|
||||
});
|
||||
res.write('<!doctype html>\n');
|
||||
res.write('<title>403 Forbidden</title>\n');
|
||||
res.write('<h1>Forbidden</h1>');
|
||||
res.write(
|
||||
'<p>You do not have permission to access ' +
|
||||
escapeHtml(path) + ' on this server.</p>'
|
||||
);
|
||||
res.end();
|
||||
sys.puts('403 Forbidden: ' + path);
|
||||
};
|
||||
|
||||
StaticServlet.prototype.sendRedirect_ = function(req, res, redirectUrl) {
|
||||
res.writeHead(301, {
|
||||
'Content-Type': 'text/html',
|
||||
'Location': redirectUrl
|
||||
});
|
||||
res.write('<!doctype html>\n');
|
||||
res.write('<title>301 Moved Permanently</title>\n');
|
||||
res.write('<h1>Moved Permanently</h1>');
|
||||
res.write(
|
||||
'<p>The document has moved <a href="' +
|
||||
redirectUrl +
|
||||
'">here</a>.</p>'
|
||||
);
|
||||
res.end();
|
||||
sys.puts('401 Moved Permanently: ' + redirectUrl);
|
||||
};
|
||||
|
||||
StaticServlet.prototype.sendFile_ = function(req, res, path) {
|
||||
var self = this;
|
||||
var file = fs.createReadStream(path);
|
||||
res.writeHead(200, {
|
||||
// CSP headers, uncomment to enable CSP
|
||||
//"X-WebKit-CSP": "default-src 'self';",
|
||||
//"X-Content-Security-Policy": "default-src 'self'",
|
||||
'Content-Type': StaticServlet.
|
||||
MimeMap[path.split('.').pop()] || 'text/plain'
|
||||
});
|
||||
if (req.method === 'HEAD') {
|
||||
res.end();
|
||||
} else {
|
||||
file.on('data', res.write.bind(res));
|
||||
file.on('close', function() {
|
||||
res.end();
|
||||
});
|
||||
file.on('error', function(error) {
|
||||
self.sendError_(req, res, error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
StaticServlet.prototype.sendDirectory_ = function(req, res, path) {
|
||||
var self = this;
|
||||
if (path.match(/[^\/]$/)) {
|
||||
req.url.pathname += '/';
|
||||
var redirectUrl = url.format(url.parse(url.format(req.url)));
|
||||
return self.sendRedirect_(req, res, redirectUrl);
|
||||
}
|
||||
fs.readdir(path, function(err, files) {
|
||||
if (err)
|
||||
return self.sendError_(req, res, error);
|
||||
|
||||
if (!files.length)
|
||||
return self.writeDirectoryIndex_(req, res, path, []);
|
||||
|
||||
var remaining = files.length;
|
||||
files.forEach(function(fileName, index) {
|
||||
fs.stat(path + '/' + fileName, function(err, stat) {
|
||||
if (err)
|
||||
return self.sendError_(req, res, err);
|
||||
if (stat.isDirectory()) {
|
||||
files[index] = fileName + '/';
|
||||
}
|
||||
if (!(--remaining))
|
||||
return self.writeDirectoryIndex_(req, res, path, files);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
StaticServlet.prototype.writeDirectoryIndex_ = function(req, res, path, files) {
|
||||
path = path.substring(1);
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/html'
|
||||
});
|
||||
if (req.method === 'HEAD') {
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
res.write('<!doctype html>\n');
|
||||
res.write('<title>' + escapeHtml(path) + '</title>\n');
|
||||
res.write('<style>\n');
|
||||
res.write(' ol { list-style-type: none; font-size: 1.2em; }\n');
|
||||
res.write('</style>\n');
|
||||
res.write('<h1>Directory: ' + escapeHtml(path) + '</h1>');
|
||||
res.write('<ol>');
|
||||
files.forEach(function(fileName) {
|
||||
if (fileName.charAt(0) !== '.') {
|
||||
res.write('<li><a href="' +
|
||||
escapeHtml(fileName) + '">' +
|
||||
escapeHtml(fileName) + '</a></li>');
|
||||
}
|
||||
});
|
||||
res.write('</ol>');
|
||||
res.end();
|
||||
};
|
||||
|
||||
// Must be last,
|
||||
main(process.argv);
|
||||
@@ -1 +0,0 @@
|
||||
node lib/nodeserver/server.js $1
|
||||
+13
-5
@@ -1,10 +1,18 @@
|
||||
{
|
||||
"name": "AngularJS",
|
||||
"version": "0.0.0",
|
||||
"dependencies" : {
|
||||
"testacular" : "canary",
|
||||
"jasmine-node" : "*",
|
||||
"q-fs" : "*",
|
||||
"qq" : "*"
|
||||
"dependencies": {
|
||||
"grunt": "0.4.0",
|
||||
"grunt-contrib-clean": "0.4.0",
|
||||
"grunt-contrib-compress": "0.4.1",
|
||||
"grunt-contrib-connect": "0.1.2",
|
||||
"grunt-contrib-copy": "0.4.0",
|
||||
"jasmine-node": "1.2.3",
|
||||
"q": "~0.9.2",
|
||||
"q-fs": "0.1.36",
|
||||
"qq": "0.3.5",
|
||||
"shelljs": "0.1.2",
|
||||
"karma": "0.8.4",
|
||||
"yaml-js": "0.0.5"
|
||||
}
|
||||
}
|
||||
|
||||
+104
-35
@@ -28,12 +28,12 @@ var uppercase = function(string){return isString(string) ? string.toUpperCase()
|
||||
|
||||
var manualLowercase = function(s) {
|
||||
return isString(s)
|
||||
? s.replace(/[A-Z]/g, function(ch) {return fromCharCode(ch.charCodeAt(0) | 32);})
|
||||
? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
|
||||
: s;
|
||||
};
|
||||
var manualUppercase = function(s) {
|
||||
return isString(s)
|
||||
? s.replace(/[a-z]/g, function(ch) {return fromCharCode(ch.charCodeAt(0) & ~32);})
|
||||
? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
|
||||
: s;
|
||||
};
|
||||
|
||||
@@ -46,11 +46,8 @@ if ('i' !== 'I'.toLowerCase()) {
|
||||
uppercase = manualUppercase;
|
||||
}
|
||||
|
||||
function fromCharCode(code) {return String.fromCharCode(code);}
|
||||
|
||||
|
||||
var Error = window.Error,
|
||||
/** holds major version number for IE or NaN for real browsers */
|
||||
var /** holds major version number for IE or NaN for real browsers */
|
||||
msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]),
|
||||
jqLite, // delay binding since jQuery could be loaded after us.
|
||||
jQuery, // delay binding
|
||||
@@ -58,12 +55,32 @@ var Error = window.Error,
|
||||
push = [].push,
|
||||
toString = Object.prototype.toString,
|
||||
|
||||
|
||||
_angular = window.angular,
|
||||
/** @name angular */
|
||||
angular = window.angular || (window.angular = {}),
|
||||
angularModule,
|
||||
nodeName_,
|
||||
uid = ['0', '0', '0'];
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.noConflict
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Restores the previous global value of angular and returns the current instance. Other libraries may already use the
|
||||
* angular namespace. Or a previous version of angular is already loaded on the page. In these cases you may want to
|
||||
* restore the previous namespace and keep a reference to angular.
|
||||
*
|
||||
* @return {Object} The current angular namespace
|
||||
*/
|
||||
function noConflict() {
|
||||
var a = window.angular;
|
||||
window.angular = _angular;
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.forEach
|
||||
@@ -91,6 +108,30 @@ var Error = window.Error,
|
||||
* @param {Object=} context Object to become context (`this`) for the iterator function.
|
||||
* @returns {Object|Array} Reference to `obj`.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {*} obj
|
||||
* @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, ...)
|
||||
*/
|
||||
function isArrayLike(obj) {
|
||||
if (!obj || (typeof obj.length !== 'number')) return false;
|
||||
|
||||
// We have on object which has length property. Should we treat it as array?
|
||||
if (typeof obj.hasOwnProperty != 'function' &&
|
||||
typeof obj.constructor != 'function') {
|
||||
// This is here for IE8: it is a bogus object treat it as array;
|
||||
return true;
|
||||
} else {
|
||||
return obj instanceof JQLite || // JQLite
|
||||
(jQuery && obj instanceof jQuery) || // jQuery
|
||||
toString.call(obj) !== '[object Object]' || // some browser native object
|
||||
typeof obj.callee === 'function'; // arguments (on IE8 looks like regular obj)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function forEach(obj, iterator, context) {
|
||||
var key;
|
||||
if (obj) {
|
||||
@@ -102,7 +143,7 @@ function forEach(obj, iterator, context) {
|
||||
}
|
||||
} else if (obj.forEach && obj.forEach !== forEach) {
|
||||
obj.forEach(iterator, context);
|
||||
} else if (isObject(obj) && isNumber(obj.length)) {
|
||||
} else if (isArrayLike(obj)) {
|
||||
for (key = 0; key < obj.length; key++)
|
||||
iterator.call(context, obj[key], key);
|
||||
} else {
|
||||
@@ -147,7 +188,7 @@ function reverseParams(iteratorFn) {
|
||||
/**
|
||||
* A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric
|
||||
* characters such as '012ABC'. The reason why we are not using simply a number counter is that
|
||||
* the number string gets longer over time, and it can also overflow, where as the the nextId
|
||||
* the number string gets longer over time, and it can also overflow, where as the nextId
|
||||
* will grow much slower, it is a string, and it will never overflow.
|
||||
*
|
||||
* @returns an unique alpha-numeric string
|
||||
@@ -206,6 +247,11 @@ function inherit(parent, extra) {
|
||||
return extend(new (extend(function() {}, {prototype:parent}))(), extra);
|
||||
}
|
||||
|
||||
var START_SPACE = /^\s*/;
|
||||
var END_SPACE = /\s*$/;
|
||||
function stripWhitespace(str) {
|
||||
return isString(str) ? str.replace(START_SPACE, '').replace(END_SPACE, '') : str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
@@ -543,9 +589,7 @@ function copy(source, destination){
|
||||
} else {
|
||||
if (source === destination) throw Error("Can't copy equivalent objects or arrays");
|
||||
if (isArray(source)) {
|
||||
while(destination.length) {
|
||||
destination.pop();
|
||||
}
|
||||
destination.length = 0;
|
||||
for ( var i = 0; i < source.length; i++) {
|
||||
destination.push(copy(source[i]));
|
||||
}
|
||||
@@ -592,7 +636,7 @@ function shallowCopy(src, dst) {
|
||||
* * Both objects or values are of the same type and all of their properties pass `===` comparison.
|
||||
* * Both values are NaN. (In JavasScript, NaN == NaN => false. But we consider two NaN as equal)
|
||||
*
|
||||
* During a property comparision, properties of `function` type and properties with names
|
||||
* During a property comparison, properties of `function` type and properties with names
|
||||
* that begin with `$` are ignored.
|
||||
*
|
||||
* Scope and DOMWindow objects are being compared only be identify (`===`).
|
||||
@@ -756,9 +800,18 @@ function startingTag(element) {
|
||||
// are not allowed to have children. So we just ignore it.
|
||||
element.html('');
|
||||
} catch(e) {}
|
||||
return jqLite('<div>').append(element).html().
|
||||
match(/^(<[^>]+>)/)[1].
|
||||
replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
|
||||
// As Per DOM Standards
|
||||
var TEXT_NODE = 3;
|
||||
var elemHtml = jqLite('<div>').append(element).html();
|
||||
try {
|
||||
return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) :
|
||||
elemHtml.
|
||||
match(/^(<[^>]+>)/)[1].
|
||||
replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
|
||||
} catch(e) {
|
||||
return lowercase(elemHtml);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -790,7 +843,7 @@ function toKeyValue(obj) {
|
||||
|
||||
|
||||
/**
|
||||
* We need our custom method because encodeURIComponent is too agressive and doesn't follow
|
||||
* We need our custom method because encodeURIComponent is too aggressive and doesn't follow
|
||||
* http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
|
||||
* segments:
|
||||
* segment = *pchar
|
||||
@@ -810,7 +863,7 @@ function encodeUriSegment(val) {
|
||||
|
||||
/**
|
||||
* This method is intended for encoding *key* or *value* parts of query component. We need a custom
|
||||
* method becuase encodeURIComponent is too agressive and encodes stuff that doesn't have to be
|
||||
* method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
|
||||
* encoded per http://tools.ietf.org/html/rfc3986:
|
||||
* query = *( pchar / "/" / "?" )
|
||||
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||
@@ -825,7 +878,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
|
||||
replace(/%3A/gi, ':').
|
||||
replace(/%24/g, '$').
|
||||
replace(/%2C/gi, ',').
|
||||
replace((pctEncodeSpaces ? null : /%20/g), '+');
|
||||
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
|
||||
}
|
||||
|
||||
|
||||
@@ -842,7 +895,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
|
||||
* Use this directive to auto-bootstrap on application. Only
|
||||
* one directive can be used per HTML document. The directive
|
||||
* designates the root of the application and is typically placed
|
||||
* ot the root of the page.
|
||||
* at the root of the page.
|
||||
*
|
||||
* In the example below if the `ngApp` directive would not be placed
|
||||
* on the `html` element then the document would not be compiled
|
||||
@@ -914,22 +967,38 @@ function angularInit(element, bootstrap) {
|
||||
* @returns {AUTO.$injector} Returns the newly created injector for this app.
|
||||
*/
|
||||
function bootstrap(element, modules) {
|
||||
element = jqLite(element);
|
||||
modules = modules || [];
|
||||
modules.unshift(['$provide', function($provide) {
|
||||
$provide.value('$rootElement', element);
|
||||
}]);
|
||||
modules.unshift('ng');
|
||||
var injector = createInjector(modules);
|
||||
injector.invoke(
|
||||
['$rootScope', '$rootElement', '$compile', '$injector', function(scope, element, compile, injector){
|
||||
scope.$apply(function() {
|
||||
element.data('$injector', injector);
|
||||
compile(element)(scope);
|
||||
});
|
||||
}]
|
||||
);
|
||||
return injector;
|
||||
var resumeBootstrapInternal = function() {
|
||||
element = jqLite(element);
|
||||
modules = modules || [];
|
||||
modules.unshift(['$provide', function($provide) {
|
||||
$provide.value('$rootElement', element);
|
||||
}]);
|
||||
modules.unshift('ng');
|
||||
var injector = createInjector(modules);
|
||||
injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
|
||||
function(scope, element, compile, injector) {
|
||||
scope.$apply(function() {
|
||||
element.data('$injector', injector);
|
||||
compile(element)(scope);
|
||||
});
|
||||
}]
|
||||
);
|
||||
return injector;
|
||||
};
|
||||
|
||||
var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
|
||||
|
||||
if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
|
||||
return resumeBootstrapInternal();
|
||||
}
|
||||
|
||||
window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
|
||||
angular.resumeBootstrap = function(extraModules) {
|
||||
forEach(extraModules, function(module) {
|
||||
modules.push(module);
|
||||
});
|
||||
resumeBootstrapInternal();
|
||||
};
|
||||
}
|
||||
|
||||
var SNAKE_CASE_REGEXP = /[A-Z]/g;
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
|
||||
*/
|
||||
var version = {
|
||||
full: '"NG_VERSION_FULL"', // all of these placeholder strings will be replaced by rake's
|
||||
major: "NG_VERSION_MAJOR", // compile task
|
||||
full: '"NG_VERSION_FULL"', // all of these placeholder strings will be replaced by grunt's
|
||||
major: "NG_VERSION_MAJOR", // package task
|
||||
minor: "NG_VERSION_MINOR",
|
||||
dot: "NG_VERSION_DOT",
|
||||
codeName: '"NG_VERSION_CODENAME"'
|
||||
@@ -48,7 +48,8 @@ function publishExternalAPI(angular){
|
||||
'isDate': isDate,
|
||||
'lowercase': lowercase,
|
||||
'uppercase': uppercase,
|
||||
'callbacks': {counter: 0}
|
||||
'callbacks': {counter: 0},
|
||||
'noConflict': noConflict
|
||||
});
|
||||
|
||||
angularModule = setupModuleLoader(window);
|
||||
@@ -106,6 +107,8 @@ function publishExternalAPI(angular){
|
||||
directive(ngEventDirectives);
|
||||
$provide.provider({
|
||||
$anchorScroll: $AnchorScrollProvider,
|
||||
$animation: $AnimationProvider,
|
||||
$animator: $AnimatorProvider,
|
||||
$browser: $BrowserProvider,
|
||||
$cacheFactory: $CacheFactoryProvider,
|
||||
$controller: $ControllerProvider,
|
||||
|
||||
-44
@@ -65,47 +65,3 @@ HashMap.prototype = {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A map where multiple values can be added to the same key such that they form a queue.
|
||||
* @returns {HashQueueMap}
|
||||
*/
|
||||
function HashQueueMap() {}
|
||||
HashQueueMap.prototype = {
|
||||
/**
|
||||
* Same as array push, but using an array as the value for the hash
|
||||
*/
|
||||
push: function(key, value) {
|
||||
var array = this[key = hashKey(key)];
|
||||
if (!array) {
|
||||
this[key] = [value];
|
||||
} else {
|
||||
array.push(value);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Same as array shift, but using an array as the value for the hash
|
||||
*/
|
||||
shift: function(key) {
|
||||
var array = this[key = hashKey(key)];
|
||||
if (array) {
|
||||
if (array.length == 1) {
|
||||
delete this[key];
|
||||
return array[0];
|
||||
} else {
|
||||
return array.shift();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* return the first item without deleting it
|
||||
*/
|
||||
peek: function(key) {
|
||||
var array = this[hashKey(key)];
|
||||
if (array) {
|
||||
return array[0];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -100,15 +100,15 @@ function annotate(fn) {
|
||||
*
|
||||
* <pre>
|
||||
* // inferred (only works if code not minified/obfuscated)
|
||||
* $inject.invoke(function(serviceA){});
|
||||
* $injector.invoke(function(serviceA){});
|
||||
*
|
||||
* // annotated
|
||||
* function explicit(serviceA) {};
|
||||
* explicit.$inject = ['serviceA'];
|
||||
* $inject.invoke(explicit);
|
||||
* $injector.invoke(explicit);
|
||||
*
|
||||
* // inline
|
||||
* $inject.invoke(['serviceA', function(serviceA){}]);
|
||||
* $injector.invoke(['serviceA', function(serviceA){}]);
|
||||
* </pre>
|
||||
*
|
||||
* ## Inference
|
||||
@@ -192,7 +192,7 @@ function annotate(fn) {
|
||||
* This method does not work with code minfication / obfuscation. For this reason the following annotation strategies
|
||||
* are supported.
|
||||
*
|
||||
* # The `$injector` property
|
||||
* # The `$inject` property
|
||||
*
|
||||
* If a function has an `$inject` property and its value is an array of strings, then the strings represent names of
|
||||
* services to be injected into the function.
|
||||
@@ -254,7 +254,7 @@ function annotate(fn) {
|
||||
* @description
|
||||
*
|
||||
* Use `$provide` to register new providers with the `$injector`. The providers are the factories for the instance.
|
||||
* The providers share the same name as the instance they create with the `Provider` suffixed to them.
|
||||
* The providers share the same name as the instance they create with `Provider` suffixed to them.
|
||||
*
|
||||
* A provider is an object with a `$get()` method. The injector calls the `$get` method to create a new instance of
|
||||
* a service. The Provider can have additional methods which would allow for configuration of the provider.
|
||||
|
||||
Vendored
+8
-5
@@ -4,10 +4,10 @@ var directive = {};
|
||||
var service = { value: {} };
|
||||
|
||||
var DEPENDENCIES = {
|
||||
'angular.js': 'http://code.angularjs.org/' + angular.version.full + 'angular.min.js',
|
||||
'angular-resource.js': 'http://code.angularjs.org/' + angular.version.full + 'angular-resource.min.js',
|
||||
'angular-sanitize.js': 'http://code.angularjs.org/' + angular.version.full + 'angular-sanitize.min.js',
|
||||
'angular-cookies.js': 'http://code.angularjs.org/' + angular.version.full + 'angular-cookies.min.js'
|
||||
'angular.js': 'http://code.angularjs.org/' + angular.version.full + '/angular.min.js',
|
||||
'angular-resource.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-resource.min.js',
|
||||
'angular-sanitize.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-sanitize.min.js',
|
||||
'angular-cookies.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-cookies.min.js'
|
||||
};
|
||||
|
||||
|
||||
@@ -179,7 +179,8 @@ directive.ngEvalJavascript = ['getEmbeddedTemplate', function(getEmbeddedTemplat
|
||||
}];
|
||||
|
||||
|
||||
directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location', function($templateCache, $browser, docsRootScope, $location) {
|
||||
directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location', '$sniffer',
|
||||
function($templateCache, $browser, docsRootScope, $location, $sniffer) {
|
||||
return {
|
||||
terminal: true,
|
||||
link: function(scope, element, attrs) {
|
||||
@@ -189,6 +190,7 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
|
||||
$provide.value('$templateCache', $templateCache);
|
||||
$provide.value('$anchorScroll', angular.noop);
|
||||
$provide.value('$browser', $browser);
|
||||
$provide.value('$sniffer', $sniffer);
|
||||
$provide.provider('$location', function() {
|
||||
this.$get = ['$rootScope', function($rootScope) {
|
||||
docsRootScope.$on('$locationChangeSuccess', function(event, oldUrl, newUrl) {
|
||||
@@ -223,6 +225,7 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
angular.bootstrap(element, modules);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -643,7 +643,7 @@ var REGEXP_PRECEDER_PATTERN = '(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[
|
||||
* recognized.
|
||||
*
|
||||
* Shortcut is an optional string of characters, any of which, if the first
|
||||
* character, gurantee that this pattern and only this pattern matches.
|
||||
* character, guarantee that this pattern and only this pattern matches.
|
||||
*
|
||||
* @param {Array} shortcutStylePatterns patterns that always start with
|
||||
* a known character. Must have a shortcut string.
|
||||
|
||||
+9
-4
@@ -59,7 +59,7 @@
|
||||
* - [val()](http://api.jquery.com/val/)
|
||||
* - [wrap()](http://api.jquery.com/wrap/)
|
||||
*
|
||||
* ## In addtion to the above, Angular provides additional methods to both jQuery and jQuery lite:
|
||||
* ## In addition to the above, Angular provides additional methods to both jQuery and jQuery lite:
|
||||
*
|
||||
* - `controller(name)` - retrieves the controller of the current element or its parent. By default
|
||||
* retrieves controller associated with the `ngController` directive. If `name` is provided as
|
||||
@@ -327,9 +327,14 @@ var JQLitePrototype = JQLite.prototype = {
|
||||
fn();
|
||||
}
|
||||
|
||||
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.
|
||||
JQLite(window).bind('load', trigger); // fallback to window.onload for others
|
||||
// check if document already is loaded
|
||||
if (document.readyState === 'complete'){
|
||||
setTimeout(trigger);
|
||||
} else {
|
||||
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.
|
||||
JQLite(window).bind('load', trigger); // fallback to window.onload for others
|
||||
}
|
||||
},
|
||||
toString: function() {
|
||||
var value = [];
|
||||
|
||||
@@ -163,6 +163,33 @@ function setupModuleLoader(window) {
|
||||
*/
|
||||
constant: invokeLater('$provide', 'constant', 'unshift'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#animation
|
||||
* @methodOf angular.Module
|
||||
* @param {string} name animation name
|
||||
* @param {Function} animationFactory Factory function for creating new instance of an animation.
|
||||
* @description
|
||||
*
|
||||
* Defines an animation hook that can be later used with {@link ng.directive:ngAnimate ngAnimate}
|
||||
* alongside {@link ng.directive:ngAnimate#Description common ng directives} as well as custom directives.
|
||||
* <pre>
|
||||
* module.animation('animation-name', function($inject1, $inject2) {
|
||||
* return {
|
||||
* //this gets called in preparation to setup an animation
|
||||
* setup : function(element) { ... },
|
||||
*
|
||||
* //this gets called once the animation is run
|
||||
* start : function(element, done, memo) { ... }
|
||||
* }
|
||||
* })
|
||||
* </pre>
|
||||
*
|
||||
* See {@link ng.$animationProvider#register $animationProvider.register()} and
|
||||
* {@link ng.directive:ngAnimate ngAnimate} for more information.
|
||||
*/
|
||||
animation: invokeLater('$animationProvider', 'register'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#filter
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name ng.$animationProvider
|
||||
* @description
|
||||
*
|
||||
* The $AnimationProvider provider allows developers to register and access custom JavaScript animations directly inside
|
||||
* of a module.
|
||||
*
|
||||
*/
|
||||
$AnimationProvider.$inject = ['$provide'];
|
||||
function $AnimationProvider($provide) {
|
||||
var suffix = 'Animation';
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.$animation#register
|
||||
* @methodOf ng.$animationProvider
|
||||
*
|
||||
* @description
|
||||
* Registers a new injectable animation factory function. The factory function produces the animation object which
|
||||
* has these two properties:
|
||||
*
|
||||
* * `setup`: `function(Element):*` A function which receives the starting state of the element. The purpose
|
||||
* of this function is to get the element ready for animation. Optionally the function returns an memento which
|
||||
* is passed to the `start` function.
|
||||
* * `start`: `function(Element, doneFunction, *)` The element to animate, the `doneFunction` to be called on
|
||||
* element animation completion, and an optional memento from the `setup` function.
|
||||
*
|
||||
* @param {string} name The name of the animation.
|
||||
* @param {function} factory The factory function that will be executed to return the animation object.
|
||||
*
|
||||
*/
|
||||
this.register = function(name, factory) {
|
||||
$provide.factory(camelCase(name) + suffix, factory);
|
||||
};
|
||||
|
||||
this.$get = ['$injector', function($injector) {
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.$animation
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* The $animation service is used to retrieve any defined animation functions. When executed, the $animation service
|
||||
* will return a object that contains the setup and start functions that were defined for the animation.
|
||||
*
|
||||
* @param {String} name Name of the animation function to retrieve. Animation functions are registered and stored
|
||||
* inside of the AngularJS DI so a call to $animate('custom') is the same as injecting `customAnimation`
|
||||
* via dependency injection.
|
||||
* @return {Object} the animation object which contains the `setup` and `start` functions that perform the animation.
|
||||
*/
|
||||
return function $animation(name) {
|
||||
if (name) {
|
||||
try {
|
||||
return $injector.get(camelCase(name) + suffix);
|
||||
} catch (e) {
|
||||
//TODO(misko): this is a hack! we should have a better way to test if the injector has a given key.
|
||||
// The issue is that the animations are optional, and if not present they should be silently ignored.
|
||||
// The proper way to fix this is to add API onto the injector so that we can ask to see if a given
|
||||
// animation is supported.
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
};
|
||||
@@ -0,0 +1,311 @@
|
||||
'use strict';
|
||||
|
||||
// NOTE: this is a pseudo directive.
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ng.directive:ngAnimate
|
||||
*
|
||||
* @description
|
||||
* The `ngAnimate` directive works as an attribute that is attached alongside pre-existing directives.
|
||||
* It effects how the directive will perform DOM manipulation. This allows for complex animations to take place while
|
||||
* without burduning the directive which uses the animation with animation details. The built dn directives
|
||||
* `ngRepeat`, `ngInclude`, `ngSwitch`, `ngShow`, `ngHide` and `ngView` already accept `ngAnimate` directive.
|
||||
* Custom directives can take advantage of animation through {@link ng.$animator $animator service}.
|
||||
*
|
||||
* Below is a more detailed breakdown of the supported callback events provided by pre-exisitng ng directives:
|
||||
*
|
||||
* * {@link ng.directive:ngRepeat#animations ngRepeat} — enter, leave and move
|
||||
* * {@link ng.directive:ngView#animations ngView} — enter and leave
|
||||
* * {@link ng.directive:ngInclude#animations ngInclude} — enter and leave
|
||||
* * {@link ng.directive:ngSwitch#animations ngSwitch} — enter and leave
|
||||
* * {@link ng.directive:ngShow#animations ngShow & ngHide} - show and hide respectively
|
||||
*
|
||||
* You can find out more information about animations upon visiting each directive page.
|
||||
*
|
||||
* Below is an example of a directive that makes use of the ngAnimate attribute:
|
||||
*
|
||||
* <pre>
|
||||
* <!-- you can also use data-ng-animate, ng:animate or x-ng-animate as well -->
|
||||
* <ANY ng-directive ng-animate="{event1: 'animation-name', event2: 'animation-name-2'}"></ANY>
|
||||
*
|
||||
* <!-- you can also use a short hand -->
|
||||
* <ANY ng-directive ng-animate=" 'animation' "></ANY>
|
||||
* <!-- which expands to -->
|
||||
* <ANY ng-directive ng-animate="{ enter: 'animation-enter', leave: 'animation-leave', ...}"></ANY>
|
||||
*
|
||||
* <!-- keep in mind that ng-animate can take expressions -->
|
||||
* <ANY ng-directive ng-animate=" computeCurrentAnimation() "></ANY>
|
||||
* </pre>
|
||||
*
|
||||
* The `event1` and `event2` attributes refer to the animation events specific to the directive that has been assigned.
|
||||
*
|
||||
* <h2>CSS-defined Animations</h2>
|
||||
* By default, ngAnimate attaches two CSS3 classes per animation event to the DOM element to achieve the animation.
|
||||
* This is up to you, the developer, to ensure that the animations take place using cross-browser CSS3 transitions.
|
||||
* All that is required is the following CSS code:
|
||||
*
|
||||
* <pre>
|
||||
* <style type="text/css">
|
||||
* /*
|
||||
* The animate-enter prefix is the event name that you
|
||||
* have provided within the ngAnimate attribute.
|
||||
* */
|
||||
* .animate-enter-setup {
|
||||
* -webkit-transition: 1s linear all; /* Safari/Chrome */
|
||||
* -moz-transition: 1s linear all; /* Firefox */
|
||||
* -ms-transition: 1s linear all; /* IE10 */
|
||||
* -o-transition: 1s linear all; /* Opera */
|
||||
* transition: 1s linear all; /* Future Browsers */
|
||||
*
|
||||
* /* The animation preparation code */
|
||||
* opacity: 0;
|
||||
* }
|
||||
*
|
||||
* /*
|
||||
* Keep in mind that you want to combine both CSS
|
||||
* classes together to avoid any CSS-specificity
|
||||
* conflicts
|
||||
* */
|
||||
* .animate-enter-setup.animate-enter-start {
|
||||
* /* The animation code itself */
|
||||
* opacity: 1;
|
||||
* }
|
||||
* </style>
|
||||
*
|
||||
* <div ng-directive ng-animate="{enter: 'animate-enter'}"></div>
|
||||
* </pre>
|
||||
*
|
||||
* Upon DOM mutation, the setup class is added first, then the browser is allowed to reflow the content and then,
|
||||
* the start class is added to trigger the animation. The ngAnimate directive will automatically extract the duration
|
||||
* of the animation to determine when the animation ends. Once the animation is over then both CSS classes will be
|
||||
* removed from the DOM. If a browser does not support CSS transitions then the animation will start and end
|
||||
* immediately resulting in a DOM element that is at it's final state. This final state is when the DOM element
|
||||
* has no CSS animation classes surrounding it.
|
||||
*
|
||||
* <h2>JavaScript-defined Animations</h2>
|
||||
* In the event that you do not want to use CSS3 animations or if you wish to offer animations to browsers that do not
|
||||
* yet support them, then you can make use of JavaScript animations defined inside ngModule.
|
||||
*
|
||||
* <pre>
|
||||
* var ngModule = angular.module('YourApp', []);
|
||||
* ngModule.animation('animate-enter', function() {
|
||||
* return {
|
||||
* setup : function(element) {
|
||||
* //prepare the element for animation
|
||||
* element.css({ 'opacity': 0 });
|
||||
* var memo = "..."; //this value is passed to the start function
|
||||
* return memo;
|
||||
* },
|
||||
* start : function(element, done, memo) {
|
||||
* //start the animation
|
||||
* element.animate({
|
||||
* 'opacity' : 1
|
||||
* }, function() {
|
||||
* //call when the animation is complete
|
||||
* done()
|
||||
* });
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* As you can see, the JavaScript code follows a similar template to the CSS3 animations. Once defined, the animation
|
||||
* can be used in the same way with the ngAnimate attribute. Keep in mind that, when using JavaScript-enabled
|
||||
* animations, ngAnimate will also add in the same CSS classes that CSS-enabled animations do (even if you're using
|
||||
* JavaScript animations) to animated the element, but it will not attempt to find any CSS3 transition duration value.
|
||||
* It will instead close off the animation once the provided done function is executed. So it's important that you
|
||||
* make sure your animations remember to fire off the done function once the animations are complete.
|
||||
*
|
||||
* @param {expression} ngAnimate Used to configure the DOM manipulation animations.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.$animator
|
||||
*
|
||||
* @description
|
||||
* The $animator service provides the DOM manipulation API which is decorated with animations.
|
||||
*
|
||||
* @param {Scope} scope the scope for the ng-animate.
|
||||
* @param {Attributes} attr the attributes object which contains the ngAnimate key / value pair. (The attributes are
|
||||
* passed into the linking function of the directive using the `$animator`.)
|
||||
* @return {object} the animator object which contains the enter, leave, move, show, hide and animate methods.
|
||||
*/
|
||||
var $AnimatorProvider = function() {
|
||||
this.$get = ['$animation', '$window', '$sniffer', function($animation, $window, $sniffer) {
|
||||
return function(scope, attrs) {
|
||||
var ngAnimateAttr = attrs.ngAnimate;
|
||||
var animator = {};
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.animator#enter
|
||||
* @methodOf ng.$animator
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Injects the element object into the DOM (inside of the parent element) and then runs the enter animation.
|
||||
*
|
||||
* @param {jQuery/jqLite element} element the element that will be the focus of the enter animation
|
||||
* @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the enter animation
|
||||
* @param {jQuery/jqLite element} after the sibling element (which is the previous element) of the element that will be the focus of the enter animation
|
||||
*/
|
||||
animator.enter = animateActionFactory('enter', insert, noop);
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.animator#leave
|
||||
* @methodOf ng.$animator
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Runs the leave animation operation and, upon completion, removes the element from the DOM.
|
||||
*
|
||||
* @param {jQuery/jqLite element} element the element that will be the focus of the leave animation
|
||||
* @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the leave animation
|
||||
*/
|
||||
animator.leave = animateActionFactory('leave', noop, remove);
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.animator#move
|
||||
* @methodOf ng.$animator
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Fires the move DOM operation. Just before the animation starts, the animator will either append it into the parent container or
|
||||
* add the element directly after the after element if present. Then the move animation will be run.
|
||||
*
|
||||
* @param {jQuery/jqLite element} element the element that will be the focus of the move animation
|
||||
* @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the move animation
|
||||
* @param {jQuery/jqLite element} after the sibling element (which is the previous element) of the element that will be the focus of the move animation
|
||||
*/
|
||||
animator.move = animateActionFactory('move', move, noop);
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.animator#show
|
||||
* @methodOf ng.$animator
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Reveals the element by setting the CSS property `display` to `block` and then starts the show animation directly after.
|
||||
*
|
||||
* @param {jQuery/jqLite element} element the element that will be rendered visible or hidden
|
||||
*/
|
||||
animator.show = animateActionFactory('show', show, noop);
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.animator#hide
|
||||
* @methodOf ng.$animator
|
||||
*
|
||||
* @description
|
||||
* Starts the hide animation first and sets the CSS `display` property to `none` upon completion.
|
||||
*
|
||||
* @param {jQuery/jqLite element} element the element that will be rendered visible or hidden
|
||||
*/
|
||||
animator.hide = animateActionFactory('hide', noop, hide);
|
||||
return animator;
|
||||
|
||||
function animateActionFactory(type, beforeFn, afterFn) {
|
||||
var ngAnimateValue = ngAnimateAttr && scope.$eval(ngAnimateAttr);
|
||||
var className = ngAnimateAttr
|
||||
? isObject(ngAnimateValue) ? ngAnimateValue[type] : ngAnimateValue + '-' + type
|
||||
: '';
|
||||
var animationPolyfill = $animation(className);
|
||||
|
||||
var polyfillSetup = animationPolyfill && animationPolyfill.setup;
|
||||
var polyfillStart = animationPolyfill && animationPolyfill.start;
|
||||
|
||||
if (!className) {
|
||||
return function(element, parent, after) {
|
||||
beforeFn(element, parent, after);
|
||||
afterFn(element, parent, after);
|
||||
}
|
||||
} else {
|
||||
var setupClass = className + '-setup';
|
||||
var startClass = className + '-start';
|
||||
|
||||
return function(element, parent, after) {
|
||||
if (!$sniffer.supportsTransitions && !polyfillSetup && !polyfillStart) {
|
||||
beforeFn(element, parent, after);
|
||||
afterFn(element, parent, after);
|
||||
return;
|
||||
}
|
||||
|
||||
element.addClass(setupClass);
|
||||
beforeFn(element, parent, after);
|
||||
if (element.length == 0) return done();
|
||||
|
||||
var memento = (polyfillSetup || noop)(element);
|
||||
|
||||
// $window.setTimeout(beginAnimation, 0); this was causing the element not to animate
|
||||
// keep at 1 for animation dom rerender
|
||||
$window.setTimeout(beginAnimation, 1);
|
||||
|
||||
function beginAnimation() {
|
||||
element.addClass(startClass);
|
||||
if (polyfillStart) {
|
||||
polyfillStart(element, done, memento);
|
||||
} else if (isFunction($window.getComputedStyle)) {
|
||||
var vendorTransitionProp = $sniffer.vendorPrefix + 'Transition';
|
||||
var w3cTransitionProp = 'transition'; //one day all browsers will have this
|
||||
|
||||
var durationKey = 'Duration';
|
||||
var duration = 0;
|
||||
//we want all the styles defined before and after
|
||||
forEach(element, function(element) {
|
||||
var globalStyles = $window.getComputedStyle(element) || {};
|
||||
duration = Math.max(
|
||||
parseFloat(globalStyles[w3cTransitionProp + durationKey]) ||
|
||||
parseFloat(globalStyles[vendorTransitionProp + durationKey]) ||
|
||||
0,
|
||||
duration);
|
||||
});
|
||||
|
||||
$window.setTimeout(done, duration * 1000);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
function done() {
|
||||
afterFn(element, parent, after);
|
||||
element.removeClass(setupClass);
|
||||
element.removeClass(startClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function show(element) {
|
||||
element.css('display', '');
|
||||
}
|
||||
|
||||
function hide(element) {
|
||||
element.css('display', 'none');
|
||||
}
|
||||
|
||||
function insert(element, parent, after) {
|
||||
if (after) {
|
||||
after.after(element);
|
||||
} else {
|
||||
parent.append(element);
|
||||
}
|
||||
}
|
||||
|
||||
function remove(element) {
|
||||
element.remove();
|
||||
}
|
||||
|
||||
function move(element, parent, after) {
|
||||
// Do not remove element before insert. Removing will cause data associated with the
|
||||
// element to be dropped. Insert will implicitly do the remove.
|
||||
insert(element, parent, after);
|
||||
}
|
||||
}];
|
||||
};
|
||||
+4
-4
@@ -237,7 +237,7 @@ function Browser(window, document, $log, $sniffer) {
|
||||
*/
|
||||
self.baseHref = function() {
|
||||
var href = baseElement.attr('href');
|
||||
return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : href;
|
||||
return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : '';
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
@@ -252,7 +252,7 @@ function Browser(window, document, $log, $sniffer) {
|
||||
* @methodOf ng.$browser
|
||||
*
|
||||
* @param {string=} name Cookie name
|
||||
* @param {string=} value Cokkie value
|
||||
* @param {string=} value Cookie value
|
||||
*
|
||||
* @description
|
||||
* The cookies method provides a 'private' low level access to browser cookies.
|
||||
@@ -314,7 +314,7 @@ function Browser(window, document, $log, $sniffer) {
|
||||
* @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
|
||||
*
|
||||
* @description
|
||||
* Executes a fn asynchroniously via `setTimeout(fn, delay)`.
|
||||
* Executes a fn asynchronously via `setTimeout(fn, delay)`.
|
||||
*
|
||||
* Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
|
||||
* `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
|
||||
@@ -341,7 +341,7 @@ function Browser(window, document, $log, $sniffer) {
|
||||
* Cancels a defered task identified with `deferId`.
|
||||
*
|
||||
* @param {*} deferId Token returned by the `$browser.defer` function.
|
||||
* @returns {boolean} Returns `true` if the task hasn't executed yet and was successfuly canceled.
|
||||
* @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully canceled.
|
||||
*/
|
||||
self.defer.cancel = function(deferId) {
|
||||
if (pendingDeferIds[deferId]) {
|
||||
|
||||
+111
-31
@@ -155,7 +155,8 @@ function $CompileProvider($provide) {
|
||||
Suffix = 'Directive',
|
||||
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
|
||||
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
|
||||
MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ';
|
||||
MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ',
|
||||
urlSanitizationWhitelist = /^\s*(https?|ftp|mailto|file):/;
|
||||
|
||||
|
||||
/**
|
||||
@@ -169,7 +170,7 @@ function $CompileProvider($provide) {
|
||||
*
|
||||
* @param {string} name Name of the directive in camel-case. (ie <code>ngBind</code> which will match as
|
||||
* <code>ng-bind</code>).
|
||||
* @param {function} directiveFactory An injectable directive factroy function. See {@link guide/directive} for more
|
||||
* @param {function} directiveFactory An injectable directive factory function. See {@link guide/directive} for more
|
||||
* info.
|
||||
* @returns {ng.$compileProvider} Self for chaining.
|
||||
*/
|
||||
@@ -209,11 +210,41 @@ function $CompileProvider($provide) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.$compileProvider#urlSanitizationWhitelist
|
||||
* @methodOf ng.$compileProvider
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
|
||||
* urls during a[href] sanitization.
|
||||
*
|
||||
* The sanitization is a security measure aimed at prevent XSS attacks via html links.
|
||||
*
|
||||
* Any url about to be assigned to a[href] via data-binding is first normalized and turned into an
|
||||
* absolute url. Afterwards the url is matched against the `urlSanitizationWhitelist` regular
|
||||
* expression. If a match is found the original url is written into the dom. Otherwise the
|
||||
* absolute url is prefixed with `'unsafe:'` string and only then it is written into the DOM.
|
||||
*
|
||||
* @param {RegExp=} regexp New regexp to whitelist urls with.
|
||||
* @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
|
||||
* chaining otherwise.
|
||||
*/
|
||||
this.urlSanitizationWhitelist = function(regexp) {
|
||||
if (isDefined(regexp)) {
|
||||
urlSanitizationWhitelist = regexp;
|
||||
return this;
|
||||
}
|
||||
return urlSanitizationWhitelist;
|
||||
};
|
||||
|
||||
|
||||
this.$get = [
|
||||
'$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
|
||||
'$controller', '$rootScope',
|
||||
'$controller', '$rootScope', '$document',
|
||||
function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse,
|
||||
$controller, $rootScope) {
|
||||
$controller, $rootScope, $document) {
|
||||
|
||||
var Attributes = function(element, attr) {
|
||||
this.$$element = element;
|
||||
@@ -235,7 +266,8 @@ function $CompileProvider($provide) {
|
||||
*/
|
||||
$set: function(key, value, writeAttr, attrName) {
|
||||
var booleanKey = getBooleanAttrName(this.$$element[0], key),
|
||||
$$observers = this.$$observers;
|
||||
$$observers = this.$$observers,
|
||||
normalizedVal;
|
||||
|
||||
if (booleanKey) {
|
||||
this.$$element.prop(key, value);
|
||||
@@ -254,6 +286,19 @@ function $CompileProvider($provide) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// sanitize a[href] values
|
||||
if (nodeName_(this.$$element[0]) === 'A' && key === 'href') {
|
||||
urlSanitizationNode.setAttribute('href', value);
|
||||
|
||||
// href property always returns normalized absolute url, so we can match against that
|
||||
normalizedVal = urlSanitizationNode.href;
|
||||
if (!normalizedVal.match(urlSanitizationWhitelist)) {
|
||||
this[key] = value = 'unsafe:' + normalizedVal;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (writeAttr !== false) {
|
||||
if (value === null || value === undefined) {
|
||||
this.$$element.removeAttr(attrName);
|
||||
@@ -297,13 +342,15 @@ function $CompileProvider($provide) {
|
||||
}
|
||||
};
|
||||
|
||||
var startSymbol = $interpolate.startSymbol(),
|
||||
var urlSanitizationNode = $document[0].createElement('a'),
|
||||
startSymbol = $interpolate.startSymbol(),
|
||||
endSymbol = $interpolate.endSymbol(),
|
||||
denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
|
||||
? identity
|
||||
: function denormalizeTemplate(template) {
|
||||
return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
|
||||
};
|
||||
},
|
||||
NG_ATTR_BINDING = /^ngAttr[A-Z]/;
|
||||
|
||||
|
||||
return compile;
|
||||
@@ -312,7 +359,7 @@ function $CompileProvider($provide) {
|
||||
|
||||
function compile($compileNodes, transcludeFn, maxPriority) {
|
||||
if (!($compileNodes instanceof jqLite)) {
|
||||
// jquery always rewraps, where as we need to preserve the original selector so that we can modify it.
|
||||
// jquery always rewraps, whereas we need to preserve the original selector so that we can modify it.
|
||||
$compileNodes = jqLite($compileNodes);
|
||||
}
|
||||
// We can not compile top level text elements since text nodes can be merged and we will
|
||||
@@ -330,7 +377,14 @@ function $CompileProvider($provide) {
|
||||
var $linkNode = cloneConnectFn
|
||||
? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!!
|
||||
: $compileNodes;
|
||||
$linkNode.data('$scope', scope);
|
||||
|
||||
// Attach scope only to non-text nodes.
|
||||
for(var i = 0, ii = $linkNode.length; i<ii; i++) {
|
||||
var node = $linkNode[i];
|
||||
if (node.nodeType == 1 /* element */ || node.nodeType == 9 /* document */) {
|
||||
$linkNode.eq(i).data('$scope', scope);
|
||||
}
|
||||
}
|
||||
safeAddClass($linkNode, 'ng-scope');
|
||||
if (cloneConnectFn) cloneConnectFn($linkNode, scope);
|
||||
if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode);
|
||||
@@ -357,7 +411,7 @@ function $CompileProvider($provide) {
|
||||
* functions return values - the linking functions - are combined into a composite linking
|
||||
* function, which is the a linking function for the node.
|
||||
*
|
||||
* @param {NodeList} nodeList an array of nodes to compile
|
||||
* @param {NodeList} nodeList an array of nodes or NodeList to compile
|
||||
* @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
|
||||
* scope argument is auto-generated to the new child of the transcluded parent scope.
|
||||
* @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then the
|
||||
@@ -380,7 +434,7 @@ function $CompileProvider($provide) {
|
||||
? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement)
|
||||
: null;
|
||||
|
||||
childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes.length)
|
||||
childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes || !nodeList[i].childNodes.length)
|
||||
? null
|
||||
: compileNodes(nodeList[i].childNodes,
|
||||
nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);
|
||||
@@ -420,6 +474,7 @@ function $CompileProvider($provide) {
|
||||
(function(transcludeFn) {
|
||||
return function(cloneFn) {
|
||||
var transcludeScope = scope.$new();
|
||||
transcludeScope.$$transcluded = true;
|
||||
|
||||
return transcludeFn(transcludeScope, cloneFn).
|
||||
bind('$destroy', bind(transcludeScope, transcludeScope.$destroy));
|
||||
@@ -460,11 +515,16 @@ function $CompileProvider($provide) {
|
||||
directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority);
|
||||
|
||||
// iterate over the attributes
|
||||
for (var attr, name, nName, value, nAttrs = node.attributes,
|
||||
for (var attr, name, nName, ngAttrName, value, nAttrs = node.attributes,
|
||||
j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
|
||||
attr = nAttrs[j];
|
||||
if (attr.specified) {
|
||||
name = attr.name;
|
||||
// support ngAttr attribute binding
|
||||
ngAttrName = directiveNormalize(name);
|
||||
if (NG_ATTR_BINDING.test(ngAttrName)) {
|
||||
name = ngAttrName.substr(6).toLowerCase();
|
||||
}
|
||||
nName = directiveNormalize(name.toLowerCase());
|
||||
attrsMap[nName] = name;
|
||||
attrs[nName] = value = trim((msie && name == 'href')
|
||||
@@ -592,9 +652,14 @@ function $CompileProvider($provide) {
|
||||
}
|
||||
}
|
||||
|
||||
if ((directiveValue = directive.template)) {
|
||||
if (directive.template) {
|
||||
assertNoDuplicate('template', templateDirective, directive, $compileNode);
|
||||
templateDirective = directive;
|
||||
|
||||
directiveValue = (isFunction(directive.template))
|
||||
? directive.template($compileNode, templateAttrs)
|
||||
: directive.template;
|
||||
|
||||
directiveValue = denormalizeTemplate(directiveValue);
|
||||
|
||||
if (directive.replace) {
|
||||
@@ -714,17 +779,20 @@ function $CompileProvider($provide) {
|
||||
$element = attrs.$$element;
|
||||
|
||||
if (newIsolateScopeDirective) {
|
||||
var LOCAL_REGEXP = /^\s*([@=&])\s*(\w*)\s*$/;
|
||||
var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/;
|
||||
|
||||
var parentScope = scope.$parent || scope;
|
||||
|
||||
forEach(newIsolateScopeDirective.scope, function(definiton, scopeName) {
|
||||
var match = definiton.match(LOCAL_REGEXP) || [],
|
||||
attrName = match[2]|| scopeName,
|
||||
attrName = match[3] || scopeName,
|
||||
optional = (match[2] == '?'),
|
||||
mode = match[1], // @, =, or &
|
||||
lastValue,
|
||||
parentGet, parentSet;
|
||||
|
||||
scope.$$isolateBindings[scopeName] = mode + attrName;
|
||||
|
||||
switch (mode) {
|
||||
|
||||
case '@': {
|
||||
@@ -732,10 +800,17 @@ function $CompileProvider($provide) {
|
||||
scope[scopeName] = value;
|
||||
});
|
||||
attrs.$$observers[attrName].$$scope = parentScope;
|
||||
if( attrs[attrName] ) {
|
||||
// If the attribute has been provided then we trigger an interpolation to ensure the value is there for use in the link fn
|
||||
scope[scopeName] = $interpolate(attrs[attrName])(parentScope);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case '=': {
|
||||
if (optional && !attrs[attrName]) {
|
||||
return;
|
||||
}
|
||||
parentGet = $parse(attrs[attrName]);
|
||||
parentSet = parentGet.assign || function() {
|
||||
// reset the change, or we will throw this exception on every $digest
|
||||
@@ -907,11 +982,14 @@ function $CompileProvider($provide) {
|
||||
// The fact that we have to copy and patch the directive seems wrong!
|
||||
derivedSyncDirective = extend({}, origAsyncDirective, {
|
||||
controller: null, templateUrl: null, transclude: null, scope: null
|
||||
});
|
||||
}),
|
||||
templateUrl = (isFunction(origAsyncDirective.templateUrl))
|
||||
? origAsyncDirective.templateUrl($compileNode, tAttrs)
|
||||
: origAsyncDirective.templateUrl;
|
||||
|
||||
$compileNode.html('');
|
||||
|
||||
$http.get(origAsyncDirective.templateUrl, {cache: $templateCache}).
|
||||
$http.get(templateUrl, {cache: $templateCache}).
|
||||
success(function(content) {
|
||||
var compileNode, tempTemplateAttrs, $template;
|
||||
|
||||
@@ -935,15 +1013,15 @@ function $CompileProvider($provide) {
|
||||
}
|
||||
|
||||
directives.unshift(derivedSyncDirective);
|
||||
afterTemplateNodeLinkFn = applyDirectivesToNode(directives, $compileNode, tAttrs, childTranscludeFn);
|
||||
afterTemplateChildLinkFn = compileNodes($compileNode.contents(), childTranscludeFn);
|
||||
afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn);
|
||||
afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
|
||||
|
||||
|
||||
while(linkQueue.length) {
|
||||
var controller = linkQueue.pop(),
|
||||
linkRootElement = linkQueue.pop(),
|
||||
beforeTemplateLinkNode = linkQueue.pop(),
|
||||
scope = linkQueue.pop(),
|
||||
var scope = linkQueue.shift(),
|
||||
beforeTemplateLinkNode = linkQueue.shift(),
|
||||
linkRootElement = linkQueue.shift(),
|
||||
controller = linkQueue.shift(),
|
||||
linkNode = compileNode;
|
||||
|
||||
if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
|
||||
@@ -1015,22 +1093,24 @@ function $CompileProvider($provide) {
|
||||
function addAttrInterpolateDirective(node, directives, value, name) {
|
||||
var interpolateFn = $interpolate(value, true);
|
||||
|
||||
|
||||
// no interpolation found -> ignore
|
||||
if (!interpolateFn) return;
|
||||
|
||||
|
||||
directives.push({
|
||||
priority: 100,
|
||||
compile: valueFn(function attrInterpolateLinkFn(scope, element, attr) {
|
||||
var $$observers = (attr.$$observers || (attr.$$observers = {}));
|
||||
|
||||
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 need to interpolate again, in case the attribute value has been updated
|
||||
// (e.g. by another directive's compile function)
|
||||
interpolateFn = $interpolate(attr[name], true);
|
||||
|
||||
attr[name] = undefined;
|
||||
// if attribute was updated so that there is no interpolation going on we don't want to
|
||||
// register any observers
|
||||
if (!interpolateFn) return;
|
||||
|
||||
attr[name] = interpolateFn(scope);
|
||||
($$observers[name] || ($$observers[name] = [])).$$inter = true;
|
||||
(attr.$$observers && attr.$$observers[name].$$scope || scope).
|
||||
$watch(interpolateFn, function interpolateFnWatchAction(value) {
|
||||
@@ -1125,7 +1205,7 @@ function directiveNormalize(name) {
|
||||
* @param {string} name Normalized element attribute name of the property to modify. The name is
|
||||
* revers translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
|
||||
* property to the original name.
|
||||
* @param {string} value Value to set the attribute to.
|
||||
* @param {string} value Value to set the attribute to. The value can be an interpolated string.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ function $ControllerProvider() {
|
||||
* @description
|
||||
* `$controller` service is responsible for instantiating controllers.
|
||||
*
|
||||
* It's just simple call to {@link AUTO.$injector $injector}, but extracted into
|
||||
* It's just a simple call to {@link AUTO.$injector $injector}, but extracted into
|
||||
* a service, so that one can override this service with {@link https://gist.github.com/1649788
|
||||
* BC version}.
|
||||
*/
|
||||
|
||||
+15
-5
@@ -11,15 +11,25 @@
|
||||
*
|
||||
* The reasoning for this change is to allow easy creation of action links with `ngClick` directive
|
||||
* without changing the location or causing page reloads, e.g.:
|
||||
* <a href="" ng-click="model.$save()">Save</a>
|
||||
* `<a href="" ng-click="model.$save()">Save</a>`
|
||||
*/
|
||||
var htmlAnchorDirective = valueFn({
|
||||
restrict: 'E',
|
||||
compile: function(element, attr) {
|
||||
// turn <a href ng-click="..">link</a> into a link in IE
|
||||
// but only if it doesn't have name attribute, in which case it's an anchor
|
||||
if (!attr.href) {
|
||||
attr.$set('href', '');
|
||||
|
||||
if (msie <= 8) {
|
||||
|
||||
// turn <a href ng-click="..">link</a> into a stylable link in IE
|
||||
// but only if it doesn't have name attribute, in which case it's an anchor
|
||||
if (!attr.href && !attr.name) {
|
||||
attr.$set('href', '');
|
||||
}
|
||||
|
||||
// add a comment node to anchors to workaround IE bug that causes element content to be reset
|
||||
// to new attribute content if attribute is updated with value containing @ and element also
|
||||
// contains value with @
|
||||
// see issue #1949
|
||||
element.append(document.createComment('IE fix'));
|
||||
}
|
||||
|
||||
return function(scope, element) {
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
it('should execute ng-click but not reload when no href but name specified', function() {
|
||||
element('#link-5').click();
|
||||
expect(input('value').val()).toEqual('5');
|
||||
expect(element('#link-5').attr('href')).toBe('');
|
||||
expect(element('#link-5').attr('href')).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should only change url when only ng-href', function() {
|
||||
@@ -340,8 +340,9 @@ forEach(['src', 'href'], function(attrName) {
|
||||
|
||||
// on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
|
||||
// then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
|
||||
// to set the property as well to achieve the desired effect
|
||||
if (msie) element.prop(attrName, value);
|
||||
// to set the property as well to achieve the desired effect.
|
||||
// we use attr[attrName] value since $set can sanitize the url.
|
||||
if (msie) element.prop(attrName, attr[attrName]);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -805,7 +805,7 @@ var VALID_CLASS = 'ng-valid',
|
||||
* @property {Array.<Function>} $formatters Whenever the model value changes, it executes all of
|
||||
* these functions to convert the value as well as validate.
|
||||
*
|
||||
* @property {Object} $error An bject hash with all errors as keys.
|
||||
* @property {Object} $error An object hash with all errors as keys.
|
||||
*
|
||||
* @property {boolean} $pristine True if user has not interacted with the control yet.
|
||||
* @property {boolean} $dirty True if user has already interacted with the control.
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
|
||||
* `{{ expression }}` which is similar but less verbose.
|
||||
*
|
||||
* Once scenario in which the use of `ngBind` is prefered over `{{ expression }}` binding is when
|
||||
* Once scenario in which the use of `ngBind` is preferred over `{{ expression }}` binding is when
|
||||
* it's desirable to put bindings into template that is momentarily displayed by the browser in its
|
||||
* raw state before Angular compiles it. Since `ngBind` is an element attribute, it makes the
|
||||
* bindings invisible to the user while the page is loading.
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
function classDirective(name, selector) {
|
||||
name = 'ngClass' + name;
|
||||
return ngDirective(function(scope, element, attr) {
|
||||
var oldVal = undefined;
|
||||
|
||||
scope.$watch(attr[name], ngClassWatchAction, true);
|
||||
|
||||
@@ -26,13 +27,14 @@ function classDirective(name, selector) {
|
||||
}
|
||||
|
||||
|
||||
function ngClassWatchAction(newVal, oldVal) {
|
||||
function ngClassWatchAction(newVal) {
|
||||
if (selector === true || scope.$index % 2 === selector) {
|
||||
if (oldVal && (newVal !== oldVal)) {
|
||||
removeClass(oldVal);
|
||||
}
|
||||
addClass(newVal);
|
||||
}
|
||||
oldVal = newVal;
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +67,7 @@ function classDirective(name, selector) {
|
||||
*
|
||||
* The directive won't add duplicate classes if a particular class was already set.
|
||||
*
|
||||
* When the expression changes, the previously added classes are removed and only then the classes
|
||||
* When the expression changes, the previously added classes are removed and only then the
|
||||
* new classes are added.
|
||||
*
|
||||
* @element ANY
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
* directive to avoid the undesirable flicker effect caused by the html template display.
|
||||
*
|
||||
* The directive can be applied to the `<body>` element, but typically a fine-grained application is
|
||||
* prefered in order to benefit from progressive rendering of the browser view.
|
||||
* preferred in order to benefit from progressive rendering of the browser view.
|
||||
*
|
||||
* `ngCloak` works in cooperation with a css rule that is embedded within `angular.js` and
|
||||
* `angular.min.js` files. Following is the css rule:
|
||||
*
|
||||
* <pre>
|
||||
* [ng\:cloak], [ng-cloak], .ng-cloak {
|
||||
* [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
|
||||
* display: none;
|
||||
* }
|
||||
* </pre>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
*/
|
||||
var ngEventDirectives = {};
|
||||
forEach(
|
||||
'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup'.split(' '),
|
||||
'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress'.split(' '),
|
||||
function(name) {
|
||||
var directiveName = directiveNormalize('ng-' + name);
|
||||
ngEventDirectives[directiveName] = ['$parse', function($parse) {
|
||||
@@ -196,6 +196,22 @@ forEach(
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ng.directive:ngKeypress
|
||||
*
|
||||
* @description
|
||||
* Specify custom behavior on keypress event.
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
|
||||
* keypress. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
|
||||
*
|
||||
* @example
|
||||
* See {@link ng.directive:ngClick ngClick}
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ng.directive:ngSubmit
|
||||
|
||||
@@ -12,6 +12,13 @@
|
||||
* (e.g. ngInclude won't work for cross-domain requests on all browsers and for
|
||||
* file:// access on some browsers).
|
||||
*
|
||||
* Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter**
|
||||
* and **leave** effects.
|
||||
*
|
||||
* @animations
|
||||
* enter - happens just after the ngInclude contents change and a new DOM element is created and injected into the ngInclude container
|
||||
* leave - happens just after the ngInclude contents change and just before the former contents are removed from the DOM
|
||||
*
|
||||
* @scope
|
||||
*
|
||||
* @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
|
||||
@@ -34,7 +41,9 @@
|
||||
</select>
|
||||
url of the template: <tt>{{template.url}}</tt>
|
||||
<hr/>
|
||||
<div ng-include src="template.url"></div>
|
||||
<div class="example-animate-container"
|
||||
ng-include="template.url"
|
||||
ng-animate="{enter: 'example-enter', leave: 'example-leave'}"></div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
@@ -46,10 +55,45 @@
|
||||
}
|
||||
</file>
|
||||
<file name="template1.html">
|
||||
Content of template1.html
|
||||
<div>Content of template1.html</div>
|
||||
</file>
|
||||
<file name="template2.html">
|
||||
Content of template2.html
|
||||
<div>Content of template2.html</div>
|
||||
</file>
|
||||
<file name="animations.css">
|
||||
.example-leave-setup,
|
||||
.example-enter-setup {
|
||||
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
-moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
-ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
-o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
|
||||
position:absolute;
|
||||
top:0;
|
||||
left:0;
|
||||
right:0;
|
||||
bottom:0;
|
||||
}
|
||||
|
||||
.example-animate-container > * {
|
||||
display:block;
|
||||
padding:10px;
|
||||
}
|
||||
|
||||
.example-enter-setup {
|
||||
top:-50px;
|
||||
}
|
||||
.example-enter-setup.example-enter-start {
|
||||
top:0;
|
||||
}
|
||||
|
||||
.example-leave-setup {
|
||||
top:0;
|
||||
}
|
||||
.example-leave-setup.example-leave-start {
|
||||
top:50px;
|
||||
}
|
||||
</file>
|
||||
<file name="scenario.js">
|
||||
it('should load template1.html', function() {
|
||||
@@ -78,8 +122,8 @@
|
||||
* @description
|
||||
* Emitted every time the ngInclude content is reloaded.
|
||||
*/
|
||||
var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile',
|
||||
function($http, $templateCache, $anchorScroll, $compile) {
|
||||
var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile', '$animator',
|
||||
function($http, $templateCache, $anchorScroll, $compile, $animator) {
|
||||
return {
|
||||
restrict: 'ECA',
|
||||
terminal: true,
|
||||
@@ -88,7 +132,8 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
|
||||
onloadExp = attr.onload || '',
|
||||
autoScrollExp = attr.autoscroll;
|
||||
|
||||
return function(scope, element) {
|
||||
return function(scope, element, attr) {
|
||||
var animate = $animator(scope, attr);
|
||||
var changeCounter = 0,
|
||||
childScope;
|
||||
|
||||
@@ -97,8 +142,7 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
|
||||
childScope.$destroy();
|
||||
childScope = null;
|
||||
}
|
||||
|
||||
element.html('');
|
||||
animate.leave(element.contents(), element);
|
||||
};
|
||||
|
||||
scope.$watch(srcExp, function ngIncludeWatchAction(src) {
|
||||
@@ -110,9 +154,12 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
|
||||
|
||||
if (childScope) childScope.$destroy();
|
||||
childScope = scope.$new();
|
||||
animate.leave(element.contents(), element);
|
||||
|
||||
element.html(response);
|
||||
$compile(element.contents())(childScope);
|
||||
var contents = jqLite('<div/>').html(response).contents();
|
||||
|
||||
animate.enter(contents, element);
|
||||
$compile(contents)(childScope);
|
||||
|
||||
if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
|
||||
$anchorScroll();
|
||||
@@ -123,7 +170,9 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
|
||||
}).error(function() {
|
||||
if (thisChangeId === changeCounter) clearContent();
|
||||
});
|
||||
} else clearContent();
|
||||
} else {
|
||||
clearContent();
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
* {@link http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
|
||||
* plural categories} in Angular's default en-US locale: "one" and "other".
|
||||
*
|
||||
* While a pural category may match many numbers (for example, in en-US locale, "other" can match
|
||||
* While a plural category may match many numbers (for example, in en-US locale, "other" can match
|
||||
* any number that is not 1), an explicit number rule can only match one number. For example, the
|
||||
* explicit number rule for "3" matches the number 3. You will see the use of plural categories
|
||||
* and explicit number rules throughout later parts of this documentation.
|
||||
@@ -87,7 +87,7 @@
|
||||
* plural categories "one" and "other".
|
||||
*
|
||||
* @param {string|expression} count The variable to be bounded to.
|
||||
* @param {string} when The mapping between plural category to its correspoding strings.
|
||||
* @param {string} when The mapping between plural category to its corresponding strings.
|
||||
* @param {number=} offset Offset to deduct from the total number.
|
||||
*
|
||||
* @example
|
||||
|
||||
+259
-133
@@ -16,11 +16,18 @@
|
||||
* * `$middle` – `{boolean}` – true if the repeated element is between the first and last in the iterator.
|
||||
* * `$last` – `{boolean}` – true if the repeated element is last in the iterator.
|
||||
*
|
||||
* Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter**,
|
||||
* **leave** and **move** effects.
|
||||
*
|
||||
* @animations
|
||||
* enter - when a new item is added to the list or when an item is revealed after a filter
|
||||
* leave - when an item is removed from the list or when an item is filtered out
|
||||
* move - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
|
||||
*
|
||||
* @element ANY
|
||||
* @scope
|
||||
* @priority 1000
|
||||
* @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. Two
|
||||
* @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
|
||||
* formats are currently supported:
|
||||
*
|
||||
* * `variable in expression` – where variable is the user defined loop variable and `expression`
|
||||
@@ -33,157 +40,276 @@
|
||||
*
|
||||
* For example: `(name, age) in {'adam':10, 'amalie':12}`.
|
||||
*
|
||||
* * `variable in expression track by tracking_expression` – You can also provide an optional tracking function
|
||||
* which can be used to associate the objects in the collection with the DOM elements. If no tractking function
|
||||
* is specified the ng-repeat associates elements by identity in the collection. It is an error to have
|
||||
* more then one tractking function to resolve to the same key. (This would mean that two distinct objects are
|
||||
* mapped to the same DOM element, which is not possible.)
|
||||
*
|
||||
* For example: `item in items` is equivalent to `item in items track by $id(item)'. This implies that the DOM elements
|
||||
* will be associated by item identity in the array.
|
||||
*
|
||||
* For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
|
||||
* `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
|
||||
* with the corresponding item in the array by identity. Moving the same object in array would move the DOM
|
||||
* element in the same way ian the DOM.
|
||||
*
|
||||
* For example: `item in items track by item.id` Is a typical pattern when the items come from the database. In this
|
||||
* case the object identity does not matter. Two objects are considered equivalent as long as their `id`
|
||||
* property is same.
|
||||
*
|
||||
* @example
|
||||
* This example initializes the scope to a list of names and
|
||||
* then uses `ngRepeat` to display every person:
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<div ng-init="friends = [{name:'John', age:25}, {name:'Mary', age:28}]">
|
||||
I have {{friends.length}} friends. They are:
|
||||
<ul>
|
||||
<li ng-repeat="friend in friends">
|
||||
[{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should check ng-repeat', function() {
|
||||
var r = using('.doc-example-live').repeater('ul li');
|
||||
expect(r.count()).toBe(2);
|
||||
expect(r.row(0)).toEqual(["1","John","25"]);
|
||||
expect(r.row(1)).toEqual(["2","Mary","28"]);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
<example animations="true">
|
||||
<file name="index.html">
|
||||
<div ng-init="friends = [
|
||||
{name:'John', age:25, gender:'boy'},
|
||||
{name:'Jessie', age:30, gender:'girl'},
|
||||
{name:'Johanna', age:28, gender:'girl'},
|
||||
{name:'Joy', age:15, gender:'girl'},
|
||||
{name:'Mary', age:28, gender:'girl'},
|
||||
{name:'Peter', age:95, gender:'boy'},
|
||||
{name:'Sebastian', age:50, gender:'boy'},
|
||||
{name:'Erika', age:27, gender:'girl'},
|
||||
{name:'Patrick', age:40, gender:'boy'},
|
||||
{name:'Samantha', age:60, gender:'girl'}
|
||||
]">
|
||||
I have {{friends.length}} friends. They are:
|
||||
<input type="search" ng-model="q" placeholder="filter friends..." />
|
||||
<ul>
|
||||
<li ng-repeat="friend in friends | filter:q"
|
||||
ng-animate="{enter: 'example-repeat-enter',
|
||||
leave: 'example-repeat-leave',
|
||||
move: 'example-repeat-move'}">
|
||||
[{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</file>
|
||||
<file name="animations.css">
|
||||
.example-repeat-enter-setup,
|
||||
.example-repeat-leave-setup,
|
||||
.example-repeat-move-setup {
|
||||
-webkit-transition:all linear 0.5s;
|
||||
-moz-transition:all linear 0.5s;
|
||||
-ms-transition:all linear 0.5s;
|
||||
-o-transition:all linear 0.5s;
|
||||
transition:all linear 0.5s;
|
||||
}
|
||||
|
||||
.example-repeat-enter-setup {
|
||||
line-height:0;
|
||||
opacity:0;
|
||||
}
|
||||
.example-repeat-enter-setup.example-repeat-enter-start {
|
||||
line-height:20px;
|
||||
opacity:1;
|
||||
}
|
||||
|
||||
.example-repeat-leave-setup {
|
||||
opacity:1;
|
||||
line-height:20px;
|
||||
}
|
||||
.example-repeat-leave-setup.example-repeat-leave-start {
|
||||
opacity:0;
|
||||
line-height:0;
|
||||
}
|
||||
|
||||
.example-repeat-move-setup { }
|
||||
.example-repeat-move-setup.example-repeat-move-start { }
|
||||
</file>
|
||||
<file name="scenario.js">
|
||||
it('should render initial data set', function() {
|
||||
var r = using('.doc-example-live').repeater('ul li');
|
||||
expect(r.count()).toBe(10);
|
||||
expect(r.row(0)).toEqual(["1","John","25"]);
|
||||
expect(r.row(1)).toEqual(["2","Jessie","30"]);
|
||||
expect(r.row(9)).toEqual(["10","Samantha","60"]);
|
||||
expect(binding('friends.length')).toBe("10");
|
||||
});
|
||||
|
||||
it('should update repeater when filter predicate changes', function() {
|
||||
var r = using('.doc-example-live').repeater('ul li');
|
||||
expect(r.count()).toBe(10);
|
||||
|
||||
input('q').enter('ma');
|
||||
|
||||
expect(r.count()).toBe(2);
|
||||
expect(r.row(0)).toEqual(["1","Mary","28"]);
|
||||
expect(r.row(1)).toEqual(["2","Samantha","60"]);
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
var ngRepeatDirective = ngDirective({
|
||||
transclude: 'element',
|
||||
priority: 1000,
|
||||
terminal: true,
|
||||
compile: function(element, attr, linker) {
|
||||
return function(scope, iterStartElement, attr){
|
||||
var expression = attr.ngRepeat;
|
||||
var match = expression.match(/^\s*(.+)\s+in\s+(.*)\s*$/),
|
||||
lhs, rhs, valueIdent, keyIdent;
|
||||
if (! match) {
|
||||
throw Error("Expected ngRepeat in form of '_item_ in _collection_' but got '" +
|
||||
expression + "'.");
|
||||
}
|
||||
lhs = match[1];
|
||||
rhs = match[2];
|
||||
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 '" +
|
||||
lhs + "'.");
|
||||
}
|
||||
valueIdent = match[3] || match[1];
|
||||
keyIdent = match[2];
|
||||
var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
|
||||
var NG_REMOVED = '$$NG_REMOVED';
|
||||
return {
|
||||
transclude: 'element',
|
||||
priority: 1000,
|
||||
terminal: true,
|
||||
compile: function(element, attr, linker) {
|
||||
return function($scope, $element, $attr){
|
||||
var animate = $animator($scope, $attr);
|
||||
var expression = $attr.ngRepeat;
|
||||
var match = expression.match(/^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/),
|
||||
trackByExp, hashExpFn, trackByIdFn, lhs, rhs, valueIdentifier, keyIdentifier,
|
||||
hashFnLocals = {$id: hashKey};
|
||||
|
||||
// Store a list of elements from previous run. This is a hash where key is the item from the
|
||||
// iterator, and the value is an array of objects with following properties.
|
||||
// - scope: bound scope
|
||||
// - element: previous element.
|
||||
// - index: position
|
||||
// We need an array of these objects since the same object can be returned from the iterator.
|
||||
// We expect this to be a rare case.
|
||||
var lastOrder = new HashQueueMap();
|
||||
if (!match) {
|
||||
throw Error("Expected ngRepeat in form of '_item_ in _collection_[ track by _id_]' but got '" +
|
||||
expression + "'.");
|
||||
}
|
||||
|
||||
scope.$watch(function ngRepeatWatch(scope){
|
||||
var index, length,
|
||||
collection = scope.$eval(rhs),
|
||||
cursor = iterStartElement, // current position of the node
|
||||
// Same as lastOrder but it has the current state. It will become the
|
||||
// lastOrder on the next iteration.
|
||||
nextOrder = new HashQueueMap(),
|
||||
arrayLength,
|
||||
childScope,
|
||||
key, value, // key/value of iteration
|
||||
array,
|
||||
last; // last object information {scope, element, index}
|
||||
lhs = match[1];
|
||||
rhs = match[2];
|
||||
trackByExp = match[4];
|
||||
|
||||
|
||||
|
||||
if (!isArray(collection)) {
|
||||
// if object, extract keys, sort them and use to determine order of iteration over obj props
|
||||
array = [];
|
||||
for(key in collection) {
|
||||
if (collection.hasOwnProperty(key) && key.charAt(0) != '$') {
|
||||
array.push(key);
|
||||
}
|
||||
}
|
||||
array.sort();
|
||||
if (trackByExp) {
|
||||
hashExpFn = $parse(trackByExp);
|
||||
trackByIdFn = function(key, value, index) {
|
||||
// assign key, value, and $index to the locals so that they can be used in hash functions
|
||||
if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
|
||||
hashFnLocals[valueIdentifier] = value;
|
||||
hashFnLocals.$index = index;
|
||||
return hashExpFn($scope, hashFnLocals);
|
||||
};
|
||||
} else {
|
||||
array = collection || [];
|
||||
trackByIdFn = function(key, value) {
|
||||
return hashKey(value);
|
||||
}
|
||||
}
|
||||
|
||||
arrayLength = array.length;
|
||||
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 '" +
|
||||
lhs + "'.");
|
||||
}
|
||||
valueIdentifier = match[3] || match[1];
|
||||
keyIdentifier = match[2];
|
||||
|
||||
// we are not using forEach for perf reasons (trying to avoid #call)
|
||||
for (index = 0, length = array.length; index < length; index++) {
|
||||
key = (collection === array) ? index : array[index];
|
||||
value = collection[key];
|
||||
// Store a list of elements from previous run. This is a hash where key is the item from the
|
||||
// iterator, and the value is objects with following properties.
|
||||
// - scope: bound scope
|
||||
// - element: previous element.
|
||||
// - index: position
|
||||
var lastBlockMap = {};
|
||||
|
||||
last = lastOrder.shift(value);
|
||||
//watch props
|
||||
$scope.$watchCollection(rhs, function ngRepeatAction(collection){
|
||||
var index, length,
|
||||
cursor = $element, // current position of the node
|
||||
nextCursor,
|
||||
// Same as lastBlockMap but it has the current state. It will become the
|
||||
// lastBlockMap on the next iteration.
|
||||
nextBlockMap = {},
|
||||
arrayLength,
|
||||
childScope,
|
||||
key, value, // key/value of iteration
|
||||
trackById,
|
||||
collectionKeys,
|
||||
block, // last object information {scope, element, id}
|
||||
nextBlockOrder = [];
|
||||
|
||||
if (last) {
|
||||
// if we have already seen this object, then we need to reuse the
|
||||
// associated scope/element
|
||||
childScope = last.scope;
|
||||
nextOrder.push(value, last);
|
||||
|
||||
if (index === last.index) {
|
||||
// do nothing
|
||||
cursor = last.element;
|
||||
} else {
|
||||
// existing item which got moved
|
||||
last.index = index;
|
||||
// This may be a noop, if the element is next, but I don't know of a good way to
|
||||
// figure this out, since it would require extra DOM access, so let's just hope that
|
||||
// the browsers realizes that it is noop, and treats it as such.
|
||||
cursor.after(last.element);
|
||||
cursor = last.element;
|
||||
}
|
||||
if (isArray(collection)) {
|
||||
collectionKeys = collection;
|
||||
} else {
|
||||
// new item which we don't know about
|
||||
childScope = scope.$new();
|
||||
// if object, extract keys, sort them and use to determine order of iteration over obj props
|
||||
collectionKeys = [];
|
||||
for (key in collection) {
|
||||
if (collection.hasOwnProperty(key) && key.charAt(0) != '$') {
|
||||
collectionKeys.push(key);
|
||||
}
|
||||
}
|
||||
collectionKeys.sort();
|
||||
}
|
||||
|
||||
childScope[valueIdent] = value;
|
||||
if (keyIdent) childScope[keyIdent] = key;
|
||||
childScope.$index = index;
|
||||
arrayLength = collectionKeys.length;
|
||||
|
||||
childScope.$first = (index === 0);
|
||||
childScope.$last = (index === (arrayLength - 1));
|
||||
childScope.$middle = !(childScope.$first || childScope.$last);
|
||||
// locate existing items
|
||||
length = nextBlockOrder.length = collectionKeys.length;
|
||||
for(index = 0; index < length; index++) {
|
||||
key = (collection === collectionKeys) ? index : collectionKeys[index];
|
||||
value = collection[key];
|
||||
trackById = trackByIdFn(key, value, index);
|
||||
if((block = lastBlockMap[trackById])) {
|
||||
delete lastBlockMap[trackById];
|
||||
nextBlockMap[trackById] = block;
|
||||
nextBlockOrder[index] = block;
|
||||
} else if (nextBlockMap.hasOwnProperty(trackById)) {
|
||||
// restore lastBlockMap
|
||||
forEach(nextBlockOrder, function(block) {
|
||||
if (block && block.element) lastBlockMap[block.id] = block;
|
||||
});
|
||||
// This is a duplicate and we need to throw an error
|
||||
throw new Error('Duplicates in a repeater are not allowed. Repeater: ' + expression);
|
||||
} else {
|
||||
// new never before seen block
|
||||
nextBlockOrder[index] = { id: trackById };
|
||||
}
|
||||
}
|
||||
|
||||
if (!last) {
|
||||
linker(childScope, function(clone){
|
||||
cursor.after(clone);
|
||||
last = {
|
||||
scope: childScope,
|
||||
element: (cursor = clone),
|
||||
index: index
|
||||
};
|
||||
nextOrder.push(value, last);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//shrink children
|
||||
for (key in lastOrder) {
|
||||
if (lastOrder.hasOwnProperty(key)) {
|
||||
array = lastOrder[key];
|
||||
while(array.length) {
|
||||
value = array.pop();
|
||||
value.element.remove();
|
||||
value.scope.$destroy();
|
||||
// remove existing items
|
||||
for (key in lastBlockMap) {
|
||||
if (lastBlockMap.hasOwnProperty(key)) {
|
||||
block = lastBlockMap[key];
|
||||
animate.leave(block.element);
|
||||
block.element[0][NG_REMOVED] = true;
|
||||
block.scope.$destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastOrder = nextOrder;
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
// we are not using forEach for perf reasons (trying to avoid #call)
|
||||
for (index = 0, length = collectionKeys.length; index < length; index++) {
|
||||
key = (collection === collectionKeys) ? index : collectionKeys[index];
|
||||
value = collection[key];
|
||||
block = nextBlockOrder[index];
|
||||
|
||||
if (block.element) {
|
||||
// if we have already seen this object, then we need to reuse the
|
||||
// associated scope/element
|
||||
childScope = block.scope;
|
||||
|
||||
nextCursor = cursor[0];
|
||||
do {
|
||||
nextCursor = nextCursor.nextSibling;
|
||||
} while(nextCursor && nextCursor[NG_REMOVED]);
|
||||
|
||||
if (block.element[0] == nextCursor) {
|
||||
// do nothing
|
||||
cursor = block.element;
|
||||
} else {
|
||||
// existing item which got moved
|
||||
animate.move(block.element, null, cursor);
|
||||
cursor = block.element;
|
||||
}
|
||||
} else {
|
||||
// new item which we don't know about
|
||||
childScope = $scope.$new();
|
||||
}
|
||||
|
||||
childScope[valueIdentifier] = value;
|
||||
if (keyIdentifier) childScope[keyIdentifier] = key;
|
||||
childScope.$index = index;
|
||||
childScope.$first = (index === 0);
|
||||
childScope.$last = (index === (arrayLength - 1));
|
||||
childScope.$middle = !(childScope.$first || childScope.$last);
|
||||
|
||||
if (!block.element) {
|
||||
linker(childScope, function(clone) {
|
||||
animate.enter(clone, null, cursor);
|
||||
cursor = clone;
|
||||
block.scope = childScope;
|
||||
block.element = clone;
|
||||
nextBlockMap[block.id] = block;
|
||||
});
|
||||
}
|
||||
}
|
||||
lastBlockMap = nextBlockMap;
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
||||
|
||||
+173
-35
@@ -6,20 +6,86 @@
|
||||
*
|
||||
* @description
|
||||
* The `ngShow` and `ngHide` directives show or hide a portion of the DOM tree (HTML)
|
||||
* conditionally.
|
||||
* conditionally based on **"truthy"** values evaluated within an {expression}. In other
|
||||
* words, if the expression assigned to **ngShow evaluates to a true value** then **the element is set to visible**
|
||||
* (via `display:block` in css) and **if false** then **the element is set to hidden** (so display:none).
|
||||
* With ngHide this is the reverse whereas true values cause the element itself to become
|
||||
* hidden.
|
||||
*
|
||||
* Additionally, you can also provide animations via the ngAnimate attribute to animate the **show**
|
||||
* and **hide** effects.
|
||||
*
|
||||
* @animations
|
||||
* show - happens after the ngShow expression evaluates to a truthy value and the contents are set to visible
|
||||
* hide - happens before the ngShow expression evaluates to a non truthy value and just before the contents are set to hidden
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} ngShow If the {@link guide/expression expression} is truthy
|
||||
* then the element is shown or hidden respectively.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Click me: <input type="checkbox" ng-model="checked"><br/>
|
||||
Show: <span ng-show="checked">I show up when your checkbox is checked.</span> <br/>
|
||||
Hide: <span ng-hide="checked">I hide when your checkbox is checked.</span>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<example animations="true">
|
||||
<file name="index.html">
|
||||
Click me: <input type="checkbox" ng-model="checked"><br/>
|
||||
<div>
|
||||
Show:
|
||||
<span class="check-element"
|
||||
ng-show="checked"
|
||||
ng-animate="{show: 'example-show', hide: 'example-hide'}">
|
||||
<span class="icon-thumbs-up"></span> I show up when your checkbox is checked.
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
Hide:
|
||||
<span class="check-element"
|
||||
ng-hide="checked"
|
||||
ng-animate="{show: 'example-show', hide: 'example-hide'}">
|
||||
<span class="icon-thumbs-down"></span> I hide when your checkbox is checked.
|
||||
</span>
|
||||
</div>
|
||||
</file>
|
||||
<file name="animations.css">
|
||||
.example-show-setup, .example-hide-setup {
|
||||
-webkit-transition:all linear 0.5s;
|
||||
-moz-transition:all linear 0.5s;
|
||||
-ms-transition:all linear 0.5s;
|
||||
-o-transition:all linear 0.5s;
|
||||
transition:all linear 0.5s;
|
||||
}
|
||||
|
||||
.example-show-setup {
|
||||
line-height:0;
|
||||
opacity:0;
|
||||
padding:0 10px;
|
||||
}
|
||||
.example-show-start.example-show-start {
|
||||
line-height:20px;
|
||||
opacity:1;
|
||||
padding:10px;
|
||||
border:1px solid black;
|
||||
background:white;
|
||||
}
|
||||
|
||||
.example-hide-setup {
|
||||
line-height:20px;
|
||||
opacity:1;
|
||||
padding:10px;
|
||||
border:1px solid black;
|
||||
background:white;
|
||||
}
|
||||
.example-hide-start.example-hide-start {
|
||||
line-height:0;
|
||||
opacity:0;
|
||||
padding:0 10px;
|
||||
}
|
||||
|
||||
.check-element {
|
||||
padding:10px;
|
||||
border:1px solid black;
|
||||
background:white;
|
||||
}
|
||||
</file>
|
||||
<file name="scenario.js">
|
||||
it('should check ng-show / ng-hide', function() {
|
||||
expect(element('.doc-example-live span:first:hidden').count()).toEqual(1);
|
||||
expect(element('.doc-example-live span:last:visible').count()).toEqual(1);
|
||||
@@ -29,15 +95,18 @@
|
||||
expect(element('.doc-example-live span:first:visible').count()).toEqual(1);
|
||||
expect(element('.doc-example-live span:last:hidden').count()).toEqual(1);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
//TODO(misko): refactor to remove element from the DOM
|
||||
var ngShowDirective = ngDirective(function(scope, element, attr){
|
||||
scope.$watch(attr.ngShow, function ngShowWatchAction(value){
|
||||
element.css('display', toBoolean(value) ? '' : 'none');
|
||||
});
|
||||
});
|
||||
var ngShowDirective = ['$animator', function($animator) {
|
||||
return function(scope, element, attr) {
|
||||
var animate = $animator(scope, attr);
|
||||
scope.$watch(attr.ngShow, function ngShowWatchAction(value){
|
||||
animate[toBoolean(value) ? 'show' : 'hide'](element);
|
||||
});
|
||||
};
|
||||
}];
|
||||
|
||||
|
||||
/**
|
||||
@@ -45,36 +114,105 @@ var ngShowDirective = ngDirective(function(scope, element, attr){
|
||||
* @name ng.directive:ngHide
|
||||
*
|
||||
* @description
|
||||
* The `ngHide` and `ngShow` directives hide or show a portion of the DOM tree (HTML)
|
||||
* conditionally.
|
||||
* The `ngShow` and `ngHide` directives show or hide a portion of the DOM tree (HTML)
|
||||
* conditionally based on **"truthy"** values evaluated within an {expression}. In other
|
||||
* words, if the expression assigned to **ngShow evaluates to a true value** then **the element is set to visible**
|
||||
* (via `display:block` in css) and **if false** then **the element is set to hidden** (so display:none).
|
||||
* With ngHide this is the reverse whereas true values cause the element itself to become
|
||||
* hidden.
|
||||
*
|
||||
* Additionally, you can also provide animations via the ngAnimate attribute to animate the **show**
|
||||
* and **hide** effects.
|
||||
*
|
||||
* @animations
|
||||
* show - happens after the ngHide expression evaluates to a non truthy value and the contents are set to visible
|
||||
* hide - happens after the ngHide expression evaluates to a truthy value and just before the contents are set to hidden
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} ngHide If the {@link guide/expression expression} is truthy then
|
||||
* the element is shown or hidden respectively.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Click me: <input type="checkbox" ng-model="checked"><br/>
|
||||
Show: <span ng-show="checked">I show up when you checkbox is checked?</span> <br/>
|
||||
Hide: <span ng-hide="checked">I hide when you checkbox is checked?</span>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<example animations="true">
|
||||
<file name="index.html">
|
||||
Click me: <input type="checkbox" ng-model="checked"><br/>
|
||||
<div>
|
||||
Show:
|
||||
<span class="check-element"
|
||||
ng-show="checked"
|
||||
ng-animate="{show: 'example-show', hide: 'example-hide'}">
|
||||
<span class="icon-thumbs-up"></span> I show up when your checkbox is checked.
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
Hide:
|
||||
<span class="check-element"
|
||||
ng-hide="checked"
|
||||
ng-animate="{show: 'example-show', hide: 'example-hide'}">
|
||||
<span class="icon-thumbs-down"></span> I hide when your checkbox is checked.
|
||||
</span>
|
||||
</div>
|
||||
</file>
|
||||
<file name="animations.css">
|
||||
.example-show-setup, .example-hide-setup {
|
||||
-webkit-transition:all linear 0.5s;
|
||||
-moz-transition:all linear 0.5s;
|
||||
-ms-transition:all linear 0.5s;
|
||||
-o-transition:all linear 0.5s;
|
||||
transition:all linear 0.5s;
|
||||
}
|
||||
|
||||
.example-show-setup {
|
||||
line-height:0;
|
||||
opacity:0;
|
||||
padding:0 10px;
|
||||
}
|
||||
.example-show-start.example-show-start {
|
||||
line-height:20px;
|
||||
opacity:1;
|
||||
padding:10px;
|
||||
border:1px solid black;
|
||||
background:white;
|
||||
}
|
||||
|
||||
.example-hide-setup {
|
||||
line-height:20px;
|
||||
opacity:1;
|
||||
padding:10px;
|
||||
border:1px solid black;
|
||||
background:white;
|
||||
}
|
||||
.example-hide-start.example-hide-start {
|
||||
line-height:0;
|
||||
opacity:0;
|
||||
padding:0 10px;
|
||||
}
|
||||
|
||||
.check-element {
|
||||
padding:10px;
|
||||
border:1px solid black;
|
||||
background:white;
|
||||
}
|
||||
</file>
|
||||
<file name="scenario.js">
|
||||
it('should check ng-show / ng-hide', function() {
|
||||
expect(element('.doc-example-live span:first:hidden').count()).toEqual(1);
|
||||
expect(element('.doc-example-live span:last:visible').count()).toEqual(1);
|
||||
expect(element('.doc-example-live .check-element:first:hidden').count()).toEqual(1);
|
||||
expect(element('.doc-example-live .check-element:last:visible').count()).toEqual(1);
|
||||
|
||||
input('checked').check();
|
||||
|
||||
expect(element('.doc-example-live span:first:visible').count()).toEqual(1);
|
||||
expect(element('.doc-example-live span:last:hidden').count()).toEqual(1);
|
||||
expect(element('.doc-example-live .check-element:first:visible').count()).toEqual(1);
|
||||
expect(element('.doc-example-live .check-element:last:hidden').count()).toEqual(1);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
//TODO(misko): refactor to remove element from the DOM
|
||||
var ngHideDirective = ngDirective(function(scope, element, attr){
|
||||
scope.$watch(attr.ngHide, function ngHideWatchAction(value){
|
||||
element.css('display', toBoolean(value) ? 'none' : '');
|
||||
});
|
||||
});
|
||||
var ngHideDirective = ['$animator', function($animator) {
|
||||
return function(scope, element, attr) {
|
||||
var animate = $animator(scope, attr);
|
||||
scope.$watch(attr.ngHide, function ngHideWatchAction(value){
|
||||
animate[toBoolean(value) ? 'hide' : 'show'](element);
|
||||
});
|
||||
};
|
||||
}];
|
||||
|
||||
+142
-68
@@ -6,89 +6,161 @@
|
||||
* @restrict EA
|
||||
*
|
||||
* @description
|
||||
* Conditionally change the DOM structure.
|
||||
* The ngSwitch directive is used to conditionally swap DOM structure on your template based on a scope expression.
|
||||
* Elements within ngSwitch but without ngSwitchWhen or ngSwitchDefault directives will be preserved at the location
|
||||
* as specified in the template.
|
||||
*
|
||||
* @usageContent
|
||||
* <ANY ng-switch-when="matchValue1">...</ANY>
|
||||
* The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
|
||||
* from the template cache), ngSwitch simply choses one of the nested elements and makes it visible based on which element
|
||||
* matches the value obtained from the evaluated expression. In other words, you define a container element
|
||||
* (where you place the directive), place an expression on the **on="..." attribute**
|
||||
* (or the **ng-switch="..." attribute**), define any inner elements inside of the directive and place
|
||||
* a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
|
||||
* expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
|
||||
* attribute is displayed.
|
||||
*
|
||||
* Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter**
|
||||
* and **leave** effects.
|
||||
*
|
||||
* @animations
|
||||
* enter - happens after the ngSwtich contents change and the matched child element is placed inside the container
|
||||
* leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
|
||||
*
|
||||
* @usage
|
||||
* <ANY ng-switch="expression">
|
||||
* <ANY ng-switch-when="matchValue1">...</ANY>
|
||||
* <ANY ng-switch-when="matchValue2">...</ANY>
|
||||
* ...
|
||||
* <ANY ng-switch-default>...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
* @scope
|
||||
* @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.
|
||||
* @paramDescription
|
||||
* On child elments add:
|
||||
* On child elements add:
|
||||
*
|
||||
* * `ngSwitchWhen`: the case statement to match against. If match then this
|
||||
* case will be displayed.
|
||||
* * `ngSwitchDefault`: the default case when no other casses match.
|
||||
* case will be displayed. If the same match appears multiple times, all the
|
||||
* elements will be displayed.
|
||||
* * `ngSwitchDefault`: the default case when no other case match. If there
|
||||
* are multiple default cases, all of them will be displayed when no other
|
||||
* case match.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
function Ctrl($scope) {
|
||||
$scope.items = ['settings', 'home', 'other'];
|
||||
$scope.selection = $scope.items[0];
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="Ctrl">
|
||||
<select ng-model="selection" ng-options="item for item in items">
|
||||
</select>
|
||||
<tt>selection={{selection}}</tt>
|
||||
<hr/>
|
||||
<div ng-switch on="selection" >
|
||||
<example animations="true">
|
||||
<file name="index.html">
|
||||
<div ng-controller="Ctrl">
|
||||
<select ng-model="selection" ng-options="item for item in items">
|
||||
</select>
|
||||
<tt>selection={{selection}}</tt>
|
||||
<hr/>
|
||||
<div
|
||||
class="example-animate-container"
|
||||
ng-switch on="selection"
|
||||
ng-animate="{enter: 'example-enter', leave: 'example-leave'}">
|
||||
<div ng-switch-when="settings">Settings Div</div>
|
||||
<span ng-switch-when="home">Home Span</span>
|
||||
<span ng-switch-default>default</span>
|
||||
</div>
|
||||
<div ng-switch-when="home">Home Span</div>
|
||||
<div ng-switch-default>default</div>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should start in settings', function() {
|
||||
expect(element('.doc-example-live [ng-switch]').text()).toMatch(/Settings Div/);
|
||||
});
|
||||
it('should change to home', function() {
|
||||
select('selection').option('home');
|
||||
expect(element('.doc-example-live [ng-switch]').text()).toMatch(/Home Span/);
|
||||
});
|
||||
it('should select deafault', function() {
|
||||
select('selection').option('other');
|
||||
expect(element('.doc-example-live [ng-switch]').text()).toMatch(/default/);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
var NG_SWITCH = 'ng-switch';
|
||||
var ngSwitchDirective = valueFn({
|
||||
restrict: 'EA',
|
||||
require: 'ngSwitch',
|
||||
controller: function ngSwitchController() {
|
||||
this.cases = {};
|
||||
},
|
||||
link: function(scope, element, attr, ctrl) {
|
||||
var watchExpr = attr.ngSwitch || attr.on,
|
||||
selectedTransclude,
|
||||
selectedElement,
|
||||
selectedScope;
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
function Ctrl($scope) {
|
||||
$scope.items = ['settings', 'home', 'other'];
|
||||
$scope.selection = $scope.items[0];
|
||||
}
|
||||
</file>
|
||||
<file name="animations.css">
|
||||
.example-leave-setup, .example-enter-setup {
|
||||
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
-moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
-ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
-o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
|
||||
scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
|
||||
if (selectedElement) {
|
||||
selectedScope.$destroy();
|
||||
selectedElement.remove();
|
||||
selectedElement = selectedScope = null;
|
||||
position:absolute;
|
||||
top:0;
|
||||
left:0;
|
||||
right:0;
|
||||
bottom:0;
|
||||
}
|
||||
if ((selectedTransclude = ctrl.cases['!' + value] || ctrl.cases['?'])) {
|
||||
scope.$eval(attr.change);
|
||||
selectedScope = scope.$new();
|
||||
selectedTransclude(selectedScope, function(caseElement) {
|
||||
selectedElement = caseElement;
|
||||
element.append(caseElement);
|
||||
});
|
||||
|
||||
.example-animate-container > * {
|
||||
display:block;
|
||||
padding:10px;
|
||||
}
|
||||
});
|
||||
|
||||
.example-enter-setup {
|
||||
top:-50px;
|
||||
}
|
||||
.example-enter-start.example-enter-start {
|
||||
top:0;
|
||||
}
|
||||
|
||||
.example-leave-setup {
|
||||
top:0;
|
||||
}
|
||||
.example-leave-start.example-leave-start {
|
||||
top:50px;
|
||||
}
|
||||
</file>
|
||||
<file name="scenario.js">
|
||||
it('should start in settings', function() {
|
||||
expect(element('.doc-example-live [ng-switch]').text()).toMatch(/Settings Div/);
|
||||
});
|
||||
it('should change to home', function() {
|
||||
select('selection').option('home');
|
||||
expect(element('.doc-example-live [ng-switch]').text()).toMatch(/Home Span/);
|
||||
});
|
||||
it('should select default', function() {
|
||||
select('selection').option('other');
|
||||
expect(element('.doc-example-live [ng-switch]').text()).toMatch(/default/);
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
var ngSwitchDirective = ['$animator', function($animator) {
|
||||
return {
|
||||
restrict: 'EA',
|
||||
require: 'ngSwitch',
|
||||
|
||||
// asks for $scope to fool the BC controller module
|
||||
controller: ['$scope', function ngSwitchController() {
|
||||
this.cases = {};
|
||||
}],
|
||||
link: function(scope, element, attr, ngSwitchController) {
|
||||
var animate = $animator(scope, attr);
|
||||
var watchExpr = attr.ngSwitch || attr.on,
|
||||
selectedTranscludes,
|
||||
selectedElements,
|
||||
selectedScopes = [];
|
||||
|
||||
scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
|
||||
for (var i= 0, ii=selectedScopes.length; i<ii; i++) {
|
||||
selectedScopes[i].$destroy();
|
||||
animate.leave(selectedElements[i]);
|
||||
}
|
||||
|
||||
selectedElements = [];
|
||||
selectedScopes = [];
|
||||
|
||||
if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
|
||||
scope.$eval(attr.change);
|
||||
forEach(selectedTranscludes, function(selectedTransclude) {
|
||||
var selectedScope = scope.$new();
|
||||
selectedScopes.push(selectedScope);
|
||||
selectedTransclude.transclude(selectedScope, function(caseElement) {
|
||||
var anchor = selectedTransclude.element;
|
||||
|
||||
selectedElements.push(caseElement);
|
||||
animate.enter(caseElement, anchor.parent(), anchor);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}];
|
||||
|
||||
var ngSwitchWhenDirective = ngDirective({
|
||||
transclude: 'element',
|
||||
@@ -96,7 +168,8 @@ var ngSwitchWhenDirective = ngDirective({
|
||||
require: '^ngSwitch',
|
||||
compile: function(element, attrs, transclude) {
|
||||
return function(scope, element, attr, ctrl) {
|
||||
ctrl.cases['!' + attrs.ngSwitchWhen] = transclude;
|
||||
ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
|
||||
ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: transclude, element: element });
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -107,7 +180,8 @@ var ngSwitchDefaultDirective = ngDirective({
|
||||
require: '^ngSwitch',
|
||||
compile: function(element, attrs, transclude) {
|
||||
return function(scope, element, attr, ctrl) {
|
||||
ctrl.cases['?'] = transclude;
|
||||
ctrl.cases['?'] = (ctrl.cases['?'] || []);
|
||||
ctrl.cases['?'].push({ transclude: transclude, element: element });
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
+69
-14
@@ -12,9 +12,16 @@
|
||||
* Every time the current route changes, the included view changes with it according to the
|
||||
* configuration of the `$route` service.
|
||||
*
|
||||
* Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter**
|
||||
* and **leave** effects.
|
||||
*
|
||||
* @animations
|
||||
* enter - happens just after the ngView contents are changed (when the new view DOM element is inserted into the DOM)
|
||||
* leave - happens just after the current ngView contents change and just before the former contents are removed from the DOM
|
||||
*
|
||||
* @scope
|
||||
* @example
|
||||
<example module="ngView">
|
||||
<example module="ngView" animations="true">
|
||||
<file name="index.html">
|
||||
<div ng-controller="MainCntl">
|
||||
Choose:
|
||||
@@ -24,7 +31,10 @@
|
||||
<a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
|
||||
<a href="Book/Scarlet">Scarlet Letter</a><br/>
|
||||
|
||||
<div ng-view></div>
|
||||
<div
|
||||
ng-view
|
||||
class="example-animate-container"
|
||||
ng-animate="{enter: 'example-enter', leave: 'example-leave'}"></div>
|
||||
<hr />
|
||||
|
||||
<pre>$location.path() = {{$location.path()}}</pre>
|
||||
@@ -36,14 +46,58 @@
|
||||
</file>
|
||||
|
||||
<file name="book.html">
|
||||
controller: {{name}}<br />
|
||||
Book Id: {{params.bookId}}<br />
|
||||
<div>
|
||||
controller: {{name}}<br />
|
||||
Book Id: {{params.bookId}}<br />
|
||||
</div>
|
||||
</file>
|
||||
|
||||
<file name="chapter.html">
|
||||
controller: {{name}}<br />
|
||||
Book Id: {{params.bookId}}<br />
|
||||
Chapter Id: {{params.chapterId}}
|
||||
<div>
|
||||
controller: {{name}}<br />
|
||||
Book Id: {{params.bookId}}<br />
|
||||
Chapter Id: {{params.chapterId}}
|
||||
</div>
|
||||
</file>
|
||||
|
||||
<file name="animations.css">
|
||||
.example-leave-setup, .example-enter-setup {
|
||||
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
|
||||
-moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
|
||||
-ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
|
||||
-o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
|
||||
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
|
||||
}
|
||||
|
||||
.example-animate-container {
|
||||
position:relative;
|
||||
height:100px;
|
||||
}
|
||||
|
||||
.example-animate-container > * {
|
||||
display:block;
|
||||
width:100%;
|
||||
border-left:1px solid black;
|
||||
|
||||
position:absolute;
|
||||
top:0;
|
||||
left:0;
|
||||
right:0;
|
||||
bottom:0;
|
||||
padding:10px;
|
||||
}
|
||||
|
||||
.example-enter-setup {
|
||||
left:100%;
|
||||
}
|
||||
.example-enter-setup.example-enter-start {
|
||||
left:0;
|
||||
}
|
||||
|
||||
.example-leave-setup { }
|
||||
.example-leave-setup.example-leave-start {
|
||||
left:-100%;
|
||||
}
|
||||
</file>
|
||||
|
||||
<file name="script.js">
|
||||
@@ -105,15 +159,16 @@
|
||||
* Emitted every time the ngView content is reloaded.
|
||||
*/
|
||||
var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$compile',
|
||||
'$controller',
|
||||
'$controller', '$animator',
|
||||
function($http, $templateCache, $route, $anchorScroll, $compile,
|
||||
$controller) {
|
||||
$controller, $animator) {
|
||||
return {
|
||||
restrict: 'ECA',
|
||||
terminal: true,
|
||||
link: function(scope, element, attr) {
|
||||
var lastScope,
|
||||
onloadExp = attr.onload || '';
|
||||
onloadExp = attr.onload || '',
|
||||
animate = $animator(scope, attr);
|
||||
|
||||
scope.$on('$routeChangeSuccess', update);
|
||||
update();
|
||||
@@ -127,7 +182,7 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c
|
||||
}
|
||||
|
||||
function clearContent() {
|
||||
element.html('');
|
||||
animate.leave(element.contents(), element);
|
||||
destroyLastScope();
|
||||
}
|
||||
|
||||
@@ -136,8 +191,8 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c
|
||||
template = locals && locals.$template;
|
||||
|
||||
if (template) {
|
||||
element.html(template);
|
||||
destroyLastScope();
|
||||
clearContent();
|
||||
animate.enter(jqLite('<div></div>').html(template).contents(), element);
|
||||
|
||||
var link = $compile(element.contents()),
|
||||
current = $route.current,
|
||||
@@ -147,7 +202,7 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c
|
||||
if (current.controller) {
|
||||
locals.$scope = lastScope;
|
||||
controller = $controller(current.controller, locals);
|
||||
element.contents().data('$ngControllerController', controller);
|
||||
element.children().data('$ngControllerController', controller);
|
||||
}
|
||||
|
||||
link(lastScope);
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
|
||||
var ngOptionsDirective = valueFn({ terminal: true });
|
||||
var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
//00001111100000000000222200000000000000000000003333000000000000044444444444444444000000000555555555555555550000000666666666666666660000000000000007777
|
||||
//0000111110000000000022220000000000000000000000333300000000000000444444444444444440000000005555555555555555500000006666666666666666600000000000000077770
|
||||
var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/,
|
||||
nullModelCtrl = {$setViewValue: noop};
|
||||
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
*
|
||||
* Filters are just functions which transform input to an output. However filters need to be Dependency Injected. To
|
||||
* achieve this a filter definition consists of a factory function which is annotated with dependencies and is
|
||||
* responsible for creating a the filter function.
|
||||
* responsible for creating a filter function.
|
||||
*
|
||||
* <pre>
|
||||
* // Filter registration
|
||||
|
||||
+67
-21
@@ -32,6 +32,22 @@
|
||||
* called for each element of `array`. The final result is an array of those elements that
|
||||
* the predicate returned true for.
|
||||
*
|
||||
* @param {function(expected, actual)|true|undefined} comparator Comparator which is used in
|
||||
* determining if the expected value (from the filter expression) and actual value (from
|
||||
* the object in the array) should be considered a match.
|
||||
*
|
||||
* Can be one of:
|
||||
*
|
||||
* - `function(expected, actual)`:
|
||||
* The function will be given the object value and the predicate value to compare and
|
||||
* should return true if the item should be included in filtered result.
|
||||
*
|
||||
* - `true`: A shorthand for `function(expected, actual) { return angular.equals(expected, actual)}`.
|
||||
* this is essentially strict comparison of expected and actual.
|
||||
*
|
||||
* - `false|undefined`: A short hand for a function which will look for a substring match in case
|
||||
* insensitive way.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
@@ -39,26 +55,28 @@
|
||||
{name:'Mary', phone:'800-BIG-MARY'},
|
||||
{name:'Mike', phone:'555-4321'},
|
||||
{name:'Adam', phone:'555-5678'},
|
||||
{name:'Julie', phone:'555-8765'}]"></div>
|
||||
{name:'Julie', phone:'555-8765'},
|
||||
{name:'Juliette', phone:'555-5678'}]"></div>
|
||||
|
||||
Search: <input ng-model="searchText">
|
||||
<table id="searchTextResults">
|
||||
<tr><th>Name</th><th>Phone</th><tr>
|
||||
<tr><th>Name</th><th>Phone</th></tr>
|
||||
<tr ng-repeat="friend in friends | filter:searchText">
|
||||
<td>{{friend.name}}</td>
|
||||
<td>{{friend.phone}}</td>
|
||||
<tr>
|
||||
</tr>
|
||||
</table>
|
||||
<hr>
|
||||
Any: <input ng-model="search.$"> <br>
|
||||
Name only <input ng-model="search.name"><br>
|
||||
Phone only <input ng-model="search.phone"å><br>
|
||||
Equality <input type="checkbox" ng-model="strict"><br>
|
||||
<table id="searchObjResults">
|
||||
<tr><th>Name</th><th>Phone</th><tr>
|
||||
<tr ng-repeat="friend in friends | filter:search">
|
||||
<tr><th>Name</th><th>Phone</th></tr>
|
||||
<tr ng-repeat="friend in friends | filter:search:strict">
|
||||
<td>{{friend.name}}</td>
|
||||
<td>{{friend.phone}}</td>
|
||||
<tr>
|
||||
</tr>
|
||||
</table>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
@@ -75,14 +93,20 @@
|
||||
it('should search in specific fields when filtering with a predicate object', function() {
|
||||
input('search.$').enter('i');
|
||||
expect(repeater('#searchObjResults tr', 'friend in friends').column('friend.name')).
|
||||
toEqual(['Mary', 'Mike', 'Julie']);
|
||||
toEqual(['Mary', 'Mike', 'Julie', 'Juliette']);
|
||||
});
|
||||
it('should use a equal comparison when comparator is true', function() {
|
||||
input('search.name').enter('Julie');
|
||||
input('strict').check();
|
||||
expect(repeater('#searchObjResults tr', 'friend in friends').column('friend.name')).
|
||||
toEqual(['Julie']);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
function filterFilter() {
|
||||
return function(array, expression) {
|
||||
if (!(array instanceof Array)) return array;
|
||||
return function(array, expression, comperator) {
|
||||
if (!isArray(array)) return array;
|
||||
var predicates = [];
|
||||
predicates.check = function(value) {
|
||||
for (var j = 0; j < predicates.length; j++) {
|
||||
@@ -92,20 +116,43 @@ function filterFilter() {
|
||||
}
|
||||
return true;
|
||||
};
|
||||
switch(typeof comperator) {
|
||||
case "function":
|
||||
break;
|
||||
case "boolean":
|
||||
if(comperator == true) {
|
||||
comperator = function(obj, text) {
|
||||
return angular.equals(obj, text);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
comperator = function(obj, text) {
|
||||
text = (''+text).toLowerCase();
|
||||
return (''+obj).toLowerCase().indexOf(text) > -1
|
||||
};
|
||||
}
|
||||
var search = function(obj, text){
|
||||
if (text.charAt(0) === '!') {
|
||||
if (typeof text == 'string' && text.charAt(0) === '!') {
|
||||
return !search(obj, text.substr(1));
|
||||
}
|
||||
switch (typeof obj) {
|
||||
case "boolean":
|
||||
case "number":
|
||||
case "string":
|
||||
return ('' + obj).toLowerCase().indexOf(text) > -1;
|
||||
return comperator(obj, text);
|
||||
case "object":
|
||||
for ( var objKey in obj) {
|
||||
if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) {
|
||||
return true;
|
||||
}
|
||||
switch (typeof text) {
|
||||
case "object":
|
||||
return comperator(obj, text);
|
||||
break;
|
||||
default:
|
||||
for ( var objKey in obj) {
|
||||
if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
case "array":
|
||||
@@ -128,19 +175,18 @@ function filterFilter() {
|
||||
for (var key in expression) {
|
||||
if (key == '$') {
|
||||
(function() {
|
||||
var text = (''+expression[key]).toLowerCase();
|
||||
if (!text) return;
|
||||
if (!expression[key]) return;
|
||||
var path = key
|
||||
predicates.push(function(value) {
|
||||
return search(value, text);
|
||||
return search(value, expression[path]);
|
||||
});
|
||||
})();
|
||||
} else {
|
||||
(function() {
|
||||
if (!expression[key]) return;
|
||||
var path = key;
|
||||
var text = (''+expression[key]).toLowerCase();
|
||||
if (!text) return;
|
||||
predicates.push(function(value) {
|
||||
return search(getter(value, path), text);
|
||||
return search(getter(value,path), expression[path]);
|
||||
});
|
||||
})();
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
|
||||
fraction += '0';
|
||||
}
|
||||
|
||||
if (fractionSize) formatedText += decimalSep + fraction.substr(0, fractionSize);
|
||||
if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
|
||||
}
|
||||
|
||||
parts.push(isNegative ? pattern.negPre : pattern.posPre);
|
||||
@@ -211,8 +211,13 @@ function dateStrGetter(name, shortForm) {
|
||||
}
|
||||
|
||||
function timeZoneGetter(date) {
|
||||
var offset = date.getTimezoneOffset();
|
||||
return padNumber(offset / 60, 2) + padNumber(Math.abs(offset % 60), 2);
|
||||
var zone = -1 * date.getTimezoneOffset();
|
||||
var paddedZone = (zone >= 0) ? "+" : "";
|
||||
|
||||
paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
|
||||
padNumber(Math.abs(zone % 60), 2);
|
||||
|
||||
return paddedZone;
|
||||
}
|
||||
|
||||
function ampmGetter(date, formats) {
|
||||
@@ -237,6 +242,9 @@ var DATE_FORMATS = {
|
||||
m: dateGetter('Minutes', 1),
|
||||
ss: dateGetter('Seconds', 2),
|
||||
s: dateGetter('Seconds', 1),
|
||||
// while ISO 8601 requires fractions to be prefixed with `.` or `,`
|
||||
// we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
|
||||
sss: dateGetter('Milliseconds', 3),
|
||||
EEEE: dateStrGetter('Day'),
|
||||
EEE: dateStrGetter('Day', true),
|
||||
a: ampmGetter,
|
||||
@@ -275,8 +283,9 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
|
||||
* * `'m'`: Minute in hour (0-59)
|
||||
* * `'ss'`: Second in minute, padded (00-59)
|
||||
* * `'s'`: Second in minute (0-59)
|
||||
* * `'.sss' or ',sss'`: Millisecond in second, padded (000-999)
|
||||
* * `'a'`: am/pm marker
|
||||
* * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-1200)
|
||||
* * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
|
||||
*
|
||||
* `format` string can also be one of the following predefined
|
||||
* {@link guide/i18n localizable formats}:
|
||||
@@ -298,7 +307,8 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
|
||||
*
|
||||
* @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
|
||||
* 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).
|
||||
* shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
|
||||
* specified in the string input, the time is considered to be in the local timezone.
|
||||
* @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.
|
||||
@@ -318,7 +328,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
|
||||
expect(binding("1288323623006 | date:'medium'")).
|
||||
toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
|
||||
expect(binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).
|
||||
toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} \-?\d{4}/);
|
||||
toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
|
||||
expect(binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).
|
||||
toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* Orders a specified `array` by the `expression` predicate.
|
||||
*
|
||||
* Note: this function is used to augment the `Array` type in Angular expressions. See
|
||||
* {@link ng.$filter} for more informaton about Angular arrays.
|
||||
* {@link ng.$filter} for more information about Angular arrays.
|
||||
*
|
||||
* @param {Array} array The array to sort.
|
||||
* @param {function(*)|string|Array.<(function(*)|string)>} expression A predicate to be
|
||||
@@ -52,12 +52,12 @@
|
||||
(<a href ng-click="predicate = '-name'; reverse=false">^</a>)</th>
|
||||
<th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
|
||||
<th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th>
|
||||
<tr>
|
||||
</tr>
|
||||
<tr ng-repeat="friend in friends | orderBy:predicate:reverse">
|
||||
<td>{{friend.name}}</td>
|
||||
<td>{{friend.phone}}</td>
|
||||
<td>{{friend.age}}</td>
|
||||
<tr>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</doc:source>
|
||||
@@ -89,7 +89,7 @@
|
||||
orderByFilter.$inject = ['$parse'];
|
||||
function orderByFilter($parse){
|
||||
return function(array, sortPredicate, reverseOrder) {
|
||||
if (!(array instanceof Array)) return array;
|
||||
if (!isArray(array)) return array;
|
||||
if (!sortPredicate) return array;
|
||||
sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
|
||||
sortPredicate = map(sortPredicate, function(predicate){
|
||||
|
||||
+205
-46
@@ -149,23 +149,58 @@ function $HttpProvider() {
|
||||
},
|
||||
post: {'Content-Type': 'application/json;charset=utf-8'},
|
||||
put: {'Content-Type': 'application/json;charset=utf-8'}
|
||||
}
|
||||
},
|
||||
|
||||
xsrfCookieName: 'XSRF-TOKEN',
|
||||
xsrfHeaderName: 'X-XSRF-TOKEN'
|
||||
};
|
||||
|
||||
var providerResponseInterceptors = this.responseInterceptors = [];
|
||||
/**
|
||||
* Are order by request. I.E. they are applied in the same order as
|
||||
* array on request, but revers order on response.
|
||||
*/
|
||||
var interceptorFactories = this.interceptors = [];
|
||||
/**
|
||||
* For historical reasons, response interceptors ordered by the order in which
|
||||
* they are applied to response. (This is in revers to interceptorFactories)
|
||||
*/
|
||||
var responseInterceptorFactories = this.responseInterceptors = [];
|
||||
|
||||
this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector',
|
||||
function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) {
|
||||
|
||||
var defaultCache = $cacheFactory('$http'),
|
||||
responseInterceptors = [];
|
||||
var defaultCache = $cacheFactory('$http');
|
||||
|
||||
forEach(providerResponseInterceptors, function(interceptor) {
|
||||
responseInterceptors.push(
|
||||
isString(interceptor)
|
||||
? $injector.get(interceptor)
|
||||
: $injector.invoke(interceptor)
|
||||
);
|
||||
/**
|
||||
* Interceptors stored in reverse order. Inner interceptors before outer interceptors.
|
||||
* The reversal is needed so that we can build up the interception chain around the
|
||||
* server request.
|
||||
*/
|
||||
var reversedInterceptors = [];
|
||||
|
||||
forEach(interceptorFactories, function(interceptorFactory) {
|
||||
reversedInterceptors.unshift(isString(interceptorFactory)
|
||||
? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
|
||||
});
|
||||
|
||||
forEach(responseInterceptorFactories, function(interceptorFactory, index) {
|
||||
var responseFn = isString(interceptorFactory)
|
||||
? $injector.get(interceptorFactory)
|
||||
: $injector.invoke(interceptorFactory);
|
||||
|
||||
/**
|
||||
* Response interceptors go before "around" interceptors (no real reason, just
|
||||
* had to pick one.) But they are already revesed, so we can't use unshift, hence
|
||||
* the splice.
|
||||
*/
|
||||
reversedInterceptors.splice(index, 0, {
|
||||
response: function(response) {
|
||||
return responseFn($q.when(response));
|
||||
},
|
||||
responseError: function(response) {
|
||||
return responseFn($q.reject(response));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -262,7 +297,7 @@ function $HttpProvider() {
|
||||
* `$httpProvider.defaults.headers.get['My-Header']='value'`.
|
||||
*
|
||||
* Additionally, the defaults can be set at runtime via the `$http.defaults` object in a similar
|
||||
* fassion as described above.
|
||||
* fashion as described above.
|
||||
*
|
||||
*
|
||||
* # Transforming Requests and Responses
|
||||
@@ -280,10 +315,14 @@ function $HttpProvider() {
|
||||
* - if XSRF prefix is detected, strip it (see Security Considerations section below)
|
||||
* - if json response is detected, deserialize it using a JSON parser
|
||||
*
|
||||
* To override these transformation locally, specify transform functions as `transformRequest`
|
||||
* and/or `transformResponse` properties of the config object. To globally override the default
|
||||
* transforms, override the `$httpProvider.defaults.transformRequest` and
|
||||
* `$httpProvider.defaults.transformResponse` properties of the `$httpProvider`.
|
||||
* To globally augment or override the default transforms, modify the `$httpProvider.defaults.transformRequest` and
|
||||
* `$httpProvider.defaults.transformResponse` properties of the `$httpProvider`. These properties are by default an
|
||||
* array of transform functions, which allows you to `push` or `unshift` a new transformation function into the
|
||||
* transformation chain. You can also decide to completely override any default transformations by assigning your
|
||||
* transformation functions to these properties directly without the array wrapper.
|
||||
*
|
||||
* Similarly, to locally override the request/response transforms, augment the `transformRequest` and/or
|
||||
* `transformResponse` properties of the config object passed into `$http`.
|
||||
*
|
||||
*
|
||||
* # Caching
|
||||
@@ -299,8 +338,94 @@ function $HttpProvider() {
|
||||
* cache, but the cache is not populated yet, only one request to the server will be made and
|
||||
* the remaining requests will be fulfilled using the response for the first request.
|
||||
*
|
||||
* A custom default cache built with $cacheFactory can be provided in $http.defaults.cache.
|
||||
* To skip it, set configuration property `cache` to `false`.
|
||||
*
|
||||
*
|
||||
* # Response interceptors
|
||||
* # Interceptors
|
||||
*
|
||||
* Before you start creating interceptors, be sure to understand the
|
||||
* {@link ng.$q $q and deferred/promise APIs}.
|
||||
*
|
||||
* For purposes of global error handling, authentication or any kind of synchronous or
|
||||
* asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
|
||||
* able to intercept requests before they are handed to the server and
|
||||
* responses before they are handed over to the application code that
|
||||
* initiated these requests. The interceptors leverage the {@link ng.$q
|
||||
* promise APIs} to fulfil this need for both synchronous and asynchronous pre-processing.
|
||||
*
|
||||
* The interceptors are service factories that are registered with the $httpProvider by
|
||||
* adding them to the `$httpProvider.interceptors` array. The factory is called and
|
||||
* injected with dependencies (if specified) and returns the interceptor.
|
||||
*
|
||||
* There are two kinds of interceptors (and two kinds of rejection interceptors):
|
||||
*
|
||||
* * `request`: interceptors get called with http `config` object. The function is free to modify
|
||||
* the `config` or create a new one. The function needs to return the `config` directly or as a
|
||||
* promise.
|
||||
* * `requestError`: interceptor gets called when a previous interceptor threw an error or resolved
|
||||
* with a rejection.
|
||||
* * `response`: interceptors get called with http `response` object. The function is free to modify
|
||||
* the `response` or create a new one. The function needs to return the `response` directly or as a
|
||||
* promise.
|
||||
* * `responseError`: interceptor gets called when a previous interceptor threw an error or resolved
|
||||
* with a rejection.
|
||||
*
|
||||
*
|
||||
* <pre>
|
||||
* // register the interceptor as a service
|
||||
* $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
|
||||
* return {
|
||||
* // optional method
|
||||
* 'request': function(config) {
|
||||
* // do something on success
|
||||
* return config || $q.when(config);
|
||||
* },
|
||||
*
|
||||
* // optional method
|
||||
* 'requestError': function(rejection) {
|
||||
* // do something on error
|
||||
* if (canRecover(rejection)) {
|
||||
* return responseOrNewPromise
|
||||
* }
|
||||
* return $q.reject(rejection);
|
||||
* },
|
||||
*
|
||||
*
|
||||
*
|
||||
* // optional method
|
||||
* 'response': function(response) {
|
||||
* // do something on success
|
||||
* return response || $q.when(response);
|
||||
* },
|
||||
*
|
||||
* // optional method
|
||||
* 'responseError': function(rejection) {
|
||||
* // do something on error
|
||||
* if (canRecover(rejection)) {
|
||||
* return responseOrNewPromise
|
||||
* }
|
||||
* return $q.reject(rejection);
|
||||
* };
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* $httpProvider.interceptors.push('myHttpInterceptor');
|
||||
*
|
||||
*
|
||||
* // register the interceptor via an anonymous factory
|
||||
* $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
|
||||
* return {
|
||||
* 'request': function(config) {
|
||||
* // same as above
|
||||
* },
|
||||
* 'response': function(response) {
|
||||
* // same as above
|
||||
* }
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* # Response interceptors (DEPRECATED)
|
||||
*
|
||||
* Before you start creating interceptors, be sure to understand the
|
||||
* {@link ng.$q $q and deferred/promise APIs}.
|
||||
@@ -383,9 +508,10 @@ function $HttpProvider() {
|
||||
* {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} is a technique by which
|
||||
* an unauthorized site can gain your user's private data. Angular provides following mechanism
|
||||
* to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
|
||||
* called `XSRF-TOKEN` and sets it as the HTTP header `X-XSRF-TOKEN`. Since only JavaScript that
|
||||
* runs on your domain could read the cookie, your server can be assured that the XHR came from
|
||||
* JavaScript running on your domain. The header will not be set for cross-domain requests.
|
||||
* (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
|
||||
* JavaScript that runs on your domain could read the cookie, your server can be assured that
|
||||
* the XHR came from JavaScript running on your domain. The header will not be set for
|
||||
* cross-domain requests.
|
||||
*
|
||||
* To take advantage of this, your server needs to set a token in a JavaScript readable session
|
||||
* cookie called `XSRF-TOKEN` on first HTTP GET request. On subsequent non-GET requests the
|
||||
@@ -395,6 +521,9 @@ function $HttpProvider() {
|
||||
* up its own tokens). We recommend that the token is a digest of your site's authentication
|
||||
* cookie with {@link http://en.wikipedia.org/wiki/Rainbow_table salt for added security}.
|
||||
*
|
||||
* The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
|
||||
* properties of either $httpProvider.defaults, or the per-request config object.
|
||||
*
|
||||
*
|
||||
* @param {object} config Object describing the request to be made and how it should be
|
||||
* processed. The object has following properties:
|
||||
@@ -405,6 +534,8 @@ function $HttpProvider() {
|
||||
* `?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.
|
||||
* - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
|
||||
* - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
|
||||
* - **transformRequest** – `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
|
||||
* transform function or an array of such functions. The transform function takes the http
|
||||
* request body and headers and returns its transformed (typically serialized) version.
|
||||
@@ -510,40 +641,66 @@ function $HttpProvider() {
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
function $http(config) {
|
||||
function $http(requestConfig) {
|
||||
var config = {
|
||||
transformRequest: defaults.transformRequest,
|
||||
transformResponse: defaults.transformResponse
|
||||
};
|
||||
var headers = {};
|
||||
|
||||
extend(config, requestConfig);
|
||||
config.headers = headers;
|
||||
config.method = uppercase(config.method);
|
||||
|
||||
var reqTransformFn = config.transformRequest || defaults.transformRequest,
|
||||
respTransformFn = config.transformResponse || defaults.transformResponse,
|
||||
defHeaders = defaults.headers,
|
||||
xsrfToken = isSameDomain(config.url, $browser.url()) ?
|
||||
$browser.cookies()['XSRF-TOKEN'] : undefined,
|
||||
reqHeaders = extend({'X-XSRF-TOKEN': xsrfToken},
|
||||
defHeaders.common, defHeaders[lowercase(config.method)], config.headers),
|
||||
reqData = transformData(config.data, headersGetter(reqHeaders), reqTransformFn),
|
||||
promise;
|
||||
extend(headers,
|
||||
defaults.headers.common,
|
||||
defaults.headers[lowercase(config.method)],
|
||||
requestConfig.headers);
|
||||
|
||||
// strip content-type if data is undefined
|
||||
if (isUndefined(config.data)) {
|
||||
delete reqHeaders['Content-Type'];
|
||||
var xsrfValue = isSameDomain(config.url, $browser.url())
|
||||
? $browser.cookies()[config.xsrfCookieName || defaults.xsrfCookieName]
|
||||
: undefined;
|
||||
if (xsrfValue) {
|
||||
headers[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
|
||||
}
|
||||
|
||||
if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
|
||||
config.withCredentials = defaults.withCredentials;
|
||||
}
|
||||
|
||||
// send request
|
||||
promise = sendReq(config, reqData, reqHeaders);
|
||||
var serverRequest = function(config) {
|
||||
var reqData = transformData(config.data, headersGetter(headers), config.transformRequest);
|
||||
|
||||
// strip content-type if data is undefined
|
||||
if (isUndefined(config.data)) {
|
||||
delete headers['Content-Type'];
|
||||
}
|
||||
|
||||
// transform future response
|
||||
promise = promise.then(transformResponse, transformResponse);
|
||||
if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
|
||||
config.withCredentials = defaults.withCredentials;
|
||||
}
|
||||
|
||||
// send request
|
||||
return sendReq(config, reqData, headers).then(transformResponse, transformResponse);
|
||||
};
|
||||
|
||||
var chain = [serverRequest, undefined];
|
||||
var promise = $q.when(config);
|
||||
|
||||
// apply interceptors
|
||||
forEach(responseInterceptors, function(interceptor) {
|
||||
promise = interceptor(promise);
|
||||
forEach(reversedInterceptors, function(interceptor) {
|
||||
if (interceptor.request || interceptor.requestError) {
|
||||
chain.unshift(interceptor.request, interceptor.requestError);
|
||||
}
|
||||
if (interceptor.response || interceptor.responseError) {
|
||||
chain.push(interceptor.response, interceptor.responseError);
|
||||
}
|
||||
});
|
||||
|
||||
while(chain.length) {
|
||||
var thenFn = chain.shift();
|
||||
var rejectFn = chain.shift();
|
||||
|
||||
promise = promise.then(thenFn, rejectFn);
|
||||
};
|
||||
|
||||
promise.success = function(fn) {
|
||||
promise.then(function(response) {
|
||||
fn(response.data, response.status, response.headers, config);
|
||||
@@ -563,7 +720,7 @@ function $HttpProvider() {
|
||||
function transformResponse(response) {
|
||||
// make a copy since the response must be cacheable
|
||||
var resp = extend({}, response, {
|
||||
data: transformData(response.data, response.headers, respTransformFn)
|
||||
data: transformData(response.data, response.headers, config.transformResponse)
|
||||
});
|
||||
return (isSuccess(response.status))
|
||||
? resp
|
||||
@@ -715,8 +872,10 @@ function $HttpProvider() {
|
||||
promise.then(removePendingReq, removePendingReq);
|
||||
|
||||
|
||||
if (config.cache && config.method == 'GET') {
|
||||
cache = isObject(config.cache) ? config.cache : defaultCache;
|
||||
if ((config.cache || defaults.cache) && config.cache !== false && config.method == 'GET') {
|
||||
cache = isObject(config.cache) ? config.cache
|
||||
: isObject(defaults.cache) ? defaults.cache
|
||||
: defaultCache;
|
||||
}
|
||||
|
||||
if (cache) {
|
||||
@@ -804,8 +963,8 @@ function $HttpProvider() {
|
||||
if (isObject(v)) {
|
||||
v = toJson(v);
|
||||
}
|
||||
parts.push(encodeURIComponent(key) + '=' +
|
||||
encodeURIComponent(v));
|
||||
parts.push(encodeUriQuery(key) + '=' +
|
||||
encodeUriQuery(v));
|
||||
});
|
||||
});
|
||||
return url + ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
|
||||
|
||||
+28
-2
@@ -65,8 +65,34 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
|
||||
// always async
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState == 4) {
|
||||
completeRequest(callback, status || xhr.status, xhr.response || xhr.responseText,
|
||||
xhr.getAllResponseHeaders());
|
||||
var responseHeaders = xhr.getAllResponseHeaders();
|
||||
|
||||
// TODO(vojta): remove once Firefox 21 gets released.
|
||||
// begin: workaround to overcome Firefox CORS http response headers bug
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=608735
|
||||
// Firefox already patched in nightly. Should land in Firefox 21.
|
||||
|
||||
// CORS "simple response headers" http://www.w3.org/TR/cors/
|
||||
var value,
|
||||
simpleHeaders = ["Cache-Control", "Content-Language", "Content-Type",
|
||||
"Expires", "Last-Modified", "Pragma"];
|
||||
if (!responseHeaders) {
|
||||
responseHeaders = "";
|
||||
forEach(simpleHeaders, function (header) {
|
||||
var value = xhr.getResponseHeader(header);
|
||||
if (value) {
|
||||
responseHeaders += header + ": " + value + "\n";
|
||||
}
|
||||
});
|
||||
}
|
||||
// end of the workaround.
|
||||
|
||||
// responseText is the old-school way of retrieving response (supported by IE8 & 9)
|
||||
// response and responseType properties were introduced in XHR Level2 spec (supported by IE10)
|
||||
completeRequest(callback,
|
||||
status || xhr.status,
|
||||
(xhr.responseType ? xhr.response : xhr.responseText),
|
||||
responseHeaders);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var URL_MATCH = /^([^:]+):\/\/(\w+:{0,1}\w*@)?([\w\.-]*)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/,
|
||||
var URL_MATCH = /^([^:]+):\/\/(\w+:{0,1}\w*@)?(\{?[\w\.-]*\}?)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/,
|
||||
PATH_MATCH = /^([^\?#]*)?(\?([^#]*))?(#(.*))?$/,
|
||||
HASH_MATCH = PATH_MATCH,
|
||||
DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
|
||||
@@ -79,7 +79,8 @@ function convertToHashbangUrl(url, basePath, hashPrefix) {
|
||||
var match = matchUrl(url);
|
||||
|
||||
// already hashbang url
|
||||
if (decodeURIComponent(match.path) == basePath) {
|
||||
if (decodeURIComponent(match.path) == basePath && !isUndefined(match.hash) &&
|
||||
match.hash.indexOf(hashPrefix) === 0) {
|
||||
return url;
|
||||
// convert html5 url -> hashbang url
|
||||
} else {
|
||||
|
||||
+46
-16
@@ -303,6 +303,8 @@ function parser(text, json, $filter, csp){
|
||||
if (tokens.length !== 0) {
|
||||
throwError("is an unexpected token", tokens[0]);
|
||||
}
|
||||
value.literal = !!value.literal;
|
||||
value.constant = !!value.constant;
|
||||
return value;
|
||||
|
||||
///////////////////////////////////
|
||||
@@ -350,15 +352,19 @@ function parser(text, json, $filter, csp){
|
||||
}
|
||||
|
||||
function unaryFn(fn, right) {
|
||||
return function(self, locals) {
|
||||
return extend(function(self, locals) {
|
||||
return fn(self, locals, right);
|
||||
};
|
||||
}, {
|
||||
constant:right.constant
|
||||
});
|
||||
}
|
||||
|
||||
function binaryFn(left, fn, right) {
|
||||
return function(self, locals) {
|
||||
return extend(function(self, locals) {
|
||||
return fn(self, locals, left, right);
|
||||
};
|
||||
}, {
|
||||
constant:left.constant && right.constant
|
||||
});
|
||||
}
|
||||
|
||||
function statements() {
|
||||
@@ -526,6 +532,9 @@ function parser(text, json, $filter, csp){
|
||||
if (!primary) {
|
||||
throwError("not a primary expression", token);
|
||||
}
|
||||
if (token.json) {
|
||||
primary.constant = primary.literal = true;
|
||||
}
|
||||
}
|
||||
|
||||
var next, context;
|
||||
@@ -614,23 +623,32 @@ function parser(text, json, $filter, csp){
|
||||
// This is used with json array declaration
|
||||
function arrayDeclaration () {
|
||||
var elementFns = [];
|
||||
var allConstant = true;
|
||||
if (peekToken().text != ']') {
|
||||
do {
|
||||
elementFns.push(expression());
|
||||
var elementFn = expression();
|
||||
elementFns.push(elementFn);
|
||||
if (!elementFn.constant) {
|
||||
allConstant = false;
|
||||
}
|
||||
} while (expect(','));
|
||||
}
|
||||
consume(']');
|
||||
return function(self, locals){
|
||||
return extend(function(self, locals){
|
||||
var array = [];
|
||||
for ( var i = 0; i < elementFns.length; i++) {
|
||||
array.push(elementFns[i](self, locals));
|
||||
}
|
||||
return array;
|
||||
};
|
||||
}, {
|
||||
literal:true,
|
||||
constant:allConstant
|
||||
});
|
||||
}
|
||||
|
||||
function object () {
|
||||
var keyValues = [];
|
||||
var allConstant = true;
|
||||
if (peekToken().text != '}') {
|
||||
do {
|
||||
var token = expect(),
|
||||
@@ -638,10 +656,13 @@ function parser(text, json, $filter, csp){
|
||||
consume(":");
|
||||
var value = expression();
|
||||
keyValues.push({key:key, value:value});
|
||||
if (!value.constant) {
|
||||
allConstant = false;
|
||||
}
|
||||
} while (expect(','));
|
||||
}
|
||||
consume('}');
|
||||
return function(self, locals){
|
||||
return extend(function(self, locals){
|
||||
var object = {};
|
||||
for ( var i = 0; i < keyValues.length; i++) {
|
||||
var keyValue = keyValues[i];
|
||||
@@ -649,7 +670,10 @@ function parser(text, json, $filter, csp){
|
||||
object[keyValue.key] = value;
|
||||
}
|
||||
return object;
|
||||
};
|
||||
}, {
|
||||
literal:true,
|
||||
constant:allConstant
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -673,11 +697,11 @@ function setter(obj, path, setValue) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value accesible from the object by path. Any undefined traversals are ignored
|
||||
* Return the value accessible from the object by path. Any undefined traversals are ignored
|
||||
* @param {Object} obj starting object
|
||||
* @param {string} path path to traverse
|
||||
* @param {boolean=true} bindFnToScope
|
||||
* @returns value as accesbile by path
|
||||
* @returns value as accessible by path
|
||||
*/
|
||||
//TODO(misko): this function needs to be removed
|
||||
function getter(obj, path, bindFnToScope) {
|
||||
@@ -848,12 +872,18 @@ function getterFn(path, csp) {
|
||||
* @param {string} expression String expression to compile.
|
||||
* @returns {function(context, locals)} a function which represents the compiled expression:
|
||||
*
|
||||
* * `context`: an object against which any expressions embedded in the strings are evaluated
|
||||
* against (Topically a scope object).
|
||||
* * `locals`: local variables context object, useful for overriding values in `context`.
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the strings
|
||||
* are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
|
||||
* `context`.
|
||||
*
|
||||
* The return function also has an `assign` property, if the expression is assignable, which
|
||||
* allows one to set values to expressions.
|
||||
* The returned function also has the following properties:
|
||||
* * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
|
||||
* literal.
|
||||
* * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
|
||||
* constant literals.
|
||||
* * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
|
||||
* set to a function to change its value on the given context.
|
||||
*
|
||||
*/
|
||||
function $ParseProvider() {
|
||||
|
||||
+25
-24
@@ -12,7 +12,7 @@
|
||||
* interface for interacting with an object that represents the result of an action that is
|
||||
* performed asynchronously, and may or may not be finished at any given point in time.
|
||||
*
|
||||
* From the perspective of dealing with error handling, deferred and promise apis are to
|
||||
* From the perspective of dealing with error handling, deferred and promise APIs are to
|
||||
* asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
|
||||
*
|
||||
* <pre>
|
||||
@@ -47,7 +47,7 @@
|
||||
*
|
||||
* At first it might not be obvious why this extra complexity is worth the trouble. The payoff
|
||||
* comes in the way of
|
||||
* [guarantees that promise and deferred apis make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md).
|
||||
* [guarantees that promise and deferred APIs make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md).
|
||||
*
|
||||
* Additionally the promise api allows for composition that is very hard to do with the
|
||||
* traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
|
||||
@@ -59,7 +59,7 @@
|
||||
*
|
||||
* A new instance of deferred is constructed by calling `$q.defer()`.
|
||||
*
|
||||
* The purpose of the deferred object is to expose the associated Promise instance as well as apis
|
||||
* The purpose of the deferred object is to expose the associated Promise instance as well as APIs
|
||||
* that can be used for signaling the successful or unsuccessful completion of the task.
|
||||
*
|
||||
* **Methods**
|
||||
@@ -102,7 +102,7 @@
|
||||
* return result + 1;
|
||||
* });
|
||||
*
|
||||
* // promiseB will be resolved immediately after promiseA is resolved and it's value will be
|
||||
* // promiseB will be resolved immediately after promiseA is resolved and its value will be
|
||||
* // the result of promiseA incremented by 1
|
||||
* </pre>
|
||||
*
|
||||
@@ -127,7 +127,7 @@
|
||||
* # Testing
|
||||
*
|
||||
* <pre>
|
||||
* it('should simulate promise', inject(function($q, $rootSCope) {
|
||||
* it('should simulate promise', inject(function($q, $rootScope) {
|
||||
* var deferred = $q.defer();
|
||||
* var promise = deferred.promise;
|
||||
* var resolvedValue;
|
||||
@@ -136,7 +136,7 @@
|
||||
* expect(resolvedValue).toBeUndefined();
|
||||
*
|
||||
* // Simulate resolving of promise
|
||||
* defered.resolve(123);
|
||||
* deferred.resolve(123);
|
||||
* // Note that the 'then' function does not get called synchronously.
|
||||
* // This is because we want the promise API to always be async, whether or not
|
||||
* // it got called synchronously or asynchronously.
|
||||
@@ -312,12 +312,12 @@ function qFactory(nextTick, exceptionHandler) {
|
||||
* @methodOf ng.$q
|
||||
* @description
|
||||
* Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
|
||||
* This is useful when you are dealing with on object that might or might not be a promise, or if
|
||||
* This is useful when you are dealing with an object that might or might not be a promise, or if
|
||||
* the promise comes from a source that can't be trusted.
|
||||
*
|
||||
* @param {*} value Value or a promise
|
||||
* @returns {Promise} Returns a single promise that will be resolved with an array of values,
|
||||
* each value coresponding to the promise at the same index in the `promises` array. If any of
|
||||
* each value corresponding to the promise at the same index in the `promises` array. If any of
|
||||
* the promises is resolved with a rejection, this resulting promise will be resolved with the
|
||||
* same rejection.
|
||||
*/
|
||||
@@ -377,29 +377,30 @@ function qFactory(nextTick, exceptionHandler) {
|
||||
* Combines multiple promises into a single promise that is resolved when all of the input
|
||||
* promises are resolved.
|
||||
*
|
||||
* @param {Array.<Promise>} promises An array of promises.
|
||||
* @returns {Promise} Returns a single promise that will be resolved with an array of values,
|
||||
* each value coresponding to the promise at the same index in the `promises` array. If any of
|
||||
* @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
|
||||
* @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
|
||||
* each value corresponding to the promise at the same index/key in the `promises` array/hash. If any of
|
||||
* the promises is resolved with a rejection, this resulting promise will be resolved with the
|
||||
* same rejection.
|
||||
*/
|
||||
function all(promises) {
|
||||
var deferred = defer(),
|
||||
counter = promises.length,
|
||||
results = [];
|
||||
counter = 0,
|
||||
results = isArray(promises) ? [] : {};
|
||||
|
||||
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);
|
||||
});
|
||||
forEach(promises, function(promise, key) {
|
||||
counter++;
|
||||
ref(promise).then(function(value) {
|
||||
if (results.hasOwnProperty(key)) return;
|
||||
results[key] = value;
|
||||
if (!(--counter)) deferred.resolve(results);
|
||||
}, function(reason) {
|
||||
if (results.hasOwnProperty(key)) return;
|
||||
deferred.reject(reason);
|
||||
});
|
||||
} else {
|
||||
});
|
||||
|
||||
if (counter === 0) {
|
||||
deferred.resolve(results);
|
||||
}
|
||||
|
||||
|
||||
+157
-25
@@ -83,25 +83,7 @@ function $RootScopeProvider(){
|
||||
*
|
||||
* Here is a simple scope snippet to show how you can interact with the scope.
|
||||
* <pre>
|
||||
angular.injector(['ng']).invoke(function($rootScope) {
|
||||
var scope = $rootScope.$new();
|
||||
scope.salutation = 'Hello';
|
||||
scope.name = 'World';
|
||||
|
||||
expect(scope.greeting).toEqual(undefined);
|
||||
|
||||
scope.$watch('name', function() {
|
||||
scope.greeting = scope.salutation + ' ' + scope.name + '!';
|
||||
}); // initialize the watch
|
||||
|
||||
expect(scope.greeting).toEqual(undefined);
|
||||
scope.name = 'Misko';
|
||||
// still old value, since watches have not been called yet
|
||||
expect(scope.greeting).toEqual(undefined);
|
||||
|
||||
scope.$digest(); // fire all the watches
|
||||
expect(scope.greeting).toEqual('Hello Misko!');
|
||||
});
|
||||
* <file src="./test/ng/rootScopeSpec.js" tag="docs1" />
|
||||
* </pre>
|
||||
*
|
||||
* # Inheritance
|
||||
@@ -137,6 +119,7 @@ function $RootScopeProvider(){
|
||||
this.$$destroyed = false;
|
||||
this.$$asyncQueue = [];
|
||||
this.$$listeners = {};
|
||||
this.$$isolateBindings = {};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -299,6 +282,14 @@ function $RootScopeProvider(){
|
||||
watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);};
|
||||
}
|
||||
|
||||
if (typeof watchExp == 'string' && get.constant) {
|
||||
var originalFn = watcher.fn;
|
||||
watcher.fn = function(newVal, oldVal, scope) {
|
||||
originalFn.call(this, newVal, oldVal, scope);
|
||||
arrayRemove(array, watcher);
|
||||
};
|
||||
}
|
||||
|
||||
if (!array) {
|
||||
array = scope.$$watchers = [];
|
||||
}
|
||||
@@ -311,6 +302,147 @@ function $RootScopeProvider(){
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.$rootScope.Scope#$watchCollection
|
||||
* @methodOf ng.$rootScope.Scope
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Shallow watches the properties of an object and fires whenever any of the properties change
|
||||
* (for arrays this implies watching the array items, for object maps this implies watching the properties).
|
||||
* If a change is detected the `listener` callback is fired.
|
||||
*
|
||||
* - The `obj` collection is observed via standard $watch operation and is examined on every call to $digest() to
|
||||
* see if any items have been added, removed, or moved.
|
||||
* - The `listener` is called whenever anything within the `obj` has changed. Examples include adding new items
|
||||
* into the object or array, removing and moving items around.
|
||||
*
|
||||
*
|
||||
* # Example
|
||||
* <pre>
|
||||
$scope.names = ['igor', 'matias', 'misko', 'james'];
|
||||
$scope.dataCount = 4;
|
||||
|
||||
$scope.$watchCollection('names', function(newNames, oldNames) {
|
||||
$scope.dataCount = newNames.length;
|
||||
});
|
||||
|
||||
expect($scope.dataCount).toEqual(4);
|
||||
$scope.$digest();
|
||||
|
||||
//still at 4 ... no changes
|
||||
expect($scope.dataCount).toEqual(4);
|
||||
|
||||
$scope.names.pop();
|
||||
$scope.$digest();
|
||||
|
||||
//now there's been a change
|
||||
expect($scope.dataCount).toEqual(3);
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @param {string|Function(scope)} obj Evaluated as {@link guide/expression expression}. The expression value
|
||||
* should evaluate to an object or an array which is observed on each
|
||||
* {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the collection will trigger
|
||||
* a call to the `listener`.
|
||||
*
|
||||
* @param {function(newCollection, oldCollection, scope)} listener a callback function that is fired with both
|
||||
* the `newCollection` and `oldCollection` as parameters.
|
||||
* The `newCollection` object is the newly modified data obtained from the `obj` expression and the
|
||||
* `oldCollection` object is a copy of the former collection data.
|
||||
* The `scope` refers to the current scope.
|
||||
*
|
||||
* @returns {function()} Returns a de-registration function for this listener. When the de-registration function is executed
|
||||
* then the internal watch operation is terminated.
|
||||
*/
|
||||
$watchCollection: function(obj, listener) {
|
||||
var self = this;
|
||||
var oldValue;
|
||||
var newValue;
|
||||
var changeDetected = 0;
|
||||
var objGetter = $parse(obj);
|
||||
var internalArray = [];
|
||||
var internalObject = {};
|
||||
var oldLength = 0;
|
||||
|
||||
function $watchCollectionWatch() {
|
||||
newValue = objGetter(self);
|
||||
var newLength, key;
|
||||
|
||||
if (!isObject(newValue)) {
|
||||
if (oldValue !== newValue) {
|
||||
oldValue = newValue;
|
||||
changeDetected++;
|
||||
}
|
||||
} else if (isArray(newValue)) {
|
||||
if (oldValue !== internalArray) {
|
||||
// we are transitioning from something which was not an array into array.
|
||||
oldValue = internalArray;
|
||||
oldLength = oldValue.length = 0;
|
||||
changeDetected++;
|
||||
}
|
||||
|
||||
newLength = newValue.length;
|
||||
|
||||
if (oldLength !== newLength) {
|
||||
// if lengths do not match we need to trigger change notification
|
||||
changeDetected++;
|
||||
oldValue.length = oldLength = newLength;
|
||||
}
|
||||
// copy the items to oldValue and look for changes.
|
||||
for (var i = 0; i < newLength; i++) {
|
||||
if (oldValue[i] !== newValue[i]) {
|
||||
changeDetected++;
|
||||
oldValue[i] = newValue[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (oldValue !== internalObject) {
|
||||
// we are transitioning from something which was not an object into object.
|
||||
oldValue = internalObject = {};
|
||||
oldLength = 0;
|
||||
changeDetected++;
|
||||
}
|
||||
// copy the items to oldValue and look for changes.
|
||||
newLength = 0;
|
||||
for (key in newValue) {
|
||||
if (newValue.hasOwnProperty(key)) {
|
||||
newLength++;
|
||||
if (oldValue.hasOwnProperty(key)) {
|
||||
if (oldValue[key] !== newValue[key]) {
|
||||
changeDetected++;
|
||||
oldValue[key] = newValue[key];
|
||||
}
|
||||
} else {
|
||||
oldLength++;
|
||||
oldValue[key] = newValue[key];
|
||||
changeDetected++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oldLength > newLength) {
|
||||
// we used to have more keys, need to find them and destroy them.
|
||||
changeDetected++;
|
||||
for(key in oldValue) {
|
||||
if (oldValue.hasOwnProperty(key) && !newValue.hasOwnProperty(key)) {
|
||||
oldLength--;
|
||||
delete oldValue[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return changeDetected;
|
||||
}
|
||||
|
||||
function $watchCollectionAction() {
|
||||
listener(newValue, oldValue, self);
|
||||
}
|
||||
|
||||
return this.$watch($watchCollectionWatch, $watchCollectionAction);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.$rootScope.Scope#$digest
|
||||
@@ -618,10 +750,6 @@ function $RootScopeProvider(){
|
||||
* Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for discussion of
|
||||
* event life cycle.
|
||||
*
|
||||
* @param {string} name Event name to listen on.
|
||||
* @param {function(event)} listener Function to call when the event is emitted.
|
||||
* @returns {function()} Returns a deregistration function for this listener.
|
||||
*
|
||||
* The event listener function format is: `function(event, args...)`. The `event` object
|
||||
* passed into the listener has the following attributes:
|
||||
*
|
||||
@@ -632,6 +760,10 @@ function $RootScopeProvider(){
|
||||
* propagation (available only for events that were `$emit`-ed).
|
||||
* - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag to true.
|
||||
* - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
|
||||
*
|
||||
* @param {string} name Event name to listen on.
|
||||
* @param {function(event, args...)} listener Function to call when the event is emitted.
|
||||
* @returns {function()} Returns a deregistration function for this listener.
|
||||
*/
|
||||
$on: function(name, listener) {
|
||||
var namedListeners = this.$$listeners[name];
|
||||
@@ -727,7 +859,7 @@ function $RootScopeProvider(){
|
||||
* Afterwards, the event propagates to all direct and indirect scopes of the current scope and
|
||||
* calls all registered listeners along the way. The event cannot be canceled.
|
||||
*
|
||||
* Any exception emmited from the {@link ng.$rootScope.Scope#$on listeners} will be passed
|
||||
* Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
|
||||
* onto the {@link ng.$exceptionHandler $exceptionHandler} service.
|
||||
*
|
||||
* @param {string} name Event name to emit.
|
||||
@@ -809,7 +941,7 @@ function $RootScopeProvider(){
|
||||
|
||||
/**
|
||||
* function used as an initial value for watchers.
|
||||
* because it's uniqueue we can easily tell it apart from other values
|
||||
* because it's unique we can easily tell it apart from other values
|
||||
*/
|
||||
function initWatchVal() {}
|
||||
}];
|
||||
|
||||
+45
-29
@@ -23,9 +23,18 @@ function $RouteProvider(){
|
||||
* `$location.path` will be updated to add or drop the trailing slash to exactly match the
|
||||
* route definition.
|
||||
*
|
||||
* `path` can contain named groups starting with a colon (`:name`). All characters up to the
|
||||
* next slash are matched and stored in `$routeParams` under the given `name` when the route
|
||||
* matches.
|
||||
* * `path` can contain named groups starting with a colon (`:name`). All characters up
|
||||
* to the next slash are matched and stored in `$routeParams` under the given `name`
|
||||
* when the route matches.
|
||||
* * `path` can contain named groups starting with a star (`*name`). All characters are
|
||||
* eagerly stored in `$routeParams` under the given `name` when the route matches.
|
||||
*
|
||||
* For example, routes like `/color/:color/largecode/*largecode/edit` will match
|
||||
* `/color/brown/largecode/code/with/slashs/edit` and extract:
|
||||
*
|
||||
* * `color: brown`
|
||||
* * `largecode: code/with/slashs`.
|
||||
*
|
||||
*
|
||||
* @param {Object} route Mapping information to be assigned to `$route.current` on route
|
||||
* match.
|
||||
@@ -83,13 +92,18 @@ function $RouteProvider(){
|
||||
* If the option is set to `false` and url in the browser changes, then
|
||||
* `$routeUpdate` event is broadcasted on the root scope.
|
||||
*
|
||||
* - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive
|
||||
*
|
||||
* If the option is set to `true`, then the particular route can be matched without being
|
||||
* case sensitive
|
||||
*
|
||||
* @returns {Object} self
|
||||
*
|
||||
* @description
|
||||
* Adds a new route definition to the `$route` service.
|
||||
*/
|
||||
this.when = function(path, route) {
|
||||
routes[path] = extend({reloadOnSearch: true}, route);
|
||||
routes[path] = extend({reloadOnSearch: true, caseInsensitiveMatch: false}, route);
|
||||
|
||||
// create redirection for trailing slashes
|
||||
if (path) {
|
||||
@@ -275,8 +289,9 @@ function $RouteProvider(){
|
||||
* {@link ng.directive:ngView ngView} listens for the directive
|
||||
* to instantiate the controller and render the view.
|
||||
*
|
||||
* @param {Object} angularEvent Synthetic event object.
|
||||
* @param {Route} current Current route information.
|
||||
* @param {Route} previous Previous route information.
|
||||
* @param {Route|Undefined} previous Previous route information, or undefined if current is first route entered.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -334,19 +349,21 @@ function $RouteProvider(){
|
||||
/**
|
||||
* @param on {string} current url
|
||||
* @param when {string} route when template to match the url against
|
||||
* @param whenProperties {Object} properties to define when's matching behavior
|
||||
* @return {?Object}
|
||||
*/
|
||||
function switchRouteMatcher(on, when) {
|
||||
function switchRouteMatcher(on, when, whenProperties) {
|
||||
// TODO(i): this code is convoluted and inefficient, we should construct the route matching
|
||||
// regex only once and then reuse it
|
||||
|
||||
// Escape regexp special characters.
|
||||
when = '^' + when.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&") + '$';
|
||||
when = '^' + when.replace(/[-\/\\^$:*+?.()|[\]{}]/g, "\\$&") + '$';
|
||||
|
||||
var regex = '',
|
||||
params = [],
|
||||
dst = {};
|
||||
|
||||
var re = /:(\w+)/g,
|
||||
var re = /\\([:*])(\w+)/g,
|
||||
paramMatch,
|
||||
lastMatchedIndex = 0;
|
||||
|
||||
@@ -354,14 +371,21 @@ function $RouteProvider(){
|
||||
// Find each :param in `when` and replace it with a capturing group.
|
||||
// Append all other sections of when unchanged.
|
||||
regex += when.slice(lastMatchedIndex, paramMatch.index);
|
||||
regex += '([^\\/]*)';
|
||||
params.push(paramMatch[1]);
|
||||
switch(paramMatch[1]) {
|
||||
case ':':
|
||||
regex += '([^\\/]*)';
|
||||
break;
|
||||
case '*':
|
||||
regex += '(.*)';
|
||||
break;
|
||||
}
|
||||
params.push(paramMatch[2]);
|
||||
lastMatchedIndex = re.lastIndex;
|
||||
}
|
||||
// Append trailing path part.
|
||||
regex += when.substr(lastMatchedIndex);
|
||||
|
||||
var match = on.match(new RegExp(regex));
|
||||
var match = on.match(new RegExp(regex, whenProperties.caseInsensitiveMatch ? 'i' : ''));
|
||||
if (match) {
|
||||
forEach(params, function(name, index) {
|
||||
dst[name] = match[index + 1];
|
||||
@@ -374,7 +398,7 @@ function $RouteProvider(){
|
||||
var next = parseRoute(),
|
||||
last = $route.current;
|
||||
|
||||
if (next && last && next.$route === last.$route
|
||||
if (next && last && next.$$route === last.$$route
|
||||
&& equals(next.pathParams, last.pathParams) && !next.reloadOnSearch && !forceReload) {
|
||||
last.params = next.params;
|
||||
copy(last.params, $routeParams);
|
||||
@@ -398,14 +422,13 @@ function $RouteProvider(){
|
||||
$q.when(next).
|
||||
then(function() {
|
||||
if (next) {
|
||||
var keys = [],
|
||||
values = [],
|
||||
var locals = extend({}, next.resolve),
|
||||
template;
|
||||
|
||||
forEach(next.resolve || {}, function(value, key) {
|
||||
keys.push(key);
|
||||
values.push(isString(value) ? $injector.get(value) : $injector.invoke(value));
|
||||
forEach(locals, function(value, key) {
|
||||
locals[key] = isString(value) ? $injector.get(value) : $injector.invoke(value);
|
||||
});
|
||||
|
||||
if (isDefined(template = next.template)) {
|
||||
if (isFunction(template)) {
|
||||
template = template(next.params);
|
||||
@@ -421,16 +444,9 @@ function $RouteProvider(){
|
||||
}
|
||||
}
|
||||
if (isDefined(template)) {
|
||||
keys.push('$template');
|
||||
values.push(template);
|
||||
locals['$template'] = template;
|
||||
}
|
||||
return $q.all(values).then(function(values) {
|
||||
var locals = {};
|
||||
forEach(values, function(value, index) {
|
||||
locals[keys[index]] = value;
|
||||
});
|
||||
return locals;
|
||||
});
|
||||
return $q.all(locals);
|
||||
}
|
||||
}).
|
||||
// after route change
|
||||
@@ -458,11 +474,11 @@ function $RouteProvider(){
|
||||
// Match a route
|
||||
var params, match;
|
||||
forEach(routes, function(route, path) {
|
||||
if (!match && (params = switchRouteMatcher($location.path(), path))) {
|
||||
if (!match && (params = switchRouteMatcher($location.path(), path, route))) {
|
||||
match = inherit(route, {
|
||||
params: extend({}, $location.search(), params),
|
||||
pathParams: params});
|
||||
match.$route = route;
|
||||
match.$$route = route;
|
||||
}
|
||||
});
|
||||
// No route matched; fallback to "otherwise" route
|
||||
@@ -470,7 +486,7 @@ function $RouteProvider(){
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns interpolation of the redirect path with the parametrs
|
||||
* @returns interpolation of the redirect path with the parameters
|
||||
*/
|
||||
function interpolate(string, params) {
|
||||
var result = [];
|
||||
|
||||
+23
-3
@@ -9,6 +9,7 @@
|
||||
*
|
||||
* @property {boolean} history Does the browser support html5 history api ?
|
||||
* @property {boolean} hashchange Does the browser support hashchange event ?
|
||||
* @property {boolean} supportsTransitions Does the browser support CSS transition events ?
|
||||
*
|
||||
* @description
|
||||
* This is very simple implementation of testing browser's features.
|
||||
@@ -16,8 +17,25 @@
|
||||
function $SnifferProvider() {
|
||||
this.$get = ['$window', '$document', function($window, $document) {
|
||||
var eventSupport = {},
|
||||
android = int((/android (\d+)/.exec(lowercase($window.navigator.userAgent)) || [])[1]),
|
||||
document = $document[0];
|
||||
android = int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
|
||||
document = $document[0] || {},
|
||||
vendorPrefix,
|
||||
vendorRegex = /^(Moz|webkit|O|ms)(?=[A-Z])/,
|
||||
bodyStyle = document.body && document.body.style,
|
||||
transitions = false,
|
||||
match;
|
||||
|
||||
if (bodyStyle) {
|
||||
for(var prop in bodyStyle) {
|
||||
if(match = vendorRegex.exec(prop)) {
|
||||
vendorPrefix = match[0];
|
||||
vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
transitions = !!(vendorPrefix + 'Transition' in bodyStyle);
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
// Android has history.pushState, but it does not update location correctly
|
||||
@@ -41,7 +59,9 @@ function $SnifferProvider() {
|
||||
|
||||
return eventSupport[event];
|
||||
},
|
||||
csp: document.securityPolicy ? document.securityPolicy.isActive : false
|
||||
csp: document.securityPolicy ? document.securityPolicy.isActive : false,
|
||||
vendorPrefix: vendorPrefix,
|
||||
supportsTransitions : transitions
|
||||
};
|
||||
}];
|
||||
}
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
* A reference to the browser's `window` object. While `window`
|
||||
* is globally available in JavaScript, it causes testability problems, because
|
||||
* it is a global variable. In angular we always refer to it through the
|
||||
* `$window` service, so it may be overriden, removed or mocked for testing.
|
||||
* `$window` service, so it may be overridden, removed or mocked for testing.
|
||||
*
|
||||
* All expressions are evaluated with respect to current scope so they don't
|
||||
* suffer from window globality.
|
||||
|
||||
@@ -19,6 +19,18 @@ angular.module('ngCookies', ['ng']).
|
||||
* this object, new cookies are created/deleted at the end of current $eval.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
function ExampleController($cookies) {
|
||||
// Retrieving a cookie
|
||||
var favoriteCookie = $cookies.myFavorite;
|
||||
// Setting a cookie
|
||||
$cookies.myFavorite = 'oatmeal';
|
||||
}
|
||||
</script>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
factory('$cookies', ['$rootScope', '$browser', function ($rootScope, $browser) {
|
||||
var cookies = {},
|
||||
|
||||
Vendored
+98
@@ -0,0 +1,98 @@
|
||||
angular.module("ngLocale", [], ["$provide", function($provide) {
|
||||
var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
|
||||
$provide.value("$locale", {
|
||||
"DATETIME_FORMATS": {
|
||||
"AMPMS": {
|
||||
"0": "vm.",
|
||||
"1": "nm."
|
||||
},
|
||||
"DAY": {
|
||||
"0": "Sondag",
|
||||
"1": "Maandag",
|
||||
"2": "Dinsdag",
|
||||
"3": "Woensdag",
|
||||
"4": "Donderdag",
|
||||
"5": "Vrydag",
|
||||
"6": "Saterdag"
|
||||
},
|
||||
"MONTH": {
|
||||
"0": "Januarie",
|
||||
"1": "Februarie",
|
||||
"2": "Maart",
|
||||
"3": "April",
|
||||
"4": "Mei",
|
||||
"5": "Junie",
|
||||
"6": "Julie",
|
||||
"7": "Augustus",
|
||||
"8": "September",
|
||||
"9": "Oktober",
|
||||
"10": "November",
|
||||
"11": "Desember"
|
||||
},
|
||||
"SHORTDAY": {
|
||||
"0": "So",
|
||||
"1": "Ma",
|
||||
"2": "Di",
|
||||
"3": "Wo",
|
||||
"4": "Do",
|
||||
"5": "Vr",
|
||||
"6": "Sa"
|
||||
},
|
||||
"SHORTMONTH": {
|
||||
"0": "Jan",
|
||||
"1": "Feb",
|
||||
"2": "Mar",
|
||||
"3": "Apr",
|
||||
"4": "Mei",
|
||||
"5": "Jun",
|
||||
"6": "Jul",
|
||||
"7": "Aug",
|
||||
"8": "Sep",
|
||||
"9": "Okt",
|
||||
"10": "Nov",
|
||||
"11": "Des"
|
||||
},
|
||||
"fullDate": "EEEE d MMMM y",
|
||||
"longDate": "d MMMM y",
|
||||
"medium": "d MMM y HH:mm:ss",
|
||||
"mediumDate": "d MMM y",
|
||||
"mediumTime": "HH:mm:ss",
|
||||
"short": "yyyy-MM-dd HH:mm",
|
||||
"shortDate": "yyyy-MM-dd",
|
||||
"shortTime": "HH:mm"
|
||||
},
|
||||
"NUMBER_FORMATS": {
|
||||
"CURRENCY_SYM": "R",
|
||||
"DECIMAL_SEP": ",",
|
||||
"GROUP_SEP": " ",
|
||||
"PATTERNS": {
|
||||
"0": {
|
||||
"gSize": 3,
|
||||
"lgSize": 3,
|
||||
"macFrac": 0,
|
||||
"maxFrac": 3,
|
||||
"minFrac": 0,
|
||||
"minInt": 1,
|
||||
"negPre": "-",
|
||||
"negSuf": "",
|
||||
"posPre": "",
|
||||
"posSuf": ""
|
||||
},
|
||||
"1": {
|
||||
"gSize": 3,
|
||||
"lgSize": 3,
|
||||
"macFrac": 0,
|
||||
"maxFrac": 2,
|
||||
"minFrac": 2,
|
||||
"minInt": 1,
|
||||
"negPre": "(\u00A4",
|
||||
"negSuf": ")",
|
||||
"posPre": "\u00A4",
|
||||
"posSuf": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "af-na",
|
||||
"pluralCat": function (n) { if (n == 1) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
Vendored
+98
@@ -0,0 +1,98 @@
|
||||
angular.module("ngLocale", [], ["$provide", function($provide) {
|
||||
var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
|
||||
$provide.value("$locale", {
|
||||
"DATETIME_FORMATS": {
|
||||
"AMPMS": {
|
||||
"0": "vm.",
|
||||
"1": "nm."
|
||||
},
|
||||
"DAY": {
|
||||
"0": "Sondag",
|
||||
"1": "Maandag",
|
||||
"2": "Dinsdag",
|
||||
"3": "Woensdag",
|
||||
"4": "Donderdag",
|
||||
"5": "Vrydag",
|
||||
"6": "Saterdag"
|
||||
},
|
||||
"MONTH": {
|
||||
"0": "Januarie",
|
||||
"1": "Februarie",
|
||||
"2": "Maart",
|
||||
"3": "April",
|
||||
"4": "Mei",
|
||||
"5": "Junie",
|
||||
"6": "Julie",
|
||||
"7": "Augustus",
|
||||
"8": "September",
|
||||
"9": "Oktober",
|
||||
"10": "November",
|
||||
"11": "Desember"
|
||||
},
|
||||
"SHORTDAY": {
|
||||
"0": "So",
|
||||
"1": "Ma",
|
||||
"2": "Di",
|
||||
"3": "Wo",
|
||||
"4": "Do",
|
||||
"5": "Vr",
|
||||
"6": "Sa"
|
||||
},
|
||||
"SHORTMONTH": {
|
||||
"0": "Jan",
|
||||
"1": "Feb",
|
||||
"2": "Mar",
|
||||
"3": "Apr",
|
||||
"4": "Mei",
|
||||
"5": "Jun",
|
||||
"6": "Jul",
|
||||
"7": "Aug",
|
||||
"8": "Sep",
|
||||
"9": "Okt",
|
||||
"10": "Nov",
|
||||
"11": "Des"
|
||||
},
|
||||
"fullDate": "EEEE dd MMMM y",
|
||||
"longDate": "dd MMMM y",
|
||||
"medium": "dd MMM y h:mm:ss a",
|
||||
"mediumDate": "dd MMM y",
|
||||
"mediumTime": "h:mm:ss a",
|
||||
"short": "yyyy-MM-dd h:mm a",
|
||||
"shortDate": "yyyy-MM-dd",
|
||||
"shortTime": "h:mm a"
|
||||
},
|
||||
"NUMBER_FORMATS": {
|
||||
"CURRENCY_SYM": "R",
|
||||
"DECIMAL_SEP": ",",
|
||||
"GROUP_SEP": " ",
|
||||
"PATTERNS": {
|
||||
"0": {
|
||||
"gSize": 3,
|
||||
"lgSize": 3,
|
||||
"macFrac": 0,
|
||||
"maxFrac": 3,
|
||||
"minFrac": 0,
|
||||
"minInt": 1,
|
||||
"negPre": "-",
|
||||
"negSuf": "",
|
||||
"posPre": "",
|
||||
"posSuf": ""
|
||||
},
|
||||
"1": {
|
||||
"gSize": 3,
|
||||
"lgSize": 3,
|
||||
"macFrac": 0,
|
||||
"maxFrac": 2,
|
||||
"minFrac": 2,
|
||||
"minInt": 1,
|
||||
"negPre": "(\u00A4",
|
||||
"negSuf": ")",
|
||||
"posPre": "\u00A4",
|
||||
"posSuf": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "af-za",
|
||||
"pluralCat": function (n) { if (n == 1) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
Vendored
+98
@@ -0,0 +1,98 @@
|
||||
angular.module("ngLocale", [], ["$provide", function($provide) {
|
||||
var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
|
||||
$provide.value("$locale", {
|
||||
"DATETIME_FORMATS": {
|
||||
"AMPMS": {
|
||||
"0": "vm.",
|
||||
"1": "nm."
|
||||
},
|
||||
"DAY": {
|
||||
"0": "Sondag",
|
||||
"1": "Maandag",
|
||||
"2": "Dinsdag",
|
||||
"3": "Woensdag",
|
||||
"4": "Donderdag",
|
||||
"5": "Vrydag",
|
||||
"6": "Saterdag"
|
||||
},
|
||||
"MONTH": {
|
||||
"0": "Januarie",
|
||||
"1": "Februarie",
|
||||
"2": "Maart",
|
||||
"3": "April",
|
||||
"4": "Mei",
|
||||
"5": "Junie",
|
||||
"6": "Julie",
|
||||
"7": "Augustus",
|
||||
"8": "September",
|
||||
"9": "Oktober",
|
||||
"10": "November",
|
||||
"11": "Desember"
|
||||
},
|
||||
"SHORTDAY": {
|
||||
"0": "So",
|
||||
"1": "Ma",
|
||||
"2": "Di",
|
||||
"3": "Wo",
|
||||
"4": "Do",
|
||||
"5": "Vr",
|
||||
"6": "Sa"
|
||||
},
|
||||
"SHORTMONTH": {
|
||||
"0": "Jan",
|
||||
"1": "Feb",
|
||||
"2": "Mar",
|
||||
"3": "Apr",
|
||||
"4": "Mei",
|
||||
"5": "Jun",
|
||||
"6": "Jul",
|
||||
"7": "Aug",
|
||||
"8": "Sep",
|
||||
"9": "Okt",
|
||||
"10": "Nov",
|
||||
"11": "Des"
|
||||
},
|
||||
"fullDate": "EEEE dd MMMM y",
|
||||
"longDate": "dd MMMM y",
|
||||
"medium": "dd MMM y h:mm:ss a",
|
||||
"mediumDate": "dd MMM y",
|
||||
"mediumTime": "h:mm:ss a",
|
||||
"short": "yyyy-MM-dd h:mm a",
|
||||
"shortDate": "yyyy-MM-dd",
|
||||
"shortTime": "h:mm a"
|
||||
},
|
||||
"NUMBER_FORMATS": {
|
||||
"CURRENCY_SYM": "R",
|
||||
"DECIMAL_SEP": ",",
|
||||
"GROUP_SEP": " ",
|
||||
"PATTERNS": {
|
||||
"0": {
|
||||
"gSize": 3,
|
||||
"lgSize": 3,
|
||||
"macFrac": 0,
|
||||
"maxFrac": 3,
|
||||
"minFrac": 0,
|
||||
"minInt": 1,
|
||||
"negPre": "-",
|
||||
"negSuf": "",
|
||||
"posPre": "",
|
||||
"posSuf": ""
|
||||
},
|
||||
"1": {
|
||||
"gSize": 3,
|
||||
"lgSize": 3,
|
||||
"macFrac": 0,
|
||||
"maxFrac": 2,
|
||||
"minFrac": 2,
|
||||
"minInt": 1,
|
||||
"negPre": "(\u00A4",
|
||||
"negSuf": ")",
|
||||
"posPre": "\u00A4",
|
||||
"posSuf": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "af",
|
||||
"pluralCat": function (n) { if (n == 1) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
Vendored
+95
-1
@@ -1,4 +1,98 @@
|
||||
angular.module("ngLocale", [], ["$provide", function($provide) {
|
||||
var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
|
||||
$provide.value("$locale", {"NUMBER_FORMATS":{"DECIMAL_SEP":".","GROUP_SEP":",","PATTERNS":[{"minInt":1,"minFrac":0,"macFrac":0,"posPre":"","posSuf":"","negPre":"-","negSuf":"","gSize":3,"lgSize":3,"maxFrac":3},{"minInt":1,"minFrac":2,"macFrac":0,"posPre":"\u00A4","posSuf":"","negPre":"(\u00A4","negSuf":")","gSize":3,"lgSize":3,"maxFrac":2}],"CURRENCY_SYM":"Br"},"pluralCat":function (n) { if (n == 0 || n == 1) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;},"DATETIME_FORMATS":{"MONTH":["ጃንዩወሪ","ፌብሩወሪ","ማርች","ኤፕረል","ሜይ","ጁን","ጁላይ","ኦገስት","ሴፕቴምበር","ኦክተውበር","ኖቬምበር","ዲሴምበር"],"SHORTMONTH":["ጃንዩ","ፌብሩ","ማርች","ኤፕረ","ሜይ","ጁን","ጁላይ","ኦገስ","ሴፕቴ","ኦክተ","ኖቬም","ዲሴም"],"DAY":["እሑድ","ሰኞ","ማክሰኞ","ረቡዕ","ሐሙስ","ዓርብ","ቅዳሜ"],"SHORTDAY":["እሑድ","ሰኞ","ማክሰ","ረቡዕ","ሐሙስ","ዓርብ","ቅዳሜ"],"AMPMS":["ጡዋት","ከሳዓት"],"medium":"d MMM y h:mm:ss a","short":"dd/MM/yyyy h:mm a","fullDate":"EEEE, d MMMM y","longDate":"d MMMM y","mediumDate":"d MMM y","shortDate":"dd/MM/yyyy","mediumTime":"h:mm:ss a","shortTime":"h:mm a"},"id":"am-et"});
|
||||
$provide.value("$locale", {
|
||||
"DATETIME_FORMATS": {
|
||||
"AMPMS": {
|
||||
"0": "ጡዋት",
|
||||
"1": "ከሳዓት"
|
||||
},
|
||||
"DAY": {
|
||||
"0": "እሑድ",
|
||||
"1": "ሰኞ",
|
||||
"2": "ማክሰኞ",
|
||||
"3": "ረቡዕ",
|
||||
"4": "ሐሙስ",
|
||||
"5": "ዓርብ",
|
||||
"6": "ቅዳሜ"
|
||||
},
|
||||
"MONTH": {
|
||||
"0": "ጃንዩወሪ",
|
||||
"1": "ፌብሩወሪ",
|
||||
"2": "ማርች",
|
||||
"3": "ኤፕረል",
|
||||
"4": "ሜይ",
|
||||
"5": "ጁን",
|
||||
"6": "ጁላይ",
|
||||
"7": "ኦገስት",
|
||||
"8": "ሴፕቴምበር",
|
||||
"9": "ኦክተውበር",
|
||||
"10": "ኖቬምበር",
|
||||
"11": "ዲሴምበር"
|
||||
},
|
||||
"SHORTDAY": {
|
||||
"0": "እሑድ",
|
||||
"1": "ሰኞ",
|
||||
"2": "ማክሰ",
|
||||
"3": "ረቡዕ",
|
||||
"4": "ሐሙስ",
|
||||
"5": "ዓርብ",
|
||||
"6": "ቅዳሜ"
|
||||
},
|
||||
"SHORTMONTH": {
|
||||
"0": "ጃንዩ",
|
||||
"1": "ፌብሩ",
|
||||
"2": "ማርች",
|
||||
"3": "ኤፕረ",
|
||||
"4": "ሜይ",
|
||||
"5": "ጁን",
|
||||
"6": "ጁላይ",
|
||||
"7": "ኦገስ",
|
||||
"8": "ሴፕቴ",
|
||||
"9": "ኦክተ",
|
||||
"10": "ኖቬም",
|
||||
"11": "ዲሴም"
|
||||
},
|
||||
"fullDate": "EEEE, d MMMM y",
|
||||
"longDate": "d MMMM y",
|
||||
"medium": "d MMM y h:mm:ss a",
|
||||
"mediumDate": "d MMM y",
|
||||
"mediumTime": "h:mm:ss a",
|
||||
"short": "dd/MM/yyyy h:mm a",
|
||||
"shortDate": "dd/MM/yyyy",
|
||||
"shortTime": "h:mm a"
|
||||
},
|
||||
"NUMBER_FORMATS": {
|
||||
"CURRENCY_SYM": "Birr",
|
||||
"DECIMAL_SEP": ".",
|
||||
"GROUP_SEP": ",",
|
||||
"PATTERNS": {
|
||||
"0": {
|
||||
"gSize": 3,
|
||||
"lgSize": 3,
|
||||
"macFrac": 0,
|
||||
"maxFrac": 3,
|
||||
"minFrac": 0,
|
||||
"minInt": 1,
|
||||
"negPre": "-",
|
||||
"negSuf": "",
|
||||
"posPre": "",
|
||||
"posSuf": ""
|
||||
},
|
||||
"1": {
|
||||
"gSize": 3,
|
||||
"lgSize": 3,
|
||||
"macFrac": 0,
|
||||
"maxFrac": 2,
|
||||
"minFrac": 2,
|
||||
"minInt": 1,
|
||||
"negPre": "(\u00A4",
|
||||
"negSuf": ")",
|
||||
"posPre": "\u00A4",
|
||||
"posSuf": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "am-et",
|
||||
"pluralCat": function (n) { if (n == 0 || n == 1) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
Vendored
+95
-1
@@ -1,4 +1,98 @@
|
||||
angular.module("ngLocale", [], ["$provide", function($provide) {
|
||||
var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
|
||||
$provide.value("$locale", {"DATETIME_FORMATS":{"MONTH":["ጃንዩወሪ","ፌብሩወሪ","ማርች","ኤፕረል","ሜይ","ጁን","ጁላይ","ኦገስት","ሴፕቴምበር","ኦክተውበር","ኖቬምበር","ዲሴምበር"],"SHORTMONTH":["ጃንዩ","ፌብሩ","ማርች","ኤፕረ","ሜይ","ጁን","ጁላይ","ኦገስ","ሴፕቴ","ኦክተ","ኖቬም","ዲሴም"],"DAY":["እሑድ","ሰኞ","ማክሰኞ","ረቡዕ","ሐሙስ","ዓርብ","ቅዳሜ"],"SHORTDAY":["እሑድ","ሰኞ","ማክሰ","ረቡዕ","ሐሙስ","ዓርብ","ቅዳሜ"],"AMPMS":["ጡዋት","ከሳዓት"],"medium":"d MMM y h:mm:ss a","short":"dd/MM/yyyy h:mm a","fullDate":"EEEE, d MMMM y","longDate":"d MMMM y","mediumDate":"d MMM y","shortDate":"dd/MM/yyyy","mediumTime":"h:mm:ss a","shortTime":"h:mm a"},"NUMBER_FORMATS":{"DECIMAL_SEP":".","GROUP_SEP":",","PATTERNS":[{"minInt":1,"minFrac":0,"macFrac":0,"posPre":"","posSuf":"","negPre":"-","negSuf":"","gSize":3,"lgSize":3,"maxFrac":3},{"minInt":1,"minFrac":2,"macFrac":0,"posPre":"\u00A4","posSuf":"","negPre":"(\u00A4","negSuf":")","gSize":3,"lgSize":3,"maxFrac":2}],"CURRENCY_SYM":"Br"},"pluralCat":function (n) { if (n == 0 || n == 1) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;},"id":"am"});
|
||||
$provide.value("$locale", {
|
||||
"DATETIME_FORMATS": {
|
||||
"AMPMS": {
|
||||
"0": "ጡዋት",
|
||||
"1": "ከሳዓት"
|
||||
},
|
||||
"DAY": {
|
||||
"0": "እሑድ",
|
||||
"1": "ሰኞ",
|
||||
"2": "ማክሰኞ",
|
||||
"3": "ረቡዕ",
|
||||
"4": "ሐሙስ",
|
||||
"5": "ዓርብ",
|
||||
"6": "ቅዳሜ"
|
||||
},
|
||||
"MONTH": {
|
||||
"0": "ጃንዩወሪ",
|
||||
"1": "ፌብሩወሪ",
|
||||
"2": "ማርች",
|
||||
"3": "ኤፕረል",
|
||||
"4": "ሜይ",
|
||||
"5": "ጁን",
|
||||
"6": "ጁላይ",
|
||||
"7": "ኦገስት",
|
||||
"8": "ሴፕቴምበር",
|
||||
"9": "ኦክተውበር",
|
||||
"10": "ኖቬምበር",
|
||||
"11": "ዲሴምበር"
|
||||
},
|
||||
"SHORTDAY": {
|
||||
"0": "እሑድ",
|
||||
"1": "ሰኞ",
|
||||
"2": "ማክሰ",
|
||||
"3": "ረቡዕ",
|
||||
"4": "ሐሙስ",
|
||||
"5": "ዓርብ",
|
||||
"6": "ቅዳሜ"
|
||||
},
|
||||
"SHORTMONTH": {
|
||||
"0": "ጃንዩ",
|
||||
"1": "ፌብሩ",
|
||||
"2": "ማርች",
|
||||
"3": "ኤፕረ",
|
||||
"4": "ሜይ",
|
||||
"5": "ጁን",
|
||||
"6": "ጁላይ",
|
||||
"7": "ኦገስ",
|
||||
"8": "ሴፕቴ",
|
||||
"9": "ኦክተ",
|
||||
"10": "ኖቬም",
|
||||
"11": "ዲሴም"
|
||||
},
|
||||
"fullDate": "EEEE, d MMMM y",
|
||||
"longDate": "d MMMM y",
|
||||
"medium": "d MMM y h:mm:ss a",
|
||||
"mediumDate": "d MMM y",
|
||||
"mediumTime": "h:mm:ss a",
|
||||
"short": "dd/MM/yyyy h:mm a",
|
||||
"shortDate": "dd/MM/yyyy",
|
||||
"shortTime": "h:mm a"
|
||||
},
|
||||
"NUMBER_FORMATS": {
|
||||
"CURRENCY_SYM": "Birr",
|
||||
"DECIMAL_SEP": ".",
|
||||
"GROUP_SEP": ",",
|
||||
"PATTERNS": {
|
||||
"0": {
|
||||
"gSize": 3,
|
||||
"lgSize": 3,
|
||||
"macFrac": 0,
|
||||
"maxFrac": 3,
|
||||
"minFrac": 0,
|
||||
"minInt": 1,
|
||||
"negPre": "-",
|
||||
"negSuf": "",
|
||||
"posPre": "",
|
||||
"posSuf": ""
|
||||
},
|
||||
"1": {
|
||||
"gSize": 3,
|
||||
"lgSize": 3,
|
||||
"macFrac": 0,
|
||||
"maxFrac": 2,
|
||||
"minFrac": 2,
|
||||
"minInt": 1,
|
||||
"negPre": "(\u00A4",
|
||||
"negSuf": ")",
|
||||
"posPre": "\u00A4",
|
||||
"posSuf": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "am",
|
||||
"pluralCat": function (n) { if (n == 0 || n == 1) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
angular.module("ngLocale", [], ["$provide", function($provide) {
|
||||
var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
|
||||
$provide.value("$locale", {
|
||||
"DATETIME_FORMATS": {
|
||||
"AMPMS": {
|
||||
"0": "ص",
|
||||
"1": "م"
|
||||
},
|
||||
"DAY": {
|
||||
"0": "الأحد",
|
||||
"1": "الاثنين",
|
||||
"2": "الثلاثاء",
|
||||
"3": "الأربعاء",
|
||||
"4": "الخميس",
|
||||
"5": "الجمعة",
|
||||
"6": "السبت"
|
||||
},
|
||||
"MONTH": {
|
||||
"0": "يناير",
|
||||
"1": "فبراير",
|
||||
"2": "مارس",
|
||||
"3": "أبريل",
|
||||
"4": "مايو",
|
||||
"5": "يونيو",
|
||||
"6": "يوليو",
|
||||
"7": "أغسطس",
|
||||
"8": "سبتمبر",
|
||||
"9": "أكتوبر",
|
||||
"10": "نوفمبر",
|
||||
"11": "ديسمبر"
|
||||
},
|
||||
"SHORTDAY": {
|
||||
"0": "الأحد",
|
||||
"1": "الاثنين",
|
||||
"2": "الثلاثاء",
|
||||
"3": "الأربعاء",
|
||||
"4": "الخميس",
|
||||
"5": "الجمعة",
|
||||
"6": "السبت"
|
||||
},
|
||||
"SHORTMONTH": {
|
||||
"0": "يناير",
|
||||
"1": "فبراير",
|
||||
"2": "مارس",
|
||||
"3": "أبريل",
|
||||
"4": "مايو",
|
||||
"5": "يونيو",
|
||||
"6": "يوليو",
|
||||
"7": "أغسطس",
|
||||
"8": "سبتمبر",
|
||||
"9": "أكتوبر",
|
||||
"10": "نوفمبر",
|
||||
"11": "ديسمبر"
|
||||
},
|
||||
"fullDate": "EEEE، d MMMM، y",
|
||||
"longDate": "d MMMM، y",
|
||||
"medium": "dd/MM/yyyy h:mm:ss a",
|
||||
"mediumDate": "dd/MM/yyyy",
|
||||
"mediumTime": "h:mm:ss a",
|
||||
"short": "d/M/yyyy h:mm a",
|
||||
"shortDate": "d/M/yyyy",
|
||||
"shortTime": "h:mm a"
|
||||
},
|
||||
"NUMBER_FORMATS": {
|
||||
"CURRENCY_SYM": "£",
|
||||
"DECIMAL_SEP": "٫",
|
||||
"GROUP_SEP": "٬",
|
||||
"PATTERNS": {
|
||||
"0": {
|
||||
"gSize": 0,
|
||||
"lgSize": 0,
|
||||
"macFrac": 0,
|
||||
"maxFrac": 3,
|
||||
"minFrac": 0,
|
||||
"minInt": 1,
|
||||
"negPre": "",
|
||||
"negSuf": "-",
|
||||
"posPre": "",
|
||||
"posSuf": ""
|
||||
},
|
||||
"1": {
|
||||
"gSize": 0,
|
||||
"lgSize": 0,
|
||||
"macFrac": 0,
|
||||
"maxFrac": 2,
|
||||
"minFrac": 2,
|
||||
"minInt": 1,
|
||||
"negPre": "\u00A4 ",
|
||||
"negSuf": "-",
|
||||
"posPre": "\u00A4 ",
|
||||
"posSuf": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "ar-001",
|
||||
"pluralCat": function (n) { if (n == 0) { return PLURAL_CATEGORY.ZERO; } if (n == 1) { return PLURAL_CATEGORY.ONE; } if (n == 2) { return PLURAL_CATEGORY.TWO; } if (n == (n | 0) && n % 100 >= 3 && n % 100 <= 10) { return PLURAL_CATEGORY.FEW; } if (n == (n | 0) && n % 100 >= 11 && n % 100 <= 99) { return PLURAL_CATEGORY.MANY; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
Vendored
+98
@@ -0,0 +1,98 @@
|
||||
angular.module("ngLocale", [], ["$provide", function($provide) {
|
||||
var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
|
||||
$provide.value("$locale", {
|
||||
"DATETIME_FORMATS": {
|
||||
"AMPMS": {
|
||||
"0": "ص",
|
||||
"1": "م"
|
||||
},
|
||||
"DAY": {
|
||||
"0": "الأحد",
|
||||
"1": "الاثنين",
|
||||
"2": "الثلاثاء",
|
||||
"3": "الأربعاء",
|
||||
"4": "الخميس",
|
||||
"5": "الجمعة",
|
||||
"6": "السبت"
|
||||
},
|
||||
"MONTH": {
|
||||
"0": "يناير",
|
||||
"1": "فبراير",
|
||||
"2": "مارس",
|
||||
"3": "أبريل",
|
||||
"4": "مايو",
|
||||
"5": "يونيو",
|
||||
"6": "يوليو",
|
||||
"7": "أغسطس",
|
||||
"8": "سبتمبر",
|
||||
"9": "أكتوبر",
|
||||
"10": "نوفمبر",
|
||||
"11": "ديسمبر"
|
||||
},
|
||||
"SHORTDAY": {
|
||||
"0": "الأحد",
|
||||
"1": "الاثنين",
|
||||
"2": "الثلاثاء",
|
||||
"3": "الأربعاء",
|
||||
"4": "الخميس",
|
||||
"5": "الجمعة",
|
||||
"6": "السبت"
|
||||
},
|
||||
"SHORTMONTH": {
|
||||
"0": "يناير",
|
||||
"1": "فبراير",
|
||||
"2": "مارس",
|
||||
"3": "أبريل",
|
||||
"4": "مايو",
|
||||
"5": "يونيو",
|
||||
"6": "يوليو",
|
||||
"7": "أغسطس",
|
||||
"8": "سبتمبر",
|
||||
"9": "أكتوبر",
|
||||
"10": "نوفمبر",
|
||||
"11": "ديسمبر"
|
||||
},
|
||||
"fullDate": "EEEE، d MMMM، y",
|
||||
"longDate": "d MMMM، y",
|
||||
"medium": "dd/MM/yyyy h:mm:ss a",
|
||||
"mediumDate": "dd/MM/yyyy",
|
||||
"mediumTime": "h:mm:ss a",
|
||||
"short": "d/M/yyyy h:mm a",
|
||||
"shortDate": "d/M/yyyy",
|
||||
"shortTime": "h:mm a"
|
||||
},
|
||||
"NUMBER_FORMATS": {
|
||||
"CURRENCY_SYM": "£",
|
||||
"DECIMAL_SEP": "٫",
|
||||
"GROUP_SEP": "٬",
|
||||
"PATTERNS": {
|
||||
"0": {
|
||||
"gSize": 0,
|
||||
"lgSize": 0,
|
||||
"macFrac": 0,
|
||||
"maxFrac": 3,
|
||||
"minFrac": 0,
|
||||
"minInt": 1,
|
||||
"negPre": "",
|
||||
"negSuf": "-",
|
||||
"posPre": "",
|
||||
"posSuf": ""
|
||||
},
|
||||
"1": {
|
||||
"gSize": 0,
|
||||
"lgSize": 0,
|
||||
"macFrac": 0,
|
||||
"maxFrac": 2,
|
||||
"minFrac": 2,
|
||||
"minInt": 1,
|
||||
"negPre": "\u00A4 ",
|
||||
"negSuf": "-",
|
||||
"posPre": "\u00A4 ",
|
||||
"posSuf": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "ar-ae",
|
||||
"pluralCat": function (n) { if (n == 0) { return PLURAL_CATEGORY.ZERO; } if (n == 1) { return PLURAL_CATEGORY.ONE; } if (n == 2) { return PLURAL_CATEGORY.TWO; } if (n == (n | 0) && n % 100 >= 3 && n % 100 <= 10) { return PLURAL_CATEGORY.FEW; } if (n == (n | 0) && n % 100 >= 11 && n % 100 <= 99) { return PLURAL_CATEGORY.MANY; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user