Compare commits
393 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 56817e9faa | |||
| e87fb8a8e1 | |||
| 5be0fc40ed | |||
| a98337b359 | |||
| 143d016899 | |||
| 3d70e55d72 | |||
| dbcc44dc80 | |||
| db87fd52ca | |||
| 792509e987 | |||
| 166e0d63d0 | |||
| 0d7f19bb62 | |||
| 607045d592 | |||
| 51d32243fe | |||
| 1c1a1bc9ed | |||
| 553fdb318f | |||
| 607ed4ee46 | |||
| ec1cece270 | |||
| 4656e386fb | |||
| f29f2f99b1 | |||
| cc27f08588 | |||
| 99fe398b59 | |||
| cb89e02432 | |||
| ac69392cd7 | |||
| a5fb372e1e | |||
| da720712f3 | |||
| 0cb3dc8782 | |||
| dfd95f0115 | |||
| 7636670a77 | |||
| fe6247a7f8 | |||
| 2b90ef1694 | |||
| 099138fb9a | |||
| cbe31d8dfd | |||
| 06b0930b6a | |||
| 7b7be341b6 | |||
| 751c77f87b | |||
| 634ac03c5e | |||
| 8cab53c64d | |||
| edef295b11 | |||
| 64e447354e | |||
| da1f7c762d | |||
| 89366bdbf9 | |||
| 78efa0e36c | |||
| 3a8b3db174 | |||
| 1a01e80b9c | |||
| d59027c40e | |||
| c197c2aa27 | |||
| 01cd34957e | |||
| 864517e5a2 | |||
| 1dd5d2ec1f | |||
| 6da835f4bc | |||
| 5cca077e4a | |||
| e290aa8c13 | |||
| 9c51d50318 | |||
| 1c3a46adda | |||
| 4407e81c61 | |||
| ad76e77fce | |||
| ac5b9055f6 | |||
| c18074a310 | |||
| ed703d8e2c | |||
| 9c53d0769e | |||
| 90532f5e3c | |||
| 2bc04d23fb | |||
| 7f6da764e1 | |||
| 6926ef8f67 | |||
| bba2b7cfce | |||
| dc1e55ce1a | |||
| 408e868237 | |||
| 97abb12473 | |||
| d26bffbc3f | |||
| 2f3bd9dae7 | |||
| 256e5dff55 | |||
| acb6b75fe9 | |||
| 683fd713c4 | |||
| 3591ae0103 | |||
| 5fedfd79a5 | |||
| bdde40e755 | |||
| 88c4963328 | |||
| 67a81eff42 | |||
| add43e91dc | |||
| b3c7a6d566 | |||
| 424bd49ede | |||
| 6a58404507 | |||
| d4ce8362b1 | |||
| caa12dbc57 | |||
| 1122b3c14d | |||
| a357649da5 | |||
| 332a3c7984 | |||
| fcd761b9d7 | |||
| b0d5f062e3 | |||
| 2c0753225a | |||
| 3b898664ee | |||
| 61fb5863df | |||
| a4ec297925 | |||
| 93d7e60d43 | |||
| 338264b5f6 | |||
| 19b51caa2c | |||
| 8c08b4373c | |||
| 0b38882a91 | |||
| 3364d69a3b | |||
| e6c9bfa4a9 | |||
| 7a3e182e9c | |||
| 2471f6b01c | |||
| 1fefafd09f | |||
| 8a63dc3151 | |||
| 403008816c | |||
| 62d552ffe2 | |||
| bcaa4217bc | |||
| 0823f6dfab | |||
| 87bb554aec | |||
| 36447cb2b5 | |||
| 3b2c6f09cb | |||
| 63414b9653 | |||
| 5f24bb0267 | |||
| 52519d45b9 | |||
| 78728df099 | |||
| 732db27cd6 | |||
| 3cad63fbd8 | |||
| d2be5939dc | |||
| 9a77d03047 | |||
| 8efcec67cc | |||
| 4fbd4bbd8d | |||
| 2fae296cbc | |||
| cef8466419 | |||
| 083159ebbe | |||
| 32e440cffc | |||
| 89c8c93b9a | |||
| 296074f548 | |||
| 2ccfaffa74 | |||
| 192672a162 | |||
| 7f4e658d3d | |||
| ff57695855 | |||
| ae8deb1246 | |||
| b9dcb35e9b | |||
| 25d9f5a804 | |||
| 1b234cb7af | |||
| d219442945 | |||
| 2b33be47cb | |||
| 499baced12 | |||
| 7aa9fecab8 | |||
| 9b6c82d804 | |||
| c3117b7544 | |||
| 67744384e8 | |||
| 17c401d09a | |||
| 488aea15f4 | |||
| 43df853ee3 | |||
| 28d5dcb578 | |||
| 7eb15c46a2 | |||
| 1fac36e2cb | |||
| 4b6c87b6e7 | |||
| ce6c2b2072 | |||
| 3e94a2c54d | |||
| b1e488f5d7 | |||
| 03d867160f | |||
| 9870e65c5f | |||
| aa839b9ff0 | |||
| fdb66aa237 | |||
| e0ca5fdd51 | |||
| 681c1c53e4 | |||
| 631c4863d8 | |||
| 944bda12c7 | |||
| bc36c4dea4 | |||
| 722766958b | |||
| 73fd3ca2eb | |||
| efe8ad51ed | |||
| d5b62465f0 | |||
| bc76e7255b | |||
| 8dd23ad2f2 | |||
| bce75d7c68 | |||
| 815053e403 | |||
| 6173abe20b | |||
| 6fcf0afa35 | |||
| 05521e276f | |||
| 38ffbbd7dd | |||
| 47e1878e4c | |||
| dc6f149973 | |||
| 420f6bfccb | |||
| ae33e11694 | |||
| 4d8b0282b4 | |||
| 00845fca88 | |||
| 84fe86c7fd | |||
| 5e1ed9d5d2 | |||
| 2ba668732a | |||
| 2cb73b71d1 | |||
| 16a2ce2b13 | |||
| 18e87a7544 | |||
| f81431dd72 | |||
| 04bdb9f813 | |||
| 1cfe281a76 | |||
| 6165bd7d1e | |||
| 0135564ad9 | |||
| 6847cbeff8 | |||
| 84daf33c03 | |||
| 86cbdb893a | |||
| 11ee680d38 | |||
| 4c5b382b69 | |||
| 39f4a776d6 | |||
| a250116694 | |||
| 9a73d71f47 | |||
| 73aaca05f8 | |||
| a993112098 | |||
| 9145d5ec3e | |||
| efc863844c | |||
| cc260e7864 | |||
| 42ce8f7f55 | |||
| 661390aef3 | |||
| ed4a70e765 | |||
| 1866968310 | |||
| 5fbf98ec23 | |||
| 5d2bb2c1b9 | |||
| 9039ddbb56 | |||
| 8f8510fc22 | |||
| 1e99ea6a51 | |||
| 016e1e675e | |||
| 1240641f76 | |||
| f1a34f0908 | |||
| 01bda54e05 | |||
| bc04afe183 | |||
| ac086ae616 | |||
| f75a2b093f | |||
| 4dba7b0203 | |||
| d3cd3c0a9b | |||
| e351874a0a | |||
| dcdf4fc78b | |||
| cf38d8c55b | |||
| b0233a33a1 | |||
| c3235db9ee | |||
| 738113bac8 | |||
| 6613ee40e6 | |||
| ce6035d953 | |||
| 2bc4793ee8 | |||
| 9c6e34bfc1 | |||
| 4e65ff31a8 | |||
| 15461c7883 | |||
| de5352cfcf | |||
| ae26ed994e | |||
| 94745f6274 | |||
| dbfa0d88bb | |||
| cf3b5cb2fc | |||
| 192a225854 | |||
| 3508f76719 | |||
| a7d081fac0 | |||
| ee774f6e5b | |||
| 80f34598f8 | |||
| 7e168c8ad2 | |||
| 3ebc2c2442 | |||
| 631d86f723 | |||
| 3ac97f2b3d | |||
| ed55346be7 | |||
| e31ec1f7ed | |||
| 70e4fd2865 | |||
| 7898490779 | |||
| 0893e83c92 | |||
| ef334d0070 | |||
| 9f08d03978 | |||
| 3ca6c4bfb9 | |||
| cfea2095ca | |||
| 171bec3b0e | |||
| 93891ad2e9 | |||
| eece726651 | |||
| dc6665caed | |||
| 5b592cbaf4 | |||
| 348138d7cd | |||
| 6a34a4ebeb | |||
| 5ca247c749 | |||
| b16bffbf76 | |||
| b2aec3706d | |||
| d577c5def1 | |||
| a4dd14952e | |||
| 8879b3733e | |||
| 35b02226ca | |||
| 814feaa2ab | |||
| aa2e66dcaf | |||
| 7b7b12e477 | |||
| 187cd0a058 | |||
| 0c690af2fe | |||
| 40ecd2d8e5 | |||
| 484286d536 | |||
| d56b62dcda | |||
| b94125ac14 | |||
| 3f34319398 | |||
| ba076a29b9 | |||
| 13000c7350 | |||
| fd2649fc65 | |||
| 437b09c155 | |||
| b2b015a53b | |||
| 5bea4c5692 | |||
| 1b527b7acf | |||
| 4b8629b6b8 | |||
| ed81d19ce9 | |||
| 3497bf2d82 | |||
| afcf03fd2c | |||
| 582612b000 | |||
| 7bf32ccadb | |||
| f5c18861b6 | |||
| 4acd75b904 | |||
| cd6dd22b19 | |||
| 139c532019 | |||
| e5b57bf01c | |||
| 5ecd6d4e0a | |||
| 03bbe9aab1 | |||
| 0ef9d54ccd | |||
| 6ade77efa2 | |||
| 91fa865bf2 | |||
| 10ae76673c | |||
| 9497757842 | |||
| bb5abe0e9c | |||
| 76c0ddfc0b | |||
| 36b888e781 | |||
| a476972e2e | |||
| 866d3fb573 | |||
| 70cf0a389f | |||
| 7d4ccea579 | |||
| 654dd1d5e8 | |||
| bf114f6ee3 | |||
| 5727eaf767 | |||
| 346e98330c | |||
| 2b1f10266a | |||
| fc7970fdf0 | |||
| 65957e99ba | |||
| 7f9a94f8bc | |||
| cb560e2441 | |||
| 55856565c2 | |||
| 13968343d4 | |||
| 4c428121b9 | |||
| 4816f7ee5c | |||
| ce53fbde50 | |||
| 1516a69cd2 | |||
| 7a77fdae4f | |||
| b13da18e11 | |||
| f98f8a3892 | |||
| 77c4a7fd66 | |||
| e281413919 | |||
| 2007ddd3f8 | |||
| d8922fe3e9 | |||
| 6c611df8f0 | |||
| 6be24df5bc | |||
| 4759aacba9 | |||
| 802bfc259c | |||
| 64db8d166e | |||
| d2a769e196 | |||
| 68a8c8907d | |||
| 701d61080a | |||
| a8cc449706 | |||
| 2aa212b19c | |||
| 1f23cfe9c7 | |||
| 0fa8e47fb5 | |||
| 8043784fd7 | |||
| 526a6b31e5 | |||
| 14fd064a62 | |||
| 3178afbf0c | |||
| ce3b616432 | |||
| 54a761905d | |||
| aa531d7bd1 | |||
| d7e9ae1215 | |||
| 6cf9ede88e | |||
| 6092291bd7 | |||
| 3d0f11212f | |||
| 6194e002e2 | |||
| 75545d4d1c | |||
| d67eb2f2db | |||
| 6b8153ff0f | |||
| fb132732f1 | |||
| 336b157497 | |||
| d16975a9de | |||
| 87f6b36bab | |||
| 43fccf5617 | |||
| a5b3bcf41c | |||
| 8801d9c286 | |||
| a8e114f351 | |||
| 9a3a9b46e5 | |||
| 934204ec18 | |||
| 7cb8f8fb44 | |||
| 8d34bf2fea | |||
| 8801e69dba | |||
| f4afa398a1 | |||
| 32063278bd | |||
| 92208d2f85 | |||
| ab7c74b4b9 | |||
| e283abe171 | |||
| d7620f68bb | |||
| 971d97e2ec | |||
| 559d5efc04 | |||
| 85042820fb | |||
| 24a2eec815 | |||
| d987a79ab1 | |||
| eba09353e6 | |||
| 297660c9a3 | |||
| 8343c05fd8 | |||
| 7c3d064786 | |||
| c2ccc1cbdf | |||
| 04e080660a | |||
| f3cca88384 | |||
| 978bbd2d49 |
@@ -12,3 +12,5 @@ angular.js.tmproj
|
||||
node_modules
|
||||
angular.xcodeproj
|
||||
.idea
|
||||
libpeerconnection.log
|
||||
npm-debug.log
|
||||
|
||||
+13
-7
@@ -1,13 +1,19 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 0.8
|
||||
- 0.10
|
||||
|
||||
env:
|
||||
global:
|
||||
- SAUCE_USERNAME=angular-ci
|
||||
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||
- SAUCE_CONNECT_READY_FILE=/tmp/sauce-connect-ready
|
||||
|
||||
before_script:
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
- npm install -g testacular@canary
|
||||
- rake package
|
||||
- ./nodeserver.sh > /dev/null &
|
||||
- export SAUCE_ACCESS_KEY=`echo $SAUCE_ACCESS_KEY | rev`
|
||||
- ./lib/sauce/sauce_connect_setup.sh
|
||||
- npm install -g grunt-cli
|
||||
- grunt ci-checks package
|
||||
- ./lib/sauce/sauce_connect_block.sh
|
||||
|
||||
script:
|
||||
- rake test[Firefox,"--reporters=dots"]
|
||||
- grunt parallel:travis --reporters dots --browsers SL_Chrome
|
||||
|
||||
+1127
-2
File diff suppressed because it is too large
Load Diff
+228
@@ -0,0 +1,228 @@
|
||||
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.loadNpmTasks('grunt-ddescribe-iit');
|
||||
grunt.loadNpmTasks('grunt-merge-conflict');
|
||||
grunt.loadNpmTasks('grunt-parallel');
|
||||
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,
|
||||
|
||||
parallel: {
|
||||
travis: {
|
||||
options: {
|
||||
stream: true,
|
||||
},
|
||||
tasks: [
|
||||
util.parallelTask('test:modules'),
|
||||
util.parallelTask('test:jquery'),
|
||||
util.parallelTask('test:jqlite'),
|
||||
util.parallelTask('test:e2e')
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
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: {
|
||||
options: {
|
||||
// We use end2end task (which does not start the webserver)
|
||||
// and start the webserver as a separate process (in travis_build.sh)
|
||||
// to avoid https://github.com/joyent/libuv/issues/826
|
||||
port: 8000,
|
||||
hostname: '0.0.0.0',
|
||||
middleware: function(connect, options){
|
||||
return [
|
||||
function(req, resp, next) {
|
||||
// cache get requests to speed up tests on travis
|
||||
if (req.method === 'GET') {
|
||||
resp.setHeader('Cache-control', 'public, max-age=3600');
|
||||
}
|
||||
|
||||
next();
|
||||
},
|
||||
connect.favicon('images/favicon.ico'),
|
||||
connect.static(options.base)
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
test: {
|
||||
jqlite: 'karma-jqlite.conf.js',
|
||||
jquery: 'karma-jquery.conf.js',
|
||||
modules: 'karma-modules.conf.js',
|
||||
//NOTE run grunt test:e2e instead and it will start a webserver for you
|
||||
end2end: 'karma-e2e.conf.js'
|
||||
},
|
||||
|
||||
|
||||
autotest: {
|
||||
jqlite: 'karma-jqlite.conf.js',
|
||||
jquery: 'karma-jquery.conf.js'
|
||||
},
|
||||
|
||||
|
||||
clean: {build: ['build']},
|
||||
|
||||
|
||||
build: {
|
||||
scenario: {
|
||||
dest: 'build/angular-scenario.js',
|
||||
src: [
|
||||
'lib/jquery/jquery.js',
|
||||
util.wrap([files['angularSrc'], files['angularScenario']], 'ngScenario/angular')
|
||||
],
|
||||
styles: {
|
||||
css: ['css/angular.css', 'css/angular-scenario.css']
|
||||
}
|
||||
},
|
||||
angular: {
|
||||
dest: 'build/angular.js',
|
||||
src: util.wrap([files['angularSrc']], 'angular'),
|
||||
styles: {
|
||||
css: ['css/angular.css'],
|
||||
minify: true
|
||||
}
|
||||
},
|
||||
loader: {
|
||||
dest: 'build/angular-loader.js',
|
||||
src: util.wrap(['src/loader.js'], 'loader')
|
||||
},
|
||||
mocks: {
|
||||
dest: 'build/angular-mocks.js',
|
||||
src: ['src/ngMock/angular-mocks.js'],
|
||||
strict: false
|
||||
},
|
||||
sanitize: {
|
||||
dest: 'build/angular-sanitize.js',
|
||||
src: util.wrap([
|
||||
'src/ngSanitize/sanitize.js',
|
||||
'src/ngSanitize/directive/ngBindHtml.js',
|
||||
'src/ngSanitize/filter/linky.js',
|
||||
], 'module')
|
||||
},
|
||||
resource: {
|
||||
dest: 'build/angular-resource.js',
|
||||
src: util.wrap(['src/ngResource/resource.js'], 'module')
|
||||
},
|
||||
cookies: {
|
||||
dest: 'build/angular-cookies.js',
|
||||
src: util.wrap(['src/ngCookies/cookies.js'], 'module')
|
||||
},
|
||||
bootstrap: {
|
||||
dest: 'build/angular-bootstrap.js',
|
||||
src: util.wrap(['src/bootstrap/bootstrap.js'], 'module')
|
||||
},
|
||||
bootstrapPrettify: {
|
||||
dest: 'build/angular-bootstrap-prettify.js',
|
||||
src: util.wrap(['src/bootstrap/bootstrap-prettify.js', 'src/bootstrap/google-prettify/prettify.js'], 'module'),
|
||||
styles: {
|
||||
css: ['src/bootstrap/google-prettify/prettify.css'],
|
||||
minify: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
min: {
|
||||
angular: 'build/angular.js',
|
||||
cookies: 'build/angular-cookies.js',
|
||||
loader: 'build/angular-loader.js',
|
||||
resource: 'build/angular-resource.js',
|
||||
sanitize: 'build/angular-sanitize.js',
|
||||
bootstrap: 'build/angular-bootstrap.js',
|
||||
bootstrapPrettify: 'build/angular-bootstrap-prettify.js',
|
||||
},
|
||||
|
||||
|
||||
docs: {
|
||||
process: ['build/docs/*.html', 'build/docs/.htaccess']
|
||||
},
|
||||
|
||||
|
||||
"ddescribe-iit": {
|
||||
files: [
|
||||
'test/**/*.js',
|
||||
'!test/ngScenario/DescribeSpec.js'
|
||||
]
|
||||
},
|
||||
|
||||
"merge-conflict": {
|
||||
files: [
|
||||
'src/**/*',
|
||||
'test/**/*',
|
||||
'docs/**/*',
|
||||
'css/**/*'
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
copy: {
|
||||
i18n: {
|
||||
files: [
|
||||
{ src: 'src/ngLocale/**', dest: 'build/i18n/', expand: true, flatten: true }
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
compress: {
|
||||
build: {
|
||||
options: {archive: 'build/' + dist +'.zip', mode: '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('ci-checks', ['ddescribe-iit', 'merge-conflict']);
|
||||
grunt.registerTask('default', ['package']);
|
||||
};
|
||||
@@ -21,25 +21,19 @@ Building AngularJS
|
||||
---------
|
||||
[Once you have your environment setup](http://docs.angularjs.org/misc/contribute) just run:
|
||||
|
||||
rake package
|
||||
grunt package
|
||||
|
||||
|
||||
Running Tests
|
||||
-------------
|
||||
Running tests requires installation of [Testacular](http://vojtajina.github.com/testacular):
|
||||
|
||||
sudo npm install -g testacular
|
||||
|
||||
To execute all unit tests, use:
|
||||
|
||||
rake test:unit
|
||||
grunt test:unit
|
||||
|
||||
To execute end-to-end (e2e) tests, use:
|
||||
|
||||
rake package
|
||||
rake webserver &
|
||||
rake test:e2e
|
||||
grunt package
|
||||
grunt test:e2e
|
||||
|
||||
To learn more about the rake tasks, run `rake -T` and also read our
|
||||
[contribution guidelines](http://docs.angularjs.org/misc/contribute) and instructions in this
|
||||
[commit message](https://github.com/angular/angular.js/commit/9d168f058f9c6d7eeae0daa7cb72ea4e02a0003a).
|
||||
To learn more about the grunt tasks, run `grunt --help` and also read our
|
||||
[contribution guidelines](http://docs.angularjs.org/misc/contribute).
|
||||
|
||||
@@ -1,374 +0,0 @@
|
||||
require 'yaml'
|
||||
include FileUtils
|
||||
|
||||
|
||||
## High level flow of the build:
|
||||
##
|
||||
## clean -> init -> concat -> minify -> package
|
||||
##
|
||||
|
||||
|
||||
content = File.open('angularFiles.js', 'r') {|f| f.read }
|
||||
files = eval(content.gsub(/\};(\s|\S)*/, '}').
|
||||
gsub(/angularFiles = /, '').
|
||||
gsub(/:/, '=>').
|
||||
gsub(/\/\//, '#'));
|
||||
|
||||
BUILD_DIR = 'build'
|
||||
|
||||
task :default => [:package]
|
||||
|
||||
|
||||
desc 'Init the build workspace'
|
||||
task :init do
|
||||
FileUtils.mkdir(BUILD_DIR) unless File.directory?(BUILD_DIR)
|
||||
|
||||
v = YAML::load( File.open( 'version.yaml' ) )
|
||||
match = v['version'].match(/^([^-]*)(-snapshot)?$/)
|
||||
|
||||
NG_VERSION = Struct.new(:full, :major, :minor, :dot, :codename, :stable).
|
||||
new(match[1] + (match[2] ? ('-' + %x(git rev-parse HEAD)[0..7]) : ''),
|
||||
match[1].split('.')[0],
|
||||
match[1].split('.')[1],
|
||||
match[1].split('.')[2].sub(/\D+.*$/, ''),
|
||||
v['codename'],
|
||||
v['stable'])
|
||||
end
|
||||
|
||||
|
||||
desc 'Clean Generated Files'
|
||||
task :clean do
|
||||
FileUtils.rm_r(BUILD_DIR, :force => true)
|
||||
FileUtils.mkdir(BUILD_DIR)
|
||||
FileUtils.rm_r('test_out', :force => true)
|
||||
end
|
||||
|
||||
|
||||
desc 'Concat Scenario'
|
||||
task :concat_scenario => :init do
|
||||
|
||||
concat_file('angular-scenario.js', [
|
||||
'lib/jquery/jquery.js',
|
||||
'src/ngScenario/angular.prefix',
|
||||
files['angularSrc'],
|
||||
files['angularScenario'],
|
||||
'src/ngScenario/angular.suffix',
|
||||
], gen_css('css/angular.css') + "\n" + gen_css('css/angular-scenario.css'))
|
||||
end
|
||||
|
||||
|
||||
desc 'Concat JSTD Scenario Adapter'
|
||||
task :concat_jstd_scenario_adapter => :init do
|
||||
|
||||
concat_file('jstd-scenario-adapter.js', [
|
||||
'src/ngScenario/jstd-scenario-adapter/angular.prefix',
|
||||
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
|
||||
'src/ngScenario/jstd-scenario-adapter/angular.suffix',
|
||||
])
|
||||
|
||||
# TODO(vojta) use jstd configuration when implemented
|
||||
# (instead of including jstd-adapter-config.js)
|
||||
File.open(path_to('jstd-scenario-adapter-config.js'), 'w') do |f|
|
||||
f.write("/**\r\n" +
|
||||
" * Configuration for jstd scenario adapter \n */\n" +
|
||||
"var jstdScenarioAdapter = {\n relativeUrlPrefix: '/build/docs/'\n};\n")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
desc 'Concat AngularJS files'
|
||||
task :concat => :init do
|
||||
concat_file('angular.js', [
|
||||
'src/angular.prefix',
|
||||
files['angularSrc'],
|
||||
'src/angular.suffix',
|
||||
], gen_css('css/angular.css', true))
|
||||
|
||||
FileUtils.cp_r 'src/ngLocale', path_to('i18n')
|
||||
|
||||
concat_file('angular-loader.js', [
|
||||
'src/loader.prefix',
|
||||
'src/loader.js',
|
||||
'src/loader.suffix'])
|
||||
|
||||
|
||||
concat_module('sanitize', [
|
||||
'src/ngSanitize/sanitize.js',
|
||||
'src/ngSanitize/directive/ngBindHtml.js',
|
||||
'src/ngSanitize/filter/linky.js'])
|
||||
|
||||
concat_module('resource', ['src/ngResource/resource.js'])
|
||||
concat_module('cookies', ['src/ngCookies/cookies.js'])
|
||||
concat_module('bootstrap', ['src/bootstrap/bootstrap.js'])
|
||||
concat_module('bootstrap-prettify', ['src/bootstrap/bootstrap-prettify.js',
|
||||
'src/bootstrap/google-prettify/prettify.js'],
|
||||
gen_css('src/bootstrap/google-prettify/prettify.css', true))
|
||||
|
||||
|
||||
FileUtils.cp 'src/ngMock/angular-mocks.js', path_to('angular-mocks.js')
|
||||
|
||||
rewrite_file(path_to('angular-mocks.js')) do |content|
|
||||
content.sub!('"NG_VERSION_FULL"', NG_VERSION.full)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
desc 'Minify JavaScript'
|
||||
task :minify => [:init, :concat, :concat_scenario, :concat_jstd_scenario_adapter] do
|
||||
[ 'angular.js',
|
||||
'angular-cookies.js',
|
||||
'angular-loader.js',
|
||||
'angular-resource.js',
|
||||
'angular-sanitize.js',
|
||||
'angular-bootstrap.js',
|
||||
'angular-bootstrap-prettify.js'
|
||||
].each do |file|
|
||||
unless ENV['TRAVIS']
|
||||
fork { closure_compile(file) }
|
||||
else
|
||||
closure_compile(file)
|
||||
end
|
||||
end
|
||||
|
||||
Process.waitall
|
||||
end
|
||||
|
||||
|
||||
desc 'Generate version.txt and version.json files'
|
||||
task :version => [:init] do
|
||||
`echo #{NG_VERSION.full} > #{path_to('version.txt')}`
|
||||
`echo '{
|
||||
"full": "#{NG_VERSION.full}",
|
||||
"major": "#{NG_VERSION.major}",
|
||||
"minor": "#{NG_VERSION.minor}",
|
||||
"dot": "#{NG_VERSION.dot}",
|
||||
"codename": "#{NG_VERSION.codename}"\n}' > #{path_to('version.json')}`
|
||||
end
|
||||
|
||||
|
||||
desc 'Generate docs'
|
||||
task :docs => [:init] do
|
||||
`node docs/src/gen-docs.js`
|
||||
|
||||
[ path_to('docs/.htaccess'),
|
||||
path_to('docs/index.html'),
|
||||
path_to('docs/index-debug.html'),
|
||||
path_to('docs/index-nocache.html'),
|
||||
path_to('docs/index-jq.html'),
|
||||
path_to('docs/index-jq-debug.html'),
|
||||
path_to('docs/index-jq-nocache.html'),
|
||||
path_to('docs/docs-scenario.html')
|
||||
].each do |src|
|
||||
rewrite_file(src) do |content|
|
||||
content.sub!('"NG_VERSION_FULL"', NG_VERSION.full).
|
||||
sub('"NG_VERSION_STABLE"', NG_VERSION.stable)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
desc 'Create angular distribution'
|
||||
task :package => [:clean, :minify, :version, :docs] do
|
||||
zip_dir = "angular-#{NG_VERSION.full}"
|
||||
zip_file = "#{zip_dir}.zip"
|
||||
|
||||
FileUtils.ln_s BUILD_DIR, zip_dir
|
||||
%x(zip -r #{zip_file} #{zip_dir})
|
||||
FileUtils.rm zip_dir
|
||||
|
||||
FileUtils.mv zip_file, path_to(zip_file)
|
||||
|
||||
puts "Package created: #{path_to(zip_file)}"
|
||||
end
|
||||
|
||||
|
||||
desc 'Start development webserver'
|
||||
task :webserver, :port do |t, args|
|
||||
exec "node lib/nodeserver/server.js #{args[:port]}"
|
||||
end
|
||||
|
||||
|
||||
desc 'Run all AngularJS tests'
|
||||
task :test, :browsers, :misc_options do |t, args|
|
||||
[ 'test:jqlite',
|
||||
'test:jquery',
|
||||
'test:modules',
|
||||
'test:e2e'
|
||||
].each do |task|
|
||||
Rake::Task[task].invoke(args[:browsers], args[:misc_options])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
namespace :test do
|
||||
|
||||
desc 'Run all unit tests (single run)'
|
||||
task :unit, :browsers, :misc_options do |t, args|
|
||||
[ 'test:jqlite',
|
||||
'test:jquery',
|
||||
'test:modules'
|
||||
].each do |task|
|
||||
Rake::Task[task].invoke(args[:browsers], args[:misc_options])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
desc 'Run jqLite-based unit test suite (single run)'
|
||||
task :jqlite, :browsers, :misc_options do |t, args|
|
||||
start_testacular('testacular-jqlite.conf.js', true, args[:browsers], args[:misc_options])
|
||||
end
|
||||
|
||||
|
||||
desc 'Run jQuery-based unit test suite (single run)'
|
||||
task :jquery, :browsers, :misc_options do |t, args|
|
||||
start_testacular('testacular-jquery.conf.js', true, args[:browsers], args[:misc_options])
|
||||
end
|
||||
|
||||
|
||||
desc 'Run bundled modules unit test suite (single run)'
|
||||
task :modules, :browsers, :misc_options do |t, args|
|
||||
start_testacular('testacular-modules.conf.js', true, args[:browsers], args[:misc_options])
|
||||
end
|
||||
|
||||
|
||||
desc 'Run e2e test suite (single run)'
|
||||
task :e2e, :browsers, :misc_options do |t, args|
|
||||
start_testacular('testacular-e2e.conf.js', true, args[:browsers], args[:misc_options])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
namespace :autotest do
|
||||
|
||||
desc 'Run jqLite-based unit test suite (autowatch)'
|
||||
task :jqlite, :browsers, :misc_options do |t, args|
|
||||
start_testacular('testacular-jqlite.conf.js', false, args[:browsers], args[:misc_options])
|
||||
end
|
||||
|
||||
|
||||
desc 'Run jQuery-based unit test suite (autowatch)'
|
||||
task :jquery, :browsers, :misc_options do |t, args|
|
||||
start_testacular('testacular-jquery.conf.js', false, args[:browsers], args[:misc_options])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
###################
|
||||
# utility methods #
|
||||
###################
|
||||
|
||||
|
||||
##
|
||||
# generates css snippet from a given files and optionally applies simple minification rules
|
||||
#
|
||||
def gen_css(cssFile, minify = false)
|
||||
css = ''
|
||||
File.open(cssFile, 'r') do |f|
|
||||
css = f.read
|
||||
end
|
||||
|
||||
if minify
|
||||
css.gsub! /\n/, ''
|
||||
css.gsub! /\/\*.*?\*\//, ''
|
||||
css.gsub! /:\s+/, ':'
|
||||
css.gsub! /\s*\{\s*/, '{'
|
||||
css.gsub! /\s*\}\s*/, '}'
|
||||
css.gsub! /\s*\,\s*/, ','
|
||||
css.gsub! /\s*\;\s*/, ';'
|
||||
end
|
||||
|
||||
#escape for js
|
||||
css.gsub! /\\/, "\\\\\\"
|
||||
css.gsub! /'/, "\\\\'"
|
||||
css.gsub! /\n/, "\\n"
|
||||
|
||||
return %Q{angular.element(document).find('head').append('<style type="text/css">#{css}</style>');}
|
||||
end
|
||||
|
||||
|
||||
##
|
||||
# returns path to the file in the build directory
|
||||
#
|
||||
def path_to(filename)
|
||||
return File.join(BUILD_DIR, *filename)
|
||||
end
|
||||
|
||||
|
||||
##
|
||||
# returns the 32-bit mode force flags for java compiler if supported, this makes the build much
|
||||
# faster
|
||||
#
|
||||
def java32flags
|
||||
return '-d32 -client' unless Rake::Win32.windows? || `java -version -d32 2>&1`.match(/Error/i)
|
||||
end
|
||||
|
||||
|
||||
def closure_compile(filename)
|
||||
puts "Minifying #{filename} ..."
|
||||
|
||||
min_path = path_to(filename.gsub(/\.js$/, '.min.js'))
|
||||
|
||||
%x(java \
|
||||
#{java32flags()} \
|
||||
-jar lib/closure-compiler/compiler.jar \
|
||||
--compilation_level SIMPLE_OPTIMIZATIONS \
|
||||
--language_in ECMASCRIPT5_STRICT \
|
||||
--js #{path_to(filename)} \
|
||||
--js_output_file #{min_path})
|
||||
|
||||
rewrite_file(min_path) do |content|
|
||||
content.sub!("'use strict';", "").
|
||||
sub!(/\(function\([^)]*\)\{/, "\\0'use strict';")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def concat_file(filename, deps, footer='')
|
||||
puts "Creating #{filename} ..."
|
||||
File.open(path_to(filename), 'w') do |f|
|
||||
concat = 'cat ' + deps.flatten.join(' ')
|
||||
|
||||
content = %x{#{concat}}.
|
||||
gsub('"NG_VERSION_FULL"', NG_VERSION.full).
|
||||
gsub('"NG_VERSION_MAJOR"', NG_VERSION.major).
|
||||
gsub('"NG_VERSION_MINOR"', NG_VERSION.minor).
|
||||
gsub('"NG_VERSION_DOT"', NG_VERSION.dot).
|
||||
gsub('"NG_VERSION_CODENAME"', NG_VERSION.codename).
|
||||
gsub(/^\s*['"]use strict['"];?\s*$/, ''). # remove all file-specific strict mode flags
|
||||
sub(/\(function\([^)]*\)\s*\{/, "\\0\n'use strict';") # add single strict mode flag
|
||||
|
||||
f.write(content)
|
||||
f.write(footer)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def concat_module(name, files, footer='')
|
||||
concat_file('angular-' + name + '.js', ['src/module.prefix'] + files + ['src/module.suffix'], footer)
|
||||
end
|
||||
|
||||
|
||||
def rewrite_file(filename)
|
||||
File.open(filename, File::RDWR) do |f|
|
||||
content = f.read
|
||||
|
||||
content = yield content
|
||||
|
||||
raise "File rewrite failed - No content!" unless content
|
||||
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write content
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def start_testacular(config, singleRun, browsers, misc_options)
|
||||
sh "./node_modules/testacular/bin/testacular start " +
|
||||
"#{config} " +
|
||||
"#{'--single-run=true' if singleRun} " +
|
||||
"#{'--browsers=' + browsers.gsub('+', ',') if browsers} " +
|
||||
"#{(misc_options || '').gsub('+', ',')}"
|
||||
end
|
||||
Vendored
+19
-53
@@ -94,7 +94,6 @@ angularFiles = {
|
||||
'test/matchers.js',
|
||||
'test/ngScenario/*.js',
|
||||
'test/ngScenario/output/*.js',
|
||||
'test/ngScenario/jstd-scenario-adapter/*.js',
|
||||
'test/*.js',
|
||||
'test/auto/*.js',
|
||||
'test/bootstrap/*.js',
|
||||
@@ -109,37 +108,30 @@ angularFiles = {
|
||||
'test/ngMock/*.js'
|
||||
],
|
||||
|
||||
'jstd': [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'karma': [
|
||||
'lib/jquery/jquery.js',
|
||||
'test/jquery_remove.js',
|
||||
'@angularSrc',
|
||||
'src/publishExternalApis.js',
|
||||
'@angularSrcModules',
|
||||
'@angularScenario',
|
||||
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
|
||||
'@angularTest',
|
||||
'example/personalLog/*.js',
|
||||
'example/personalLog/test/*.js'
|
||||
],
|
||||
|
||||
'jstdExclude': [
|
||||
'karmaExclude': [
|
||||
'test/jquery_alias.js',
|
||||
'src/angular-bootstrap.js',
|
||||
'src/ngScenario/angular-bootstrap.js'
|
||||
],
|
||||
|
||||
'jstdScenario': [
|
||||
'karmaScenario': [
|
||||
'build/angular-scenario.js',
|
||||
'build/jstd-scenario-adapter-config.js',
|
||||
'build/jstd-scenario-adapter.js',
|
||||
'build/docs/docs-scenario.js'
|
||||
],
|
||||
|
||||
"jstdModules": [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
"karmaModules": [
|
||||
'build/angular.js',
|
||||
'src/ngMock/angular-mocks.js',
|
||||
'src/ngCookies/cookies.js',
|
||||
@@ -156,39 +148,20 @@ angularFiles = {
|
||||
'test/ngSanitize/filter/*.js'
|
||||
],
|
||||
|
||||
'jstdPerf': [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'@angularSrc',
|
||||
'@angularSrcModules',
|
||||
'src/ngMock/angular-mocks.js',
|
||||
'perf/data/*.js',
|
||||
'perf/testUtils.js',
|
||||
'perf/*.js'
|
||||
],
|
||||
|
||||
'jstdPerfExclude': [
|
||||
'src/ng/angular-bootstrap.js',
|
||||
'src/ngScenario/angular-bootstrap.js'
|
||||
],
|
||||
|
||||
'jstdJquery': [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'karmaJquery': [
|
||||
'lib/jquery/jquery.js',
|
||||
'test/jquery_alias.js',
|
||||
'@angularSrc',
|
||||
'src/publishExternalApis.js',
|
||||
'@angularSrcModules',
|
||||
'@angularScenario',
|
||||
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
|
||||
'@angularTest',
|
||||
'example/personalLog/*.js',
|
||||
|
||||
'example/personalLog/test/*.js'
|
||||
],
|
||||
|
||||
'jstdJqueryExclude': [
|
||||
'karmaJqueryExclude': [
|
||||
'src/angular-bootstrap.js',
|
||||
'src/ngScenario/angular-bootstrap.js',
|
||||
'test/jquery_remove.js'
|
||||
@@ -196,29 +169,22 @@ angularFiles = {
|
||||
};
|
||||
|
||||
if (exports) {
|
||||
exports.files = angularFiles
|
||||
exports.mergeFiles = function mergeFiles() {
|
||||
exports.files = angularFiles;
|
||||
exports.mergeFilesFor = function() {
|
||||
var files = [];
|
||||
|
||||
[].splice.call(arguments, 0).forEach(function(file) {
|
||||
if (file.match(/testacular/)) {
|
||||
files.push(file);
|
||||
} else {
|
||||
angularFiles[file].forEach(function(f) {
|
||||
// replace @ref
|
||||
var match = f.match(/^\@(.*)/);
|
||||
if (match) {
|
||||
var deps = angularFiles[match[1]];
|
||||
files = files.concat(deps);
|
||||
} else {
|
||||
if (!/jstd|jasmine/.test(f)) { //TODO(i): remove once we don't have jstd/jasmine in repo
|
||||
files.push(f);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Array.prototype.slice.call(arguments, 0).forEach(function(filegroup) {
|
||||
angularFiles[filegroup].forEach(function(file) {
|
||||
// replace @ref
|
||||
var match = file.match(/^\@(.*)/);
|
||||
if (match) {
|
||||
files = files.concat(angularFiles[match[1]]);
|
||||
} else {
|
||||
files.push(file);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return files;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+1
-1
@@ -192,7 +192,7 @@ var getPreviousTag = function() {
|
||||
var generate = function(version, file) {
|
||||
getPreviousTag().then(function(tag) {
|
||||
console.log('Reading git log since', tag);
|
||||
readGitLog('^fix|^feat|Breaks', tag).then(function(commits) {
|
||||
readGitLog('^fix|^feat|BREAKING', tag).then(function(commits) {
|
||||
console.log('Parsed', commits.length, 'commits');
|
||||
console.log('Generating changelog to', file || 'stdout', '(', version, ')');
|
||||
writeChangelog(file ? fs.createWriteStream(file) : process.stdout, commits, version);
|
||||
|
||||
+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.*
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],
|
||||
.ng-cloak, .x-ng-cloak {
|
||||
display: none;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
ng\:form {
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="HelloCntl">
|
||||
Your name: <input type="text" ng-model="name" value="World"/>
|
||||
Your name: <input type="text" ng-model="name"/>
|
||||
<hr/>
|
||||
Hello {{name}}!
|
||||
Hello {{name || "World"}}!
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
This page explains the Angular initialization process and how you can manually initialize Angular
|
||||
if necessary.
|
||||
|
||||
|
||||
# Angular `<script>` Tag
|
||||
## Angular `<script>` Tag
|
||||
|
||||
This example shows the recommended path for integrating Angular with what we call automatic
|
||||
initialization.
|
||||
@@ -38,6 +37,10 @@ initialization.
|
||||
|
||||
<html ng-app>
|
||||
|
||||
* If IE7 support is required add `id="ng-app"`
|
||||
|
||||
<html ng-app id="ng-app">
|
||||
|
||||
* If you choose to use the old style directive syntax `ng:` then include xml-namespace in `html`
|
||||
to make IE happy. (This is here for historical reasons, and we no longer recommend use of
|
||||
`ng:`.)
|
||||
@@ -46,7 +49,7 @@ initialization.
|
||||
|
||||
|
||||
|
||||
# Automatic 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
|
||||
@@ -73,16 +76,14 @@ will:
|
||||
|
||||
|
||||
|
||||
# Manual Initialization
|
||||
## Manual Initialization
|
||||
|
||||
|
||||
If you need to have more control over the initialization process, you can use a manual
|
||||
bootstrapping method instead. Examples of when you'd need to do this include using script loaders
|
||||
or the need to perform an operation before Angular compiles a page.
|
||||
|
||||
|
||||
Here is an example of manually initializing Angular. The example is equivalent to using the {@link
|
||||
api/ng.directive:ngApp ng-app} directive.
|
||||
Here is an example of manually initializing Angular:
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
@@ -92,17 +93,22 @@ api/ng.directive:ngApp ng-app} directive.
|
||||
<script src="http://code.angularjs.org/angular.js"></script>
|
||||
<script>
|
||||
angular.element(document).ready(function() {
|
||||
angular.bootstrap(document);
|
||||
angular.bootstrap(document, ['optionalModuleName']);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
Note that we have provided the name of our application module to be loaded into the injector as the second
|
||||
parameter of the {@link api/angular.bootstrap} function. This example is equivalent to using the
|
||||
{@link api/ng.directive:ngApp ng-app} directive, with `ng-app="optionalModuleName"`, as in the automatic
|
||||
initialization example above.
|
||||
|
||||
This is the sequence that your code should follow:
|
||||
|
||||
1. After the page and all of the code is loaded, find the root of the HTML template, which is
|
||||
typically the root of the document.
|
||||
1. After the page and all of the code is loaded, find the root element of your AngularJS
|
||||
application, which is typically the root of the document.
|
||||
|
||||
2. Call {@link api/angular.bootstrap} to {@link compiler compile} the template into an
|
||||
2. Call {@link api/angular.bootstrap} to {@link compiler compile} the element into an
|
||||
executable, bi-directionally bound application.
|
||||
|
||||
@@ -6,21 +6,21 @@
|
||||
|
||||
Angular's {@link api/ng.$compile HTML compiler} allows the developer to teach the
|
||||
browser new HTML syntax. The compiler allows you to attach behavior to any HTML element or attribute
|
||||
and even create new HTML element or attributes with custom behavior. Angular calls these behavior
|
||||
and even create new HTML elements or attributes with custom behavior. Angular calls these behavior
|
||||
extensions {@link api/ng.$compileProvider#directive directives}.
|
||||
|
||||
HTML has a lot of constructs for formatting the HTML for static documents in a declarative fashion.
|
||||
For example if something needs to be centered, there is no need to provide instructions to the
|
||||
browser how the window size needs to be divided in half so that center is found, and that this
|
||||
center needs to be aligned with the text's center. Simply add `align="center"` attribute to any
|
||||
browser how the window size needs to be divided in half so that the center is found, and that this
|
||||
center needs to be aligned with the text's center. Simply add an `align="center"` attribute to any
|
||||
element to achieve the desired behavior. Such is the power of declarative language.
|
||||
|
||||
But the declarative language is also limited, since it does not allow you to teach the browser new
|
||||
syntax. For example there is no easy way to get the browser to align the text at 1/3 the position
|
||||
instead of 1/2. What is needed is a way to teach browser new HTML syntax.
|
||||
instead of 1/2. What is needed is a way to teach the browser new HTML syntax.
|
||||
|
||||
Angular comes pre-bundled with common directives which are useful for building any app. We also
|
||||
expect that you will create directives that are specific to your app. These extension become a
|
||||
expect that you will create directives that are specific to your app. These extensions become a
|
||||
Domain Specific Language for building your application.
|
||||
|
||||
All of this compilation takes place in the web browser; no server side or pre-compilation step is
|
||||
@@ -30,7 +30,7 @@ involved.
|
||||
# Compiler
|
||||
|
||||
Compiler is an angular service which traverses the DOM looking for attributes. The compilation
|
||||
process happens into two phases.
|
||||
process happens in two phases.
|
||||
|
||||
1. **Compile:** traverse the DOM and collect all of the directives. The result is a linking
|
||||
function.
|
||||
@@ -39,17 +39,16 @@ process happens into two phases.
|
||||
scope model are reflected in the view, and any user interactions with the view are reflected
|
||||
in the scope model. This makes the scope model the single source of truth.
|
||||
|
||||
Some directives such {@link api/ng.directive:ngRepeat
|
||||
`ng-repeat`} clone DOM elements once for each item in collection. Having a compile and link phase
|
||||
improves performance since the cloned template only needs to be compiled once, and then linked
|
||||
once for each clone instance.
|
||||
Some directives such as {@link api/ng.directive:ngRepeat `ng-repeat`} clone DOM elements once
|
||||
for each item in a collection. Having a compile and link phase improves performance since the
|
||||
cloned template only needs to be compiled once, and then linked once for each clone instance.
|
||||
|
||||
|
||||
# Directive
|
||||
|
||||
A directive is a behavior which should be triggered when specific HTML constructs are encountered in
|
||||
the compilation process. The directives can be placed in element names, attributes, class names, as
|
||||
well as comments. Here are some equivalent examples of invoking the {@link
|
||||
A directive is a behavior which should be triggered when specific HTML constructs are encountered
|
||||
during the compilation process. The directives can be placed in element names, attributes, class
|
||||
names, as well as comments. Here are some equivalent examples of invoking the {@link
|
||||
api/ng.directive:ngBind `ng-bind`} directive.
|
||||
|
||||
<pre>
|
||||
@@ -70,8 +69,8 @@ Here is a directive which makes any element draggable. Notice the `draggable` at
|
||||
<file name="script.js">
|
||||
angular.module('drag', []).
|
||||
directive('draggable', function($document) {
|
||||
var startX=0, startY=0, x = 0, y = 0;
|
||||
return function(scope, element, attr) {
|
||||
var startX = 0, startY = 0, x = 0, y = 0;
|
||||
element.css({
|
||||
position: 'relative',
|
||||
border: '1px solid red',
|
||||
@@ -79,6 +78,8 @@ Here is a directive which makes any element draggable. Notice the `draggable` at
|
||||
cursor: 'pointer'
|
||||
});
|
||||
element.bind('mousedown', function(event) {
|
||||
// Prevent default dragging of selected content
|
||||
event.preventDefault();
|
||||
startX = event.screenX - x;
|
||||
startY = event.screenY - y;
|
||||
$document.bind('mousemove', mousemove);
|
||||
|
||||
@@ -61,7 +61,7 @@ This is how we get the ball rolling (refer to the diagram and example below):
|
||||
|
||||
The diagram and the example below describe how Angular interacts with the browser's event loop.
|
||||
|
||||
1. The browser's event-loop waits for an event to arrive. An event is a user interactions, timer event,
|
||||
1. The browser's event-loop waits for an event to arrive. An event is a user interaction, timer event,
|
||||
or network event (response from a server).
|
||||
2. The event's callback gets executed. This enters the JavaScript context. The callback can
|
||||
modify the DOM structure.
|
||||
@@ -74,7 +74,7 @@ applied in Angular execution context will benefit from Angular data-binding, exc
|
||||
property watching, etc... You can also use $apply() to enter Angular execution context from JavaScript. Keep in
|
||||
mind that in most places (controllers, services) $apply has already been called for you by the
|
||||
directive which is handling the event. An explicit call to $apply is needed only when
|
||||
implementing custom event callbacks, or when working with a third-party library callbacks.
|
||||
implementing custom event callbacks, or when working with third-party library callbacks.
|
||||
|
||||
1. Enter Angular execution context by calling {@link guide/scope scope}`.`{@link
|
||||
api/ng.$rootScope.Scope#$apply $apply}`(stimulusFn)`. Where `stimulusFn` is
|
||||
@@ -101,7 +101,7 @@ implementing custom event callbacks, or when working with a third-party library
|
||||
re-rendering the DOM to reflect any changes.
|
||||
|
||||
|
||||
Here is the explanation of how the `Hello wold` example achieves the data-binding effect when the
|
||||
Here is the explanation of how the `Hello world` example achieves the data-binding effect when the
|
||||
user enters text into the text field.
|
||||
|
||||
1. During the compilation phase:
|
||||
@@ -143,8 +143,8 @@ provides the execution context for expressions. The scopes are nested in a hiera
|
||||
which closely follow the DOM structure. (See individual directive documentation to see which
|
||||
directives cause a creation of new scopes.)
|
||||
|
||||
The following example demonstrates how `name` {@link guide/expression expression} will evaluate
|
||||
into different value depending on which scope it is evaluated in. The example is followed by
|
||||
The following example demonstrates how the `name` {@link guide/expression expression} will evaluate
|
||||
into a different value depending on which scope it is evaluated in. The example is followed by
|
||||
a diagram depicting the scope boundaries.
|
||||
|
||||
<div class="clear">
|
||||
@@ -232,9 +232,9 @@ The separation of the controller and the view is important because:
|
||||
|
||||
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-model.png">
|
||||
|
||||
The model is the data which is used merged with the template to produce the view. To be able to
|
||||
The model is the data which is merged with the template to produce the view. To be able to
|
||||
render the model into the view, the model has to be able to be referenced from the scope. Unlike many
|
||||
other frameworks Angular makes no restrictions or requirements an the model. There are no classes
|
||||
other frameworks Angular makes no restrictions or requirements on the model. There are no classes
|
||||
to inherit from or special accessor methods for accessing or changing the model. The model can be
|
||||
primitive, object hash, or a full object Type. In short the model is a plain JavaScript object.
|
||||
|
||||
@@ -248,9 +248,9 @@ primitive, object hash, or a full object Type. In short the model is a plain Jav
|
||||
|
||||
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-view.png">
|
||||
|
||||
The view is what the users sees. The view begins its life as a template, it is merged with the
|
||||
The view is what the user sees. The view begins its life as a template, is merged with the
|
||||
model and finally rendered into the browser DOM. Angular takes a very different approach to
|
||||
rendering the view, compared to most other templating systems.
|
||||
rendering the view compared to most other templating systems.
|
||||
|
||||
* **Others** - Most templating systems begin as an HTML string with special templating markup.
|
||||
Often the template markup breaks the HTML syntax which means that the template can not be
|
||||
@@ -292,7 +292,7 @@ rendering the view, compared to most other templating systems.
|
||||
# Directives
|
||||
|
||||
A directive is a behavior or DOM transformation which is triggered by the presence of a custom attribute,
|
||||
element name, or a class name. A directive allows you to extend the HTML vocabulary in a
|
||||
element name, class name or comment. A directive allows you to extend the HTML vocabulary in a
|
||||
declarative fashion. Following is an example which enables data-binding for the `contenteditable`
|
||||
in HTML.
|
||||
|
||||
@@ -425,7 +425,7 @@ function arguments. When angular calls the function, it will use the {@link
|
||||
api/AUTO.$injector#invoke call} which will automatically fill the function
|
||||
arguments.
|
||||
|
||||
Examine the `ClockCtrl` bellow, and notice how it lists the dependencies in the constructor. When the
|
||||
Examine the `ClockCtrl` below, and notice how it lists the dependencies in the constructor. When the
|
||||
{@link api/ng.directive:ngController ng-controller} instantiates
|
||||
the controller it automatically provides the dependencies. There is no need to create
|
||||
dependencies, look for dependencies, or even get a reference to the injector.
|
||||
|
||||
@@ -97,7 +97,8 @@ the test frame.
|
||||
Asserts the value of the given `future` satisfies the `matcher`. All API statements return a
|
||||
`future` object, which get a `value` assigned after they are executed. Matchers are defined using
|
||||
`angular.scenario.matcher`, and they use the value of futures to run the expectation. For example:
|
||||
`expect(browser().location().href()).toEqual('http://www.google.com')`
|
||||
`expect(browser().location().href()).toEqual('http://www.google.com')`. Available matchers
|
||||
are presented further down this document.
|
||||
|
||||
## expect(future).not().{matcher}
|
||||
Asserts the value of the given `future` satisfies the negation of the `matcher`.
|
||||
@@ -109,16 +110,16 @@ Scopes the next DSL element selection.
|
||||
Returns the value of the first binding matching the given `name`.
|
||||
|
||||
## input(name).enter(value)
|
||||
Enters the given `value` in the text field with the given `name`.
|
||||
Enters the given `value` in the text field with the corresponding ng-model `name`.
|
||||
|
||||
## input(name).check()
|
||||
Checks/unchecks the checkbox with the given `name`.
|
||||
Checks/unchecks the checkbox with the corresponding ng-model `name`.
|
||||
|
||||
## input(name).select(value)
|
||||
Selects the given `value` in the radio button with the given `name`.
|
||||
Selects the given `value` in the radio button with the corresponding ng-model `name`.
|
||||
|
||||
## input(name).val()
|
||||
Returns the current value of an input field with the given `name`.
|
||||
Returns the current value of an input field with the corresponding ng-model `name`.
|
||||
|
||||
## repeater(selector, label).count()
|
||||
Returns the number of rows in the repeater matching the given jQuery `selector`. The `label` is
|
||||
@@ -135,7 +136,7 @@ the given jQuery `selector`. The `label` is used for test output.
|
||||
## select(name).option(value)
|
||||
Picks the option with the given `value` on the select with the given `name`.
|
||||
|
||||
## select(name).option(value1, value2...)
|
||||
## select(name).options(value1, value2...)
|
||||
Picks the options with the given `values` on the multi select with the given `name`.
|
||||
|
||||
## element(selector, label).count()
|
||||
@@ -176,3 +177,132 @@ JavaScript is a dynamically typed language which comes with great power of expre
|
||||
come with almost no-help from the compiler. For this reason we feel very strongly that any code
|
||||
written in JavaScript needs to come with a strong set of tests. We have built many features into
|
||||
angular which makes testing your angular applications easy. So there is no excuse for not testing.
|
||||
|
||||
# Matchers
|
||||
|
||||
Matchers are used in combination with the `expect(...)` function as described above and can
|
||||
be negated with `not()`. For instance: `expect(element('h1').text()).not().toEqual('Error')`.
|
||||
|
||||
Source: {@link https://github.com/angular/angular.js/blob/master/src/ngScenario/matchers.js}
|
||||
|
||||
<pre>
|
||||
// value and Object comparison following the rules of angular.equals().
|
||||
expect(value).toEqual(value)
|
||||
|
||||
// a simpler value comparison using ===
|
||||
expect(value).toBe(value)
|
||||
|
||||
// checks that the value is defined by checking its type.
|
||||
expect(value).toBeDefined()
|
||||
|
||||
// the following two matchers are using JavaScript's standard truthiness rules
|
||||
expect(value).toBeTruthy()
|
||||
expect(value).toBeFalsy()
|
||||
|
||||
// verify that the value matches the given regular expression. The regular
|
||||
// expression may be passed in form of a string or a regular expression
|
||||
// object.
|
||||
expect(value).toMatch(expectedRegExp)
|
||||
|
||||
// a check for null using ===
|
||||
expect(value).toBeNull()
|
||||
|
||||
// Array.indexOf(...) is used internally to check whether the element is
|
||||
// contained within the array.
|
||||
expect(value).toContain(expected)
|
||||
|
||||
// number comparison using < and >
|
||||
expect(value).toBeLessThan(expected)
|
||||
expect(value).toBeGreaterThan(expected)
|
||||
</pre>
|
||||
|
||||
# Example
|
||||
See the {@link https://github.com/angular/angular-seed angular-seed} project for more examples.
|
||||
|
||||
## Conditional actions with element(...).query(fn)
|
||||
|
||||
E2E testing with angular scenario is highly asynchronous and hides a lot of complexity by
|
||||
queueing actions and expectations that can handle futures. From time to time, you might need
|
||||
conditional assertions or element selection. Even though you should generally try to avoid this
|
||||
(as it is can be sign for unstable tests), you can add conditional behavior with
|
||||
`element(...).query(fn)`. The following code listing shows how this function can be used to delete
|
||||
added entries (where an entry is some domain object) using the application's web interface.
|
||||
|
||||
Imagine the application to be structured into two views:
|
||||
|
||||
1. *Overview view* which lists all the added entries in a table and
|
||||
2. a *detail view* which shows the entries' details and contains a delete button. When clicking the
|
||||
delete button, the user is redirected back to the *overview page*.
|
||||
|
||||
<pre>
|
||||
beforeEach(function () {
|
||||
var deleteEntry = function () {
|
||||
browser().navigateTo('/entries');
|
||||
|
||||
// we need to select the <tbody> element as it might be the case that there
|
||||
// are no entries (and therefore no rows). When the selector does not
|
||||
// result in a match, the test would be marked as a failure.
|
||||
element('table tbody').query(function (tbody, done) {
|
||||
// ngScenario gives us a jQuery lite wrapped element. We call the
|
||||
// `children()` function to retrieve the table body's rows
|
||||
var children = tbody.children();
|
||||
|
||||
if (children.length > 0) {
|
||||
// if there is at least one entry in the table, click on the link to
|
||||
// the entry's detail view
|
||||
element('table tbody a').click();
|
||||
// and, after a route change, click the delete button
|
||||
element('.btn-danger').click();
|
||||
}
|
||||
|
||||
// if there is more than one entry shown in the table, queue another
|
||||
// delete action.
|
||||
if (children.length > 1) {
|
||||
deleteEntry();
|
||||
}
|
||||
|
||||
// remember to call `done()` so that ngScenario can continue
|
||||
// test execution.
|
||||
done();
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
// start deleting entries
|
||||
deleteEntry();
|
||||
});
|
||||
</pre>
|
||||
|
||||
In order to understand what is happening, we should emphasize that ngScenario calls are not
|
||||
immediately executed, but queued (in ngScenario terms, we would be talking about adding
|
||||
future actions). If we had only one entry in our table, than the following future actions
|
||||
would be queued:
|
||||
|
||||
<pre>
|
||||
// delete entry 1
|
||||
browser().navigateTo('/entries');
|
||||
element('table tbody').query(function (tbody, done) { ... });
|
||||
element('table tbody a');
|
||||
element('.btn-danger').click();
|
||||
</pre>
|
||||
|
||||
For two entries, ngScenario would have to work on the following queue:
|
||||
|
||||
<pre>
|
||||
// delete entry 1
|
||||
browser().navigateTo('/entries');
|
||||
element('table tbody').query(function (tbody, done) { ... });
|
||||
element('table tbody a');
|
||||
element('.btn-danger').click();
|
||||
|
||||
// delete entry 2
|
||||
// indented to represent "recursion depth"
|
||||
browser().navigateTo('/entries');
|
||||
element('table tbody').query(function (tbody, done) { ... });
|
||||
element('table tbody a');
|
||||
element('.btn-danger').click();
|
||||
</pre>
|
||||
|
||||
# Caveats
|
||||
|
||||
ngScenario does not work with apps that manually bootstrap using angular.bootstrap. You must use the ng-app directive.
|
||||
|
||||
@@ -3,10 +3,7 @@
|
||||
@description
|
||||
|
||||
In Angular, a controller is a JavaScript function(type/class) that is used to augment instances of
|
||||
angular {@link scope Scope}, excluding the root scope. When you or Angular create a new
|
||||
child scope object via the {@link api/ng.$rootScope.Scope#$new scope.$new} API , there is an
|
||||
option to pass in a controller as a method argument. This will tell Angular to associate the
|
||||
controller with the new scope and to augment its behavior.
|
||||
angular {@link scope Scope}, excluding the root scope.
|
||||
|
||||
Use controllers to:
|
||||
|
||||
@@ -24,9 +21,11 @@ constructor). Constructors are always applied to an existing scope object.
|
||||
|
||||
You set up the initial state of a scope by creating model properties. For example:
|
||||
|
||||
function GreetingCtrl($scope) {
|
||||
$scope.greeting = 'Hola!';
|
||||
}
|
||||
<pre>
|
||||
function GreetingCtrl($scope) {
|
||||
$scope.greeting = 'Hola!';
|
||||
}
|
||||
</pre>
|
||||
|
||||
The `GreetingCtrl` controller creates a `greeting` model which can be referred to in a template.
|
||||
|
||||
@@ -35,11 +34,13 @@ in the global scope. This is only for demonstration purposes - in a real
|
||||
application you should use the `.controller` method of your Angular module for
|
||||
your application as follows:
|
||||
|
||||
var myApp = angular.module('myApp',[]);
|
||||
<pre>
|
||||
var myApp = angular.module('myApp',[]);
|
||||
|
||||
myApp.controller('GreetingCtrl', ['$scope', function(scope) {
|
||||
scope.greeting = 'Hola!';
|
||||
}]);
|
||||
myApp.controller('GreetingCtrl', ['$scope', function($scope) {
|
||||
$scope.greeting = 'Hola!';
|
||||
}]);
|
||||
</pre>
|
||||
|
||||
Note also that we use the array notation to explicitly specify the dependency
|
||||
of the controller on the `$scope` service provided by Angular.
|
||||
@@ -74,16 +75,14 @@ you have to perform your own manual DOM manipulation, encapsulate the presentati
|
||||
{@link guide/directive directives}.
|
||||
- Input formatting — Use {@link forms angular form controls} instead.
|
||||
- Output filtering — Use {@link dev_guide.templates.filters angular filters} instead.
|
||||
- To run stateless or stateful code shared across controllers — Use {@link dev_guide.services angular
|
||||
- Sharing stateless or stateful code across controllers — Use {@link dev_guide.services angular
|
||||
services} instead.
|
||||
- To instantiate or manage the life-cycle of other components (for example, to create service
|
||||
instances).
|
||||
- Managing the life-cycle of other components (for example, to create service instances).
|
||||
|
||||
|
||||
# Associating Controllers with Angular Scope Objects
|
||||
|
||||
You can associate controllers with scope objects explicitly via the {@link api/ng.$rootScope.Scope#$new
|
||||
scope.$new} api or implicitly via the {@link api/ng.directive:ngController ngController
|
||||
You can associate controllers with scope objects implicitly via the {@link api/ng.directive:ngController ngController
|
||||
directive} or {@link api/ng.$route $route service}.
|
||||
|
||||
|
||||
@@ -178,8 +177,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) {
|
||||
@@ -267,7 +267,7 @@ describe('state', function() {
|
||||
var mainCtrl = $controller(MainCtrl, {$scope: mainScope});
|
||||
childScope = mainScope.$new();
|
||||
var childCtrl = $controller(ChildCtrl, {$scope: childScope});
|
||||
babyScope = childCtrl.$new();
|
||||
babyScope = childScope.$new();
|
||||
var babyCtrl = $controller(BabyCtrl, {$scope: babyScope});
|
||||
}));
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ occurs in controllers:
|
||||
|
||||
* Use an {@link expression angular expression} with an assignment operator in templates:
|
||||
|
||||
<button ng-click="{{foos='ball'}}">Click me</button>
|
||||
<button ng-click="{{foo='bar'}}">Click me</button>
|
||||
|
||||
* Use {@link api/ng.directive:ngInit ngInit directive} in templates (for toy/example apps
|
||||
only, not recommended for real applications):
|
||||
|
||||
@@ -98,7 +98,7 @@ To configure the `$location` service, retrieve the
|
||||
|
||||
- **hashPrefix(prefix)**: {string}<br />
|
||||
prefix used for Hashbang URLs (used in Hashbang mode or in legacy browser in Html5 mode)<br />
|
||||
default: `'!'`
|
||||
default: `""`
|
||||
|
||||
### Example configuration
|
||||
<pre>
|
||||
@@ -212,7 +212,7 @@ In this mode, `$location` uses Hashbang URLs in all browsers.
|
||||
it('should show example', inject(
|
||||
function($locationProvider) {
|
||||
$locationProvider.html5Mode(false);
|
||||
$locationProvider.hashPrefix = '!';
|
||||
$locationProvider.hashPrefix('!');
|
||||
},
|
||||
function($location) {
|
||||
// open http://host.com/base/index.html#!/a
|
||||
@@ -261,7 +261,7 @@ having to worry about whether the browser displaying your app supports the histo
|
||||
it('should show example', inject(
|
||||
function($locationProvider) {
|
||||
$locationProvider.html5Mode(true);
|
||||
$locationProvider.hashPrefix = '!';
|
||||
$locationProvider.hashPrefix('!');
|
||||
},
|
||||
function($location) {
|
||||
// in browser with HTML5 history support:
|
||||
@@ -619,21 +619,25 @@ to the $location object (using {@link api/ng.directive:input.text
|
||||
ngModel} directive on an input field), you will need to specify an extra model property
|
||||
(e.g. `locationPath`) with two watchers which push $location updates in both directions. For
|
||||
example:
|
||||
<pre>
|
||||
<!-- html -->
|
||||
<input type="text" ng-model="locationPath" />
|
||||
</pre>
|
||||
<pre>
|
||||
// js - controller
|
||||
$scope.$watch('locationPath', function(path) {
|
||||
$location.path(path);
|
||||
});
|
||||
|
||||
$scope.$watch('$location.path()', function(path) {
|
||||
scope.locationPath = path;
|
||||
});
|
||||
</pre>
|
||||
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<div ng-controller="LocationController">
|
||||
<input type="text" ng-model="locationPath" />
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
function LocationController($scope, $location) {
|
||||
$scope.$watch('locationPath', function(path) {
|
||||
$location.path(path);
|
||||
});
|
||||
$scope.$watch(function() {
|
||||
return $location.path();
|
||||
}, function(path) {
|
||||
$scope.locationPath = path;
|
||||
});
|
||||
}
|
||||
</file>
|
||||
</example>
|
||||
|
||||
# Related API
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ myController.$inject = ['$scope','notify'];
|
||||
<p>Let's try this simple notify service, injected into the controller...</p>
|
||||
<input ng-init="message='test'" ng-model="message" >
|
||||
<button ng-click="callNotify(message);">NOTIFY</button>
|
||||
<p>(you have to click 3 times to see an alert)</p>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
@@ -98,6 +99,7 @@ function myController($scope, notify) {
|
||||
<p>Let's try the notify service, that is implicitly injected into the controller...</p>
|
||||
<input ng-init="message='test'" ng-model="message">
|
||||
<button ng-click="callNotify(message);">NOTIFY</button>
|
||||
<p>(you have to click 3 times to see an alert)</p>
|
||||
</div>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
@@ -8,7 +8,7 @@ as the first argument. Any filter arguments are passed in as additional argument
|
||||
function.
|
||||
|
||||
The following sample filter reverses a text string. In addition, it conditionally makes the
|
||||
text upper-case and assigns color.
|
||||
text upper-case.
|
||||
|
||||
<doc:example module="MyReverseModule">
|
||||
<doc:source>
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
@name Developer Guide: Templates: Understanding Angular Filters
|
||||
@description
|
||||
|
||||
Angular filters format data for display to the user. In addition to formatting data, filters can
|
||||
also modify the DOM. This allows filters to handle tasks such as conditionally applying CSS styles
|
||||
to filtered output.
|
||||
Angular filters format data for display to the user.
|
||||
|
||||
For example, you might have a data object that needs to be formatted according to the locale before
|
||||
displaying it to the user. You can pass expressions through a chain of filters like this:
|
||||
|
||||
@@ -19,6 +19,10 @@ You can also pass colon-delimited arguments to filters, for example, to display
|
||||
|
||||
123 | number:2
|
||||
|
||||
Use the same syntax for multiple arguments:
|
||||
|
||||
myArray | orderBy:'timestamp':true
|
||||
|
||||
Here are some examples that show values before and after applying different filters to an
|
||||
expression in a binding:
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
@description
|
||||
|
||||
JavaScript is a dynamically typed language which comes with great power of expression, but it also
|
||||
come with almost no-help from the compiler. For this reason we feel very strongly that any code
|
||||
comes with almost no help from the compiler. For this reason we feel very strongly that any code
|
||||
written in JavaScript needs to come with a strong set of tests. We have built many features into
|
||||
Angular which makes testing your Angular applications easy. So there is no excuse for not testing.
|
||||
|
||||
@@ -97,7 +97,7 @@ function MyClass() {
|
||||
|
||||
While no new instance of the dependency is being created, it is fundamentally the same as `new`, in
|
||||
that there is no good way to intercept the call to `global.xhr` for testing purposes, other then
|
||||
through monkey patching. The basic issue for testing is that global variable needs to be mutated in
|
||||
through monkey patching. The basic issue for testing is that a global variable needs to be mutated in
|
||||
order to replace it with call to a mock method. For further explanation why this is bad see: {@link
|
||||
http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/ Brittle Global
|
||||
State & Singletons}
|
||||
@@ -248,10 +248,11 @@ function PasswordCtrl($scope) {
|
||||
and the test is straight forward
|
||||
|
||||
<pre>
|
||||
var pc = new PasswordCtrl();
|
||||
pc.password('abc');
|
||||
pc.grade();
|
||||
expect(pc.strength).toEqual('weak');
|
||||
var $scope = {};
|
||||
var pc = $controller('PasswordCtrl', { $scope: $scope });
|
||||
$scope.password = 'abc';
|
||||
$scope.grade();
|
||||
expect($scope.strength).toEqual('weak');
|
||||
</pre>
|
||||
|
||||
Notice that the test is not only much shorter but it is easier to follow what is going on. We say
|
||||
@@ -275,22 +276,66 @@ expect(length('abc')).toEqual(3);
|
||||
</pre>
|
||||
|
||||
## Directives
|
||||
Directives in angular are responsible for updating the DOM when the state of the model changes.
|
||||
Directives in angular are responsible for encapsulating complex functionality within custom HTML tags,
|
||||
attributes, classes or comments. Unit tests are very important for directives because the components
|
||||
you create with directives may be used throughout your application and in many different contexts.
|
||||
|
||||
## Mocks
|
||||
oue
|
||||
### Simple HTML Element Directive
|
||||
|
||||
## Global State Isolation
|
||||
oue
|
||||
Lets start with an angular app with no dependencies.
|
||||
|
||||
# Preferred way of Testing
|
||||
uo
|
||||
<pre>
|
||||
var app = angular.module('myApp', []);
|
||||
</pre>
|
||||
|
||||
## JavaScriptTestDriver
|
||||
ou
|
||||
Now we can add a directive to our app.
|
||||
|
||||
<pre>
|
||||
app.directive('aGreatEye', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
template: '<h1>lidless, wreathed in flame, {{1 + 1}} times</h1>'
|
||||
};
|
||||
});
|
||||
</pre>
|
||||
|
||||
This directive is used as a tag `<a-great-eye></a-great-eye>`. It replaces the entire tag with the
|
||||
template `<h1>lidless, wreathed in flame, {{1 + 1}} times</h1>`. Now we are going to write a jasmine unit test to
|
||||
verify this functionality. Note that the expression `{{1 + 1}}` times will also be evaluated in the rendered content.
|
||||
|
||||
<pre>
|
||||
describe('Unit testing great quotes', function() {
|
||||
var $compile;
|
||||
var $rootScope;
|
||||
|
||||
// Load the myApp module, which contains the directive
|
||||
beforeEach(module('myApp'));
|
||||
|
||||
// Store references to $rootScope and $compile
|
||||
// so they are available to all tests in this describe block
|
||||
beforeEach(inject(function(_$compile_, _$rootScope_){
|
||||
// The injector unwraps the underscores (_) from around the parameter names when matching
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
}));
|
||||
|
||||
it('Replaces the element with the appropriate content', function() {
|
||||
// Compile a piece of HTML containing the directive
|
||||
var element = $compile("<a-great-eye></a-great-eye>")($rootScope);
|
||||
// fire all the watches, so the scope expression {{1 + 1}} will be evaluated
|
||||
$rootScope.$digest();
|
||||
// Check that the compiled element contains the templated content
|
||||
expect(element.html()).toContain("lidless, wreathed in flame, 2 times");
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
We inject the $compile service and $rootScope before each jasmine test. The $compile service is used
|
||||
to render the aGreatEye directive. After rendering the directive we ensure that the directive has
|
||||
replaced the content and "lidless, wreathed in flame, 2 times" is present.
|
||||
|
||||
## Jasmine
|
||||
ou
|
||||
|
||||
## Sample project
|
||||
uoe
|
||||
See the {@link https://github.com/angular/angular-seed angular-seed} project for an example.
|
||||
|
||||
|
||||
+34
-29
@@ -14,7 +14,7 @@ book.
|
||||
|
||||
## DI in a nutshell
|
||||
|
||||
There are only three ways how an object or a function can get a hold of its dependencies:
|
||||
There are only three ways an object or a function can get a hold of its dependencies:
|
||||
|
||||
1. The dependency can be created, typically using the `new` operator.
|
||||
|
||||
@@ -23,8 +23,8 @@ There are only three ways how an object or a function can get a hold of its depe
|
||||
3. The dependency can be passed in to where it is needed.
|
||||
|
||||
|
||||
The first two option of creating or looking up dependencies are not optimal, because they hard
|
||||
code the dependency, making it difficult, if not impossible, to modify the dependencies.
|
||||
The first two options of creating or looking up dependencies are not optimal because they hard
|
||||
code the dependency. This makes it difficult, if not impossible, to modify the dependencies.
|
||||
This is especially problematic in tests, where it is often desirable to provide mock dependencies
|
||||
for test isolation.
|
||||
|
||||
@@ -33,7 +33,7 @@ dependency from the component. The dependency is simply handed to the component.
|
||||
|
||||
<pre>
|
||||
function SomeClass(greeter) {
|
||||
this.greeter = greeter
|
||||
this.greeter = greeter;
|
||||
}
|
||||
|
||||
SomeClass.prototype.doSomething = function(name) {
|
||||
@@ -41,18 +41,18 @@ dependency from the component. The dependency is simply handed to the component.
|
||||
}
|
||||
</pre>
|
||||
|
||||
In the above example the `SomeClass` is not concerned with locating the `greeter` dependency, it
|
||||
In the above example `SomeClass` is not concerned with locating the `greeter` dependency, it
|
||||
is simply handed the `greeter` at runtime.
|
||||
|
||||
This is desirable, but it puts the responsibility of getting hold of the dependency onto the
|
||||
code responsible for the construction of `SomeClass`.
|
||||
This is desirable, but it puts the responsibility of getting hold of the dependency on the
|
||||
code that constructs `SomeClass`.
|
||||
|
||||
To manage the responsibility of dependency creation, each Angular application has an {@link
|
||||
api/angular.injector injector}. The injector is a service locator that is responsible for
|
||||
construction and lookup of dependencies.
|
||||
|
||||
Here is an example of using the injector service:
|
||||
|
||||
Here is an example of using the injector service.
|
||||
<pre>
|
||||
// Provide the wiring information in a module
|
||||
angular.module('myModule', []).
|
||||
@@ -101,21 +101,21 @@ dependency lookup responsibility to the injector by declaring the dependencies a
|
||||
</pre>
|
||||
|
||||
Notice that by having the `ng-controller` instantiate the class, it can satisfy all of the
|
||||
dependencies of the `MyController` without the controller ever knowing about the injector. This is
|
||||
the best outcome. The application code simply ask for the dependencies it needs, without having to
|
||||
dependencies of `MyController` without the controller ever knowing about the injector. This is
|
||||
the best outcome. The application code simply asks for the dependencies it needs, without having to
|
||||
deal with the injector. This setup does not break the Law of Demeter.
|
||||
|
||||
# Dependency Annotation
|
||||
## Dependency Annotation
|
||||
|
||||
How does the injector know what service needs to be injected?
|
||||
How does the injector know what service needs to be injected?
|
||||
|
||||
The application developer needs to provide annotation information that the injector uses in order
|
||||
to resolve the dependencies. Throughout Angular certain API functions are invoked using the
|
||||
to resolve the dependencies. Throughout Angular, certain API functions are invoked using the
|
||||
injector, as per the API documentation. The injector needs to know what services to inject into
|
||||
the function. Below are three equivalent ways of annotating your code with service name
|
||||
information. These can be used interchangeably as you see fit and are equivalent.
|
||||
|
||||
# Inferring Dependencies
|
||||
### Inferring Dependencies
|
||||
|
||||
The simplest way to get hold of the dependencies, is to assume that the function parameter names
|
||||
are the names of the dependencies.
|
||||
@@ -134,10 +134,10 @@ While straightforward, this method will not work with JavaScript minifiers/obfus
|
||||
rename the method parameter names. This makes this way of annotating only useful for {@link
|
||||
http://www.pretotyping.org/ pretotyping}, and demo applications.
|
||||
|
||||
# `$inject` Annotation
|
||||
### `$inject` Annotation
|
||||
|
||||
To allow the minifers to rename the function parameters and still be able to inject right services
|
||||
the function needs to be annotate with the `$inject` property. The `$inject` property is an array
|
||||
the function needs to be annotated with the `$inject` property. The `$inject` property is an array
|
||||
of service names to inject.
|
||||
|
||||
<pre>
|
||||
@@ -153,22 +153,24 @@ function declaration.
|
||||
This method of annotation is useful for controller declarations since it assigns the annotation
|
||||
information with the function.
|
||||
|
||||
# Inline Annotation
|
||||
### Inline Annotation
|
||||
|
||||
Sometimes using the `$inject` annotation style is not convenient such as when annotating
|
||||
directives.
|
||||
|
||||
For example:
|
||||
|
||||
<pre>
|
||||
someModule.factory('greeter', function($window) {
|
||||
...;
|
||||
...
|
||||
});
|
||||
</pre>
|
||||
|
||||
Results in code bloat due to the need of temporary variable:
|
||||
Results in code bloat due to needing a temporary variable:
|
||||
|
||||
<pre>
|
||||
var greeterFactory = function(renamed$window) {
|
||||
...;
|
||||
...
|
||||
};
|
||||
|
||||
greeterFactory.$inject = ['$window'];
|
||||
@@ -177,43 +179,46 @@ Results in code bloat due to the need of temporary variable:
|
||||
</pre>
|
||||
|
||||
For this reason the third annotation style is provided as well.
|
||||
|
||||
<pre>
|
||||
someModule.factory('greeter', ['$window', function(renamed$window) {
|
||||
...;
|
||||
...
|
||||
}]);
|
||||
</pre>
|
||||
|
||||
Keep in mind that all of the annotation styles are equivalent and can be used anywhere in Angular
|
||||
where injection is supported.
|
||||
|
||||
# Where can I use DI?
|
||||
## Where can I use DI?
|
||||
|
||||
DI is pervasive throughout Angular. It is typically used in controllers and factory methods.
|
||||
|
||||
## DI in controllers
|
||||
### DI in controllers
|
||||
|
||||
Controllers are classes which are responsible for application behavior. The recommended way of
|
||||
declaring controllers is:
|
||||
declaring controllers is using the array notation:
|
||||
|
||||
<pre>
|
||||
var MyController = function($scope, dep1, dep2) {
|
||||
someModule.controller('MyController', ['$scope', 'dep1', 'dep2', function($scope, dep1, dep2) {
|
||||
...
|
||||
$scope.aMethod = function() {
|
||||
...
|
||||
}
|
||||
}
|
||||
MyController.$inject = ['$scope', 'dep1', 'dep2'];
|
||||
...
|
||||
}]);
|
||||
</pre>
|
||||
|
||||
This avoids the creation of global functions for controllers and also protects against minification.
|
||||
|
||||
## Factory methods
|
||||
|
||||
### Factory methods
|
||||
|
||||
Factory methods are responsible for creating most objects in Angular. Examples are directives,
|
||||
services, and filters. The factory methods are registered with the module, and the recommended way
|
||||
of declaring factories is:
|
||||
|
||||
<pre>
|
||||
angualar.module('myModule', []).
|
||||
angular.module('myModule', []).
|
||||
config(['depProvider', function(depProvider){
|
||||
...
|
||||
}]).
|
||||
|
||||
@@ -39,11 +39,11 @@ the following example.
|
||||
</script>
|
||||
<div ng-controller="Ctrl1">
|
||||
Hello <input ng-model='name'> <hr/>
|
||||
<span ng:bind="name"> <span ng:bind="name"></span> <br/>
|
||||
<span ng_bind="name"> <span ng_bind="name"></span> <br/>
|
||||
<span ng-bind="name"> <span ng-bind="name"></span> <br/>
|
||||
<span data-ng-bind="name"> <span data-ng-bind="name"></span> <br/>
|
||||
<span x-ng-bind="name"> <span x-ng-bind="name"></span> <br/>
|
||||
<span ng:bind="name"> <span ng:bind="name"></span> <br/>
|
||||
<span ng_bind="name"> <span ng_bind="name"></span> <br/>
|
||||
<span ng-bind="name"> <span ng-bind="name"></span> <br/>
|
||||
<span data-ng-bind="name"> <span data-ng-bind="name"></span> <br/>
|
||||
<span x-ng-bind="name"> <span x-ng-bind="name"></span> <br/>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
@@ -95,7 +95,7 @@ Compilation of HTML happens in three phases:
|
||||
var $compile = ...; // injected into your code
|
||||
var scope = ...;
|
||||
|
||||
var html = '<div ng-bind='exp'></div>';
|
||||
var html = '<div ng-bind="exp"></div>';
|
||||
|
||||
// Step 1: parse HTML into DOM element
|
||||
var template = angular.element(html);
|
||||
@@ -132,7 +132,7 @@ able to quickly stamp out new `li`s for every `action` in `user.actions`. This m
|
||||
to save a clean copy of the `li` element for cloning purposes and as new `action`s are inserted,
|
||||
the template `li` element needs to be cloned and inserted into `ul`. But cloning the `li` element
|
||||
is not enough. It also needs to compile the `li` so that its directives such as
|
||||
`{{action.descriptions}}` evaluate against the right {@link api/ng.$rootScope.Scope
|
||||
`{{action.description}}` evaluate against the right {@link api/ng.$rootScope.Scope
|
||||
scope}. A naive method would be to simply insert a copy of the `li` element and then compile it.
|
||||
But compiling on every `li` element clone would be slow, since the compilation requires that we
|
||||
traverse the DOM tree and look for directives and execute them. If we put the compilation inside a
|
||||
@@ -206,7 +206,7 @@ In this example we will build a directive that displays the current time.
|
||||
}
|
||||
|
||||
// listen on DOM destroy (removal) event, and cancel the next UI update
|
||||
// to prevent updating time ofter the DOM element was removed.
|
||||
// to prevent updating time after the DOM element was removed.
|
||||
element.bind('$destroy', function() {
|
||||
$timeout.cancel(timeoutId);
|
||||
});
|
||||
@@ -225,7 +225,12 @@ In this example we will build a directive that displays the current time.
|
||||
|
||||
# Writing directives (long version)
|
||||
|
||||
An example skeleton of the directive is shown here, for the complete list see below.
|
||||
There are different ways to declare a directive. The difference resides in the return
|
||||
value of the factory function. You can either return a Directive Definition Object
|
||||
(see below) that defines the directive properties, or just the postLink function
|
||||
of such an object (all other properties will have the default values).
|
||||
|
||||
Here's an example directive declared with a Directive Definition Object:
|
||||
|
||||
<pre>
|
||||
var myModule = angular.module(...);
|
||||
@@ -239,6 +244,8 @@ An example skeleton of the directive is shown here, for the complete list see be
|
||||
transclude: false,
|
||||
restrict: 'A',
|
||||
scope: false,
|
||||
controller: ["$scope", "$element", "$attrs", "$transclude", "otherInjectables",
|
||||
function($scope, $element, $attrs, $transclude, otherInjectables) { ... }],
|
||||
compile: function compile(tElement, tAttrs, transclude) {
|
||||
return {
|
||||
pre: function preLink(scope, iElement, iAttrs, controller) { ... },
|
||||
@@ -251,12 +258,11 @@ An example skeleton of the directive is shown here, for the complete list see be
|
||||
});
|
||||
</pre>
|
||||
|
||||
In most cases you will not need such fine control and so the above can be simplified. All of the
|
||||
different parts of this skeleton are explained in following sections. In this section we are
|
||||
interested only in some of this skeleton.
|
||||
In most cases you will not need such fine control and so the above can be simplified. You can still
|
||||
return a Directive Definition Object, but only setting the 'compile' function property of the Object,
|
||||
and rely on the default values for other properties.
|
||||
|
||||
The first step in simplyfing the code is to rely on the default values. Therefore the above can be
|
||||
simplified as:
|
||||
Therefore the above can be simplified as:
|
||||
|
||||
<pre>
|
||||
var myModule = angular.module(...);
|
||||
@@ -271,8 +277,10 @@ simplified as:
|
||||
});
|
||||
</pre>
|
||||
|
||||
Most directives concern themselves only with instances, not with template transformations, allowing
|
||||
further simplification:
|
||||
Finally, most directives concern themselves only with instances, not with template transformations, allowing
|
||||
further simplification.
|
||||
|
||||
Here we only define the postLink function:
|
||||
|
||||
<pre>
|
||||
var myModule = angular.module(...);
|
||||
@@ -300,8 +308,9 @@ compiler}. The attributes are:
|
||||
|
||||
* `priority` - When there are multiple directives defined on a single DOM element, sometimes it
|
||||
is necessary to specify the order in which the directives are applied. The `priority` is used
|
||||
to sort the directives before their `compile` functions get called. Higher `priority` goes
|
||||
first. The order of directives within the same priority is undefined.
|
||||
to sort the directives before their `compile` functions get called. Priority is defined as a
|
||||
number. Directives with greater numerical `priority` are compiled first. The order of directives with
|
||||
the same priority is undefined. The default priority is `0`.
|
||||
|
||||
* `terminal` - If set to true then the current `priority` will be the last set of directives
|
||||
which will execute (any directives at the current priority will still execute
|
||||
@@ -355,10 +364,16 @@ compiler}. The attributes are:
|
||||
|
||||
* `$scope` - Current scope associated with the element
|
||||
* `$element` - Current element
|
||||
* `$attrs` - Current attributes obeject for the element
|
||||
* `$attrs` - Current attributes object for the element
|
||||
* `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
|
||||
`function(cloneLinkingFn)`.
|
||||
|
||||
To avoid errors after minification the bracket notation should be used:
|
||||
|
||||
<pre>
|
||||
controller: ['$scope', '$element', '$attrs', '$transclude', function($scope, $element, $attrs, $transclude) { ... }]
|
||||
</pre>
|
||||
|
||||
* `require` - Require another controller be passed into current directive linking function. The
|
||||
`require` takes a name of the directive controller to pass in. If no such controller can be
|
||||
found an error is raised. The name can be prefixed with:
|
||||
@@ -376,8 +391,8 @@ compiler}. The attributes are:
|
||||
* `M` - Comment: `<!-- directive: my-directive exp -->`
|
||||
|
||||
* `template` - replace the current element with the contents of the HTML. The replacement process
|
||||
migrates all of the attributes / classes from the old element to the new one. See Creating
|
||||
Widgets section below for more information.
|
||||
migrates all of the attributes / classes from the old element to the new one. See the
|
||||
{@link guide/directive#Components Creating Components} section below for more information.
|
||||
|
||||
* `templateUrl` - Same as `template` but the template is loaded from the specified URL. Because
|
||||
the template loading is asynchronous the compilation/linking is suspended until the template
|
||||
@@ -432,8 +447,8 @@ done in a linking function rather than in a compile function.
|
||||
|
||||
A compile function can have a return value which can be either a function or an object.
|
||||
|
||||
* returning a function - is equivalent to registering the linking function via the `link` property
|
||||
of the config object when the compile function is empty.
|
||||
* returning a (post-link) function - is equivalent to registering the linking function via the
|
||||
`link` property of the config object when the compile function is empty.
|
||||
|
||||
* returning an object with function(s) registered via `pre` and `post` properties - allows you to
|
||||
control when a linking function should be called during the linking phase. See info about
|
||||
@@ -483,7 +498,7 @@ The {@link api/ng.$compile.directive.Attributes Attributes} object - passed as a
|
||||
link() or compile() functions - is a way of accessing:
|
||||
|
||||
* *normalized attribute names:* Since a directive such as 'ngBind' can be expressed in many ways
|
||||
such as 'ng:bind', or 'x-ng-bind', the attributes object allows for normalized accessed to
|
||||
such as 'ng:bind', or 'x-ng-bind', the attributes object allows for normalized access to
|
||||
the attributes.
|
||||
|
||||
* *directive inter-communication:* All directives share the same instance of the attributes
|
||||
@@ -601,6 +616,7 @@ restrict: 'E',
|
||||
replace: true
|
||||
</pre>
|
||||
|
||||
<a name="Components"></a>
|
||||
# Creating Components
|
||||
|
||||
It is often desirable to replace a single directive with a more complex DOM structure. This
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
@description
|
||||
|
||||
Expressions are JavaScript-like code snippets that are usually placed in bindings such as `{{
|
||||
expression }}`. Expressions are processed by {@link api/ng.$parse $parse}
|
||||
expression }}`. Expressions are processed by the {@link api/ng.$parse $parse}
|
||||
service.
|
||||
|
||||
For example, these are all valid expressions in angular:
|
||||
@@ -23,8 +23,8 @@ You can think of Angular expressions as JavaScript expressions with following di
|
||||
evaluation, unlike in JavaScript where the expressions are evaluated against the global
|
||||
`window`.
|
||||
|
||||
* **Forgiving:** expression evaluation is forgiving to undefined and null, unlike in JavaScript,
|
||||
where such evaluations generate `NullPointerExceptions`.
|
||||
* **Forgiving:** expression evaluation is forgiving to `undefined` and `null`, unlike in JavaScript,
|
||||
where trying to evaluate undefined properties can generate `ReferenceError` or `TypeError`.
|
||||
|
||||
* **No Control Flow Statements:** you cannot do any of the following in angular expression:
|
||||
conditionals, loops, or throw.
|
||||
@@ -179,7 +179,7 @@ angular uses, to differentiate its API names from others. If angular didn't use
|
||||
`a.length()` would return undefined because neither a nor angular define such a property.
|
||||
|
||||
Consider that in a future version of Angular we might choose to add a length method, in which case
|
||||
the behavior of the expression would change. Worse yet, you the developer could create a length
|
||||
the behavior of the expression would change. Worse yet, you, the developer, could create a length
|
||||
property and then we would have a collision. This problem exists because Angular augments existing
|
||||
objects with additional behavior. By prefixing its additions with $ we are reserving our namespace
|
||||
so that angular developers and developers who use Angular can develop in harmony without collisions.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
@name Forms
|
||||
@description
|
||||
|
||||
Controls (`input`, `select`, `textarea`) are a way for user to enter data.
|
||||
Form is a collection of controls for the purpose of grouping related controls together.
|
||||
Controls (`input`, `select`, `textarea`) are ways for a user to enter data.
|
||||
A Form is a collection of controls for the purpose of grouping related controls together.
|
||||
|
||||
Form and controls provide validation services, so that the user can be notified of invalid input.
|
||||
This provides a better user experience, because the user gets instant feedback on how to correct the error.
|
||||
@@ -93,10 +93,10 @@ This ensures that the user is not distracted with an error until after interacti
|
||||
|
||||
<script>
|
||||
function Controller($scope) {
|
||||
$scope.master= {};
|
||||
$scope.master = {};
|
||||
|
||||
$scope.update = function(user) {
|
||||
$scope.master= angular.copy(user);
|
||||
$scope.master = angular.copy(user);
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
@@ -113,7 +113,7 @@ This ensures that the user is not distracted with an error until after interacti
|
||||
|
||||
# Binding to form and control state
|
||||
|
||||
A form is in instance of {@link api/ng.directive:form.FormController FormController}.
|
||||
A form is an instance of {@link api/ng.directive:form.FormController FormController}.
|
||||
The form instance can optionally be published into the scope using the `name` attribute.
|
||||
Similarly control is an instance of {@link api/ng.directive:ngModel.NgModelController NgModelController}.
|
||||
The control instance can similarly be published into the form instance using the `name` attribute.
|
||||
@@ -278,7 +278,7 @@ However, if you need more flexibility, you can write your own form control as a
|
||||
|
||||
In order for custom control to work with `ngModel` and to achieve two-way data-binding it needs to:
|
||||
|
||||
- implement `render` method, which is responsible for rendering the data after it passed the {@link api/ng.directive:ngModel.NgModelController#$formatters NgModelController#$formatters},
|
||||
- implement `$render` method, which is responsible for rendering the data after it passed the {@link api/ng.directive:ngModel.NgModelController#$formatters NgModelController#$formatters},
|
||||
- call `$setViewValue` method, whenever the user interacts with the control and model needs to be updated. This is usually done inside a DOM Event listener.
|
||||
|
||||
See {@link guide/directive $compileProvider.directive} for more info.
|
||||
|
||||
@@ -53,7 +53,7 @@ There are two approaches to providing locale rules to Angular:
|
||||
You can pre-bundle the desired locale file with Angular by concatenating the content of the
|
||||
locale-specific file to the end of `angular.js` or `angular.min.js` file.
|
||||
|
||||
For example on *nix, to create a an angular.js file that contains localization rules for german
|
||||
For example on *nix, to create an angular.js file that contains localization rules for german
|
||||
locale, you can do the following:
|
||||
|
||||
`cat angular.js i18n/angular-locale_de-ge.js > angular_de-ge.js`
|
||||
|
||||
+45
-24
@@ -15,33 +15,54 @@ To make your Angular application work on IE please make sure that:
|
||||
1. You polyfill JSON.stringify if necessary (IE7 will need this). You can use
|
||||
[JSON2](https://github.com/douglascrockford/JSON-js) or
|
||||
[JSON3](http://bestiejs.github.com/json3/) polyfills for this.
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<head>
|
||||
<!--[if lte IE 8]>
|
||||
<script src="/path/to/json2.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
...
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
2. you **do not** use custom element tags such as `<ng:view>` (use the attribute version
|
||||
2. add `id="ng-app"` to the root element in conjunction with `ng-app` attribute
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="optionalModuleName">
|
||||
...
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
3. you **do not** use custom element tags such as `<ng:view>` (use the attribute version
|
||||
`<div ng-view>` instead), or
|
||||
|
||||
3. if you **do use** custom element tags, then you must take these steps to make IE happy:
|
||||
|
||||
<pre>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<head>
|
||||
<!--[if lte IE 8]>
|
||||
<script>
|
||||
document.createElement('ng-include');
|
||||
document.createElement('ng-pluralize');
|
||||
document.createElement('ng-view');
|
||||
|
||||
// Optionally these for CSS
|
||||
document.createElement('ng:include');
|
||||
document.createElement('ng:pluralize');
|
||||
document.createElement('ng:view');
|
||||
</script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
...
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
4. if you **do use** custom element tags, then you must take these steps to make IE happy:
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="optionalModuleName">
|
||||
<head>
|
||||
<!--[if lte IE 8]>
|
||||
<script>
|
||||
document.createElement('ng-include');
|
||||
document.createElement('ng-pluralize');
|
||||
document.createElement('ng-view');
|
||||
|
||||
// Optionally these for CSS
|
||||
document.createElement('ng:include');
|
||||
document.createElement('ng:pluralize');
|
||||
document.createElement('ng:view');
|
||||
</script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
...
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
The **important** parts are:
|
||||
|
||||
|
||||
@@ -158,9 +158,9 @@ angular.module('myModule', []).
|
||||
|
||||
angular.module('myModule', []).
|
||||
config(function($provide, $compileProvider, $filterProvider) {
|
||||
$provide.value('a', 123)
|
||||
$provide.factory('a', function() { return 123; })
|
||||
$compileProvider.directive('directiveName', ...).
|
||||
$provide.value('a', 123);
|
||||
$provide.factory('a', function() { return 123; });
|
||||
$compileProvider.directive('directiveName', ...);
|
||||
$filterProvider.register('filterName', ...);
|
||||
});
|
||||
</pre>
|
||||
@@ -180,7 +180,7 @@ ignored in the unit-tests.
|
||||
|
||||
Modules can list other modules as their dependencies. Depending on a module implies that required
|
||||
module needs to be loaded before the requiring module is loaded. In other words the configuration
|
||||
blocks of the required modules execute before the configuration blocks or the requiring module.
|
||||
blocks of the required modules execute before the configuration blocks of the requiring module.
|
||||
The same is true for the run blocks. Each module can only be loaded once, even if multiple other
|
||||
modules require it.
|
||||
|
||||
@@ -191,6 +191,24 @@ scripts into a VM. There are existing projects which deal with script loading, w
|
||||
with Angular. Because modules do nothing at load time they can be loaded into the VM in any order
|
||||
and thus script loaders can take advantage of this property and parallelize the loading process.
|
||||
|
||||
## Creation versus Retrieval
|
||||
|
||||
Beware that using `angular.module('myModule', [])` will create the module `myModule` and overwrite any
|
||||
existing module named `myModule`. Use `angular.module('myModule')` to retrieve an existing module.
|
||||
|
||||
<pre>
|
||||
var myModule = angular.module('myModule', []);
|
||||
|
||||
// add some directives and services
|
||||
myModule.service('myService', ...);
|
||||
myModule.directive('myDirective', ...);
|
||||
|
||||
// overwrites both myService and myDirective by creating a new module
|
||||
var myModule = angular.module('myModule', []);
|
||||
|
||||
// throws an error because myOtherModule has yet to be defined
|
||||
var myModule = angular.module('myOtherModule');
|
||||
</pre>
|
||||
|
||||
# Unit Testing
|
||||
|
||||
|
||||
@@ -8,28 +8,28 @@
|
||||
AngularJS is a structural framework for dynamic web apps. It lets you use HTML as your template
|
||||
language and lets you extend HTML's syntax to express your application's components clearly and
|
||||
succinctly. Out of the box, it eliminates much of the code you currently write through data
|
||||
binding and dependency injection. And it all happens in JavaScript within the browser making it an
|
||||
ideal partner with any server technology.
|
||||
binding and dependency injection. And it all happens in JavaScript within the browser, making it
|
||||
an ideal partner with any server technology.
|
||||
|
||||
Angular is what HTML would have been had it been designed for applications. HTML is a great
|
||||
declarative language for static documents. It does not contain much in the way of creating
|
||||
applications, and as a result building web applications is an exercise in *what do I have to do, so
|
||||
that I trick the browser in to doing what I want.*
|
||||
applications, and as a result building web applications is an exercise in *what do I have to do
|
||||
to trick the browser into doing what I want.*
|
||||
|
||||
The impedance mismatch between dynamic applications and static documents is often solved as:
|
||||
The impedance mismatch between dynamic applications and static documents is often solved with:
|
||||
|
||||
* **library** - a collection of functions which are useful when writing web apps. Your code is
|
||||
* **a library** - a collection of functions which are useful when writing web apps. Your code is
|
||||
in charge and it calls into the library when it sees fit. E.g., `jQuery`.
|
||||
* **frameworks** - a particular implementation of a web application, where your code fills in
|
||||
the details. The framework is in charge and it calls into your code when it needs something
|
||||
app specific. E.g., `knockout`, `sproutcore`, etc.
|
||||
app specific. E.g., `knockout`, `ember`, etc.
|
||||
|
||||
|
||||
Angular takes another approach. It attempts to minimize the impedance mismatch between document
|
||||
centric HTML and what an application needs by creating new HTML constructs. Angular teaches the
|
||||
browser new syntax through a construct we call directives. Examples include:
|
||||
|
||||
* Data binding as in `{{}}`.
|
||||
* Data binding, as in `{{}}`.
|
||||
* DOM control structures for repeating/hiding DOM fragments.
|
||||
* Support for forms and form validation.
|
||||
* Attaching code-behind to DOM elements.
|
||||
@@ -37,13 +37,13 @@ browser new syntax through a construct we call directives. Examples include:
|
||||
|
||||
|
||||
|
||||
## End-to-end solution
|
||||
## A complete client-side solution
|
||||
|
||||
Angular tries to be an end-to-end solution, when building a web application. This means it is
|
||||
not a single piece in an overall puzzle of building a web application, but an end-to-end solution.
|
||||
This makes Angular opinionated about how a CRUD application should be built. But while it is
|
||||
opinionated, it also tries to make sure that its opinion is just a starting point, which you can
|
||||
easily change. Angular comes with the following out-of-the-box:
|
||||
Angular is not a single piece in the overall puzzle of building the client-side of a web
|
||||
application. It handles all of the DOM and AJAX glue code you once wrote by hand and puts it in a
|
||||
well-defined structure. This makes Angular opinionated about how a CRUD application should be
|
||||
built. But while it is opinionated, it also tries to make sure that its opinion is just a
|
||||
starting point you can easily change. Angular comes with the following out-of-the-box:
|
||||
|
||||
* Everything you need to build a CRUD app in a cohesive set: data-binding, basic templating
|
||||
directives, form validation, routing, deep-linking, reusable components, dependency injection.
|
||||
@@ -90,7 +90,7 @@ concepts which the application developer may face:
|
||||
<table>
|
||||
<tr><td>Quantity</td><td>Cost</td></tr>
|
||||
<tr>
|
||||
<td><input type="integer" min="0" ng-model="qty" required ></td>
|
||||
<td><input type="number" ng-pattern="/\d+/" step="1" min="0" ng-model="qty" required ></td>
|
||||
<td><input type="number" ng-model="cost" required ></td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -124,7 +124,7 @@ We load Angular using the `<script>` tag:
|
||||
From the `ng-model` attribute of the `<input>` tags, Angular automatically sets up two-way data
|
||||
binding, and we also demonstrate some easy input validation:
|
||||
|
||||
Quantity: <input type="integer" min="0" ng-model="qty" required >
|
||||
Quantity: <input type="number" ng-pattern="/\d+/" step="1" min="0" ng-model="qty" required >
|
||||
Cost: <input type="number" ng-model="cost" required >
|
||||
|
||||
These input widgets look normal enough, but consider these points:
|
||||
@@ -134,7 +134,7 @@ These input widgets look normal enough, but consider these points:
|
||||
Model-View-Controller design pattern.
|
||||
* Note that the HTML widget {@link api/ng.directive:input input}
|
||||
has special powers. The input invalidates itself by turning red when you enter invalid data or
|
||||
leave the the input fields blank. These new widget behaviors make it easier to implement field
|
||||
leave the input fields blank. These new widget behaviors make it easier to implement field
|
||||
validation common in CRUD applications.
|
||||
|
||||
And finally, the mysterious `{{ double curly braces }}`:
|
||||
@@ -202,6 +202,6 @@ Angular frees you from the following pain:
|
||||
|
||||
# Watch a Presentation About Angular
|
||||
|
||||
Here is a presentation on Angular from May 2012.
|
||||
Here is a presentation on Angular from May 2012. The {@link http://mhevery.github.io/angular-demo-slides/index.html#/list corresponding slides} are also available.
|
||||
|
||||
<iframe width="560" height="315" src="http://www.youtube.com/embed/bfrn5VNpwsg" frameborder="0" allowfullscreen></iframe>
|
||||
|
||||
@@ -165,7 +165,7 @@ Scopes are attached to the DOM as `$scope` data property, and can be retrieved f
|
||||
purposes. (It is unlikely that one would need to retrieve scopes in this way inside the
|
||||
application.) The location where the root scope is attached to the DOM is defined by the location
|
||||
of {@link api/ng.directive:ngApp `ng-app`} directive. Typically
|
||||
`ng-app` is placed an the `<html>` element, but it can be placed on other elements as well, if,
|
||||
`ng-app` is placed on the `<html>` element, but it can be placed on other elements as well, if,
|
||||
for example, only a portion of the view needs to be controlled by Angular.
|
||||
|
||||
To examine the scope in the debugger:
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Type
|
||||
@description
|
||||
@@ -81,23 +81,27 @@ Several steps are needed to check out and build AngularJS:
|
||||
Before you can build AngularJS, you must install or configure the following dependencies on your
|
||||
machine:
|
||||
|
||||
* {@link http://rake.rubyforge.org Rake}: We use Rake as our build system, which is pre-installed
|
||||
on most Macintosh and Linux machines. If that is not true in your case, you can grab it from the
|
||||
Rake website.
|
||||
|
||||
* Git: The {@link http://help.github.com/mac-git-installation Github Guide to Installing Git} is
|
||||
quite a good source for information on Git.
|
||||
|
||||
* {@link http://nodejs.org Node.js}: We use Node to generate the documentation and to run a
|
||||
development web server. Depending on your system, you can install Node either from source or as a
|
||||
* {@link http://nodejs.org Node.js}: We use Node to generate the documentation, run a
|
||||
development web server, run tests, and generate a build. Depending on your system, you can install Node either from source or as a
|
||||
pre-packaged bundle.
|
||||
|
||||
* {@link http://www.java.com Java}: JavaScript is minified using
|
||||
{@link https://developers.google.com/closure/ Closure Tools} jar. Make sure you have Java (version 6 or higher) installed
|
||||
and included in your {@link http://docs.oracle.com/javase/tutorial/essential/environment/paths.html PATH} variable.
|
||||
|
||||
Once installed, you'll also need several npms (node packages), which you can install once you checked out a local copy
|
||||
of the Angular repository (see below) with:
|
||||
|
||||
* `cd angular.js`
|
||||
* `npm install`
|
||||
|
||||
* {@link http://gruntjs.com Grunt}: We use Grunt as our build system. Install the grunt command-line tool globally with:
|
||||
|
||||
* `sudo npm install -g grunt-cli`
|
||||
|
||||
|
||||
## Creating a Github Account and Forking Angular
|
||||
|
||||
@@ -108,7 +112,7 @@ https://github.com/angular/angular.js main angular repository}.
|
||||
|
||||
## Building AngularJS
|
||||
|
||||
To build AngularJS, you check out the source code and use Rake to generate the non-minified and
|
||||
To build AngularJS, you check out the source code and use Grunt to generate the non-minified and
|
||||
minified AngularJS files:
|
||||
|
||||
1. To clone your Github repository, run:
|
||||
@@ -129,7 +133,11 @@ minified AngularJS files:
|
||||
|
||||
5. To build AngularJS, run:
|
||||
|
||||
rake package
|
||||
grunt package
|
||||
|
||||
NOTE: If you're using Windows you must run your command line with administrative privileges (right click, run as
|
||||
Administrator).
|
||||
|
||||
|
||||
The build output can be located under the `build` directory. It consists of the following files and
|
||||
directories:
|
||||
@@ -158,7 +166,7 @@ made available a local web server based on Node.js.
|
||||
|
||||
1. To start the web server, run:
|
||||
|
||||
rake webserver
|
||||
grunt webserver
|
||||
|
||||
2. To access the local server, go to this website:
|
||||
|
||||
@@ -170,33 +178,35 @@ made available a local web server based on Node.js.
|
||||
<a name="unit-tests"></a>
|
||||
## Running the Unit Test Suite
|
||||
|
||||
Our unit and integration tests are written with Jasmine and executed with Testacular. To run all of the
|
||||
Our unit and integration tests are written with Jasmine and executed with Karma. To run all of the
|
||||
tests once on Chrome run:
|
||||
|
||||
rake test:unit
|
||||
grunt test:unit
|
||||
|
||||
To run the tests on other browsers (Chrome, ChromeCanary, Firefox, Opera and Safari are pre-configured) use:
|
||||
|
||||
rake test:unit[Opera+Firefox]
|
||||
grunt test:unit --browsers Opera,Firefox
|
||||
|
||||
Note there should be _no spaces between browsers_. `Opera, Firefox` is INVALID.
|
||||
|
||||
During development it's however more productive to continuously run unit tests every time the source or test files
|
||||
change. To execute tests in this mode run:
|
||||
|
||||
1. To start the Testacular server, capture Chrome browser and run unit tests, run:
|
||||
1. To start the Karma server, capture Chrome browser and run unit tests, run:
|
||||
|
||||
rake autotest:jqlite
|
||||
grunt autotest:jqlite
|
||||
|
||||
2. To capture more browsers, open this url in the desired browser (url might be different if you have multiple instance
|
||||
of Testacular running, read Testacular's console output for the correct url):
|
||||
of Karma running, read Karma's console output for the correct url):
|
||||
|
||||
http://localhost:9876/
|
||||
|
||||
3. To re-run tests just change any source or test file.
|
||||
|
||||
|
||||
To learn more about all of the preconfigured Rake tasks run:
|
||||
To learn more about all of the preconfigured Grunt tasks run:
|
||||
|
||||
rake -T
|
||||
grunt --help
|
||||
|
||||
|
||||
## Running the end-to-end Test Suite
|
||||
@@ -205,7 +215,7 @@ To run the E2E test suite:
|
||||
|
||||
1. Start the local web server if it's not running already.
|
||||
|
||||
rake webserver
|
||||
grunt webserver
|
||||
|
||||
2. In a browser, go to:
|
||||
|
||||
@@ -213,7 +223,13 @@ To run the E2E test suite:
|
||||
|
||||
or in terminal run:
|
||||
|
||||
rake test:e2e
|
||||
grunt test:end2end
|
||||
|
||||
For convenience you can also simply run:
|
||||
|
||||
grunt test:e2e
|
||||
|
||||
This will start the webserver for you and run the tests.
|
||||
|
||||
|
||||
|
||||
@@ -222,7 +238,8 @@ To run the E2E test suite:
|
||||
|
||||
To create and submit a change:
|
||||
|
||||
1. Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code changes to be
|
||||
1. <a name="CLA"></a>
|
||||
Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code changes to be
|
||||
accepted, the CLA must be signed. It's a quick process, we promise!
|
||||
|
||||
For individuals we have a [simple click-through form](http://code.google.com/legal/individual-cla-v1.0.html). For
|
||||
@@ -230,31 +247,27 @@ To create and submit a change:
|
||||
[print, sign and one of scan+email, fax or mail the form](http://code.google.com/legal/corporate-cla-v1.0.html).
|
||||
|
||||
|
||||
2. Create a new branch off the master for your changes:
|
||||
2. Create and checkout a new branch off the master branch for your changes:
|
||||
|
||||
git branch my-fix-branch
|
||||
git checkout -b my-fix-branch master
|
||||
|
||||
3. Check out the branch:
|
||||
3. Create your patch, make sure to have plenty of tests (that pass).
|
||||
|
||||
git checkout my-fix-branch
|
||||
|
||||
4. Create your patch, make sure to have plenty of tests (that pass).
|
||||
|
||||
5. Commit your changes and create a descriptive commit message (the commit message is used to generate release notes,
|
||||
4. Commit your changes and create a descriptive commit message (the commit message is used to generate release notes,
|
||||
please check out our
|
||||
[commit message conventions](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#)
|
||||
and our commit message presubmit hook `validate-commit-msg.js`):
|
||||
|
||||
git commit -a
|
||||
|
||||
6. Push your branch to Github:
|
||||
5. Push your branch to Github:
|
||||
|
||||
git push origin my-fix-branch
|
||||
|
||||
7. In Github, send a pull request to `angular:master`.
|
||||
6. In Github, send a pull request to `angular:master`.
|
||||
|
||||
|
||||
8. When the patch is reviewed and merged, delete your branch and pull yours — and other — changes
|
||||
7. When the patch is reviewed and merged, delete your branch and pull yours — and other — changes
|
||||
from the main (upstream) repository:
|
||||
|
||||
1. To delete the branch in Github, run:
|
||||
|
||||
@@ -51,7 +51,8 @@ Yes. See instructions in {@link downloading}.
|
||||
### What browsers does Angular work with?
|
||||
|
||||
We run our extensive test suite against the following browsers: Safari, Chrome, Firefox, Opera,
|
||||
IE8, IE9 and mobile browsers (Android, Chrome Mobile, iOS Safari).
|
||||
IE8, IE9 and mobile browsers (Android, Chrome Mobile, iOS Safari). See {@link guide/ie Internet
|
||||
Explorer Compatibility} for more details in supporting legacy IE browsers.
|
||||
|
||||
|
||||
### What's Angular's performance like?
|
||||
@@ -100,9 +101,9 @@ The MIT License.
|
||||
|
||||
### Can I download and use the Angular logo artwork?
|
||||
|
||||
Yes! You can find design files in our github repository, under "{@link https://github.com/angular/angular.js/tree/master/images/logo
|
||||
Yes! You can find design files in our github repository, under "{@link https://github.com/angular/angular.js/tree/master/images/logo
|
||||
angular.js/images/logo}"
|
||||
The logo design is licensed under a "{@link http://creativecommons.org/licenses/by-sa/3.0/
|
||||
The logo design is licensed under a "{@link http://creativecommons.org/licenses/by-sa/3.0/
|
||||
Creative Commons Attribution-ShareAlike 3.0 Unported License}". If you have some other use in mind, contact us.
|
||||
|
||||
### How can I get some AngularJS schwag?
|
||||
@@ -113,15 +114,10 @@ they'll waive the setup costs, and you can order any quantity you need.
|
||||
|
||||
**Stickers**
|
||||
Contact Tom Witting (or anyone in sales) via email at tom@stickergiant.com, and tell him you want to order some AngularJS
|
||||
stickers just like the ones in job #42711. You'll have to give them your own info for billing and shipping.
|
||||
stickers just like the ones in job #42711. You'll have to give them your own info for billing and shipping.
|
||||
|
||||
As long as the design stays exactly the same, {@link http://www.stickergiant.com StickerGiant} will give you a reorder discount.
|
||||
|
||||
**T-shirts**
|
||||
Contact sales at {@link http://www.customink.com www.customink.com} and tell them you want some shirts with design name "angularjs",
|
||||
just like past order #2106371. You'll have to give them your own info for billing and shipping.
|
||||
|
||||
As long as the design stays exactly the same, CustomInk won't charge for any set up fees, and they'll give you a reorder discount.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ becoming an Angular expert.
|
||||
##Watch Videos
|
||||
|
||||
If you haven’t had a chance to watch the videos from the homepage, please check out:
|
||||
|
||||
* {@link http://www.youtube.com/watch?v=WuiHuZq_cg4&list=PL173F1A311439C05D&context=C48ac877ADvjVQa1PpcFONnl4Q5x8hqvT6tRBTE-m0-Ym47jO3PEE%3D Introduction to AngularJS}
|
||||
* {@link http://www.youtube.com/watch?v=Yg-R1gchccg&list=PL173F1A311439C05D&context=C48ac877ADvjVQa1PpcFONnl4Q5x8hqvT6tRBTE-m0-Ym47jO3PEE%3D Creating Directives}
|
||||
* {@link http://www.youtube.com/watch?v=IRelx4-ISbs&list=PL173F1A311439C05D&context=C48ac877ADvjVQa1PpcFONnl4Q5x8hqvT6tRBTE-m0-Ym47jO3PEE%3D Communicating with Servers}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@ngdoc overview
|
||||
@name Tutorial
|
||||
@name Tutorial: Index
|
||||
@description
|
||||
|
||||
A great way to get introduced to AngularJS is to work through this tutorial, which walks you through
|
||||
@@ -54,24 +54,24 @@ and follow the instructions for setting up your computer.
|
||||
<div class="tabbable" show="true">
|
||||
<div class="tab-pane well" id="git-mac" title="Git on Mac/Linux">
|
||||
<ol>
|
||||
<li><p>You will need Node.js and Testacular to run unit tests, so please verify that you have
|
||||
<li><p>You will need Node.js and Karma to run unit tests, so please verify that you have
|
||||
<a href="http://nodejs.org/">Node.js</a> v0.8 or better installed
|
||||
and that the <code>node</code> executable is on your <code>PATH</code> by running the following
|
||||
command in a terminal window:</p>
|
||||
<pre>node --version</pre>
|
||||
<p>Additionally install <a href="http://vojtajina.github.com/testacular">Testacular</a> if you
|
||||
<p>Additionally install <a href="http://karma-runner.github.io/">Karma</a> if you
|
||||
don't have it already:</p>
|
||||
<pre>npm install -g testacular</pre>
|
||||
<pre>npm install -g karma</pre>
|
||||
<li><p>You'll also need Git, which you can get from
|
||||
<a href="http://git-scm.com/download">the Git site</a>.</p></li>
|
||||
<li><p>Clone the angular-phonecat repository located at <a
|
||||
href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
|
||||
<li><p>Clone the angular-phonecat repository located at
|
||||
<a href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
|
||||
<pre>git clone git://github.com/angular/angular-phonecat.git</pre>
|
||||
<p>This command creates the <code>angular-phonecat</code> directory in your current
|
||||
directory.</p></li>
|
||||
<li><p>Change your current directory to <code>angular-phonecat</code>:</p>
|
||||
<pre>cd angular-phonecat</pre>
|
||||
<p>The tutorial instructions assume you are running all commands from the angular-phonecat
|
||||
<p>The tutorial instructions assume you are running all commands from the <code>angular-phonecat</code>
|
||||
directory.</p></li>
|
||||
<li><p>You will need an http server running on your system. Mac and Linux machines typically
|
||||
have Apache pre-installed, but If you don't already have one installed, you can use <code>node</code>
|
||||
@@ -81,24 +81,24 @@ to run <code>scripts/web-server.js</code>, a simple bundled http server.</p></l
|
||||
|
||||
<div class="tab-pane well" id="git-win" title="Git on Windows">
|
||||
<ol>
|
||||
<li><p>You will need Node.js and Testacular to run unit tests, so please verify that you have
|
||||
<li><p>You will need Node.js and Karma to run unit tests, so please verify that you have
|
||||
<a href="http://nodejs.org/">Node.js</a> v0.8 or better installed
|
||||
and that the <code>node</code> executable is on your <code>PATH</code> by running the following
|
||||
command in a terminal window:</p>
|
||||
<pre>node --version</pre>
|
||||
<p>Additionally install <a href="http://vojtajina.github.com/testacular">Testacular</a> if you
|
||||
<p>Additionally install <a href="http://karma-runner.github.io/">Karma</a> if you
|
||||
don't have it already:</p>
|
||||
<pre>npm install -g testacular</pre>
|
||||
<pre>npm install -g karma</pre>
|
||||
</li>
|
||||
<li><p>You'll also need Git, which you can get from
|
||||
<a href="http://git-scm.com/download">the Git site</a>.</p></li>
|
||||
<li><p>Clone the angular-phonecat repository located at <a
|
||||
href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
|
||||
<pre>git clone git://github.com/angular/angular-phonecat.git</pre>
|
||||
<p>This command creates the angular-phonecat directory in your current directory.</p></li>
|
||||
<li><p>Change your current directory to angular-phonecat.</p>
|
||||
<p>This command creates the <code>angular-phonecat</code> directory in your current directory.</p></li>
|
||||
<li><p>Change your current directory to <code>angular-phonecat</code>:</p>
|
||||
<pre>cd angular-phonecat</pre>
|
||||
<p>The tutorial instructions assume you are running all commands from the angular-phonecat
|
||||
<p>The tutorial instructions assume you are running all commands from the <code>angular-phonecat</code>
|
||||
directory.</p>
|
||||
<p>You should run all <code>git</code> commands from Git bash.</p>
|
||||
<p>Other commands like <code>test.bat</code> or <code>e2e-test.bat</code> should be
|
||||
|
||||
@@ -13,7 +13,7 @@ angular-seed, and run the application in the browser.
|
||||
<div class="tabbable" show="true" ng-model="$cookies.platformPreference">
|
||||
<div class="tab-pane well" id="git-mac" title="Git on Mac/Linux" value="gitUnix">
|
||||
<ol>
|
||||
<li><p>In angular-phonecat directory, run this command:</p>
|
||||
<li><p>In <code>angular-phonecat</code> directory, run this command:</p>
|
||||
<pre>git checkout -f step-0</pre>
|
||||
<p>This resets your workspace to step 0 of the tutorial app.</p>
|
||||
<p>You must repeat this for every future step in the tutorial and change the number to
|
||||
@@ -25,7 +25,7 @@ angular-seed, and run the application in the browser.
|
||||
<li><b>For node.js users:</b>
|
||||
<ol>
|
||||
<li>In a <i>separate</i> terminal tab or window, run
|
||||
<code>./scripts/web-server.js</code> to start the web server.</li>
|
||||
<code>node ./scripts/web-server.js</code> to start the web server.</li>
|
||||
<li>Open a browser window for the app and navigate to <a
|
||||
href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html</a></li>
|
||||
</ol>
|
||||
@@ -46,7 +46,7 @@ directory.</li>
|
||||
|
||||
<div class="tab-pane well" id="git-win" title="Git on Windows" value="gitWin">
|
||||
<ol>
|
||||
<li><p>Open Git bash and run this command (in angular-phonecat directory):</p>
|
||||
<li><p>Open Git bash and run this command (in <code>angular-phonecat</code> directory):</p>
|
||||
<pre>git checkout -f step-0</pre>
|
||||
<p>This resets your workspace to step 0 of the tutorial app.</p>
|
||||
<p>You must repeat this for every future step in the tutorial and change the number to
|
||||
@@ -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.
|
||||
|
||||
@@ -134,10 +134,10 @@ describe('PhoneCat controllers', function() {
|
||||
});
|
||||
</pre>
|
||||
|
||||
The test verifies that we have three records in the phones array and the example demonstrates how
|
||||
easy it is to create a unit test for code in Angular. Since testing is such a critical part of
|
||||
software development, we make it easy to create tests in Angular so that developers are encouraged
|
||||
to write them.
|
||||
The test instantiates our PhoneListCtrl and verifies that its phones array property contains three
|
||||
records. This example demonstrates how easy it is to create a unit test for code in Angular. Since
|
||||
testing is such a critical part of software development, we make it easy to create tests in Angular
|
||||
so that developers are encouraged to write them.
|
||||
|
||||
Angular developers prefer the syntax of Jasmine's Behavior-driven Development (BDD) framework when
|
||||
writing tests. Although Angular does not require you to use Jasmine, we wrote all of the tests in
|
||||
@@ -146,24 +146,25 @@ http://pivotal.github.com/jasmine/ Jasmine home page} and on the {@link
|
||||
https://github.com/pivotal/jasmine/wiki Jasmine wiki}.
|
||||
|
||||
The angular-seed project is pre-configured to run all unit tests using {@link
|
||||
http://vojtajina.github.com/testacular/ Testacular}. To run the test, do the following:
|
||||
http://karma-runner.github.io/ Karma}. To run the test, do the following:
|
||||
|
||||
1. In a _separate_ terminal window or tab, go to the `angular-phonecat` directory and run
|
||||
`./scripts/test.sh` to start the Testacular server.
|
||||
`./scripts/test.sh` to start the Karma server (the config file necessary to start the server
|
||||
is located at `./config/karma.conf.js`).
|
||||
|
||||
2. Testacular will start a new instance of Chrome browser automatically. Just ignore it and let it run in
|
||||
the background. Testacular will use this browser for test execution.
|
||||
2. Karma will start a new instance of Chrome browser automatically. Just ignore it and let it run in
|
||||
the background. Karma will use this browser for test execution.
|
||||
|
||||
3. You should see the following or similar output in the terminal:
|
||||
|
||||
info: Testacular server started at http://localhost:9876/
|
||||
info: Karma server started at http://localhost:9876/
|
||||
info (launcher): Starting browser "Chrome"
|
||||
info (Chrome 22.0): Connected on socket id tPUm9DXcLHtZTKbAEO-n
|
||||
Chrome 22.0: Executed 1 of 1 SUCCESS (0.093 secs / 0.004 secs)
|
||||
|
||||
Yay! The test passed! Or not...
|
||||
|
||||
4. To rerun the tests, just change any of the source or test files. Testacular will notice the change
|
||||
4. To rerun the tests, just change any of the source or test files. Karma will notice the change
|
||||
and will rerun the tests for you. Now isn't that sweet?
|
||||
|
||||
# Experiments
|
||||
|
||||
@@ -122,9 +122,9 @@ To run the end-to-end test, open one of the following in a new browser tab:
|
||||
`http://localhost:[port-number]/[context-path]/test/e2e/runner.html`
|
||||
* casual reader: {@link http://angular.github.com/angular-phonecat/step-3/test/e2e/runner.html}
|
||||
|
||||
Previously we've seen how Testacular can be used to execute unit tests. Well, it can also run the
|
||||
Previously we've seen how Karma can be used to execute unit tests. Well, it can also run the
|
||||
end-to-end tests! Use `./scripts/e2e-test.sh` script for that. End-to-end tests are slow, so unlike
|
||||
with unit tests, Testacular will exit after the test run and will not automatically rerun the test
|
||||
with unit tests, Karma will exit after the test run and will not automatically rerun the test
|
||||
suite on every file change. To rerun the test suite, execute the `e2e-test.sh` script again.
|
||||
|
||||
This test verifies that the search box and the repeater are correctly wired together. Notice how
|
||||
@@ -138,12 +138,12 @@ really is that easy to set up any functional, readable, end-to-end test.
|
||||
|
||||
* Let's see how we can get the current value of the `query` model to appear in the HTML page title.
|
||||
|
||||
You might think you could just add the {{query}} to the title tag element as follows:
|
||||
You might think you could just add the `{{query}}` to the title tag element as follows:
|
||||
|
||||
<title>Google Phone Gallery: {{query}}</title>
|
||||
|
||||
However, when you reload the page, you won't see the expected result. This is because the "query"
|
||||
model lives in the scope defined by the body element:
|
||||
model lives in the scope, defined by the `ng-controller="PhoneListCtrl"` directive, on the body element:
|
||||
|
||||
<body ng-controller="PhoneListCtrl">
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ The unit test now verifies that the default ordering property is set.
|
||||
We used Jasmine's API to extract the controller construction into a `beforeEach` block, which is
|
||||
shared by all tests in the parent `describe` block.
|
||||
|
||||
You should now see the following output in the Testacular tab:
|
||||
You should now see the following output in the Karma tab:
|
||||
|
||||
Chrome 22.0: Executed 2 of 2 SUCCESS (0.021 secs / 0.001 secs)
|
||||
|
||||
@@ -178,6 +178,8 @@ ordering will default to unordered/natural order.
|
||||
* Add an `{{orderProp}}` binding into the `index.html` template to display its current value as
|
||||
text.
|
||||
|
||||
* Reverse the sort order by adding a `-` symbol before the sorting value: `<option value="-age">Oldest</option>`
|
||||
|
||||
# Summary
|
||||
|
||||
Now that you have added list sorting and tested the app, go to {@link step_05 step 5} to learn
|
||||
|
||||
@@ -44,7 +44,7 @@ Following is a sample of the file:
|
||||
|
||||
We'll use angular's {@link api/ng.$http $http} service in our controller to make an HTTP
|
||||
request to your web server to fetch the data in the `app/phones/phones.json` file. `$http` is just
|
||||
one of several built-in {@link api/ng angular services} that handle common operations
|
||||
one of several built-in {@link guide/dev_guide.services angular services} that handle common operations
|
||||
in web apps. Angular injects these services for you where you need them.
|
||||
|
||||
Services are managed by angular's {@link guide/di DI subsystem}. Dependency injection
|
||||
@@ -138,6 +138,9 @@ describe('PhoneCat controllers', function() {
|
||||
describe('PhoneListCtrl', function(){
|
||||
var scope, ctrl, $httpBackend;
|
||||
|
||||
// The injector ignores leading and trailing underscores here (i.e. _$httpBackend_).
|
||||
// This allows us to inject a service but then attach it to a variable
|
||||
// with the same name as the service.
|
||||
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
|
||||
$httpBackend = _$httpBackend_;
|
||||
$httpBackend.expectGET('phones/phones.json').
|
||||
@@ -204,11 +207,9 @@ Finally, we verify that the default value of `orderProp` is set correctly:
|
||||
it('should set the default value of orderProp model', function() {
|
||||
expect(scope.orderProp).toBe('age');
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
You should now see the following output in the Testacular tab:
|
||||
You should now see the following output in the Karma tab:
|
||||
|
||||
Chrome 22.0: Executed 2 of 2 SUCCESS (0.028 secs / 0.007 secs)
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ When the application bootstraps, Angular creates an injector that will be used f
|
||||
this app. The injector itself doesn't know anything about what `$http` or `$route` services do, in
|
||||
fact it doesn't even know about the existence of these services unless it is configured with proper
|
||||
module definitions. The sole responsibilities of the injector are to load specified module
|
||||
definition(s), register all service providers defined in these modules and when asked inject
|
||||
definition(s), register all service providers defined in these modules, and when asked, inject
|
||||
a specified function with dependencies (services) that it lazily instantiates via their providers.
|
||||
|
||||
Providers are objects that provide (create) instances of services and expose configuration APIs
|
||||
|
||||
@@ -147,7 +147,7 @@ __`test/unit/controllersSpec.js`:__
|
||||
...
|
||||
</pre>
|
||||
|
||||
You should now see the following output in the Testacular tab:
|
||||
You should now see the following output in the Karma tab:
|
||||
|
||||
Chrome 22.0: Executed 3 of 3 SUCCESS (0.039 secs / 0.012 secs)
|
||||
|
||||
|
||||
@@ -37,8 +37,7 @@ angular.module('phonecatFilters', []).filter('checkmark', function() {
|
||||
</pre>
|
||||
|
||||
The name of our filter is "checkmark". The `input` evaluates to either `true` or `false`, and we
|
||||
return one of two unicode characters we have chosen to represent true or false (`\u2713` and
|
||||
`\u2718`).
|
||||
return one of the two unicode characters we have chosen to represent true (`\u2713` -> ✓) or false (`\u2718` -> ✘).
|
||||
|
||||
Now that our filter is ready, we need to register the `phonecatFilters` module as a dependency for
|
||||
our main `phonecat` module.
|
||||
@@ -110,7 +109,7 @@ describe('filter', function() {
|
||||
Note that you need to configure our test injector with the `phonecatFilters` module before any of
|
||||
our filter tests execute.
|
||||
|
||||
You should now see the following output in the Testacular tab:
|
||||
You should now see the following output in the Karma tab:
|
||||
|
||||
Chrome 22.0: Executed 4 of 4 SUCCESS (0.034 secs / 0.012 secs)
|
||||
|
||||
|
||||
@@ -214,7 +214,7 @@ describe('PhoneCat controllers', function() {
|
||||
});
|
||||
</pre>
|
||||
|
||||
You should now see the following output in the Testacular tab:
|
||||
You should now see the following output in the Karma tab:
|
||||
|
||||
Chrome 22.0: Executed 4 of 4 SUCCESS (0.038 secs / 0.01 secs)
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
|
||||
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
|
||||
]>
|
||||
<svg version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
|
||||
x="0px" y="0px" width="687px" height="176px" viewBox="0 0 687 176" overflow="visible" enable-background="new 0 0 687 176"
|
||||
xml:space="preserve">
|
||||
<defs>
|
||||
</defs>
|
||||
<path fill="#FFFFFF" d="M179.011,125.328V54.527h9.158l43.322,57.035V54.527h8.666v70.801h-9.158l-43.326-57.536v57.536H179.011z
|
||||
M179.011,125.328"/>
|
||||
<path fill="#FFFFFF" d="M310.46,122.554c-5.708,2.182-11.864,3.269-18.467,3.269c-25.644,0-38.469-12.294-38.469-36.887
|
||||
c0-23.27,12.378-34.908,37.134-34.908c7.096,0,13.7,0.994,19.802,2.976v7.921c-6.103-2.311-12.378-3.468-18.813-3.468
|
||||
c-19.306,0-28.96,9.162-28.96,27.479c0,19.639,9.504,29.463,28.517,29.463c3.034,0,6.404-0.396,10.103-1.193V93.145h9.154V122.554z
|
||||
M310.46,122.554"/>
|
||||
<path fill="#FFFFFF" d="M325.067,97.996V54.523h9.154v43.473c0,13.598,6.768,20.4,20.303,20.4c13.531,0,20.301-6.803,20.301-20.4
|
||||
V54.523h9.158v43.473c0,18.556-9.82,27.825-29.459,27.825C334.886,125.821,325.067,116.552,325.067,97.996L325.067,97.996z
|
||||
M325.067,97.996"/>
|
||||
<path fill="#FFFFFF" d="M409.48,54.523v63.376h37.037v7.425h-46.191V54.523H409.48z M409.48,54.523"/>
|
||||
<path fill="#FFFFFF" d="M459.736,125.327h-9.504l35.201-80.146l35.199,80.146h-10.15l-9.158-22.282h-23.418l2.527-7.424h17.82
|
||||
l-13.217-32.088L459.736,125.327z M459.736,125.327"/>
|
||||
<path fill="#FFFFFF" d="M530.289,125.328V54.527h30.203c13.469,0,20.197,5.659,20.197,16.982c0,9.207-6.578,16.028-19.75,20.445
|
||||
l24.309,33.374h-12.086l-22.521-31.835v-5.992c13.531-2.151,20.301-7.344,20.301-15.598c0-6.533-3.766-9.801-11.293-9.801h-20.201
|
||||
v63.226H530.289z M530.289,125.328"/>
|
||||
<path fill="#B52E31" d="M619.561,54.523v50.405c0,13.603-8.006,20.396-24.016,20.396V117.9c9.902,0,14.857-4.329,14.857-12.973
|
||||
V54.523H619.561z M619.561,54.523"/>
|
||||
<path fill="#B52E31" d="M635.896,122.849v-8.418c7.428,2.639,15.447,3.965,24.064,3.965c12.178,0,18.271-4.457,18.271-13.372
|
||||
c0-7.584-4.492-11.385-13.469-11.385h-9.113c-14.818,0-22.234-6.435-22.234-19.31c0-13.531,9.492-20.303,28.479-20.303
|
||||
c8.25,0,15.922,0.998,23.021,2.976v8.418c-7.1-2.644-14.771-3.965-23.021-3.965c-12.875,0-19.311,4.293-19.311,12.875
|
||||
c0,7.588,4.352,11.385,13.066,11.385h9.113c15.08,0,22.627,6.439,22.627,19.31c0,13.864-9.141,20.796-27.43,20.796
|
||||
C651.344,125.819,643.324,124.826,635.896,122.849L635.896,122.849z M635.896,122.849"/>
|
||||
<path fill="#B2B2B2" d="M82.688,0L0,29.1l13.066,108.335l69.71,38.314l70.069-38.834l13.062-108.331L82.688,0z M82.688,0"/>
|
||||
<path fill="#B52E31" d="M157.66,34.846L82.496,9.214v157.381l62.991-34.861L157.66,34.846z M157.66,34.846"/>
|
||||
<path fill="#E23237" d="M9.279,35.308l11.196,96.889l62.019,34.398V9.211L9.279,35.308z M9.279,35.308"/>
|
||||
<path fill="#F2F2F2" d="M99.918,87.493L82.632,51.396L67.415,87.493H99.918z M106.508,102.672h-45.82l-10.251,25.64l-19.067,0.352
|
||||
L82.496,14.929l52.908,113.734h-17.673L106.508,102.672z M106.508,102.672"/>
|
||||
<path fill="#B2B2B2" d="M82.496,14.929l0.136,36.467l17.268,36.125H82.534l-0.039,15.127l24.012,0.023l11.223,25.996l18.245,0.339
|
||||
L82.496,14.929z M82.496,14.929"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
+62
-30
@@ -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:'a', description:'<div class="a-page"><p>short</p></div>', type:'*', optional:false, 'default':undefined},
|
||||
{name:'b', description:'<div class="a-page"><p>med</p></div>', type:'Type', optional:false, 'default':undefined},
|
||||
{name:'c', description:'<div class="a-page"><p>long\nline</p></div>', type:'Class', optional:true, 'default':'2'},
|
||||
{name:'d', description:'<div class="a-page"><p>fn with optional arguments</p></div>',
|
||||
type: 'function(number, string=)', optional: false, 'default':undefined}
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -69,7 +72,7 @@ describe('ngdoc', function() {
|
||||
doc.parse();
|
||||
expect(doc.returns).toEqual({
|
||||
type: 'Type',
|
||||
description: '<p>text <em>bold</em>.</p>'
|
||||
description: '<div class="a-page"><p>text <em>bold</em>.</p></div>'
|
||||
});
|
||||
});
|
||||
|
||||
@@ -135,16 +138,30 @@ describe('ngdoc', function() {
|
||||
it('should not replace anything in <pre>, but escape the html escape the content', function() {
|
||||
expect(new Doc().markdown('bah x\n<pre>\n<b>angular</b>.k\n</pre>\n asdf x')).
|
||||
toEqual(
|
||||
'<p>bah x\n' +
|
||||
'<div class="docs-page"><p>bah x\n' +
|
||||
'<pre class="prettyprint linenums">\n' +
|
||||
'<b>angular</b>.k\n' +
|
||||
'</pre>\n' +
|
||||
' asdf x</p>');
|
||||
' asdf x</p></div>');
|
||||
});
|
||||
|
||||
it('should wrap everything inside a container tag', function() {
|
||||
var doc = new Doc('@name superman').parse();
|
||||
var content = doc.markdown('hello');
|
||||
|
||||
expect(content).toMatch('<div class="superman-page"><p>hello</p></div>');
|
||||
});
|
||||
|
||||
it('should use the content before a colon as the name prefix for the className of the tag container', function() {
|
||||
var doc = new Doc('@name super: man').parse();
|
||||
var content = doc.markdown('hello');
|
||||
|
||||
expect(content).toMatch('<div class="super-page super-man-page"><p>hello</p></div>');
|
||||
});
|
||||
|
||||
it('should replace text between two <pre></pre> tags', function() {
|
||||
expect(new Doc().markdown('<pre>x</pre>\n# One\n<pre>b</pre>')).
|
||||
toMatch('</pre>\n\n<h1>One</h1>\n\n<pre');
|
||||
toMatch('</pre>\n\n<h1 id="one">One</h1>\n\n<pre');
|
||||
});
|
||||
|
||||
it('should ignore nested doc widgets', function() {
|
||||
@@ -154,11 +171,11 @@ describe('ngdoc', function() {
|
||||
'\ngit bla bla\n</doc:tutorial-instruction>\n' +
|
||||
'</doc:tutorial-instructions>')).toEqual(
|
||||
|
||||
'<p>before<div class="tabbable">\n' +
|
||||
'<div class="docs-page"><p>before<div class="tabbable">\n' +
|
||||
'<div class="tab-pane well" id="git-mac" ng:model="Git on Mac/Linux">\n' +
|
||||
'git bla bla\n' +
|
||||
'</doc:tutorial-instruction>\n' +
|
||||
'</doc:tutorial-instructions></p>');
|
||||
'</doc:tutorial-instructions></p></div>');
|
||||
});
|
||||
|
||||
it('should unindent text before processing based on the second line', function() {
|
||||
@@ -260,7 +277,7 @@ describe('ngdoc', function() {
|
||||
name : 'number',
|
||||
optional: false,
|
||||
'default' : undefined,
|
||||
description : '<p>Number \nto format.</p>' }]);
|
||||
description : '<div class="a-page"><p>Number \nto format.</p></div>' }]);
|
||||
});
|
||||
|
||||
it('should parse with default and optional', function() {
|
||||
@@ -271,7 +288,7 @@ describe('ngdoc', function() {
|
||||
name : 'fractionSize',
|
||||
optional: true,
|
||||
'default' : '2',
|
||||
description : '<p>desc</p>' }]);
|
||||
description : '<div class="a-page"><p>desc</p></div>' }]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -281,8 +298,8 @@ describe('ngdoc', function() {
|
||||
doc.ngdoc = 'service';
|
||||
doc.parse();
|
||||
expect(doc.requires).toEqual([
|
||||
{name:'$service', text:'<p>for \n<code>A</code></p>'},
|
||||
{name:'$another', text:'<p>for <code>B</code></p>'}]);
|
||||
{name:'$service', text:'<div class="a-page"><p>for \n<code>A</code></p></div>'},
|
||||
{name:'$another', text:'<div class="a-page"><p>for <code>B</code></p></div>'}]);
|
||||
expect(doc.html()).toContain('<a href="api/ng.$service">$service</a>');
|
||||
expect(doc.html()).toContain('<a href="api/ng.$another">$another</a>');
|
||||
expect(doc.html()).toContain('<p>for \n<code>A</code></p>');
|
||||
@@ -318,9 +335,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() {
|
||||
@@ -334,7 +351,7 @@ describe('ngdoc', function() {
|
||||
var doc = new Doc("@name a\n@property {string} name desc rip tion");
|
||||
doc.parse();
|
||||
expect(doc.properties[0].name).toEqual('name');
|
||||
expect(doc.properties[0].description).toEqual('<p>desc rip tion</p>');
|
||||
expect(doc.properties[0].description).toEqual('<div class="a-page"><p>desc rip tion</p></div>');
|
||||
});
|
||||
|
||||
it('should parse @property with type and description both', function() {
|
||||
@@ -342,7 +359,7 @@ describe('ngdoc', function() {
|
||||
doc.parse();
|
||||
expect(doc.properties[0].name).toEqual('name');
|
||||
expect(doc.properties[0].type).toEqual('bool');
|
||||
expect(doc.properties[0].description).toEqual('<p>desc rip tion</p>');
|
||||
expect(doc.properties[0].description).toEqual('<div class="a-page"><p>desc rip tion</p></div>');
|
||||
});
|
||||
|
||||
});
|
||||
@@ -350,26 +367,41 @@ 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>'});
|
||||
expect(doc.returns).toEqual({type: 'string', description: '<div class="a-page"><p>descrip tion</p></div>'});
|
||||
});
|
||||
|
||||
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: '<div class="a-page"><p>description</p></div>'});
|
||||
});
|
||||
|
||||
it('should transform description of @returns with markdown', function() {
|
||||
var doc = new Doc("@name a\n@returns {string} descrip *tion*");
|
||||
doc.parse();
|
||||
expect(doc.returns).toEqual({type: 'string', description: '<p>descrip <em>tion</em></p>'});
|
||||
expect(doc.returns).toEqual({type: 'string', description: '<div class="a-page"><p>descrip <em>tion</em></p></div>'});
|
||||
});
|
||||
|
||||
it('should support multiline content', function() {
|
||||
var doc = new Doc("@name a\n@returns {string} description\n new line\n another line");
|
||||
doc.parse();
|
||||
expect(doc.returns).
|
||||
toEqual({type: 'string', description: '<p>description\nnew line\nanother line</p>'});
|
||||
toEqual({type: 'string', description: '<div class="a-page"><p>description\nnew line\nanother line</p></div>'});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -378,18 +410,18 @@ describe('ngdoc', function() {
|
||||
var doc = new Doc("@name a\n@description <pre><b>abc</b></pre>");
|
||||
doc.parse();
|
||||
expect(doc.description).
|
||||
toBe('<pre class="prettyprint linenums"><b>abc</b></pre>');
|
||||
toBe('<div class="a-page"><pre class="prettyprint linenums"><b>abc</b></pre></div>');
|
||||
});
|
||||
|
||||
it('should support multiple pre blocks', function() {
|
||||
var doc = new Doc("@name a\n@description foo \n<pre>abc</pre>\n#bah\nfoo \n<pre>cba</pre>");
|
||||
doc.parse();
|
||||
expect(doc.description).
|
||||
toBe('<p>foo \n' +
|
||||
toBe('<div class="a-page"><p>foo \n' +
|
||||
'<pre class="prettyprint linenums">abc</pre>\n\n' +
|
||||
'<h1>bah</h1>\n\n' +
|
||||
'<h1 id="bah">bah</h1>\n\n' +
|
||||
'<p>foo \n' +
|
||||
'<pre class="prettyprint linenums">cba</pre>');
|
||||
'<pre class="prettyprint linenums">cba</pre></div>');
|
||||
|
||||
});
|
||||
|
||||
@@ -432,7 +464,7 @@ describe('ngdoc', function() {
|
||||
it('should not remove {{}}', function() {
|
||||
var doc = new Doc('@name a\n@example text {{ abc }}');
|
||||
doc.parse();
|
||||
expect(doc.example).toEqual('<p>text {{ abc }}</p>');
|
||||
expect(doc.example).toEqual('<div class="a-page"><p>text {{ abc }}</p></div>');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -451,10 +483,10 @@ describe('ngdoc', function() {
|
||||
doc.parse();
|
||||
expect(doc.html()).toContain('<h3>Method\'s <code>this</code></h3>\n' +
|
||||
'<div>' +
|
||||
'<p>I am self.</p>' +
|
||||
'<div class="a-page"><p>I am self.</p></div>' +
|
||||
'</div>\n');
|
||||
expect(doc.html()).toContain('<h3>Method\'s <code>this</code></h3>\n' +
|
||||
'<div><p>I am self.</p></div>');
|
||||
'<div><div class="a-page"><p>I am self.</p></div></div>');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -465,7 +497,7 @@ describe('ngdoc', function() {
|
||||
var doc = new Doc('@ngdoc overview\n@name angular\n@description\n#heading\ntext');
|
||||
doc.parse();
|
||||
expect(doc.html()).toContain('text');
|
||||
expect(doc.html()).toContain('<h2>heading</h2>');
|
||||
expect(doc.html()).toContain('<h1 id="heading">heading</h2>');
|
||||
expect(doc.html()).not.toContain('Description');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,10 +5,6 @@ var reader = require('./reader.js'),
|
||||
appCache = require('./appCache.js').appCache,
|
||||
Q = require('qq');
|
||||
|
||||
process.on('uncaughtException', function(err) {
|
||||
console.error(err.stack || err);
|
||||
});
|
||||
|
||||
var start = now();
|
||||
var docs;
|
||||
|
||||
@@ -42,10 +38,10 @@ writer.makeDir('build/docs/', true).then(function() {
|
||||
function writeTheRest(writesFuture) {
|
||||
var metadata = ngdoc.metadata(docs);
|
||||
|
||||
writesFuture.push(writer.symlinkTemplate('css'));
|
||||
writesFuture.push(writer.symlinkTemplate('font'));
|
||||
writesFuture.push(writer.symlink('../../docs/img', 'build/docs/img'));
|
||||
writesFuture.push(writer.symlinkTemplate('js'));
|
||||
writesFuture.push(writer.symlinkTemplate('css', 'dir'));
|
||||
writesFuture.push(writer.symlinkTemplate('font', 'dir'));
|
||||
writesFuture.push(writer.symlink('../../docs/img', 'build/docs/img', 'dir'));
|
||||
writesFuture.push(writer.symlinkTemplate('js', 'dir'));
|
||||
|
||||
var manifest = 'manifest="/build/docs/appcache.manifest"';
|
||||
|
||||
|
||||
+80
-45
@@ -2,7 +2,7 @@
|
||||
* All parsing/transformation code goes here. All code here should be sync to ease testing.
|
||||
*/
|
||||
|
||||
var Showdown = require('../../lib/showdown').Showdown;
|
||||
var Showdown = require('showdown');
|
||||
var DOM = require('./dom.js').DOM;
|
||||
var htmlEscape = require('./dom.js').htmlEscape;
|
||||
var Example = require('./example.js').Example;
|
||||
@@ -173,8 +173,27 @@ Doc.prototype = {
|
||||
'</a>';
|
||||
});
|
||||
});
|
||||
|
||||
text = parts.join('');
|
||||
text = new Showdown.converter().makeHtml(text);
|
||||
|
||||
function prepareClassName(text) {
|
||||
return text.toLowerCase().replace(/[_\W]+/g, '-');
|
||||
};
|
||||
|
||||
var pageClassName, suffix = '-page';
|
||||
if(this.name) {
|
||||
var split = this.name.match(/^\s*(.+?)\s*:\s*(.+)/);
|
||||
if(split && split.length > 1) {
|
||||
var before = prepareClassName(split[1]);
|
||||
var after = prepareClassName(split[2]);
|
||||
pageClassName = before + suffix + ' ' + before + '-' + after + suffix;
|
||||
}
|
||||
}
|
||||
pageClassName = pageClassName || prepareClassName(this.name || 'docs') + suffix;
|
||||
|
||||
text = '<div class="' + pageClassName + '">' +
|
||||
(new Showdown.converter({ extensions : ['table'] }).makeHtml(text)) +
|
||||
'</div>';
|
||||
text = text.replace(/(?:<p>)?(REPLACEME\d+)(?:<\/p>)?/g, function(_, id) {
|
||||
return placeholderMap[id];
|
||||
});
|
||||
@@ -203,7 +222,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 +233,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 +266,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 +291,9 @@ Doc.prototype = {
|
||||
self = this;
|
||||
|
||||
dom.h(title(this.name), function() {
|
||||
notice('deprecated', 'Deprecated API', self.deprecated);
|
||||
|
||||
notice('deprecated', 'Deprecated API', self.deprecated);
|
||||
dom.tag('a', {href: 'http://github.com/angular/angular.js/edit/master/' + self.file, class: 'improve-docs btn btn-primary'}, 'Improve this doc');
|
||||
if (self.ngdoc != 'overview') {
|
||||
dom.h('Description', self.description, dom.html);
|
||||
}
|
||||
@@ -383,40 +405,53 @@ Doc.prototype = {
|
||||
var self = this;
|
||||
dom.h('Usage', function() {
|
||||
var restrict = self.restrict || 'AC';
|
||||
|
||||
if (restrict.match(/E/)) {
|
||||
dom.text('as element (see ');
|
||||
dom.text('This directive can be used as custom element, but be aware of ');
|
||||
dom.tag('a', {href:'guide/ie'}, 'IE restrictions');
|
||||
dom.text(')');
|
||||
dom.code(function() {
|
||||
dom.text('<');
|
||||
dom.text(dashCase(self.shortName));
|
||||
renderParams('\n ', '="', '"');
|
||||
dom.text('>\n</');
|
||||
dom.text(dashCase(self.shortName));
|
||||
dom.text('>');
|
||||
});
|
||||
dom.text('.');
|
||||
}
|
||||
if (restrict.match(/A/)) {
|
||||
var element = self.element || 'ANY';
|
||||
dom.text('as attribute');
|
||||
dom.code(function() {
|
||||
dom.text('<' + element + ' ');
|
||||
dom.text(dashCase(self.shortName));
|
||||
renderParams('\n ', '="', '"', true);
|
||||
dom.text('>\n ...\n');
|
||||
dom.text('</' + element + '>');
|
||||
});
|
||||
}
|
||||
if (restrict.match(/C/)) {
|
||||
dom.text('as class');
|
||||
var element = self.element || 'ANY';
|
||||
dom.code(function() {
|
||||
dom.text('<' + element + ' class="');
|
||||
dom.text(dashCase(self.shortName));
|
||||
renderParams(' ', ': ', ';', true);
|
||||
dom.text('">\n ...\n');
|
||||
dom.text('</' + element + '>');
|
||||
|
||||
if (self.usage) {
|
||||
dom.tag('pre', function() {
|
||||
dom.tag('code', function() {
|
||||
dom.text(self.usage);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
if (restrict.match(/E/)) {
|
||||
dom.text('as element:');
|
||||
dom.code(function() {
|
||||
dom.text('<');
|
||||
dom.text(dashCase(self.shortName));
|
||||
renderParams('\n ', '="', '"');
|
||||
dom.text('>\n</');
|
||||
dom.text(dashCase(self.shortName));
|
||||
dom.text('>');
|
||||
});
|
||||
}
|
||||
if (restrict.match(/A/)) {
|
||||
var element = self.element || 'ANY';
|
||||
dom.text('as attribute');
|
||||
dom.code(function() {
|
||||
dom.text('<' + element + ' ');
|
||||
dom.text(dashCase(self.shortName));
|
||||
renderParams('\n ', '="', '"', true);
|
||||
dom.text('>\n ...\n');
|
||||
dom.text('</' + element + '>');
|
||||
});
|
||||
}
|
||||
if (restrict.match(/C/)) {
|
||||
dom.text('as class');
|
||||
var element = self.element || 'ANY';
|
||||
dom.code(function() {
|
||||
dom.text('<' + element + ' class="');
|
||||
dom.text(dashCase(self.shortName));
|
||||
renderParams(' ', ': ', ';', true);
|
||||
dom.text('">\n ...\n');
|
||||
dom.text('</' + element + '>');
|
||||
});
|
||||
}
|
||||
}
|
||||
self.html_usage_directiveInfo(dom);
|
||||
self.html_usage_parameters(dom);
|
||||
|
||||
+3
-2
@@ -7,7 +7,8 @@ exports.collect = collect;
|
||||
|
||||
var ngdoc = require('./ngdoc.js'),
|
||||
Q = require('qq'),
|
||||
qfs = require('q-fs');
|
||||
qfs = require('q-fs'),
|
||||
PATH = require('path');
|
||||
|
||||
var NEW_LINE = /\n\r?/;
|
||||
|
||||
@@ -43,7 +44,7 @@ function collect() {
|
||||
var work2;
|
||||
if (file.match(/\.ngdoc$/)) {
|
||||
work2 = Q.when(qfs.read(file, 'b'), function(content){
|
||||
var section = '@section ' + file.split('/')[2] + '\n';
|
||||
var section = '@section ' + file.split(PATH.sep)[2] + '\n';
|
||||
allDocs.push(new ngdoc.Doc(section + content.toString(),file, 1).parse());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# current angular version. If this rule matches the appcache-offline.manifest will be served for
|
||||
# requests to appcache.manifest
|
||||
#
|
||||
# This file must be processed by Rake in order to replace %ANGULAR_VERSION% with the actual version.
|
||||
# This file must be processed by Grunt in order to replace %ANGULAR_VERSION% with the actual version.
|
||||
|
||||
Options -Indexes
|
||||
RewriteEngine on
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
img.AngularJS-small {
|
||||
width: 95px;
|
||||
height: 25px;
|
||||
/* Logo */
|
||||
|
||||
.header .brand {
|
||||
padding-top: 6px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
.header .brand img {
|
||||
height: 25px;
|
||||
width: 92px;
|
||||
}
|
||||
|
||||
/* end: Logo */
|
||||
|
||||
|
||||
.clear-navbar {
|
||||
margin-top: 60px;
|
||||
@@ -86,6 +95,15 @@ img.AngularJS-small {
|
||||
/* Content */
|
||||
/* =============================== */
|
||||
|
||||
.improve-docs {
|
||||
float: right;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.improve-docs {
|
||||
z-index:100;
|
||||
}
|
||||
|
||||
.hint {
|
||||
font-size: .7em;
|
||||
color: #c0c0c0;
|
||||
@@ -184,3 +202,18 @@ ul.events > li > h3 {
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.tutorial-index-page,
|
||||
.tutorial-the-end-page {
|
||||
padding-top:50px;
|
||||
}
|
||||
|
||||
.tutorial-page {
|
||||
position:relative;
|
||||
}
|
||||
|
||||
.tutorial-page .improve-docs {
|
||||
position:absolute;
|
||||
top:0;
|
||||
right:0;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
headEl = document.head,
|
||||
angularVersion = {
|
||||
current: '"NG_VERSION_FULL"', // rewrite during build
|
||||
stable: '"NG_VERSION_STABLE"'
|
||||
cdn: '"NG_VERSION_CDN"'
|
||||
};
|
||||
|
||||
addTag('script', {src: path('angular-scenario.js')}, function() {
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
function path(name) {
|
||||
return production
|
||||
? 'http://code.angularjs.org/' + angularVersion.stable + '/' + name
|
||||
? 'http://code.angularjs.org/' + angularVersion.cdn + '/' + name
|
||||
: '../' + name;
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
sync = true,
|
||||
angularVersion = {
|
||||
current: '"NG_VERSION_FULL"', // rewrite during build
|
||||
stable: '"NG_VERSION_STABLE"'
|
||||
cdn: '"NG_VERSION_CDN"'
|
||||
};
|
||||
|
||||
addTag('base', {href: baseUrl});
|
||||
@@ -48,12 +48,12 @@
|
||||
if (production) {
|
||||
if (name.match(/^angular(-\w+)?\.js/) && !name.match(/bootstrap/)) {
|
||||
name = '//ajax.googleapis.com/ajax/libs/angularjs/' +
|
||||
angularVersion.stable +
|
||||
angularVersion.cdn +
|
||||
'/' +
|
||||
name.replace(/\.js$/, '.min.js');
|
||||
} else {
|
||||
name = 'http://code.angularjs.org/' +
|
||||
angularVersion.stable +
|
||||
angularVersion.cdn +
|
||||
'/' +
|
||||
name.replace(/\.js$/, '.min.js');
|
||||
}
|
||||
@@ -114,8 +114,8 @@
|
||||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="http://angularjs.org" style="padding-top: 6px; padding-bottom: 0px;">
|
||||
<img class="AngularJS-small" src="http://angularjs.org/img/AngularJS-small.png">
|
||||
<a class="brand" href="http://angularjs.org">
|
||||
<img class="logo" src="img/angularjs-for-header-only.svg">
|
||||
</a>
|
||||
<ul class="nav">
|
||||
<li class="divider-vertical"></li>
|
||||
@@ -130,6 +130,7 @@
|
||||
<li><a href="http://www.youtube.com/user/angularjs">Watch</a></li>
|
||||
<li><a href="tutorial">Tutorial</a></li>
|
||||
<li><a href="http://builtwith.angularjs.org/">Case Studies</a></li>
|
||||
<li><a href="https://github.com/angular/angular-seed">Seed App project template</a></li>
|
||||
<li><a href="misc/faq">FAQ</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -139,9 +140,9 @@
|
||||
<i class="icon-book icon-white"></i> Develop <b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="http://docs.angularjs.org/tutorial">Tutorial</a></li>
|
||||
<li><a href="http://docs.angularjs.org/guide/">Developer Guide</a></li>
|
||||
<li><a href="http://docs.angularjs.org/api/">API Reference</a></li>
|
||||
<li><a href="./tutorial/">Tutorial</a></li>
|
||||
<li><a href="./guide/">Developer Guide</a></li>
|
||||
<li><a href="./api/">API Reference</a></li>
|
||||
<li><a href="http://docs.angularjs.org/misc/contribute">Contribute</a></li>
|
||||
<li><a href="http://code.angularjs.org/">Download</a></li>
|
||||
</ul>
|
||||
@@ -203,7 +204,7 @@
|
||||
<div class="dropdown search"
|
||||
ng-class="{open: focused && bestMatch.rank > 0 && bestMatch.page != currentPage}">
|
||||
<input type="text" ng-model="search" placeholder="search the docs"
|
||||
tabindex="1" accesskey="s" class="input-medium search-query" focused="focused">
|
||||
tabindex="1" accesskey="s" class="input-medium search-query" />
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a href="{{bestMatch.page.url}}">{{bestMatch.page.shortName}}</a>
|
||||
|
||||
@@ -18,8 +18,8 @@ docsApp.directive.focused = function($timeout) {
|
||||
scope.$eval(attrs.focused + '=false');
|
||||
});
|
||||
});
|
||||
scope.$eval(attrs.focused + '=true')
|
||||
}
|
||||
scope.$eval(attrs.focused + '=true');
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ docsApp.directive.sourceEdit = function(getEmbeddedTemplate) {
|
||||
openPlunkr(sources);
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function read(text) {
|
||||
var files = [];
|
||||
@@ -127,7 +127,7 @@ docsApp.directive.docTutorialReset = function() {
|
||||
'</div>\n');
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
docsApp.serviceFactory.angularUrls = function($document) {
|
||||
@@ -141,7 +141,7 @@ docsApp.serviceFactory.angularUrls = function($document) {
|
||||
});
|
||||
|
||||
return urls;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
docsApp.serviceFactory.formPostData = function($document) {
|
||||
@@ -174,7 +174,7 @@ docsApp.serviceFactory.openPlunkr = function(templateMerge, formPostData, angula
|
||||
var scriptDeps = '';
|
||||
angular.forEach(content.deps, function(file) {
|
||||
if (file.name !== 'angular.js') {
|
||||
scriptDeps += ' <script src="' + file.name + '"></script>\n'
|
||||
scriptDeps += ' <script src="' + file.name + '"></script>\n';
|
||||
}
|
||||
});
|
||||
indexProp = {
|
||||
@@ -310,7 +310,7 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
|
||||
last: this.$last,
|
||||
active: page1 && this.currentPage == page1 || page2 && this.currentPage == page2
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
$scope.submitForm = function() {
|
||||
$scope.bestMatch && $location.path($scope.bestMatch.page.url);
|
||||
@@ -509,7 +509,7 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
|
||||
},
|
||||
types: [],
|
||||
filters: []
|
||||
}
|
||||
};
|
||||
modules.push(module);
|
||||
}
|
||||
return module;
|
||||
@@ -560,7 +560,7 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
|
||||
|
||||
angular.element(document.getElementById('disqus_thread')).html('');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
angular.module('docsApp', ['ngResource', 'ngCookies', 'ngSanitize', 'bootstrap', 'bootstrapPrettify']).
|
||||
|
||||
+4
-5
@@ -61,22 +61,21 @@ exports.copy = function(from, to, transform) {
|
||||
|
||||
|
||||
exports.symlink = symlink;
|
||||
function symlink(from, to) {
|
||||
function symlink(from, to, type) {
|
||||
return qfs.exists(to).then(function(exists) {
|
||||
if (!exists) {
|
||||
return qfs.symbolicLink(to, from);
|
||||
return qfs.symbolicLink(to, from, type);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
exports.symlinkTemplate = symlinkTemplate;
|
||||
function symlinkTemplate(filename) {
|
||||
function symlinkTemplate(filename, type) {
|
||||
var dest = OUTPUT_DIR + filename,
|
||||
dirDepth = dest.split('/').length,
|
||||
src = Array(dirDepth).join('../') + 'docs/src/templates/' + filename;
|
||||
|
||||
return symlink(src, dest);
|
||||
return symlink(src, dest, type);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!doctype html>
|
||||
<html ng-app>
|
||||
<html ng-app="personalLog">
|
||||
<head>
|
||||
<title>Personal Log</title>
|
||||
<script src="../../src/loader.js"></script>
|
||||
|
||||
+158
-121
@@ -16,14 +16,14 @@
|
||||
/**
|
||||
* @fileoverview A utility to get better currency format pattern.
|
||||
*
|
||||
* This module implement a new currency format representation model. It
|
||||
* This module implements a new currency format representation model. It
|
||||
* provides 3 currency representation forms: global, portable and local. Local
|
||||
* format is the most popular format people use to represent currency in its
|
||||
* circulating country without worrying about how it should be distinguished
|
||||
* from other currencies. Global format is a formal representation in context
|
||||
* of multiple currencies in same page, it is ISO 4217 currency code. Portable
|
||||
* format is a compromise between global and local. It looks similar to how
|
||||
* people would like to see how their currencies is being represented in other
|
||||
* people would like to see how their currency is being represented in other
|
||||
* media. While at the same time, it should be distinguishable to world's
|
||||
* popular currencies (like USD, EUR) and currencies somewhat relevant in the
|
||||
* area (like CNY in HK, though native currency is HKD). There is no guarantee
|
||||
@@ -43,15 +43,14 @@ goog.i18n.currency.PRECISION_MASK_ = 0x07;
|
||||
|
||||
|
||||
/**
|
||||
* If this flag is set, it means the currency sign should position before
|
||||
* number.
|
||||
* Whether the currency sign should be positioned after the number.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.currency.POSITION_FLAG_ = 0x08;
|
||||
|
||||
|
||||
/**
|
||||
* Should a space to inserted between number and currency sign.
|
||||
* Whether a space should be inserted between the number and currency sign.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.currency.SPACE_FLAG_ = 0x20;
|
||||
@@ -59,8 +58,8 @@ goog.i18n.currency.SPACE_FLAG_ = 0x20;
|
||||
|
||||
/**
|
||||
* This function will add tier2 currency support. Be default, only tier1
|
||||
* (most popular currencies) are supportted. If an application really need
|
||||
* to support some of the rarely used currency, it should call this function
|
||||
* (most popular currencies) are supported. If an application really needs
|
||||
* to support some of the rarely used currencies, it should call this function
|
||||
* before any other functions in this namespace.
|
||||
*/
|
||||
goog.i18n.currency.addTier2Support = function() {
|
||||
@@ -75,8 +74,8 @@ goog.i18n.currency.addTier2Support = function() {
|
||||
* Global currency pattern always uses ISO-4217 currency code as prefix. Local
|
||||
* currency sign is added if it is different from currency code. Each currency
|
||||
* is unique in this form. The negative side is that ISO code looks weird in
|
||||
* some countries as poeple normally do not use it. Local currency sign
|
||||
* alleviate the problem, but also make it a little verbose.
|
||||
* some countries as people normally do not use it. Local currency sign
|
||||
* alleviates the problem, but also makes it a little verbose.
|
||||
*
|
||||
* @param {string} currencyCode ISO-4217 3-letter currency code.
|
||||
* @return {string} Global currency pattern string for given currency.
|
||||
@@ -85,9 +84,6 @@ goog.i18n.currency.getGlobalCurrencyPattern = function(currencyCode) {
|
||||
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
|
||||
var patternNum = info[0];
|
||||
if (currencyCode == info[1]) {
|
||||
if ((patternNum & goog.i18n.currency.POSITION_FLAG_) == 0) {
|
||||
patternNum |= goog.i18n.currency.SPACE_FLAG_;
|
||||
}
|
||||
return goog.i18n.currency.getCurrencyPattern_(patternNum, info[1]);
|
||||
}
|
||||
return currencyCode + ' ' +
|
||||
@@ -104,10 +100,8 @@ goog.i18n.currency.getGlobalCurrencyPattern = function(currencyCode) {
|
||||
*/
|
||||
goog.i18n.currency.getGlobalCurrencySign = function(currencyCode) {
|
||||
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
|
||||
if (currencyCode == info[1]) {
|
||||
return currencyCode;
|
||||
}
|
||||
return currencyCode + ' ' + info[1];
|
||||
return (currencyCode == info[1]) ? currencyCode :
|
||||
currencyCode + ' ' + info[1];
|
||||
};
|
||||
|
||||
|
||||
@@ -128,6 +122,7 @@ goog.i18n.currency.getLocalCurrencyPattern = function(currencyCode) {
|
||||
/**
|
||||
* Returns local currency sign string for those applications that need to
|
||||
* handle currency sign separately.
|
||||
*
|
||||
* @param {string} currencyCode ISO-4217 3-letter currency code.
|
||||
* @return {string} Local currency sign for given currency.
|
||||
*/
|
||||
@@ -156,6 +151,7 @@ goog.i18n.currency.getPortableCurrencyPattern = function(currencyCode) {
|
||||
/**
|
||||
* Return portable currency sign string for those applications that need to
|
||||
* handle currency sign themselves.
|
||||
*
|
||||
* @param {string} currencyCode ISO-4217 3-letter currency code.
|
||||
* @return {string} Portable currency sign for given currency.
|
||||
*/
|
||||
@@ -165,10 +161,13 @@ goog.i18n.currency.getPortableCurrencySign = function(currencyCode) {
|
||||
|
||||
|
||||
/**
|
||||
* This function returns the default currency sign position. Some application
|
||||
* This function returns the default currency sign position. Some applications
|
||||
* may want to handle currency sign and currency amount separately. This
|
||||
* function can be used in such situation to position the currency sign
|
||||
* relative to amount field correctly.
|
||||
* function can be used in such situations to correctly position the currency
|
||||
* sign relative to the amount.
|
||||
*
|
||||
* To match the behavior of ICU, position is not determined by display locale.
|
||||
*
|
||||
* @param {string} currencyCode ISO-4217 3-letter currency code.
|
||||
* @return {boolean} true if currency should be positioned before amount field.
|
||||
*/
|
||||
@@ -179,13 +178,12 @@ goog.i18n.currency.isPrefixSignPosition = function(currencyCode) {
|
||||
|
||||
|
||||
/**
|
||||
* This function construct the currency pattern. Currency sign is provided. The
|
||||
* This function constructs the currency pattern. Currency sign is provided. The
|
||||
* pattern information is encoded in patternNum.
|
||||
*
|
||||
* @param {number} patternNum Encoded pattern number that has
|
||||
* currency pattern information.
|
||||
* @param {string} sign the currency sign that will be used in pattern.
|
||||
*
|
||||
* @param {string} sign The currency sign that will be used in pattern.
|
||||
* @return {string} currency pattern string.
|
||||
* @private
|
||||
*/
|
||||
@@ -211,56 +209,97 @@ goog.i18n.currency.getCurrencyPattern_ = function(patternNum, sign) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Modify currency pattern string by adjusting precision for given currency.
|
||||
* Standard currency pattern will have 2 digit after decimal point.
|
||||
* Examples:
|
||||
* $#,##0.00 -> $#,##0 (precision == 0)
|
||||
* $#,##0.00 -> $#,##0.0 (precision == 1)
|
||||
* $#,##0.00 -> $#,##0.000 (precision == 3)
|
||||
*
|
||||
* @param {string} pattern currency pattern string.
|
||||
* @param {string} currencyCode 3-letter currency code.
|
||||
* @return {string} modified currency pattern string.
|
||||
*/
|
||||
goog.i18n.currency.adjustPrecision = function(pattern, currencyCode) {
|
||||
var strParts = ['0'];
|
||||
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
|
||||
var precision = info[0] & goog.i18n.currency.PRECISION_MASK_;
|
||||
if (precision > 0) {
|
||||
strParts.push('.');
|
||||
for (var i = 0; i < precision; i++) {
|
||||
strParts.push('0');
|
||||
}
|
||||
}
|
||||
return pattern.replace(/0.00/g, strParts.join(''));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Tier 1 currency information.
|
||||
*
|
||||
* The first number in the array is a combination of the precision mask and
|
||||
* other flags. The precision mask indicates how many decimal places to show for
|
||||
* the currency. Valid values are [0..7]. The position flag indicates whether
|
||||
* the currency sign should be positioned after the number. Valid values are 0
|
||||
* (before the number) or 16 (after the number). The space flag indicates
|
||||
* whether a space should be inserted between the currency sign and number.
|
||||
* Valid values are 0 (no space) and 24 (space).
|
||||
*
|
||||
* The number in the array is calculated by adding together the mask and flag
|
||||
* values. For example:
|
||||
*
|
||||
* 0: no precision (0), currency sign first (0), no space (0)
|
||||
* 2: two decimals precision (2), currency sign first (0), no space (0)
|
||||
* 18: two decimals precision (2), currency sign last (16), no space (0)
|
||||
* 42: two decimals precision (2), currency sign last (16), space (24)
|
||||
*
|
||||
* @type {!Object.<!Array>}
|
||||
*/
|
||||
goog.i18n.currency.CurrencyInfo = {
|
||||
'AED': [2, '\u062F\u002e\u0625', 'DH'],
|
||||
'ARS': [2, '$', 'AR$'],
|
||||
'AED': [2, 'dh', '\u062f.\u0625.', 'DH'],
|
||||
'AUD': [2, '$', 'AU$'],
|
||||
'BDT': [2, '\u09F3', 'Tk'],
|
||||
'BRL': [2, 'R$', 'R$'],
|
||||
'CAD': [2, '$', 'C$'],
|
||||
'CHF': [2, 'Fr.', 'CHF'],
|
||||
'CHF': [2, 'CHF', 'CHF'],
|
||||
'CLP': [0, '$', 'CL$'],
|
||||
'CNY': [2, '¥', 'RMB¥'],
|
||||
'COP': [2, '$', 'COL$'],
|
||||
'CRC': [2, '\u20a1', 'CR₡'],
|
||||
'CUP': [2, '$', '$MN'],
|
||||
'CZK': [10, 'Kč', 'Kč'],
|
||||
'DKK': [26, 'kr', 'kr'],
|
||||
'COP': [0, '$', 'COL$'],
|
||||
'CRC': [0, '\u20a1', 'CR\u20a1'],
|
||||
'CZK': [2, 'K\u010d', 'K\u010d'],
|
||||
'DKK': [18, 'kr', 'kr'],
|
||||
'DOP': [2, '$', 'RD$'],
|
||||
'EGP': [2, '£', 'LE'],
|
||||
'EUR': [26, '€', '€'],
|
||||
'EUR': [18, '€', '€'],
|
||||
'GBP': [2, '£', 'GB£'],
|
||||
'HKD': [2, '$', 'HK$'],
|
||||
'ILS': [10, '\u20AA', 'IL₪'],
|
||||
'INR': [2, 'Rs', 'Rs'],
|
||||
'ISK': [10, 'kr', 'kr'],
|
||||
'ILS': [2, '\u20AA', 'IL\u20AA'],
|
||||
'INR': [2, '\u20B9', 'Rs'],
|
||||
'ISK': [0, 'kr', 'kr'],
|
||||
'JMD': [2, '$', 'JA$'],
|
||||
'JPY': [0, '¥', 'JP¥'],
|
||||
'KRW': [0, '\u20A9', 'KR₩'],
|
||||
'LKR': [2, 'Rs', 'SLRs'],
|
||||
'MNT': [2, '\u20AE', 'MN₮'],
|
||||
'MNT': [0, '\u20AE', 'MN₮'],
|
||||
'MXN': [2, '$', 'Mex$'],
|
||||
'MYR': [2, 'RM', 'RM'],
|
||||
'NOK': [26, 'kr', 'NOkr'],
|
||||
'NOK': [18, 'kr', 'NOkr'],
|
||||
'PAB': [2, 'B/.', 'B/.'],
|
||||
'PEN': [2, 'S/.', 'S/.'],
|
||||
'PHP': [2, 'P', 'PHP'],
|
||||
'PKR': [2, 'Rs.', 'PKRs.'],
|
||||
'RUB': [10, 'руб', 'руб'],
|
||||
'SAR': [2, '\u0633\u002E\u0631', 'SR'],
|
||||
'SEK': [10, 'kr', 'kr'],
|
||||
'PHP': [2, '\u20B1', 'Php'],
|
||||
'PKR': [0, 'Rs', 'PKRs.'],
|
||||
'RUB': [42, 'руб.', 'руб.'],
|
||||
'SAR': [2, 'Rial', 'Rial'],
|
||||
'SEK': [2, 'kr', 'kr'],
|
||||
'SGD': [2, '$', 'S$'],
|
||||
'THB': [2, '\u0e3f', 'THB'],
|
||||
'TRY': [2, 'YTL', 'YTL'],
|
||||
'TRY': [2, 'TL', 'YTL'],
|
||||
'TWD': [2, 'NT$', 'NT$'],
|
||||
'USD': [2, '$', 'US$'],
|
||||
'UYU': [2, '$', 'UY$'],
|
||||
'VND': [10, '\u20AB', 'VN₫'],
|
||||
'YER': [2, 'YER', 'YER'],
|
||||
'VND': [0, '\u20AB', 'VN\u20AB'],
|
||||
'YER': [0, 'Rial', 'Rial'],
|
||||
'ZAR': [2, 'R', 'ZAR']
|
||||
};
|
||||
|
||||
@@ -270,116 +309,114 @@ goog.i18n.currency.CurrencyInfo = {
|
||||
* @type {!Object.<!Array>}
|
||||
*/
|
||||
goog.i18n.currency.CurrencyInfoTier2 = {
|
||||
'AFN': [18, '\u060b', 'AFN'],
|
||||
'ALL': [2, 'Lek', 'Lek'],
|
||||
'AMD': [10, '\u0564\u0580\u002e', 'dram'],
|
||||
'ANG': [2, '\u0083', 'NAƒ'],
|
||||
'AFN': [16, 'Af.', 'AFN'],
|
||||
'ALL': [0, 'Lek', 'Lek'],
|
||||
'AMD': [0, 'Dram', 'dram'],
|
||||
'AOA': [2, 'Kz', 'Kz'],
|
||||
'AWG': [2, 'ƒ', 'Afl.'],
|
||||
'AZN': [2, 'm', 'man'],
|
||||
'BAM': [18, 'КМ', 'KM'],
|
||||
'ARS': [2, '$', 'AR$'],
|
||||
'AWG': [2, 'Afl.', 'Afl.'],
|
||||
'AZN': [2, 'man.', 'man.'],
|
||||
'BAM': [18, 'KM', 'KM'],
|
||||
'BBD': [2, '$', 'Bds$'],
|
||||
'BGN': [10, '\u043b\u0432', 'лв'],
|
||||
'BHD': [3, '\u0628\u002e\u062f\u002e', 'BD'],
|
||||
'BGN': [2, 'lev', 'lev'],
|
||||
'BHD': [3, 'din', 'din'],
|
||||
'BIF': [0, 'FBu', 'FBu'],
|
||||
'BMD': [2, '$', 'BD$'],
|
||||
'BND': [2, '$', 'B$'],
|
||||
'BOB': [2, 'B$', 'B$'],
|
||||
'BSD': [2, '$', 'B$'],
|
||||
'BOB': [2, 'Bs', 'Bs'],
|
||||
'BSD': [2, '$', 'BS$'],
|
||||
'BTN': [2, 'Nu.', 'Nu.'],
|
||||
'BWP': [2, 'P', 'pula'],
|
||||
'BYR': [0, 'Br', 'Br'],
|
||||
'BYR': [0, 'BYR', 'BYR'],
|
||||
'BZD': [2, '$', 'BZ$'],
|
||||
'CDF': [2, 'F', 'CDF'],
|
||||
'CVE': [2, '$', 'Esc'],
|
||||
'CDF': [2, 'FrCD', 'CDF'],
|
||||
'CUC': [1, '$', 'CUC$'],
|
||||
'CUP': [2, '$', 'CU$'],
|
||||
'CVE': [2, 'CVE', 'Esc'],
|
||||
'DJF': [0, 'Fdj', 'Fdj'],
|
||||
'DZD': [2, '\u062f\u062C', 'DA'],
|
||||
'EEK': [10, 'EEK', 'EEK'],
|
||||
'DZD': [2, 'din', 'din'],
|
||||
'ERN': [2, 'Nfk', 'Nfk'],
|
||||
'ETB': [2, 'Br', 'Br'],
|
||||
'ETB': [2, 'Birr', 'Birr'],
|
||||
'FJD': [2, '$', 'FJ$'],
|
||||
'FKP': [2, '£', 'FK£'],
|
||||
'GEL': [2, 'GEL', 'GEL'],
|
||||
'GHS': [2, '\u20B5', 'GHS¢'],
|
||||
'GHS': [2, 'GHS', 'GHS'],
|
||||
'GIP': [2, '£', 'GI£'],
|
||||
'GMD': [2, 'D', 'GMD'],
|
||||
'GMD': [2, 'GMD', 'GMD'],
|
||||
'GNF': [0, 'FG', 'FG'],
|
||||
'GTQ': [2, 'Q', 'GTQ'],
|
||||
'GYD': [2, '$', 'GY$'],
|
||||
'GYD': [0, '$', 'GY$'],
|
||||
'HNL': [2, 'L', 'HNL'],
|
||||
'HRK': [2, 'kn', 'kn'],
|
||||
'HTG': [2, 'G', 'HTG'],
|
||||
'HUF': [10, 'Ft', 'Ft'],
|
||||
'IDR': [2, 'Rp', 'Rp'],
|
||||
'IQD': [3, '\u0639\u062F', 'IQD'],
|
||||
'IRR': [2, '\ufdfc', 'IRR'],
|
||||
'JOD': [3, 'JOD', 'JOD'],
|
||||
'KES': [2, 'KSh', 'KSh'],
|
||||
'KGS': [2, 'som', 'som'],
|
||||
'KHR': [10, '\u17DB', 'KHR'],
|
||||
'KMF': [0, 'KMF', 'KMF'],
|
||||
'KPW': [2, '\u20A9', 'KPW'],
|
||||
'KWD': [3, '\u062F\u002e\u0643', 'KWD'],
|
||||
'KYD': [2, '$', 'CI$'],
|
||||
'KZT': [10, 'KZT', 'KZT'],
|
||||
'LAK': [2, '\u20AD', 'LA₭'],
|
||||
'LBP': [2, '\u0644\u002e\u0644', 'LBP'],
|
||||
'HTG': [2, 'HTG', 'HTG'],
|
||||
'HUF': [0, 'Ft', 'Ft'],
|
||||
'IDR': [0, 'Rp', 'Rp'],
|
||||
'IQD': [0, 'din', 'IQD'],
|
||||
'IRR': [0, 'Rial', 'IRR'],
|
||||
'JOD': [3, 'din', 'JOD'],
|
||||
'KES': [2, 'Ksh', 'Ksh'],
|
||||
'KGS': [2, 'KGS', 'KGS'],
|
||||
'KHR': [2, 'Riel', 'KHR'],
|
||||
'KMF': [0, 'CF', 'KMF'],
|
||||
'KPW': [0, '\u20A9KP', 'KPW'],
|
||||
'KWD': [3, 'din', 'KWD'],
|
||||
'KYD': [2, '$', 'KY$'],
|
||||
'KZT': [2, '\u20B8', 'KZT'],
|
||||
'LAK': [0, '\u20AD', '\u20AD'],
|
||||
'LBP': [0, 'L£', 'LBP'],
|
||||
'LRD': [2, '$', 'L$'],
|
||||
'LSL': [2, 'L', 'LSL'],
|
||||
'LTL': [10, 'Lt', 'Lt'],
|
||||
'LVL': [10, 'Ls', 'Ls'],
|
||||
'LYD': [3, '\u0644\u002e\u062F', 'LD'],
|
||||
'MAD': [2, '\u0645\u002E\u062F\u002E', 'MAD'],
|
||||
'LSL': [2, 'LSL', 'LSL'],
|
||||
'LTL': [2, 'Lt', 'Lt'],
|
||||
'LVL': [2, 'Ls', 'Ls'],
|
||||
'LYD': [3, 'din', 'LD'],
|
||||
'MAD': [2, 'dh', 'MAD'],
|
||||
'MDL': [2, 'MDL', 'MDL'],
|
||||
'MGA': [1, 'MGA', 'MGA'],
|
||||
'MKD': [2, 'MKD', 'MKD'],
|
||||
'MMK': [2, 'K', 'MMK'],
|
||||
'MOP': [2, 'MOP$', 'MOP$'],
|
||||
'MRO': [1, 'UM', 'UM'],
|
||||
'MUR': [2, 'Rs', 'MURs'],
|
||||
'MVR': [2, 'Rf', 'MRF'],
|
||||
'MWK': [2, 'MK', 'MK'],
|
||||
'MGA': [0, 'Ar', 'MGA'],
|
||||
'MKD': [2, 'din', 'MKD'],
|
||||
'MMK': [0, 'K', 'MMK'],
|
||||
'MOP': [2, 'MOP', 'MOP$'],
|
||||
'MRO': [0, 'MRO', 'MRO'],
|
||||
'MUR': [0, 'MURs', 'MURs'],
|
||||
'MWK': [2, 'MWK', 'MWK'],
|
||||
'MZN': [2, 'MTn', 'MTn'],
|
||||
'NAD': [2, '$', 'N$'],
|
||||
'NGN': [2, '\u20A6', 'NG₦'],
|
||||
'NGN': [2, '\u20A6', 'NG\u20A6'],
|
||||
'NIO': [2, 'C$', 'C$'],
|
||||
'NPR': [2, 'Rs', 'NPRs'],
|
||||
'NZD': [2, '$', 'NZ$'],
|
||||
'OMR': [3, '\u0639\u002E\u062F\u002E', 'OMR'],
|
||||
'PGK': [2, 'K', 'PGK'],
|
||||
'PLN': [10, 'zł', 'zł'],
|
||||
'PYG': [0, '\u20b2', 'PYG'],
|
||||
'QAR': [2, '\u0642\u002E\u0631', 'QR'],
|
||||
'RON': [2, 'L', 'RON'],
|
||||
'RSD': [2, 'РС\u0414', 'RSD'],
|
||||
'OMR': [3, 'Rial', 'OMR'],
|
||||
'PGK': [2, 'PGK', 'PGK'],
|
||||
'PLN': [2, 'z\u0142', 'z\u0142'],
|
||||
'PYG': [0, 'Gs', 'PYG'],
|
||||
'QAR': [2, 'Rial', 'QR'],
|
||||
'RON': [2, 'RON', 'RON'],
|
||||
'RSD': [0, 'din', 'RSD'],
|
||||
'RWF': [0, 'RF', 'RF'],
|
||||
'SBD': [2, '$', 'SI$'],
|
||||
'SCR': [2, 'SR', 'SCR'],
|
||||
'SCR': [2, 'SCR', 'SCR'],
|
||||
'SDG': [2, 'SDG', 'SDG'],
|
||||
'SHP': [2, '£', 'SH£'],
|
||||
'SKK': [10, 'Sk', 'Sk'],
|
||||
'SLL': [2, 'Le', 'Le'],
|
||||
'SOS': [2, 'So. Sh.', 'So. Sh.'],
|
||||
'SLL': [0, 'SLL', 'SLL'],
|
||||
'SOS': [0, 'SOS', 'SOS'],
|
||||
'SRD': [2, '$', 'SR$'],
|
||||
'STD': [2, 'Db', 'Db'],
|
||||
'SYP': [18, 'SYP', 'SYP'],
|
||||
'SZL': [2, 'L', 'SZL'],
|
||||
'TJS': [2, 'TJS', 'TJS'],
|
||||
'TMM': [2, 'm', 'TMM'],
|
||||
'TND': [3, '\u062F\u002e\u062A ', 'DT'],
|
||||
'STD': [0, 'Db', 'Db'],
|
||||
'SYP': [16, '£', 'SY£'],
|
||||
'SZL': [2, 'SZL', 'SZL'],
|
||||
'TJS': [2, 'Som', 'TJS'],
|
||||
'TND': [3, 'din', 'DT'],
|
||||
'TOP': [2, 'T$', 'T$'],
|
||||
'TTD': [2, '$', 'TT$'],
|
||||
'TZS': [10, 'TZS', 'TZS'],
|
||||
'UAH': [10, '\u20B4', 'грн'],
|
||||
'UGX': [2, 'USh', 'USh'],
|
||||
'UZS': [2, 'UZS', 'UZS'],
|
||||
'VEF': [2, 'Bs.F', 'Bs.F'],
|
||||
'VUV': [0, 'Vt', 'Vt'],
|
||||
'WST': [2, 'WS$', 'WS$'],
|
||||
'TZS': [0, 'TSh', 'TSh'],
|
||||
'UAH': [2, '\u20B4', 'UAH'],
|
||||
'UGX': [0, 'UGX', 'UGX'],
|
||||
'UYU': [1, '$', '$U'],
|
||||
'UZS': [0, 'so\u02bcm', 'UZS'],
|
||||
'VEF': [2, 'Bs', 'Bs'],
|
||||
'VUV': [0, 'VUV', 'VUV'],
|
||||
'WST': [2, 'WST', 'WST'],
|
||||
'XAF': [0, 'FCFA', 'FCFA'],
|
||||
'XCD': [2, '$', 'EC$'],
|
||||
'XOF': [0, 'CFA', 'CFA'],
|
||||
'XPF': [0, 'F', 'XPF'],
|
||||
'ZMK': [2, 'ZK', 'ZK'],
|
||||
'ZWL': [2, '$', 'ZW$']
|
||||
'XPF': [0, 'FCFP', 'FCFP'],
|
||||
'ZMK': [0, 'ZMK', 'ZMK']
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+497
-296
File diff suppressed because it is too large
Load Diff
+358
-181
File diff suppressed because it is too large
Load Diff
+213
-146
@@ -1,4 +1,4 @@
|
||||
// Copyright 2011 The Closure Library Authors. All Rights Reserved
|
||||
// Copyright 2012 The Closure Library Authors. All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -7,35 +7,24 @@
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations under
|
||||
// the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Plural rules.
|
||||
*
|
||||
* This file is autogenerated by script:
|
||||
* http://go/generate_pluralrules.py
|
||||
* using the --for_closure flag.
|
||||
* http://go/generate_pluralrules.py
|
||||
*
|
||||
* To reduce the file size (which may cause issues in some JS
|
||||
* developing environments), this file will only contain locales
|
||||
* that are usually supported by google products. This is defined as
|
||||
* closure_tier1_locales and will change (most likely addition)
|
||||
* over time. Rest of the data can be found in another file named
|
||||
* "pluralrulesext.js", which will be generated at the
|
||||
* same time together with this file.
|
||||
*
|
||||
* Before checkin, this file could have been manually edited. This is
|
||||
* to incorporate changes before we could fix CLDR. All manual
|
||||
* modification must be documented in this section, and should be
|
||||
* removed after those changes land to CLDR.
|
||||
* Before check in, this file could have been manually edited. This is to
|
||||
* incorporate changes before we could fix CLDR. All manual modification must be
|
||||
* documented in this section, and should be removed after those changes land to
|
||||
* CLDR.
|
||||
*/
|
||||
|
||||
goog.provide('goog.i18n.pluralRules');
|
||||
|
||||
|
||||
/**
|
||||
* Plural pattern keyword
|
||||
* @enum {string}
|
||||
@@ -53,7 +42,7 @@ goog.i18n.pluralRules.Keyword = {
|
||||
/**
|
||||
* Default plural select rule.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Default plural value.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Default value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.defaultSelect_ = function(n) {
|
||||
@@ -64,25 +53,25 @@ goog.i18n.pluralRules.defaultSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for ar locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.arSelect_ = function(n) {
|
||||
if (n == 0) {
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
}
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 2) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
}
|
||||
if ((n % 100) >= 3 && (n % 100) <= 10 && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == (n | 0) && n % 100 >= 3 && n % 100 <= 10) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
if ((n % 100) >= 11 && (n % 100) <= 99 && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
if (n == (n | 0) && n % 100 >= 11 && n % 100 <= 99) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -91,13 +80,13 @@ goog.i18n.pluralRules.arSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for en locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.enSelect_ = function(n) {
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -106,13 +95,13 @@ goog.i18n.pluralRules.enSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for fil locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.filSelect_ = function(n) {
|
||||
if (n == 0 || n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -121,13 +110,13 @@ goog.i18n.pluralRules.filSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for fr locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.frSelect_ = function(n) {
|
||||
if (n >= 0 && n < 2) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
if (n >= 0 && n <= 2 && n != 2) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -136,16 +125,34 @@ goog.i18n.pluralRules.frSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for lv locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.lvSelect_ = function(n) {
|
||||
if (n == 0) {
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
}
|
||||
if ((n % 10) == 1 && (n % 100) != 11) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
if (n % 10 == 1 && n % 100 != 11) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Plural select rules for iu locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.iuSelect_ = function(n) {
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 2) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -154,16 +161,22 @@ goog.i18n.pluralRules.lvSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for ga locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.gaSelect_ = function(n) {
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 2) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
}
|
||||
if (n == (n | 0) && n >= 3 && n <= 6) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
if (n == (n | 0) && n >= 7 && n <= 10) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -172,17 +185,16 @@ goog.i18n.pluralRules.gaSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for ro locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.roSelect_ = function(n) {
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 0 || n != 1 && (n % 100) >= 1 &&
|
||||
(n % 100) <= 19 && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == 0 || n != 1 && n == (n | 0) && n % 100 >= 1 && n % 100 <= 19) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -191,40 +203,37 @@ goog.i18n.pluralRules.roSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for lt locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.ltSelect_ = function(n) {
|
||||
if ((n % 10) == 1 && ((n % 100) < 11 || (n % 100) > 19)) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
if (n % 10 == 1 && (n % 100 < 11 || n % 100 > 19)) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if ((n % 10) >= 2 && (n % 10) <= 9 &&
|
||||
((n % 100) < 11 || (n % 100) > 19) && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == (n | 0) && n % 10 >= 2 && n % 10 <= 9 && (n % 100 < 11 || n % 100 > 19)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Plural select rules for hr locale
|
||||
* Plural select rules for be locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.hrSelect_ = function(n) {
|
||||
if ((n % 10) == 1 && (n % 100) != 11) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
goog.i18n.pluralRules.beSelect_ = function(n) {
|
||||
if (n % 10 == 1 && n % 100 != 11) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if ((n % 10) >= 2 && (n % 10) <= 4 &&
|
||||
((n % 100) < 12 || (n % 100) > 14) && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == (n | 0) && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
if ((n % 10) == 0 || ((n % 10) >= 5 && (n % 10) <= 9) ||
|
||||
((n % 100) >= 11 && (n % 100) <= 14) && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
if (n % 10 == 0 || n == (n | 0) && n % 10 >= 5 && n % 10 <= 9 || n == (n | 0) && n % 100 >= 11 && n % 100 <= 14) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -233,16 +242,16 @@ goog.i18n.pluralRules.hrSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for cs locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.csSelect_ = function(n) {
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 2 || n == 3 || n == 4) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == (n | 0) && n >= 2 && n <= 4) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -251,22 +260,19 @@ goog.i18n.pluralRules.csSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for pl locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.plSelect_ = function(n) {
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if ((n % 10) >= 2 && (n % 10) <= 4 &&
|
||||
((n % 100) < 12 || (n % 100) > 14) && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == (n | 0) && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
if ((n % 10) == 0 || n != 1 && (n % 10) == 1 ||
|
||||
((n % 10) >= 5 && (n % 10) <= 9 || (n % 100) >= 12 && (n % 100) <= 14) &&
|
||||
n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
if (n != 1 && (n % 10 == 0 || n % 10 == 1) || n == (n | 0) && n % 10 >= 5 && n % 10 <= 9 || n == (n | 0) && n % 100 >= 12 && n % 100 <= 14) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -275,19 +281,19 @@ goog.i18n.pluralRules.plSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for sl locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.slSelect_ = function(n) {
|
||||
if ((n % 100) == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
if (n % 100 == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if ((n % 100) == 2) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
if (n % 100 == 2) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
}
|
||||
if ((n % 100) == 3 || (n % 100) == 4) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n % 100 == 3 || n % 100 == 4) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -296,19 +302,19 @@ goog.i18n.pluralRules.slSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for mt locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.mtSelect_ = function(n) {
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 0 || ((n % 100) >= 2 && (n % 100) <= 4 && n == Math.floor(n))) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == 0 || n == (n | 0) && n % 100 >= 2 && n % 100 <= 10) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
if ((n % 100) >= 11 && (n % 100) <= 19 && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
if (n == (n | 0) && n % 100 >= 11 && n % 100 <= 19) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -317,13 +323,13 @@ goog.i18n.pluralRules.mtSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for mk locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.mkSelect_ = function(n) {
|
||||
if ((n % 10) == 1 && n != 11) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
if (n % 10 == 1 && n != 11) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -332,25 +338,25 @@ goog.i18n.pluralRules.mkSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for cy locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.cySelect_ = function(n) {
|
||||
if (n == 0) {
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
}
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 2) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
}
|
||||
if (n == 3) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
if (n == 6) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -359,16 +365,16 @@ goog.i18n.pluralRules.cySelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for lag locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.lagSelect_ = function(n) {
|
||||
if (n == 0) {
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
}
|
||||
if (n > 0 && n < 2) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
if (n >= 0 && n <= 2 && n != 0 && n != 2) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -377,16 +383,16 @@ goog.i18n.pluralRules.lagSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for shi locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.shiSelect_ = function(n) {
|
||||
if (n >= 0 && n <= 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n >= 2 && n <= 10 && n == Math.floor(n)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
if (n == (n | 0) && n >= 2 && n <= 10) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -395,25 +401,91 @@ goog.i18n.pluralRules.shiSelect_ = function(n) {
|
||||
/**
|
||||
* Plural select rules for br locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.brSelect_ = function(n) {
|
||||
if (n % 10 == 1 && n % 100 != 11 && n % 100 != 71 && n % 100 != 91) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n % 10 == 2 && n % 100 != 12 && n % 100 != 72 && n % 100 != 92) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
}
|
||||
if ((n % 10 == 3 || n % 10 == 4 || n % 10 == 9) && ((n % 100 < 10 || n % 100 > 19) && (n % 100 < 70 || n % 100 > 79) && (n % 100 < 90 || n % 100 > 99))) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
if (n % 1000000 == 0 && n != 0) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Plural select rules for ksh locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.kshSelect_ = function(n) {
|
||||
if (n == 0) {
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
return goog.i18n.pluralRules.Keyword.ZERO;
|
||||
}
|
||||
if (n == 1) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 2) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Plural select rules for tzm locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.tzmSelect_ = function(n) {
|
||||
if (n == 0 || n == 1 || n == (n | 0) && n >= 11 && n <= 99) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 3) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Plural select rules for gv locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.gvSelect_ = function(n) {
|
||||
if (n % 10 == 1 || n % 10 == 2 || n % 20 == 0) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 6) {
|
||||
return goog.i18n.pluralRules.Keyword.MANY;
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Plural select rules for gd locale
|
||||
*
|
||||
* @param {number} n The count of items.
|
||||
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
|
||||
* @private
|
||||
*/
|
||||
goog.i18n.pluralRules.gdSelect_ = function(n) {
|
||||
if (n == 1 || n == 11) {
|
||||
return goog.i18n.pluralRules.Keyword.ONE;
|
||||
}
|
||||
if (n == 2 || n == 12) {
|
||||
return goog.i18n.pluralRules.Keyword.TWO;
|
||||
}
|
||||
if (n == (n | 0) && (n >= 3 && n <= 10 || n >= 13 && n <= 19)) {
|
||||
return goog.i18n.pluralRules.Keyword.FEW;
|
||||
}
|
||||
return goog.i18n.pluralRules.Keyword.OTHER;
|
||||
};
|
||||
@@ -423,7 +495,6 @@ goog.i18n.pluralRules.brSelect_ = function(n) {
|
||||
* Selected plural rules by locale.
|
||||
*/
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
|
||||
|
||||
if (goog.LOCALE == 'am') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.filSelect_;
|
||||
}
|
||||
@@ -557,7 +628,7 @@ if (goog.LOCALE == 'hi') {
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'hr') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.hrSelect_;
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.beSelect_;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'hu') {
|
||||
@@ -581,7 +652,7 @@ if (goog.LOCALE == 'it') {
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'iw') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'ja') {
|
||||
@@ -612,10 +683,6 @@ if (goog.LOCALE == 'ml') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'mo') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.roSelect_;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'mr') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
|
||||
}
|
||||
@@ -661,7 +728,7 @@ if (goog.LOCALE == 'ro') {
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'ru') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.hrSelect_;
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.beSelect_;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'sk') {
|
||||
@@ -677,7 +744,7 @@ if (goog.LOCALE == 'sq') {
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'sr') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.hrSelect_;
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.beSelect_;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'sv') {
|
||||
@@ -709,7 +776,7 @@ if (goog.LOCALE == 'tr') {
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'uk') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.hrSelect_;
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.beSelect_;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'ur') {
|
||||
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
PARENT_DIR="$(dirname "$0")"
|
||||
jasmine-node "$PARENT_DIR"/spec/
|
||||
@@ -0,0 +1,266 @@
|
||||
var closureI18nExtractor = require('../src/closureI18nExtractor.js');
|
||||
var converter = require('../src/converter.js');
|
||||
findLocaleId = closureI18nExtractor.findLocaleId;
|
||||
extractNumberSymbols = closureI18nExtractor.extractNumberSymbols;
|
||||
extractCurrencySymbols = closureI18nExtractor.extractCurrencySymbols;
|
||||
extractDateTimeSymbols = closureI18nExtractor.extractDateTimeSymbols;
|
||||
|
||||
|
||||
function newTestLocaleInfo() {
|
||||
return { fr_CA: {
|
||||
DATETIME_FORMATS: {
|
||||
MONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre',
|
||||
'octobre', 'novembre', 'décembre'],
|
||||
SHORTMONTH: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.',
|
||||
'nov.', 'déc.'],
|
||||
DAY: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
|
||||
SHORTDAY: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
|
||||
AMPMS: ['AM', 'PM'],
|
||||
medium: 'yyyy-MM-dd HH:mm:ss',
|
||||
short: 'yy-MM-dd HH:mm',
|
||||
fullDate: 'EEEE d MMMM y',
|
||||
longDate: 'd MMMM y',
|
||||
mediumDate: 'yyyy-MM-dd',
|
||||
shortDate: 'yy-MM-dd',
|
||||
mediumTime: 'HH:mm:ss',
|
||||
shortTime: 'HH:mm'
|
||||
},
|
||||
NUMBER_FORMATS: {
|
||||
"DECIMAL_SEP": ".",
|
||||
"GROUP_SEP": ",",
|
||||
"PATTERNS": [{
|
||||
"minInt": 1,
|
||||
"minFrac": 0,
|
||||
"macFrac": 0,
|
||||
"posPre": "",
|
||||
"posSuf": "",
|
||||
"negPre": "-",
|
||||
"negSuf": "",
|
||||
"gSize": 3,
|
||||
"lgSize": 3,
|
||||
"maxFrac": 3
|
||||
}, {
|
||||
"minInt": 1,
|
||||
"minFrac": 2,
|
||||
"macFrac": 0,
|
||||
"posPre": "¤",
|
||||
"posSuf": "",
|
||||
"negPre": "¤-",
|
||||
"negSuf": "",
|
||||
"gSize": 3,
|
||||
"lgSize": 3,
|
||||
"maxFrac": 2
|
||||
}],
|
||||
"CURRENCY_SYM": "£"
|
||||
}}};
|
||||
}
|
||||
|
||||
|
||||
describe("findLocaleId", function () {
|
||||
it("should find the id from numbers", function() {
|
||||
expect(findLocaleId("NumberFormatSymbols_en_GB", "num")).toEqual("en_GB");
|
||||
});
|
||||
|
||||
|
||||
it("should find the id from datetime", function() {
|
||||
expect(findLocaleId("DateTimeSymbols_en_ISO", "datetime")).toEqual("en_ISO");
|
||||
});
|
||||
|
||||
|
||||
it("should throw an error otherwise", function() {
|
||||
expect(function() {
|
||||
findLocaleId("str", "otherwise")
|
||||
}).toThrow("unknown type in findLocaleId: otherwise");
|
||||
});
|
||||
});
|
||||
|
||||
describe("extractNumberSymbols", function () {
|
||||
it("should extract number data", function() {
|
||||
var CONTENT = [
|
||||
"goog.provide('goog.i18n.NumberFormatSymbols_en_GB');",
|
||||
"goog.i18n.NumberFormatSymbols_en_GB = {",
|
||||
"DECIMAL_SEP: '.',",
|
||||
"GROUP_SEP: ',',",
|
||||
"PERCENT: '%',",
|
||||
"ZERO_DIGIT: '0',",
|
||||
"PLUS_SIGN: '+',",
|
||||
"MINUS_SIGN: '-',",
|
||||
"EXP_SYMBOL: 'E',",
|
||||
"PERMILL: '\u2030',",
|
||||
"INFINITY: '\u221E',",
|
||||
"NAN: 'NaN',",
|
||||
"DECIMAL_PATTERN: '#,##0.###',",
|
||||
"SCIENTIFIC_PATTERN: '#E0',",
|
||||
"PERCENT_PATTERN: '#,##0%',",
|
||||
"CURRENCY_PATTERN: '\u00A4#,##0.00',",
|
||||
"DEF_CURRENCY_CODE: 'GBP' };"
|
||||
].join('\n');
|
||||
|
||||
var currencySymbols = {'GBP':[2, '£', 'GB£']};
|
||||
|
||||
var expectedNumberFormats = converter.convertNumberData(
|
||||
{
|
||||
DECIMAL_SEP:'.',
|
||||
GROUP_SEP:',',
|
||||
DECIMAL_PATTERN:'#,##0.###',
|
||||
CURRENCY_PATTERN:'\u00A4#,##0.00',
|
||||
DEF_CURRENCY_CODE: 'GBP'
|
||||
}, currencySymbols
|
||||
);
|
||||
|
||||
var localeInfo = {};
|
||||
extractNumberSymbols(CONTENT, localeInfo, currencySymbols);
|
||||
|
||||
expect(localeInfo).toEqual({
|
||||
'en_GB': { NUMBER_FORMATS: expectedNumberFormats }
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
describe("extractCurrencySymbols", function () {
|
||||
it("should extract currency data", function() {
|
||||
var CONTENT = [
|
||||
"goog.i18n.currency.CurrencyInfo = {",
|
||||
" 'GBP':[2, '£', 'GB£'],",
|
||||
"};",
|
||||
"goog.i18n.currency.CurrencyInfoTier2 = {",
|
||||
" 'AOA':[2, 'Kz', 'Kz'],",
|
||||
"};"
|
||||
].join('\n');
|
||||
|
||||
var localeInfo = {};
|
||||
expect(extractCurrencySymbols(CONTENT)).toEqual({
|
||||
'GBP':[2, '£', 'GB£'],
|
||||
'AOA':[2, 'Kz', 'Kz']
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("extractDateTimeSymbols", function () {
|
||||
it("should extract date time data", function() {
|
||||
var CONTENT = [
|
||||
"goog.i18n.DateTimeSymbols_fr_CA = {",
|
||||
" ERAS: ['av. J.-C.', 'ap. J.-C.'],",
|
||||
" ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],",
|
||||
" NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],",
|
||||
" STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',",
|
||||
" 'N', 'D'],",
|
||||
" MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',",
|
||||
" 'août', 'septembre', 'octobre', 'novembre', 'décembre'],",
|
||||
" STANDALONEMONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin',",
|
||||
" 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],",
|
||||
" SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',",
|
||||
" 'août', 'sept.', 'oct.', 'nov.', 'déc.'],",
|
||||
" STANDALONESHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin',",
|
||||
" 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'],",
|
||||
" WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',",
|
||||
" 'samedi'],",
|
||||
" STANDALONEWEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi',",
|
||||
" 'vendredi', 'samedi'],",
|
||||
" SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],",
|
||||
" STANDALONESHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.',",
|
||||
" 'sam.'],",
|
||||
" NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],",
|
||||
" STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],",
|
||||
" SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],",
|
||||
" QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],",
|
||||
" AMPMS: ['AM', 'PM'],",
|
||||
" DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'yyyy-MM-dd', 'yy-MM-dd'],",
|
||||
" TIMEFORMATS: ['HH \\'h\\' mm \\'min\\' ss \\'s\\' zzzz', 'HH:mm:ss z',",
|
||||
" 'HH:mm:ss', 'HH:mm'],",
|
||||
" FIRSTDAYOFWEEK: 6,",
|
||||
" WEEKENDRANGE: [5, 6],",
|
||||
" FIRSTWEEKCUTOFFDAY: 2",
|
||||
"};"
|
||||
].join('\n');
|
||||
var localeInfo = {};
|
||||
var expectedLocaleInfo = {
|
||||
fr_CA: {
|
||||
DATETIME_FORMATS: {
|
||||
MONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre',
|
||||
'octobre', 'novembre', 'décembre'],
|
||||
SHORTMONTH: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.',
|
||||
'nov.', 'déc.'],
|
||||
DAY: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
|
||||
SHORTDAY: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
|
||||
AMPMS: ['AM', 'PM'],
|
||||
medium: 'yyyy-MM-dd HH:mm:ss',
|
||||
short: 'yy-MM-dd HH:mm',
|
||||
fullDate: 'EEEE d MMMM y',
|
||||
longDate: 'd MMMM y',
|
||||
mediumDate: 'yyyy-MM-dd',
|
||||
shortDate: 'yy-MM-dd',
|
||||
mediumTime: 'HH:mm:ss',
|
||||
shortTime: 'HH:mm'
|
||||
}
|
||||
}
|
||||
};
|
||||
extractDateTimeSymbols(CONTENT, localeInfo);
|
||||
expect(localeInfo).toEqual(expectedLocaleInfo);
|
||||
})
|
||||
});
|
||||
|
||||
describe("pluralExtractor", function() {
|
||||
it("should output PLURAL_CAT in the output string code", function() {
|
||||
var localeIds = ["fr_CA"];
|
||||
var content = (
|
||||
"goog.provide('goog.i18n.pluralRules');\n" +
|
||||
"\n" +
|
||||
"goog.i18n.pluralRules.Keyword = {\n" +
|
||||
" ZERO: 'zero',\n" +
|
||||
" ONE: 'one',\n" +
|
||||
" TWO: 'two',\n" +
|
||||
" FEW: 'few',\n" +
|
||||
" MANY: 'many',\n" +
|
||||
" OTHER: 'other'\n" +
|
||||
"};\n" +
|
||||
"\n" +
|
||||
"goog.i18n.pluralRules.frSelect_ = function(n) {\n" +
|
||||
" if (n >= 0 && n < 2) {\n" +
|
||||
" return goog.i18n.pluralRules.Keyword.ONE;\n" +
|
||||
" }\n" +
|
||||
" return goog.i18n.pluralRules.Keyword.OTHER;\n" +
|
||||
"};\n" +
|
||||
"\n" +
|
||||
"if (goog.LOCALE == 'fr') {\n" +
|
||||
" goog.i18n.pluralRules.select = goog.i18n.pluralRules.frSelect_;\n" +
|
||||
"}"
|
||||
);
|
||||
var localeInfo = newTestLocaleInfo();
|
||||
closureI18nExtractor.pluralExtractor(content, localeInfo);
|
||||
var pluralCat = localeInfo["fr_CA"].pluralCat;
|
||||
expect(pluralCat).toBeDefined();
|
||||
// pluralCat is the source text for the pluralCat and contains @@
|
||||
// placeholders that need to be stripped before evaluation.
|
||||
// Ref: closureI18nExtractor.pluralExtractor.
|
||||
pluralCat = pluralCat.replace(/^@@|@@$/g, '');
|
||||
// pluralCat requires these constants to exist.
|
||||
var PLURAL_CATEGORY = {
|
||||
ZERO: "zero", ONE: "one", TWO: "two",
|
||||
FEW: "few", MANY: "many", OTHER: "other"
|
||||
};
|
||||
// Obtain the function by evaluating the source text.
|
||||
pluralCat = eval("(" + pluralCat + ")");
|
||||
// Confirm some expectations for pluralCat in fr_CA.
|
||||
expect(pluralCat(0)).toEqual("one");
|
||||
expect(pluralCat(3)).toEqual("other");
|
||||
})
|
||||
});
|
||||
|
||||
describe("serializeContent", function() {
|
||||
it("should not make any modifications to the content of the locale", function() {
|
||||
var serializedContent = closureI18nExtractor.serializeContent(newTestLocaleInfo());
|
||||
expect(eval("(" + serializedContent + ")")).toEqual(newTestLocaleInfo());
|
||||
});
|
||||
it("should only have ascii characters", function() {
|
||||
var serializedContent = closureI18nExtractor.serializeContent(newTestLocaleInfo());
|
||||
expect((/[^\u0001-\u007f]/).test(serializedContent)).toBe(false);
|
||||
});
|
||||
it("should not transform arrays into objects", function() {
|
||||
var serializedContent = closureI18nExtractor.serializeContent(newTestLocaleInfo().fr_CA);
|
||||
var deserializedLocale = eval("(" + serializedContent + ")");
|
||||
expect(deserializedLocale.DATETIME_FORMATS.MONTH.length).not.toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,6 +24,8 @@ describe('parsePattern', function() {
|
||||
parseAndExpect('#,##,##0.###', '', '-', '', '', 1, 0, 3, 2, 3);
|
||||
parseAndExpect("#,##0.###;\'\u202A\'-#,##0.###\'\u202C\'",
|
||||
'', '\u202A-', '', '\u202C', 1, 0, 3, 3, 3);
|
||||
parseAndExpect('#0.###;#0.###-', '', '', '', '-', 1, 0, 3, 0, 0);
|
||||
|
||||
});
|
||||
|
||||
it('should parse CURRENCY patterns', function() {
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
'use strict';
|
||||
|
||||
var converter = require('./converter.js');
|
||||
|
||||
exports.extractNumberSymbols = extractNumberSymbols;
|
||||
exports.extractCurrencySymbols = extractCurrencySymbols;
|
||||
exports.extractDateTimeSymbols = extractDateTimeSymbols;
|
||||
exports.pluralExtractor = pluralExtractor;
|
||||
exports.outputLocale = outputLocale;
|
||||
exports.correctedLocaleId = correctedLocaleId;
|
||||
exports.findLocaleId = findLocaleId;
|
||||
exports.serializeContent = serializeContent;
|
||||
|
||||
var goog = { provide: function() {},
|
||||
require: function() {},
|
||||
i18n: {currency: {}, pluralRules: {}} };
|
||||
|
||||
function findLocaleId(str, type) {
|
||||
if (type === 'num') {
|
||||
return (str.match(/^NumberFormatSymbols_(.+)$/) || [])[1];
|
||||
}
|
||||
|
||||
if (type != 'datetime') { throw new Error('unknown type in findLocaleId: ' + type); }
|
||||
|
||||
return (str.match(/^DateTimeSymbols_(.+)$/) || [])[1];
|
||||
}
|
||||
|
||||
|
||||
function getInfoForLocale(localeInfo, localeID) {
|
||||
if (!localeInfo[localeID]) {
|
||||
localeInfo[localeID] = {};
|
||||
//localeIds.push(localeID);
|
||||
}
|
||||
return localeInfo[localeID];
|
||||
}
|
||||
|
||||
function extractNumberSymbols(content, localeInfo, currencySymbols) {
|
||||
//eval script in the current context so that we get access to all the symbols
|
||||
eval(content.toString());
|
||||
for (var propName in goog.i18n) {
|
||||
var localeID = findLocaleId(propName, 'num');
|
||||
if (localeID) {
|
||||
var info = getInfoForLocale(localeInfo, localeID);
|
||||
info.NUMBER_FORMATS =
|
||||
converter.convertNumberData(goog.i18n[propName], currencySymbols);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function extractCurrencySymbols(content) {
|
||||
//eval script in the current context so that we get access to all the symbols
|
||||
eval(content.toString());
|
||||
var currencySymbols = goog.i18n.currency.CurrencyInfo;
|
||||
currencySymbols.__proto__ = goog.i18n.currency.CurrencyInfoTier2;
|
||||
|
||||
return currencySymbols;
|
||||
}
|
||||
|
||||
function extractDateTimeSymbols(content, localeInfo) {
|
||||
//eval script in the current context so that we get access to all the symbols
|
||||
eval(content.toString());
|
||||
for (var propName in goog.i18n) {
|
||||
var localeID = findLocaleId(propName, 'datetime');
|
||||
if (localeID) {
|
||||
var info = getInfoForLocale(localeInfo, localeID);
|
||||
localeInfo[localeID].DATETIME_FORMATS =
|
||||
converter.convertDatetimeData(goog.i18n[propName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pluralExtractor(content, localeInfo) {
|
||||
var contentText = content.toString();
|
||||
var localeIds = Object.keys(localeInfo);
|
||||
for (var i = 0; i < localeIds.length; i++) {
|
||||
//We don't need to care about country ID because the plural rules in more specific id are
|
||||
//always the same as those in its language ID.
|
||||
// e.g. plural rules for en_SG is the same as those for en.
|
||||
goog.LOCALE = localeIds[i].match(/[^_]+/)[0];
|
||||
try {
|
||||
eval(contentText);
|
||||
} catch(e) {
|
||||
console.log("Error in eval(contentText): " + e.stack);
|
||||
}
|
||||
if (!goog.i18n.pluralRules.select) {
|
||||
console.log('No select for lang [' + goog.LOCALE + ']');
|
||||
continue;
|
||||
}
|
||||
var temp = goog.i18n.pluralRules.select.toString().
|
||||
replace(/goog.i18n.pluralRules.Keyword/g, 'PLURAL_CATEGORY').replace(/\n/g, '');
|
||||
|
||||
///@@ is a crazy place holder to be replaced before writing to file
|
||||
localeInfo[localeIds[i]].pluralCat = "@@" + temp + "@@";
|
||||
}
|
||||
}
|
||||
|
||||
function correctedLocaleId(localeID) {
|
||||
// e.g. from zh_CN to zh-CN, from en_US to en-US
|
||||
return localeID.replace(/_/g, '-').toLowerCase();
|
||||
}
|
||||
|
||||
function canonicalizeForJsonStringify(unused_key, object) {
|
||||
// This function is intended to be called as the 2nd argument to
|
||||
// JSON.stringify. The goal here is to ensure that the generated JSON has
|
||||
// objects with their keys in ascending order. Without this, it's much
|
||||
// harder to diff the generated files in src/ngLocale as the order isn't
|
||||
// exactly consistent. We've gotten lucky in the past.
|
||||
//
|
||||
// Iteration order, for string keys, ends up being the same as insertion
|
||||
// order. Refer :-
|
||||
// 1. http://ejohn.org/blog/javascript-in-chrome/
|
||||
// (search for "for loop order").
|
||||
// Currently all major browsers loop over the properties of an object
|
||||
// in the order in which they were defined.
|
||||
// - John Resig
|
||||
// 2. https://code.google.com/p/v8/issues/detail?id=164
|
||||
// ECMA-262 does not specify enumeration order. The de facto standard
|
||||
// is to match insertion order, which V8 also does ...
|
||||
if (typeof object != "object" || Object.prototype.toString.apply(object) === '[object Array]') {
|
||||
return object;
|
||||
}
|
||||
var result = {};
|
||||
Object.keys(object).sort().forEach(function(key) {
|
||||
result[key] = object[key];
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function serializeContent(localeObj) {
|
||||
return JSON.stringify(localeObj, canonicalizeForJsonStringify, ' ')
|
||||
.replace(new RegExp('[\\u007f-\\uffff]', 'g'), function(c) { return '\\u'+('0000'+c.charCodeAt(0).toString(16)).slice(-4); })
|
||||
.replace(/"@@|@@"/g, '');
|
||||
}
|
||||
|
||||
function outputLocale(localeInfo, localeID) {
|
||||
var fallBackID = localeID.match(/[A-Za-z]+/)[0],
|
||||
localeObj = localeInfo[localeID],
|
||||
fallBackObj = localeInfo[fallBackID];
|
||||
|
||||
// fallBack to language formats when country format is missing
|
||||
// e.g. if NUMBER_FORMATS of en_xyz is not present, use the NUMBER_FORMATS of en instead
|
||||
if (!localeObj.NUMBER_FORMATS) {
|
||||
localeObj.NUMBER_FORMATS = fallBackObj.NUMBER_FORMATS;
|
||||
}
|
||||
|
||||
// datetimesymbolsext.js provides more top level locales than the other
|
||||
// files. We process datetimesymbolsext.js because we want the country
|
||||
// specific formats that are missing from datetimesymbols.js. However, we
|
||||
// don't want to write locale files that only have dateformat (i.e. missing
|
||||
// number formats.) So we skip them.
|
||||
if (!localeObj.NUMBER_FORMATS) {
|
||||
console.log("Skipping locale %j: Don't have any number formats", localeID);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!localeObj.DATETIME_FORMATS) {
|
||||
localeObj.DATETIME_FORMATS = fallBackObj.DATETIME_FORMATS;
|
||||
}
|
||||
localeObj.id = correctedLocaleId(localeID);
|
||||
|
||||
var prefix =
|
||||
'angular.module("ngLocale", [], ["$provide", function($provide) {\n' +
|
||||
'var PLURAL_CATEGORY = {' +
|
||||
'ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"' +
|
||||
'};\n' +
|
||||
'$provide.value("$locale", ';
|
||||
|
||||
var suffix = ');\n}]);';
|
||||
|
||||
localeObj = {
|
||||
DATETIME_FORMATS: localeObj.DATETIME_FORMATS,
|
||||
NUMBER_FORMATS: localeObj.NUMBER_FORMATS,
|
||||
pluralCat: localeObj.pluralCat,
|
||||
id: localeObj.id
|
||||
};
|
||||
|
||||
var content = serializeContent(localeInfo[localeID]);
|
||||
|
||||
return prefix + content + suffix;
|
||||
}
|
||||
+57
-102
@@ -1,130 +1,85 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
var Q = require('qq'),
|
||||
var Q = require('q'),
|
||||
qfs = require('q-fs'),
|
||||
converter = require('./converter.js'),
|
||||
util = require('./util.js'),
|
||||
closureI18nExtractor = require('./closureI18nExtractor.js'),
|
||||
localeInfo = {},
|
||||
localeIds = [],
|
||||
currencySymbols,
|
||||
goog = { provide: function() {},
|
||||
require: function() {},
|
||||
i18n: {currency: {}, pluralRules: {}} };
|
||||
|
||||
createFolder('../../src/ngLocale/').then(function() {
|
||||
var promiseA = Q.defer(),
|
||||
promiseB = Q.defer();
|
||||
|
||||
qfs.read(__dirname + '/../closure/currencySymbols.js', 'b').then(function(content) {
|
||||
eval(content.toString());
|
||||
currencySymbols = goog.i18n.currency.CurrencyInfo;
|
||||
currencySymbols.__proto__ = goog.i18n.currency.CurrencyInfoTier2;
|
||||
var NG_LOCALE_DIR = '../src/ngLocale/';
|
||||
|
||||
qfs.read(__dirname + '/../closure/numberSymbols.js', 'b').then(function(content) {
|
||||
//eval script in the current context so that we get access to all the symbols
|
||||
eval(content.toString());
|
||||
for (var propName in goog.i18n) {
|
||||
var localeID = util.findLocaleId(propName, 'num');
|
||||
if (localeID) {
|
||||
if (!localeInfo[localeID]) {
|
||||
localeInfo[localeID] = {};
|
||||
localeIds.push(localeID);
|
||||
}
|
||||
var convertedData = converter.convertNumberData(goog.i18n[propName], currencySymbols);
|
||||
localeInfo[localeID].NUMBER_FORMATS = convertedData;
|
||||
}
|
||||
}
|
||||
|
||||
promiseA.resolve();
|
||||
function readSymbols() {
|
||||
console.log("Processing currency and number symbols ...");
|
||||
var numericStagePromise = qfs.read(__dirname + '/../closure/currencySymbols.js', 'b')
|
||||
.then(function(content) {
|
||||
var currencySymbols = closureI18nExtractor.extractCurrencySymbols(content);
|
||||
return qfs.read(__dirname + '/../closure/numberSymbols.js', 'b').then(function(content) {
|
||||
closureI18nExtractor.extractNumberSymbols(content, localeInfo, currencySymbols);
|
||||
});
|
||||
});
|
||||
|
||||
console.log("Processing datetime symbols ...");
|
||||
var datetimeStagePromise = qfs.read(__dirname + '/../closure/datetimeSymbols.js', 'b')
|
||||
.then(function(content) {
|
||||
closureI18nExtractor.extractDateTimeSymbols(content, localeInfo);
|
||||
return qfs.read(__dirname + '/../closure/datetimeSymbolsExt.js', 'b').then(function(content) {
|
||||
closureI18nExtractor.extractDateTimeSymbols(content, localeInfo);
|
||||
});
|
||||
});
|
||||
|
||||
return Q.all([numericStagePromise, datetimeStagePromise]);
|
||||
}
|
||||
|
||||
function extractPlurals() {
|
||||
console.log('Extracting Plurals ...');
|
||||
return qfs.read(__dirname + '/../closure/pluralRules.js').then(function(content) {
|
||||
closureI18nExtractor.pluralExtractor(content, localeInfo);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
qfs.read(__dirname + '/../closure/datetimeSymbols.js', 'b').then(function(content) {
|
||||
eval(content.toString());
|
||||
for (var propName in goog.i18n) {
|
||||
var localeID = util.findLocaleId(propName, 'datetime');
|
||||
if (localeID) {
|
||||
if (!localeInfo[localeID]) {
|
||||
localeInfo[localeID] = {};
|
||||
localeIds.push(localeID);
|
||||
}
|
||||
var convertedData = converter.convertDatetimeData(goog.i18n[propName]);
|
||||
localeInfo[localeID].DATETIME_FORMATS = convertedData;
|
||||
}
|
||||
}
|
||||
|
||||
promiseB.resolve();
|
||||
});
|
||||
|
||||
return Q.join(promiseA.promise, promiseB.promise, noop);
|
||||
}).then(function() {
|
||||
var promise = Q.defer();
|
||||
|
||||
qfs.read(__dirname + '/../closure/pluralRules.js').then(function(content) {
|
||||
for(var i = 0; i < localeIds.length; i++) {
|
||||
//We don't need to care about country ID because the plural rules in more specific id are
|
||||
//always the same as those in its language ID.
|
||||
// e.g. plural rules for en_SG is the same as those for en.
|
||||
goog.LOCALE = localeIds[i].match(/[^_]+/)[0];
|
||||
eval(content);
|
||||
var temp = goog.i18n.pluralRules.select.toString().
|
||||
replace(/goog.i18n.pluralRules.Keyword/g, 'PLURAL_CATEGORY').replace(/\n/g, '');
|
||||
|
||||
///@@ is a crazy place holder to be replaced before writing to file
|
||||
localeInfo[localeIds[i]].pluralCat = "@@" + temp + "@@";
|
||||
}
|
||||
promise.resolve();
|
||||
});
|
||||
|
||||
return promise.promise;
|
||||
}).then(function() {
|
||||
function writeLocaleFiles() {
|
||||
console.log('Final stage: Writing angular locale files to directory: %j', NG_LOCALE_DIR);
|
||||
var writePromises = [];
|
||||
var localeIds = Object.keys(localeInfo);
|
||||
var num_files = 0;
|
||||
localeIds.forEach(function(localeID) {
|
||||
var fallBackID = localeID.match(/[A-Za-z]+/)[0],
|
||||
localeObj = localeInfo[localeID],
|
||||
fallBackObj = localeInfo[fallBackID];
|
||||
|
||||
// fallBack to language formats when country format is missing
|
||||
// e.g. if NUMBER_FORMATS of en_xyz is not present, use the NUMBER_FORMATS of en instead
|
||||
if (!localeObj.NUMBER_FORMATS) {
|
||||
localeObj.NUMBER_FORMATS = fallBackObj.NUMBER_FORMATS;
|
||||
}
|
||||
|
||||
if (!localeObj.DATETIME_FORMATS) {
|
||||
localeObj.DATETIME_FORMATS = fallBackObj.DATETIME_FORMATS;
|
||||
}
|
||||
|
||||
// e.g. from zh_CN to zh-CN, from en_US to en-US
|
||||
var correctedLocaleId = localeID.replace(/_/g, '-').toLowerCase();
|
||||
localeObj.id = correctedLocaleId;
|
||||
|
||||
var prefix =
|
||||
'angular.module("ngLocale", [], ["$provide", function($provide) {\n' +
|
||||
'var PLURAL_CATEGORY = {' +
|
||||
'ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"' +
|
||||
'};\n' +
|
||||
'$provide.value("$locale", ';
|
||||
|
||||
var suffix = ');\n}]);';
|
||||
|
||||
var content = JSON.stringify(localeInfo[localeID]).replace(/\¤/g,'\\u00A4').
|
||||
replace(/"@@|@@"/g, '');
|
||||
|
||||
var toWrite = prefix + content + suffix;
|
||||
qfs.write(__dirname + '/../locale/' + 'angular-locale_' + correctedLocaleId + '.js', toWrite);
|
||||
var content = closureI18nExtractor.outputLocale(localeInfo, localeID);
|
||||
if (!content) return;
|
||||
var correctedLocaleId = closureI18nExtractor.correctedLocaleId(localeID);
|
||||
var filename = NG_LOCALE_DIR + 'angular-locale_' + correctedLocaleId + '.js'
|
||||
writePromises.push(
|
||||
qfs.write(filename, content)
|
||||
.then(function () {
|
||||
console.log('Wrote ' + filename);
|
||||
++num_files;
|
||||
}));
|
||||
console.log('Writing ' + filename);
|
||||
});
|
||||
console.log('Generated ' + localeIds.length + ' locale files!');
|
||||
}).end();
|
||||
|
||||
function noop() {};
|
||||
console.log('Generated %j locale files.', localeIds.length);
|
||||
return Q.all(writePromises).then(function() { return num_files });
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a folder under current directory.
|
||||
* @param folder {string} name of the folder to be made
|
||||
*/
|
||||
function createFolder(folder) {
|
||||
return qfs.isDirectory(__dirname + '/' + folder).then(function(isDir) {
|
||||
if (!isDir) return qfs.makeDirectory(__dirname + '/' + folder);
|
||||
return qfs.isDirectory(folder).then(function(isDir) {
|
||||
if (!isDir) return qfs.makeDirectory(folder).then(function() {
|
||||
console.log('Created directory %j', folder); });
|
||||
});
|
||||
}
|
||||
|
||||
createFolder(NG_LOCALE_DIR)
|
||||
.then(readSymbols)
|
||||
.then(extractPlurals)
|
||||
.then(writeLocaleFiles)
|
||||
.done(function(num_files) { console.log("Wrote %j files.\nAll Done!", num_files); });
|
||||
|
||||
+2
-2
@@ -45,8 +45,8 @@ function parsePattern(pattern) {
|
||||
}
|
||||
|
||||
var groups = integer.split(GROUP_SEP);
|
||||
p.gSize = groups[1].length;
|
||||
p.lgSize = (groups[2] || groups[1]).length;
|
||||
p.gSize = groups[1] ? groups[1].length : 0;
|
||||
p.lgSize = (groups[2] || groups[1]) ? (groups[2] || groups[1]).length : 0;
|
||||
|
||||
if (negative) {
|
||||
var trunkLen = positive.length - p.posPre.length - p.posSuf.length,
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e # Exit on error.
|
||||
|
||||
BASE_DIR=`dirname $0`
|
||||
cd $BASE_DIR
|
||||
|
||||
set -x # Trace commands as they're executed.
|
||||
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/currency.js > closure/currencySymbols.js
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/datetimesymbols.js > closure/datetimeSymbols.js
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/datetimesymbolsext.js > closure/datetimeSymbolsExt.js
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/numberformatsymbols.js > closure/numberSymbols.js
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/pluralrules.js > closure/pluralRules.js
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
+4
-4
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# Script to initialize angular repo
|
||||
# - install required node packages
|
||||
# - install Testacular
|
||||
# - install Karma
|
||||
# - install git hooks
|
||||
|
||||
|
||||
@@ -22,10 +22,10 @@ fi
|
||||
echo "Installing required npm packages..."
|
||||
npm install
|
||||
|
||||
testacular=`which testacular 2>&1`
|
||||
karma=`which karma 2>&1`
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Installing Testacular..."
|
||||
npm install -g testacular
|
||||
echo "Installing Karma..."
|
||||
npm install -g karma
|
||||
fi
|
||||
|
||||
echo "Installing git hooks..."
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
var sharedConfig = require('./karma-shared.conf');
|
||||
|
||||
module.exports = function(config) {
|
||||
sharedConfig(config);
|
||||
|
||||
config.set({
|
||||
frameworks: ['ng-scenario'],
|
||||
files: [
|
||||
'build/docs/docs-scenario.js'
|
||||
],
|
||||
|
||||
proxies: {
|
||||
// angular.js, angular-resource.js, etc
|
||||
'/angular': 'http://localhost:8000/build/angular',
|
||||
'/': 'http://localhost:8000/build/docs/'
|
||||
},
|
||||
|
||||
junitReporter: {
|
||||
outputFile: 'test_out/e2e.xml',
|
||||
suite: 'E2E'
|
||||
}
|
||||
});
|
||||
|
||||
config.sauceLabs.testName = 'AngularJS: e2e';
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
var angularFiles = require('./angularFiles');
|
||||
var sharedConfig = require('./karma-shared.conf');
|
||||
|
||||
module.exports = function(config) {
|
||||
sharedConfig(config);
|
||||
|
||||
config.set({
|
||||
files: angularFiles.mergeFilesFor('karma'),
|
||||
exclude: angularFiles.mergeFilesFor('karmaExclude'),
|
||||
|
||||
junitReporter: {
|
||||
outputFile: 'test_out/jqlite.xml',
|
||||
suite: 'jqLite'
|
||||
}
|
||||
});
|
||||
|
||||
config.sauceLabs.testName = 'AngularJS: jqLite';
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
var angularFiles = require('./angularFiles');
|
||||
var sharedConfig = require('./karma-shared.conf');
|
||||
|
||||
module.exports = function(config) {
|
||||
sharedConfig(config);
|
||||
|
||||
config.set({
|
||||
files: angularFiles.mergeFilesFor('karmaJquery'),
|
||||
exclude: angularFiles.mergeFilesFor('karmaJqueryExclude'),
|
||||
|
||||
junitReporter: {
|
||||
outputFile: 'test_out/jquery.xml',
|
||||
suite: 'jQuery'
|
||||
}
|
||||
});
|
||||
|
||||
config.sauceLabs.testName = 'AngularJS: jQuery';
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
var angularFiles = require('./angularFiles');
|
||||
var sharedConfig = require('./karma-shared.conf');
|
||||
|
||||
module.exports = function(config) {
|
||||
sharedConfig(config);
|
||||
|
||||
config.set({
|
||||
files: angularFiles.mergeFilesFor('karmaModules', 'angularSrcModules'),
|
||||
|
||||
junitReporter: {
|
||||
outputFile: 'test_out/modules.xml',
|
||||
suite: 'modules'
|
||||
}
|
||||
});
|
||||
|
||||
config.sauceLabs.testName = 'AngularJS: modules';
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
frameworks: ['jasmine'],
|
||||
autoWatch: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
logColors: true,
|
||||
browsers: ['Chrome'],
|
||||
runnerPort: 0,
|
||||
|
||||
// config for Travis CI
|
||||
sauceLabs: {
|
||||
testName: 'AngularJS',
|
||||
startConnect: false,
|
||||
tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER
|
||||
},
|
||||
|
||||
customLaunchers: {
|
||||
'SL_Chrome': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome'
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -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,205 @@
|
||||
var fs = require('fs');
|
||||
var shell = require('shelljs');
|
||||
var grunt = require('grunt');
|
||||
var spawn = require('child_process').spawn;
|
||||
var version;
|
||||
|
||||
module.exports = {
|
||||
|
||||
init: function() {
|
||||
if (!process.env.TRAVIS) {
|
||||
shell.exec('npm install');
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
getVersion: function(){
|
||||
if (version) return version;
|
||||
|
||||
var package = JSON.parse(fs.readFileSync('package.json', 'UTF-8'));
|
||||
// TODO(brian): change `(-|rc)` to `-` in the regex below after bower
|
||||
// fixes this issue: https://github.com/bower/bower/issues/782
|
||||
var match = package.version.match(/^([^\-]*)(?:(-|rc)(.+))?$/);
|
||||
var semver = match[1].split('.');
|
||||
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
|
||||
|
||||
var fullVersion = (match[1] + (match[2] ? '-' + hash : ''));
|
||||
var numVersion = semver[0] + '.' + semver[1] + '.' + semver[2];
|
||||
version = {
|
||||
number: numVersion,
|
||||
full: fullVersion,
|
||||
major: semver[0],
|
||||
minor: semver[1],
|
||||
dot: semver[2],
|
||||
codename: package.codename,
|
||||
cdn: package.cdnVersion
|
||||
};
|
||||
|
||||
return version;
|
||||
},
|
||||
|
||||
|
||||
startKarma: function(config, singleRun, done){
|
||||
var browsers = grunt.option('browsers');
|
||||
var reporters = grunt.option('reporters');
|
||||
var noColor = grunt.option('no-colors');
|
||||
var port = grunt.option('port');
|
||||
var p = spawn('node', ['node_modules/karma/bin/karma', 'start', config,
|
||||
singleRun ? '--single-run=true' : '',
|
||||
reporters ? '--reporters=' + reporters : '',
|
||||
browsers ? '--browsers=' + browsers : '',
|
||||
noColor ? '--no-colors' : '',
|
||||
port ? '--port=' + port : ''
|
||||
]);
|
||||
p.stdout.pipe(process.stdout);
|
||||
p.stderr.pipe(process.stderr);
|
||||
p.on('exit', function(code){
|
||||
if(code !== 0) grunt.fail.warn("Test(s) failed");
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
wrap: function(src, name){
|
||||
src.unshift('src/' + name + '.prefix');
|
||||
src.push('src/' + name + '.suffix');
|
||||
return src;
|
||||
},
|
||||
|
||||
|
||||
addStyle: function(src, styles, minify){
|
||||
styles = styles.map(processCSS.bind(this)).join('\n');
|
||||
src += styles;
|
||||
return src;
|
||||
|
||||
function processCSS(file){
|
||||
var css = fs.readFileSync(file).toString();
|
||||
if(minify){
|
||||
css = css
|
||||
.replace(/\r?\n/g, '')
|
||||
.replace(/\/\*.*?\*\//g, '')
|
||||
.replace(/:\s+/g, ':')
|
||||
.replace(/\s*\{\s*/g, '{')
|
||||
.replace(/\s*\}\s*/g, '}')
|
||||
.replace(/\s*\,\s*/g, ',')
|
||||
.replace(/\s*\;\s*/g, ';');
|
||||
}
|
||||
//escape for js
|
||||
css = css
|
||||
.replace(/\\/g, '\\\\')
|
||||
.replace(/'/g, "\\'")
|
||||
.replace(/\r?\n/g, '\\n');
|
||||
return "angular.element(document).find('head').append('<style type=\"text/css\">" + css + "</style>');";
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
process: function(src, NG_VERSION, strict){
|
||||
var processed = src
|
||||
.replace(/"NG_VERSION_FULL"/g, NG_VERSION.full)
|
||||
.replace(/"NG_VERSION_MAJOR"/, NG_VERSION.major)
|
||||
.replace(/"NG_VERSION_MINOR"/, NG_VERSION.minor)
|
||||
.replace(/"NG_VERSION_DOT"/, NG_VERSION.dot)
|
||||
.replace(/"NG_VERSION_CDN"/, NG_VERSION.cdn)
|
||||
.replace(/"NG_VERSION_CODENAME"/, NG_VERSION.codename);
|
||||
if (strict !== false) processed = this.singleStrict(processed, '\n\n', true);
|
||||
return processed;
|
||||
},
|
||||
|
||||
|
||||
build: function(config, fn){
|
||||
var files = grunt.file.expand(config.src);
|
||||
var styles = config.styles;
|
||||
//concat
|
||||
var src = files.map(function(filepath){
|
||||
return grunt.file.read(filepath);
|
||||
}).join(grunt.util.normalizelf('\n'));
|
||||
//process
|
||||
var processed = this.process(src, grunt.config('NG_VERSION'), config.strict);
|
||||
if (styles) processed = this.addStyle(processed, styles.css, styles.minify);
|
||||
//write
|
||||
grunt.file.write(config.dest, processed);
|
||||
grunt.log.ok('File ' + config.dest + ' created.');
|
||||
fn();
|
||||
},
|
||||
|
||||
|
||||
singleStrict: function(src, insert, newline){
|
||||
var useStrict = newline ? "$1\n'use strict';" : "$1'use strict';";
|
||||
return src
|
||||
.replace(/\s*("|')use strict("|');\s*/g, insert) // remove all file-specific strict mode flags
|
||||
.replace(/(\(function\([^)]*\)\s*\{)/, useStrict); // add single strict mode flag
|
||||
},
|
||||
|
||||
|
||||
min: function(file, done) {
|
||||
var minFile = file.replace(/\.js$/, '.min.js');
|
||||
shell.exec(
|
||||
'java ' +
|
||||
this.java32flags() + ' ' +
|
||||
'-jar lib/closure-compiler/compiler.jar ' +
|
||||
'--compilation_level SIMPLE_OPTIMIZATIONS ' +
|
||||
'--language_in ECMASCRIPT5_STRICT ' +
|
||||
'--js ' + file + ' ' +
|
||||
'--js_output_file ' + minFile,
|
||||
function(code) {
|
||||
if (code !== 0) grunt.fail.warn('Error minifying ' + file);
|
||||
grunt.file.write(minFile, this.singleStrict(grunt.file.read(minFile), '\n'));
|
||||
grunt.log.ok(file + ' minified into ' + minFile);
|
||||
done();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
|
||||
//returns the 32-bit mode force flags for java compiler if supported, this makes the build much faster
|
||||
java32flags: function(){
|
||||
if (process.platform === "win32") return '';
|
||||
if (shell.exec('java -version -d32 2>&1', {silent: true}).code !== 0) return '';
|
||||
return ' -d32 -client';
|
||||
},
|
||||
|
||||
|
||||
//csp connect middleware
|
||||
csp: function(){
|
||||
return function(req, res, next){
|
||||
res.setHeader("X-WebKit-CSP", "default-src 'self';");
|
||||
res.setHeader("X-Content-Security-Policy", "default-src 'self'");
|
||||
next();
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
//rewrite connect middleware
|
||||
rewrite: function(){
|
||||
return function(req, res, next){
|
||||
var REWRITE = /\/(guide|api|cookbook|misc|tutorial).*$/,
|
||||
IGNORED = /(\.(css|js|png|jpg)$|partials\/.*\.html$)/,
|
||||
match;
|
||||
|
||||
if (!IGNORED.test(req.url) && (match = req.url.match(REWRITE))) {
|
||||
console.log('rewriting', req.url);
|
||||
req.url = req.url.replace(match[0], '/index.html');
|
||||
}
|
||||
next();
|
||||
};
|
||||
},
|
||||
|
||||
parallelTask: function(name) {
|
||||
var args = [name, '--port=' + this.lastParallelTaskPort];
|
||||
|
||||
if (grunt.option('browsers')) {
|
||||
args.push('--browsers=' + grunt.option('browsers'));
|
||||
}
|
||||
|
||||
if (grunt.option('reporters')) {
|
||||
args.push('--reporters=' + grunt.option('reporters'));
|
||||
}
|
||||
|
||||
this.lastParallelTaskPort++;
|
||||
|
||||
|
||||
return {grunt: true, args: args};
|
||||
},
|
||||
|
||||
lastParallelTaskPort: 9876
|
||||
};
|
||||
@@ -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);
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
|
||||
# Wait for Connect to be ready before exiting
|
||||
while [ ! -f $SAUCE_CONNECT_READY_FILE ]; do
|
||||
sleep .5
|
||||
done
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Setup and start Sauce Connect for your TravisCI build
|
||||
# This script requires your .travis.yml to include the following two private env variables:
|
||||
# SAUCE_USERNAME
|
||||
# SAUCE_ACCESS_KEY
|
||||
# Follow the steps at https://saucelabs.com/opensource/travis to set that up.
|
||||
#
|
||||
# Curl and run this script as part of your .travis.yml before_script section:
|
||||
# before_script:
|
||||
# - curl https://gist.github.com/santiycr/5139565/raw/sauce_connect_setup.sh | bash
|
||||
|
||||
CONNECT_URL="http://saucelabs.com/downloads/Sauce-Connect-latest.zip"
|
||||
CONNECT_DIR="/tmp/sauce-connect-$RANDOM"
|
||||
CONNECT_DOWNLOAD="Sauce_Connect.zip"
|
||||
CONNECT_LOG="$CONNECT_DIR/log"
|
||||
|
||||
# Get Connect and start it
|
||||
mkdir -p $CONNECT_DIR
|
||||
cd $CONNECT_DIR
|
||||
curl $CONNECT_URL > $CONNECT_DOWNLOAD 2> /dev/null
|
||||
unzip $CONNECT_DOWNLOAD
|
||||
rm $CONNECT_DOWNLOAD
|
||||
|
||||
|
||||
|
||||
ARGS=""
|
||||
|
||||
# Set tunnel-id only on Travis, to make local testing easier.
|
||||
if [ ! -z "$TRAVIS_JOB_NUMBER" ]; then
|
||||
ARGS="$ARGS --tunnel-identifier $TRAVIS_JOB_NUMBER"
|
||||
fi
|
||||
if [ ! -z "$SAUCE_CONNECT_READY_FILE" ]; then
|
||||
ARGS="$ARGS --readyfile $SAUCE_CONNECT_READY_FILE"
|
||||
fi
|
||||
|
||||
echo "Starting Sauce Connect in the background"
|
||||
echo "Logging into $CONNECT_LOG"
|
||||
java -jar Sauce-Connect.jar $ARGS $SAUCE_USERNAME $SAUCE_ACCESS_KEY > $CONNECT_LOG &
|
||||
@@ -1,7 +0,0 @@
|
||||
var fs = require('fs');
|
||||
var vm = require('vm');
|
||||
|
||||
var filename = __dirname + '/showdown-0.9.js';
|
||||
var src = fs.readFileSync(filename);
|
||||
var Showdown = vm.runInThisContext(src + '\nShowdown;', filename);
|
||||
exports.Showdown = Showdown;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
node lib/nodeserver/server.js $1
|
||||
+39
-8
@@ -1,10 +1,41 @@
|
||||
{
|
||||
"name": "AngularJS",
|
||||
"version": "0.0.0",
|
||||
"dependencies" : {
|
||||
"testacular" : "canary",
|
||||
"jasmine-node" : "*",
|
||||
"q-fs" : "*",
|
||||
"qq" : "*"
|
||||
}
|
||||
"name": "angularjs",
|
||||
"version": "1.0.8",
|
||||
"cdnVersion": "1.0.6",
|
||||
"codename": "bubble-burst",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "0.4.0",
|
||||
"grunt-contrib-clean": "0.4.0",
|
||||
"grunt-contrib-compress": "0.4.1",
|
||||
"grunt-contrib-connect": "0.1.2",
|
||||
"grunt-contrib-copy": "0.4.1",
|
||||
"grunt-parallel": "~0.2.0",
|
||||
"grunt-ddescribe-iit": "~0.0.1",
|
||||
"grunt-merge-conflict": "~0.0.1",
|
||||
"jasmine-node": "1.2.3",
|
||||
"q": "~0.9.2",
|
||||
"q-fs": "0.1.36",
|
||||
"qq": "0.3.5",
|
||||
"shelljs": "0.1.2",
|
||||
"karma": "~0.10",
|
||||
"karma-jasmine": "~0.1",
|
||||
"karma-chrome-launcher": "~0.1",
|
||||
"karma-firefox-launcher": "~0.1",
|
||||
"karma-ng-scenario": "~0.1",
|
||||
"karma-junit-reporter": "~0.1",
|
||||
"karma-sauce-launcher": "~0.1",
|
||||
"karma-script-launcher": "~0.1",
|
||||
"yaml-js": "0.0.5",
|
||||
"showdown": "0.3.1"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT",
|
||||
"url": "https://github.com/angular/angular.js/blob/master/LICENSE"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+202
-51
@@ -28,12 +28,12 @@ var uppercase = function(string){return isString(string) ? string.toUpperCase()
|
||||
|
||||
var manualLowercase = function(s) {
|
||||
return isString(s)
|
||||
? s.replace(/[A-Z]/g, function(ch) {return fromCharCode(ch.charCodeAt(0) | 32);})
|
||||
? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
|
||||
: s;
|
||||
};
|
||||
var manualUppercase = function(s) {
|
||||
return isString(s)
|
||||
? s.replace(/[a-z]/g, function(ch) {return fromCharCode(ch.charCodeAt(0) & ~32);})
|
||||
? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
|
||||
: s;
|
||||
};
|
||||
|
||||
@@ -46,11 +46,8 @@ if ('i' !== 'I'.toLowerCase()) {
|
||||
uppercase = manualUppercase;
|
||||
}
|
||||
|
||||
function fromCharCode(code) {return String.fromCharCode(code);}
|
||||
|
||||
|
||||
var Error = window.Error,
|
||||
/** holds major version number for IE or NaN for real browsers */
|
||||
var /** holds major version number for IE or NaN for real browsers */
|
||||
msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]),
|
||||
jqLite, // delay binding since jQuery could be loaded after us.
|
||||
jQuery, // delay binding
|
||||
@@ -64,6 +61,29 @@ var Error = window.Error,
|
||||
nodeName_,
|
||||
uid = ['0', '0', '0'];
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {*} obj
|
||||
* @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, ...)
|
||||
*/
|
||||
function isArrayLike(obj) {
|
||||
if (!obj || (typeof obj.length !== 'number')) return false;
|
||||
|
||||
// We have on object which has length property. Should we treat it as array?
|
||||
if (typeof obj.hasOwnProperty != 'function' &&
|
||||
typeof obj.constructor != 'function') {
|
||||
// This is here for IE8: it is a bogus object treat it as array;
|
||||
return true;
|
||||
} else {
|
||||
return obj instanceof JQLite || // JQLite
|
||||
(jQuery && obj instanceof jQuery) || // jQuery
|
||||
toString.call(obj) !== '[object Object]' || // some browser native object
|
||||
typeof obj.callee === 'function'; // arguments (on IE8 looks like regular obj)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.forEach
|
||||
@@ -102,7 +122,7 @@ function forEach(obj, iterator, context) {
|
||||
}
|
||||
} else if (obj.forEach && obj.forEach !== forEach) {
|
||||
obj.forEach(iterator, context);
|
||||
} else if (isObject(obj) && isNumber(obj.length)) {
|
||||
} else if (isArrayLike(obj)) {
|
||||
for (key = 0; key < obj.length; key++)
|
||||
iterator.call(context, obj[key], key);
|
||||
} else {
|
||||
@@ -147,7 +167,7 @@ function reverseParams(iteratorFn) {
|
||||
/**
|
||||
* A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric
|
||||
* characters such as '012ABC'. The reason why we are not using simply a number counter is that
|
||||
* the number string gets longer over time, and it can also overflow, where as the the nextId
|
||||
* the number string gets longer over time, and it can also overflow, where as the nextId
|
||||
* will grow much slower, it is a string, and it will never overflow.
|
||||
*
|
||||
* @returns an unique alpha-numeric string
|
||||
@@ -174,6 +194,21 @@ function nextUid() {
|
||||
return uid.join('');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set or clear the hashkey for an object.
|
||||
* @param obj object
|
||||
* @param h the hashkey (!truthy to delete the hashkey)
|
||||
*/
|
||||
function setHashKey(obj, h) {
|
||||
if (h) {
|
||||
obj.$$hashKey = h;
|
||||
}
|
||||
else {
|
||||
delete obj.$$hashKey;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.extend
|
||||
@@ -185,8 +220,10 @@ function nextUid() {
|
||||
*
|
||||
* @param {Object} dst Destination object.
|
||||
* @param {...Object} src Source object(s).
|
||||
* @returns {Object} Reference to `dst`.
|
||||
*/
|
||||
function extend(dst) {
|
||||
var h = dst.$$hashKey;
|
||||
forEach(arguments, function(obj){
|
||||
if (obj !== dst) {
|
||||
forEach(obj, function(value, key){
|
||||
@@ -194,6 +231,8 @@ function extend(dst) {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
setHashKey(dst,h);
|
||||
return dst;
|
||||
}
|
||||
|
||||
@@ -237,7 +276,7 @@ noop.$inject = [];
|
||||
*
|
||||
<pre>
|
||||
function transformer(transformationFn, value) {
|
||||
return (transformationFn || identity)(value);
|
||||
return (transformationFn || angular.identity)(value);
|
||||
};
|
||||
</pre>
|
||||
*/
|
||||
@@ -364,6 +403,18 @@ function isArray(value) {
|
||||
function isFunction(value){return typeof value == 'function';}
|
||||
|
||||
|
||||
/**
|
||||
* Determines if a value is a regular expression object.
|
||||
*
|
||||
* @private
|
||||
* @param {*} value Reference to check.
|
||||
* @returns {boolean} True if `value` is a `RegExp`.
|
||||
*/
|
||||
function isRegExp(value) {
|
||||
return toString.apply(value) == '[object RegExp]';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if `obj` is a window object.
|
||||
*
|
||||
@@ -391,9 +442,20 @@ function isBoolean(value) {
|
||||
}
|
||||
|
||||
|
||||
function trim(value) {
|
||||
return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value;
|
||||
}
|
||||
var trim = (function() {
|
||||
// native trim is way faster: http://jsperf.com/angular-trim-test
|
||||
// but IE doesn't have it... :-(
|
||||
// TODO: we should move this into IE/ES5 polyfill
|
||||
if (!String.prototype.trim) {
|
||||
return function(value) {
|
||||
return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value;
|
||||
};
|
||||
}
|
||||
return function(value) {
|
||||
return isString(value) ? value.trim() : value;
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
@@ -536,6 +598,8 @@ function copy(source, destination){
|
||||
destination = copy(source, []);
|
||||
} else if (isDate(source)) {
|
||||
destination = new Date(source.getTime());
|
||||
} else if (isRegExp(source)) {
|
||||
destination = new RegExp(source.source);
|
||||
} else if (isObject(source)) {
|
||||
destination = copy(source, {});
|
||||
}
|
||||
@@ -543,19 +607,19 @@ function copy(source, destination){
|
||||
} else {
|
||||
if (source === destination) throw Error("Can't copy equivalent objects or arrays");
|
||||
if (isArray(source)) {
|
||||
while(destination.length) {
|
||||
destination.pop();
|
||||
}
|
||||
destination.length = 0;
|
||||
for ( var i = 0; i < source.length; i++) {
|
||||
destination.push(copy(source[i]));
|
||||
}
|
||||
} else {
|
||||
var h = destination.$$hashKey;
|
||||
forEach(destination, function(value, key){
|
||||
delete destination[key];
|
||||
});
|
||||
for ( var key in source) {
|
||||
destination[key] = copy(source[key]);
|
||||
}
|
||||
setHashKey(destination,h);
|
||||
}
|
||||
}
|
||||
return destination;
|
||||
@@ -583,7 +647,7 @@ function shallowCopy(src, dst) {
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Determines if two objects or two values are equivalent. Supports value types, arrays and
|
||||
* Determines if two objects or two values are equivalent. Supports value types, regular expressions, arrays and
|
||||
* objects.
|
||||
*
|
||||
* Two objects or values are considered equivalent if at least one of the following is true:
|
||||
@@ -591,11 +655,14 @@ function shallowCopy(src, dst) {
|
||||
* * Both objects or values pass `===` comparison.
|
||||
* * 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)
|
||||
* * Both values represent the same regular expression (In JavasScript,
|
||||
* /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
|
||||
* representation matches).
|
||||
*
|
||||
* During a property comparision, properties of `function` type and properties with names
|
||||
* that begin with `$` are ignored.
|
||||
*
|
||||
* Scope and DOMWindow objects are being compared only be identify (`===`).
|
||||
* Scope and DOMWindow objects are being compared only by identify (`===`).
|
||||
*
|
||||
* @param {*} o1 Object or value to compare.
|
||||
* @param {*} o2 Object or value to compare.
|
||||
@@ -609,6 +676,7 @@ function equals(o1, o2) {
|
||||
if (t1 == t2) {
|
||||
if (t1 == 'object') {
|
||||
if (isArray(o1)) {
|
||||
if (!isArray(o2)) return false;
|
||||
if ((length = o1.length) == o2.length) {
|
||||
for(key=0; key<length; key++) {
|
||||
if (!equals(o1[key], o2[key])) return false;
|
||||
@@ -617,8 +685,10 @@ function equals(o1, o2) {
|
||||
}
|
||||
} else if (isDate(o1)) {
|
||||
return isDate(o2) && o1.getTime() == o2.getTime();
|
||||
} else if (isRegExp(o1) && isRegExp(o2)) {
|
||||
return o1.toString() == o2.toString();
|
||||
} else {
|
||||
if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2)) return false;
|
||||
if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || isArray(o2)) return false;
|
||||
keySet = {};
|
||||
for(key in o1) {
|
||||
if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
|
||||
@@ -655,7 +725,7 @@ function sliceArgs(args, startIndex) {
|
||||
*
|
||||
* @description
|
||||
* Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
|
||||
* `fn`). You can supply optional `args` that are are prebound to the function. This feature is also
|
||||
* `fn`). You can supply optional `args` that are prebound to the function. This feature is also
|
||||
* known as [function currying](http://en.wikipedia.org/wiki/Currying).
|
||||
*
|
||||
* @param {Object} self Context which `fn` should be evaluated in.
|
||||
@@ -707,13 +777,15 @@ function toJsonReplacer(key, value) {
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Serializes input into a JSON-formatted string.
|
||||
* Serializes input into a JSON-formatted string. Properties with leading $ characters will be
|
||||
* stripped since angular uses this notation internally.
|
||||
*
|
||||
* @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
|
||||
* @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace.
|
||||
* @returns {string} Jsonified string representing `obj`.
|
||||
* @returns {string|undefined} Jsonified string representing `obj`.
|
||||
*/
|
||||
function toJson(obj, pretty) {
|
||||
if (typeof obj === 'undefined') return undefined;
|
||||
return JSON.stringify(obj, toJsonReplacer, pretty ? ' ' : null);
|
||||
}
|
||||
|
||||
@@ -756,14 +828,40 @@ 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Tries to decode the URI component without throwing an exception.
|
||||
*
|
||||
* @private
|
||||
* @param str value potential URI component to check.
|
||||
* @returns {boolean} True if `value` can be decoded
|
||||
* with the decodeURIComponent function.
|
||||
*/
|
||||
function tryDecodeURIComponent(value) {
|
||||
try {
|
||||
return decodeURIComponent(value);
|
||||
} catch(e) {
|
||||
// Ignore any invalid uri component
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses an escaped url query string into key-value pairs.
|
||||
* @returns Object.<(string|boolean)>
|
||||
@@ -771,10 +869,12 @@ function startingTag(element) {
|
||||
function parseKeyValue(/**string*/keyValue) {
|
||||
var obj = {}, key_value, key;
|
||||
forEach((keyValue || "").split('&'), function(keyValue){
|
||||
if (keyValue) {
|
||||
if ( keyValue ) {
|
||||
key_value = keyValue.split('=');
|
||||
key = decodeURIComponent(key_value[0]);
|
||||
obj[key] = isDefined(key_value[1]) ? decodeURIComponent(key_value[1]) : true;
|
||||
key = tryDecodeURIComponent(key_value[0]);
|
||||
if ( isDefined(key) ) {
|
||||
obj[key] = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
|
||||
}
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
@@ -825,7 +925,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
|
||||
replace(/%3A/gi, ':').
|
||||
replace(/%24/g, '$').
|
||||
replace(/%2C/gi, ',').
|
||||
replace((pctEncodeSpaces ? null : /%20/g), '+');
|
||||
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
|
||||
}
|
||||
|
||||
|
||||
@@ -839,10 +939,14 @@ function encodeUriQuery(val, pctEncodeSpaces) {
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Use this directive to auto-bootstrap on application. Only
|
||||
* one directive can be used per HTML document. The directive
|
||||
* Use this directive to auto-bootstrap an application. Only
|
||||
* one ngApp 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.
|
||||
*
|
||||
* The first ngApp found in the document will be auto-bootstrapped. To use multiple applications in an
|
||||
* HTML document you must manually bootstrap them using {@link angular.bootstrap}.
|
||||
* Applications cannot be nested.
|
||||
*
|
||||
* In the example below if the `ngApp` directive would not be placed
|
||||
* on the `html` element then the document would not be compiled
|
||||
@@ -909,27 +1013,46 @@ function angularInit(element, bootstrap) {
|
||||
*
|
||||
* See: {@link guide/bootstrap Bootstrap}
|
||||
*
|
||||
* Note that ngScenario-based end-to-end tests cannot use this function to bootstrap manually.
|
||||
* They must use {@link api/ng.directive:ngApp ngApp}.
|
||||
*
|
||||
* @param {Element} element DOM element which is the root of angular application.
|
||||
* @param {Array<String|Function>=} modules an array of module declarations. See: {@link angular.module modules}
|
||||
* @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 doBootstrap = 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 doBootstrap();
|
||||
}
|
||||
|
||||
window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
|
||||
angular.resumeBootstrap = function(extraModules) {
|
||||
forEach(extraModules, function(module) {
|
||||
modules.push(module);
|
||||
});
|
||||
doBootstrap();
|
||||
};
|
||||
}
|
||||
|
||||
var SNAKE_CASE_REGEXP = /[A-Z]/g;
|
||||
@@ -952,9 +1075,10 @@ function bindJQuery() {
|
||||
injector: JQLitePrototype.injector,
|
||||
inheritedData: JQLitePrototype.inheritedData
|
||||
});
|
||||
JQLitePatchJQueryRemove('remove', true);
|
||||
JQLitePatchJQueryRemove('empty');
|
||||
JQLitePatchJQueryRemove('html');
|
||||
// Method signature: JQLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments)
|
||||
JQLitePatchJQueryRemove('remove', true, true, false);
|
||||
JQLitePatchJQueryRemove('empty', false, false, false);
|
||||
JQLitePatchJQueryRemove('html', false, false, true);
|
||||
} else {
|
||||
jqLite = JQLite;
|
||||
}
|
||||
@@ -962,7 +1086,7 @@ function bindJQuery() {
|
||||
}
|
||||
|
||||
/**
|
||||
* throw error of the argument is falsy.
|
||||
* throw error if the argument is falsy.
|
||||
*/
|
||||
function assertArg(arg, name, reason) {
|
||||
if (!arg) {
|
||||
@@ -980,3 +1104,30 @@ function assertArgFn(arg, name, acceptArrayAnnotation) {
|
||||
(arg && typeof arg == 'object' ? arg.constructor.name || 'Object' : typeof arg));
|
||||
return arg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 accessible by path
|
||||
*/
|
||||
//TODO(misko): this function needs to be removed
|
||||
function getter(obj, path, bindFnToScope) {
|
||||
if (!path) return obj;
|
||||
var keys = path.split('.');
|
||||
var key;
|
||||
var lastInstance = obj;
|
||||
var len = keys.length;
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
key = keys[i];
|
||||
if (obj) {
|
||||
obj = (lastInstance = obj)[key];
|
||||
}
|
||||
}
|
||||
if (!bindFnToScope && isFunction(obj)) {
|
||||
return bind(lastInstance, obj);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -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"'
|
||||
@@ -87,7 +87,6 @@ function publishExternalAPI(angular){
|
||||
ngPluralize: ngPluralizeDirective,
|
||||
ngRepeat: ngRepeatDirective,
|
||||
ngShow: ngShowDirective,
|
||||
ngSubmit: ngSubmitDirective,
|
||||
ngStyle: ngStyleDirective,
|
||||
ngSwitch: ngSwitchDirective,
|
||||
ngSwitchWhen: ngSwitchWhenDirective,
|
||||
|
||||
Vendored
+1
-1
@@ -222,6 +222,6 @@
|
||||
function isActuallyNaN(val) {
|
||||
return (typeof val === 'number') && isNaN(val);
|
||||
}
|
||||
};
|
||||
}
|
||||
})(window, document);
|
||||
|
||||
|
||||
+13
-13
@@ -62,7 +62,7 @@ function annotate(fn) {
|
||||
}
|
||||
} else if (isArray(fn)) {
|
||||
last = fn.length - 1;
|
||||
assertArgFn(fn[last], 'fn')
|
||||
assertArgFn(fn[last], 'fn');
|
||||
$inject = fn.slice(0, last);
|
||||
} else {
|
||||
assertArgFn(fn, 'fn', true);
|
||||
@@ -96,19 +96,19 @@ function annotate(fn) {
|
||||
* # Injection Function Annotation
|
||||
*
|
||||
* JavaScript does not have annotations, and annotations are needed for dependency injection. The
|
||||
* following ways are all valid way of annotating function with injection arguments and are equivalent.
|
||||
* following are all valid ways of annotating function with injection arguments and are equivalent.
|
||||
*
|
||||
* <pre>
|
||||
* // inferred (only works if code not minified/obfuscated)
|
||||
* $inject.invoke(function(serviceA){});
|
||||
* $injector.invoke(function(serviceA){});
|
||||
*
|
||||
* // annotated
|
||||
* function explicit(serviceA) {};
|
||||
* explicit.$inject = ['serviceA'];
|
||||
* $inject.invoke(explicit);
|
||||
* $injector.invoke(explicit);
|
||||
*
|
||||
* // inline
|
||||
* $inject.invoke(['serviceA', function(serviceA){}]);
|
||||
* $injector.invoke(['serviceA', function(serviceA){}]);
|
||||
* </pre>
|
||||
*
|
||||
* ## Inference
|
||||
@@ -192,7 +192,7 @@ function annotate(fn) {
|
||||
* This method does not work with code minfication / obfuscation. For this reason the following annotation strategies
|
||||
* are supported.
|
||||
*
|
||||
* # The `$injector` property
|
||||
* # The `$inject` property
|
||||
*
|
||||
* If a function has an `$inject` property and its value is an array of strings, then the strings represent names of
|
||||
* services to be injected into the function.
|
||||
@@ -225,7 +225,7 @@ function annotate(fn) {
|
||||
* // ...
|
||||
* };
|
||||
* tmpFn.$inject = ['$compile', '$rootScope'];
|
||||
* injector.invoke(tempFn);
|
||||
* injector.invoke(tmpFn);
|
||||
*
|
||||
* // To better support inline function the inline annotation is supported
|
||||
* injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
|
||||
@@ -254,7 +254,7 @@ function annotate(fn) {
|
||||
* @description
|
||||
*
|
||||
* Use `$provide` to register new providers with the `$injector`. The providers are the factories for the instance.
|
||||
* The providers share the same name as the instance they create with the `Provider` suffixed to them.
|
||||
* The providers share the same name as the instance they create with `Provider` suffixed to them.
|
||||
*
|
||||
* A provider is an object with a `$get()` method. The injector calls the `$get` method to create a new instance of
|
||||
* a service. The Provider can have additional methods which would allow for configuration of the provider.
|
||||
@@ -278,7 +278,7 @@ function annotate(fn) {
|
||||
*
|
||||
* beforeEach(module(function($provide) {
|
||||
* $provide.provider('greet', GreetProvider);
|
||||
* });
|
||||
* }));
|
||||
*
|
||||
* it('should greet', inject(function(greet) {
|
||||
* expect(greet('angular')).toEqual('Hello angular!');
|
||||
@@ -291,9 +291,7 @@ function annotate(fn) {
|
||||
* inject(function(greet) {
|
||||
* expect(greet('angular')).toEqual('Ahoj angular!');
|
||||
* });
|
||||
* )};
|
||||
*
|
||||
* });
|
||||
* });
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
@@ -387,7 +385,7 @@ function annotate(fn) {
|
||||
*
|
||||
* @param {string} name The name of the service to decorate.
|
||||
* @param {function()} decorator This function will be invoked when the service needs to be
|
||||
* instanciated. The function is called using the {@link AUTO.$injector#invoke
|
||||
* instantiated. The function is called using the {@link AUTO.$injector#invoke
|
||||
* injector.invoke} method and is therefore fully injectable. Local injection arguments:
|
||||
*
|
||||
* * `$delegate` - The original service instance, which can be monkey patched, configured,
|
||||
@@ -587,6 +585,8 @@ function createInjector(modulesToLoad) {
|
||||
var Constructor = function() {},
|
||||
instance, returnedValue;
|
||||
|
||||
// Check if Type is annotated and use just the given function at n-1 as parameter
|
||||
// e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
|
||||
Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;
|
||||
instance = new Constructor();
|
||||
returnedValue = invoke(Type, instance, locals);
|
||||
|
||||
Vendored
+17
-7
@@ -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'
|
||||
};
|
||||
|
||||
|
||||
@@ -183,7 +183,9 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
|
||||
return {
|
||||
terminal: true,
|
||||
link: function(scope, element, attrs) {
|
||||
var modules = [];
|
||||
var modules = [],
|
||||
embedRootScope,
|
||||
deregisterEmbedRootScope;
|
||||
|
||||
modules.push(['$provide', function($provide) {
|
||||
$provide.value('$templateCache', $templateCache);
|
||||
@@ -209,10 +211,12 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
|
||||
}
|
||||
}, $delegate);
|
||||
}]);
|
||||
$provide.decorator('$rootScope', ['$delegate', function(embedRootScope) {
|
||||
docsRootScope.$watch(function embedRootScopeDigestWatch() {
|
||||
$provide.decorator('$rootScope', ['$delegate', function($delegate) {
|
||||
embedRootScope = $delegate;
|
||||
deregisterEmbedRootScope = docsRootScope.$watch(function embedRootScopeDigestWatch() {
|
||||
embedRootScope.$digest();
|
||||
});
|
||||
|
||||
return embedRootScope;
|
||||
}]);
|
||||
}]);
|
||||
@@ -223,6 +227,12 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
element.bind('$destroy', function() {
|
||||
deregisterEmbedRootScope();
|
||||
embedRootScope.$destroy();
|
||||
});
|
||||
|
||||
angular.bootstrap(element, modules);
|
||||
}
|
||||
};
|
||||
|
||||
Vendored
+8
@@ -143,6 +143,14 @@ directive.tabbable = function() {
|
||||
};
|
||||
};
|
||||
|
||||
directive.table = function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function(scope, element, attrs) {
|
||||
element[0].className = 'table table-bordered table-striped code-table';
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
directive.tabPane = function() {
|
||||
return {
|
||||
|
||||
+80
-48
@@ -26,24 +26,25 @@
|
||||
* Note: All element references in Angular are always wrapped with jQuery or jqLite; they are never
|
||||
* raw DOM references.
|
||||
*
|
||||
* ## Angular's jQuery lite provides the following methods:
|
||||
* ## Angular's jqLite
|
||||
* Angular's lite version of jQuery provides only the following jQuery methods:
|
||||
*
|
||||
* - [addClass()](http://api.jquery.com/addClass/)
|
||||
* - [after()](http://api.jquery.com/after/)
|
||||
* - [append()](http://api.jquery.com/append/)
|
||||
* - [attr()](http://api.jquery.com/attr/)
|
||||
* - [bind()](http://api.jquery.com/bind/)
|
||||
* - [children()](http://api.jquery.com/children/)
|
||||
* - [bind()](http://api.jquery.com/bind/) - Does not support namespaces
|
||||
* - [children()](http://api.jquery.com/children/) - Does not support selectors
|
||||
* - [clone()](http://api.jquery.com/clone/)
|
||||
* - [contents()](http://api.jquery.com/contents/)
|
||||
* - [css()](http://api.jquery.com/css/)
|
||||
* - [data()](http://api.jquery.com/data/)
|
||||
* - [eq()](http://api.jquery.com/eq/)
|
||||
* - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name.
|
||||
* - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name
|
||||
* - [hasClass()](http://api.jquery.com/hasClass/)
|
||||
* - [html()](http://api.jquery.com/html/)
|
||||
* - [next()](http://api.jquery.com/next/)
|
||||
* - [parent()](http://api.jquery.com/parent/)
|
||||
* - [next()](http://api.jquery.com/next/) - Does not support selectors
|
||||
* - [parent()](http://api.jquery.com/parent/) - Does not support selectors
|
||||
* - [prepend()](http://api.jquery.com/prepend/)
|
||||
* - [prop()](http://api.jquery.com/prop/)
|
||||
* - [ready()](http://api.jquery.com/ready/)
|
||||
@@ -55,12 +56,18 @@
|
||||
* - [text()](http://api.jquery.com/text/)
|
||||
* - [toggleClass()](http://api.jquery.com/toggleClass/)
|
||||
* - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Doesn't pass native event objects to handlers.
|
||||
* - [unbind()](http://api.jquery.com/unbind/)
|
||||
* - [unbind()](http://api.jquery.com/unbind/) - Does not support namespaces
|
||||
* - [val()](http://api.jquery.com/val/)
|
||||
* - [wrap()](http://api.jquery.com/wrap/)
|
||||
*
|
||||
* ## In addtion to the above, Angular provides additional methods to both jQuery and jQuery lite:
|
||||
* ## jQuery/jqLite Extras
|
||||
* Angular also provides the following additional methods and events to both jQuery and jqLite:
|
||||
*
|
||||
* ### Events
|
||||
* - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
|
||||
* on all DOM nodes being removed. This can be used to clean up and 3rd party bindings to the DOM
|
||||
* element before it is removed.
|
||||
* ### Methods
|
||||
* - `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
|
||||
* camelCase directive name, then the controller for this directive will be retrieved (e.g.
|
||||
@@ -107,37 +114,38 @@ function camelCase(name) {
|
||||
/////////////////////////////////////////////
|
||||
// jQuery mutation patch
|
||||
//
|
||||
// In conjunction with bindJQuery intercepts all jQuery's DOM destruction apis and fires a
|
||||
// In conjunction with bindJQuery intercepts all jQuery's DOM destruction apis and fires a
|
||||
// $destroy event on all DOM nodes being removed.
|
||||
//
|
||||
/////////////////////////////////////////////
|
||||
|
||||
function JQLitePatchJQueryRemove(name, dispatchThis) {
|
||||
function JQLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) {
|
||||
var originalJqFn = jQuery.fn[name];
|
||||
originalJqFn = originalJqFn.$original || originalJqFn;
|
||||
removePatch.$original = originalJqFn;
|
||||
jQuery.fn[name] = removePatch;
|
||||
|
||||
function removePatch() {
|
||||
var list = [this],
|
||||
function removePatch(param) {
|
||||
var list = filterElems && param ? [this.filter(param)] : [this],
|
||||
fireEvent = dispatchThis,
|
||||
set, setIndex, setLength,
|
||||
element, childIndex, childLength, children,
|
||||
fns, events;
|
||||
element, childIndex, childLength, children;
|
||||
|
||||
while(list.length) {
|
||||
set = list.shift();
|
||||
for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) {
|
||||
element = jqLite(set[setIndex]);
|
||||
if (fireEvent) {
|
||||
element.triggerHandler('$destroy');
|
||||
} else {
|
||||
fireEvent = !fireEvent;
|
||||
}
|
||||
for(childIndex = 0, childLength = (children = element.children()).length;
|
||||
childIndex < childLength;
|
||||
childIndex++) {
|
||||
list.push(jQuery(children[childIndex]));
|
||||
if (!getterIfNoArguments || param != null) {
|
||||
while(list.length) {
|
||||
set = list.shift();
|
||||
for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) {
|
||||
element = jqLite(set[setIndex]);
|
||||
if (fireEvent) {
|
||||
element.triggerHandler('$destroy');
|
||||
} else {
|
||||
fireEvent = !fireEvent;
|
||||
}
|
||||
for(childIndex = 0, childLength = (children = element.children()).length;
|
||||
childIndex < childLength;
|
||||
childIndex++) {
|
||||
list.push(jQuery(children[childIndex]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,7 +205,7 @@ function JQLiteUnbind(element, type, fn) {
|
||||
removeEventListenerFn(element, type, events[type]);
|
||||
delete events[type];
|
||||
} else {
|
||||
arrayRemove(events[type], fn);
|
||||
arrayRemove(events[type] || [], fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -471,6 +479,15 @@ forEach({
|
||||
|
||||
val: function(element, value) {
|
||||
if (isUndefined(value)) {
|
||||
if (nodeName_(element) === 'SELECT' && element.multiple) {
|
||||
var result = [];
|
||||
forEach(element.options, function (option) {
|
||||
if (option.selected) {
|
||||
result.push(option.value || option.text);
|
||||
}
|
||||
});
|
||||
return result.length === 0 ? null : result;
|
||||
}
|
||||
return element.value;
|
||||
}
|
||||
element.value = value;
|
||||
@@ -602,23 +619,43 @@ forEach({
|
||||
|
||||
if (!eventFns) {
|
||||
if (type == 'mouseenter' || type == 'mouseleave') {
|
||||
var counter = 0;
|
||||
var contains = document.body.contains || document.body.compareDocumentPosition ?
|
||||
function( a, b ) {
|
||||
var adown = a.nodeType === 9 ? a.documentElement : a,
|
||||
bup = b && b.parentNode;
|
||||
return a === bup || !!( bup && bup.nodeType === 1 && (
|
||||
adown.contains ?
|
||||
adown.contains( bup ) :
|
||||
a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
|
||||
));
|
||||
} :
|
||||
function( a, b ) {
|
||||
if ( b ) {
|
||||
while ( (b = b.parentNode) ) {
|
||||
if ( b === a ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
events.mouseenter = [];
|
||||
events.mouseleave = [];
|
||||
events[type] = [];
|
||||
|
||||
// Refer to jQuery's implementation of mouseenter & mouseleave
|
||||
// Read about mouseenter and mouseleave:
|
||||
// http://www.quirksmode.org/js/events_mouse.html#link8
|
||||
var eventmap = { mouseleave : "mouseout", mouseenter : "mouseover"}
|
||||
bindFn(element, eventmap[type], function(event) {
|
||||
var ret, target = this, related = event.relatedTarget;
|
||||
// For mousenter/leave call the handler if related is outside the target.
|
||||
// NB: No relatedTarget if the mouse left/entered the browser window
|
||||
if ( !related || (related !== target && !contains(target, related)) ){
|
||||
handle(event, type);
|
||||
}
|
||||
|
||||
bindFn(element, 'mouseover', function(event) {
|
||||
counter++;
|
||||
if (counter == 1) {
|
||||
handle(event, 'mouseenter');
|
||||
}
|
||||
});
|
||||
bindFn(element, 'mouseout', function(event) {
|
||||
counter --;
|
||||
if (counter == 0) {
|
||||
handle(event, 'mouseleave');
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
addEventListenerFn(element, type, handle);
|
||||
events[type] = [];
|
||||
@@ -668,12 +705,7 @@ forEach({
|
||||
if (element.nodeType === 1) {
|
||||
var index = element.firstChild;
|
||||
forEach(new JQLite(node), function(child){
|
||||
if (index) {
|
||||
element.insertBefore(child, index);
|
||||
} else {
|
||||
element.appendChild(child);
|
||||
index = child;
|
||||
}
|
||||
element.insertBefore(child, index);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
+2
-2
@@ -30,8 +30,8 @@ function setupModuleLoader(window) {
|
||||
*
|
||||
* # Module
|
||||
*
|
||||
* A module is a collocation of services, directives, filters, and configuration information. Module
|
||||
* is used to configure the {@link AUTO.$injector $injector}.
|
||||
* A module is a collection of services, directives, filters, and configuration information.
|
||||
* `angular.module` is used to configure the {@link AUTO.$injector $injector}.
|
||||
*
|
||||
* <pre>
|
||||
* // Create a new module
|
||||
|
||||
+21
-7
@@ -124,7 +124,8 @@ function Browser(window, document, $log, $sniffer) {
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
var lastBrowserUrl = location.href,
|
||||
baseElement = document.find('base');
|
||||
baseElement = document.find('base'),
|
||||
replacedUrl = null;
|
||||
|
||||
/**
|
||||
* @name ng.$browser#url
|
||||
@@ -159,14 +160,21 @@ function Browser(window, document, $log, $sniffer) {
|
||||
baseElement.attr('href', baseElement.attr('href'));
|
||||
}
|
||||
} else {
|
||||
if (replace) location.replace(url);
|
||||
else location.href = url;
|
||||
if (replace) {
|
||||
location.replace(url);
|
||||
replacedUrl = url;
|
||||
} else {
|
||||
location.href = url;
|
||||
replacedUrl = null;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
// getter
|
||||
} else {
|
||||
// the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
|
||||
return location.href.replace(/%27/g,"'");
|
||||
// - the replacedUrl is a workaround for an IE8-9 issue with location.replace method that doesn't update
|
||||
// location.href synchronously
|
||||
// - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
|
||||
return replacedUrl || location.href.replace(/%27/g,"'");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -237,7 +245,7 @@ function Browser(window, document, $log, $sniffer) {
|
||||
*/
|
||||
self.baseHref = function() {
|
||||
var href = baseElement.attr('href');
|
||||
return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : href;
|
||||
return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : '';
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
@@ -297,7 +305,13 @@ function Browser(window, document, $log, $sniffer) {
|
||||
cookie = cookieArray[i];
|
||||
index = cookie.indexOf('=');
|
||||
if (index > 0) { //ignore nameless cookies
|
||||
lastCookies[unescape(cookie.substring(0, index))] = unescape(cookie.substring(index + 1));
|
||||
var name = unescape(cookie.substring(0, index));
|
||||
// the first value that is seen for a cookie is the most
|
||||
// specific one. values for the same cookie name that
|
||||
// follow are for less specific paths.
|
||||
if (lastCookies[name] === undefined) {
|
||||
lastCookies[name] = unescape(cookie.substring(index + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+73
-3
@@ -3,7 +3,20 @@
|
||||
* @name ng.$cacheFactory
|
||||
*
|
||||
* @description
|
||||
* Factory that constructs cache objects.
|
||||
* Factory that constructs cache objects and gives access to them.
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* var cache = $cacheFactory('cacheId');
|
||||
* expect($cacheFactory.get('cacheId')).toBe(cache);
|
||||
* expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
|
||||
*
|
||||
* cache.put("key", "value");
|
||||
* cache.put("another key", "another value");
|
||||
*
|
||||
* expect(cache.info()).toEqual({id: 'cacheId', size: 2}); // Since we've specified no options on creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @param {string} cacheId Name or id of the newly created cache.
|
||||
@@ -135,6 +148,16 @@ function $CacheFactoryProvider() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ng.$cacheFactory#info
|
||||
* @methodOf ng.$cacheFactory
|
||||
*
|
||||
* @description
|
||||
* Get information about all the of the caches that have been created
|
||||
*
|
||||
* @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
|
||||
*/
|
||||
cacheFactory.info = function() {
|
||||
var info = {};
|
||||
forEach(caches, function(cache, cacheId) {
|
||||
@@ -144,6 +167,17 @@ function $CacheFactoryProvider() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ng.$cacheFactory#get
|
||||
* @methodOf ng.$cacheFactory
|
||||
*
|
||||
* @description
|
||||
* Get access to a cache object by the `cacheId` used when it was created.
|
||||
*
|
||||
* @param {string} cacheId Name or id of a cache to access.
|
||||
* @returns {object} Cache object identified by the cacheId or undefined if no such cache.
|
||||
*/
|
||||
cacheFactory.get = function(cacheId) {
|
||||
return caches[cacheId];
|
||||
};
|
||||
@@ -158,8 +192,44 @@ function $CacheFactoryProvider() {
|
||||
* @name ng.$templateCache
|
||||
*
|
||||
* @description
|
||||
* Cache used for storing html templates.
|
||||
*
|
||||
* The first time a template is used, it is loaded in the template cache for quick retrieval. You can
|
||||
* load templates directly into the cache in a `script` tag, or by consuming the `$templateCache`
|
||||
* service directly.
|
||||
*
|
||||
* Adding via the `script` tag:
|
||||
* <pre>
|
||||
* <html ng-app>
|
||||
* <head>
|
||||
* <script type="text/ng-template" id="templateId.html">
|
||||
* This is the content of the template
|
||||
* </script>
|
||||
* </head>
|
||||
* ...
|
||||
* </html>
|
||||
* </pre>
|
||||
*
|
||||
* **Note:** the `script` tag containing the template does not need to be included in the `head` of the document, but
|
||||
* it must be below the `ng-app` definition.
|
||||
*
|
||||
* Adding via the $templateCache service:
|
||||
*
|
||||
* <pre>
|
||||
* var myApp = angular.module('myApp', []);
|
||||
* myApp.run(function($templateCache) {
|
||||
* $templateCache.put('templateId.html', 'This is the content of the template');
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* To retrieve the template later, simply use it in your HTML:
|
||||
* <pre>
|
||||
* <div ng-include=" 'templateId.html' "></div>
|
||||
* </pre>
|
||||
*
|
||||
* or get it via Javascript:
|
||||
* <pre>
|
||||
* $templateCache.get('templateId.html')
|
||||
* </pre>
|
||||
*
|
||||
* See {@link ng.$cacheFactory $cacheFactory}.
|
||||
*
|
||||
*/
|
||||
|
||||
+80
-24
@@ -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):/;
|
||||
|
||||
|
||||
/**
|
||||
@@ -165,11 +166,11 @@ function $CompileProvider($provide) {
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Register a new directives with the compiler.
|
||||
* Register a new directive with the compiler.
|
||||
*
|
||||
* @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|Array} 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 !== '' && !normalizedVal.match(urlSanitizationWhitelist)) {
|
||||
this[key] = value = 'unsafe:' + normalizedVal;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (writeAttr !== false) {
|
||||
if (value === null || value === undefined) {
|
||||
this.$$element.removeAttr(attrName);
|
||||
@@ -297,7 +342,8 @@ function $CompileProvider($provide) {
|
||||
}
|
||||
};
|
||||
|
||||
var startSymbol = $interpolate.startSymbol(),
|
||||
var urlSanitizationNode = $document[0].createElement('a'),
|
||||
startSymbol = $interpolate.startSymbol(),
|
||||
endSymbol = $interpolate.endSymbol(),
|
||||
denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
|
||||
? identity
|
||||
@@ -312,7 +358,7 @@ function $CompileProvider($provide) {
|
||||
|
||||
function compile($compileNodes, transcludeFn, maxPriority) {
|
||||
if (!($compileNodes instanceof jqLite)) {
|
||||
// jquery always rewraps, where as we need to preserve the original selector so that we can modify it.
|
||||
// jquery always rewraps, whereas we need to preserve the original selector so that we can modify it.
|
||||
$compileNodes = jqLite($compileNodes);
|
||||
}
|
||||
// We can not compile top level text elements since text nodes can be merged and we will
|
||||
@@ -330,7 +376,14 @@ function $CompileProvider($provide) {
|
||||
var $linkNode = cloneConnectFn
|
||||
? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!!
|
||||
: $compileNodes;
|
||||
$linkNode.data('$scope', scope);
|
||||
|
||||
// Attach scope only to non-text nodes.
|
||||
for(var i = 0, ii = $linkNode.length; i<ii; i++) {
|
||||
var node = $linkNode[i];
|
||||
if (node.nodeType == 1 /* element */ || node.nodeType == 9 /* document */) {
|
||||
$linkNode.eq(i).data('$scope', scope);
|
||||
}
|
||||
}
|
||||
safeAddClass($linkNode, 'ng-scope');
|
||||
if (cloneConnectFn) cloneConnectFn($linkNode, scope);
|
||||
if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode);
|
||||
@@ -357,7 +410,7 @@ function $CompileProvider($provide) {
|
||||
* functions return values - the linking functions - are combined into a composite linking
|
||||
* function, which is the a linking function for the node.
|
||||
*
|
||||
* @param {NodeList} nodeList an array of nodes to compile
|
||||
* @param {NodeList} nodeList an array of nodes or NodeList to compile
|
||||
* @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
|
||||
* scope argument is auto-generated to the new child of the transcluded parent scope.
|
||||
* @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then the
|
||||
@@ -380,7 +433,7 @@ function $CompileProvider($provide) {
|
||||
? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement)
|
||||
: null;
|
||||
|
||||
childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes.length)
|
||||
childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes || !nodeList[i].childNodes.length)
|
||||
? null
|
||||
: compileNodes(nodeList[i].childNodes,
|
||||
nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);
|
||||
@@ -420,6 +473,7 @@ function $CompileProvider($provide) {
|
||||
(function(transcludeFn) {
|
||||
return function(cloneFn) {
|
||||
var transcludeScope = scope.$new();
|
||||
transcludeScope.$$transcluded = true;
|
||||
|
||||
return transcludeFn(transcludeScope, cloneFn).
|
||||
bind('$destroy', bind(transcludeScope, transcludeScope.$destroy));
|
||||
@@ -463,7 +517,7 @@ function $CompileProvider($provide) {
|
||||
for (var attr, name, nName, value, nAttrs = node.attributes,
|
||||
j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
|
||||
attr = nAttrs[j];
|
||||
if (attr.specified) {
|
||||
if (!msie || msie >= 8 || attr.specified) {
|
||||
name = attr.name;
|
||||
nName = directiveNormalize(name.toLowerCase());
|
||||
attrsMap[nName] = name;
|
||||
@@ -515,9 +569,9 @@ function $CompileProvider($provide) {
|
||||
|
||||
|
||||
/**
|
||||
* Once the directives have been collected their compile functions is executed. This method
|
||||
* Once the directives have been collected, their compile functions are executed. This method
|
||||
* is responsible for inlining directive templates as well as terminating the application
|
||||
* of the directives if the terminal directive has been reached..
|
||||
* of the directives if the terminal directive has been reached.
|
||||
*
|
||||
* @param {Array} directives Array of collected directives to execute their compile function.
|
||||
* this needs to be pre-sorted by priority order.
|
||||
@@ -525,11 +579,11 @@ function $CompileProvider($provide) {
|
||||
* @param {Object} templateAttrs The shared attribute function
|
||||
* @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
|
||||
* scope argument is auto-generated to the new child of the transcluded parent scope.
|
||||
* @param {DOMElement} $rootElement If we are working on the root of the compile tree then this
|
||||
* argument has the root jqLite array so that we can replace widgets on it.
|
||||
* @param {JQLite} jqCollection If we are working on the root of the compile tree then this
|
||||
* argument has the root jqLite array so that we can replace nodes on it.
|
||||
* @returns linkFn
|
||||
*/
|
||||
function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, $rootElement) {
|
||||
function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, jqCollection) {
|
||||
var terminalPriority = -Number.MAX_VALUE,
|
||||
preLinkFns = [],
|
||||
postLinkFns = [],
|
||||
@@ -583,7 +637,7 @@ function $CompileProvider($provide) {
|
||||
$compileNode = templateAttrs.$$element =
|
||||
jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' '));
|
||||
compileNode = $compileNode[0];
|
||||
replaceWith($rootElement, jqLite($template[0]), compileNode);
|
||||
replaceWith(jqCollection, jqLite($template[0]), compileNode);
|
||||
childTranscludeFn = compile($template, transcludeFn, terminalPriority);
|
||||
} else {
|
||||
$template = jqLite(JQLiteClone(compileNode)).contents();
|
||||
@@ -607,7 +661,7 @@ function $CompileProvider($provide) {
|
||||
throw new Error(MULTI_ROOT_TEMPLATE_ERROR + directiveValue);
|
||||
}
|
||||
|
||||
replaceWith($rootElement, $compileNode, compileNode);
|
||||
replaceWith(jqCollection, $compileNode, compileNode);
|
||||
|
||||
var newTemplateAttrs = {$attr: {}};
|
||||
|
||||
@@ -635,7 +689,7 @@ function $CompileProvider($provide) {
|
||||
assertNoDuplicate('template', templateDirective, directive, $compileNode);
|
||||
templateDirective = directive;
|
||||
nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i),
|
||||
nodeLinkFn, $compileNode, templateAttrs, $rootElement, directive.replace,
|
||||
nodeLinkFn, $compileNode, templateAttrs, jqCollection, directive.replace,
|
||||
childTranscludeFn);
|
||||
ii = directives.length;
|
||||
} else if (directive.compile) {
|
||||
@@ -725,6 +779,8 @@ function $CompileProvider($provide) {
|
||||
lastValue,
|
||||
parentGet, parentSet;
|
||||
|
||||
scope.$$isolateBindings[scopeName] = mode + attrName;
|
||||
|
||||
switch (mode) {
|
||||
|
||||
case '@': {
|
||||
@@ -766,7 +822,7 @@ function $CompileProvider($provide) {
|
||||
parentGet = $parse(attrs[attrName]);
|
||||
scope[scopeName] = function(locals) {
|
||||
return parentGet(parentScope, locals);
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -935,8 +991,8 @@ function $CompileProvider($provide) {
|
||||
}
|
||||
|
||||
directives.unshift(derivedSyncDirective);
|
||||
afterTemplateNodeLinkFn = applyDirectivesToNode(directives, $compileNode, tAttrs, childTranscludeFn);
|
||||
afterTemplateChildLinkFn = compileNodes($compileNode.contents(), childTranscludeFn);
|
||||
afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn);
|
||||
afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
|
||||
|
||||
|
||||
while(linkQueue.length) {
|
||||
@@ -1015,10 +1071,10 @@ function $CompileProvider($provide) {
|
||||
function addAttrInterpolateDirective(node, directives, value, name) {
|
||||
var interpolateFn = $interpolate(value, true);
|
||||
|
||||
|
||||
// no interpolation found -> ignore
|
||||
if (!interpolateFn) return;
|
||||
|
||||
|
||||
directives.push({
|
||||
priority: 100,
|
||||
compile: valueFn(function attrInterpolateLinkFn(scope, element, attr) {
|
||||
|
||||
@@ -52,7 +52,7 @@ function $ControllerProvider() {
|
||||
* @description
|
||||
* `$controller` service is responsible for instantiating controllers.
|
||||
*
|
||||
* It's just simple call to {@link AUTO.$injector $injector}, but extracted into
|
||||
* It's just a simple call to {@link AUTO.$injector $injector}, but extracted into
|
||||
* a service, so that one can override this service with {@link https://gist.github.com/1649788
|
||||
* BC version}.
|
||||
*/
|
||||
|
||||
+15
-5
@@ -11,15 +11,25 @@
|
||||
*
|
||||
* The reasoning for this change is to allow easy creation of action links with `ngClick` directive
|
||||
* without changing the location or causing page reloads, e.g.:
|
||||
* <a href="" ng-click="model.$save()">Save</a>
|
||||
* `<a href="" ng-click="model.$save()">Save</a>`
|
||||
*/
|
||||
var htmlAnchorDirective = valueFn({
|
||||
restrict: 'E',
|
||||
compile: function(element, attr) {
|
||||
// turn <a href ng-click="..">link</a> into a link in IE
|
||||
// but only if it doesn't have name attribute, in which case it's an anchor
|
||||
if (!attr.href) {
|
||||
attr.$set('href', '');
|
||||
|
||||
if (msie <= 8) {
|
||||
|
||||
// turn <a href ng-click="..">link</a> into a stylable link in IE
|
||||
// but only if it doesn't have name attribute, in which case it's an anchor
|
||||
if (!attr.href && !attr.name) {
|
||||
attr.$set('href', '');
|
||||
}
|
||||
|
||||
// add a comment node to anchors to workaround IE bug that causes element content to be reset
|
||||
// to new attribute content if attribute is updated with value containing @ and element also
|
||||
// contains value with @
|
||||
// see issue #1949
|
||||
element.append(document.createComment('IE fix'));
|
||||
}
|
||||
|
||||
return function(scope, element) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user