├── .gitignore ├── .jshintrc ├── .travis.yml ├── CHANGELOG.md ├── Gruntfile.js ├── LICENSE ├── README.md ├── bin ├── karma ├── protractor └── webdriver-manager ├── bower.json ├── dist ├── angularAMD.js ├── angularAMD.min.js ├── angularAMD.min.map ├── ngload.js ├── ngload.min.js └── ngload.min.map ├── package.json ├── src ├── angularAMD.js ├── banner.txt ├── jshintrc.json ├── ngload.js └── version.txt ├── test ├── conf │ ├── karma.unit.mustache │ ├── karma.unit.no_ngload.js │ └── protractor.e2e.js ├── e2e │ └── www.spec.js └── unit │ ├── app.spec.js │ ├── app_no_ngload.spec.js │ ├── controller.spec.js │ ├── controllerFn.spec.js │ ├── decoServices.spec.js │ ├── factory │ ├── module.js │ ├── provider.js │ ├── utestModule.js │ └── utestProvider.js │ ├── lib │ ├── app.js │ ├── app_no_ngload.js │ ├── component.js │ ├── controller.js │ ├── controllerFn.js │ ├── decoServices.js │ ├── main.mustache │ ├── main.no_ngload.js │ ├── preService.js │ ├── providerFactory.js │ ├── regController.js │ ├── regServices.js │ └── services.js │ ├── regController.spec.js │ ├── regServices.spec.js │ └── services.spec.js └── www ├── css ├── sons-of-obsidian.css └── style.css ├── index.html ├── js ├── main.js └── scripts │ ├── app.js │ ├── controller │ ├── home_ctrl.js │ ├── map_ctrl.js │ ├── modules_ctrl.js │ └── pictures_ctrl.js │ ├── directive │ ├── navMenu.js │ ├── templates │ │ └── navMenu.html │ └── write.js │ └── service │ ├── dataServices.js │ ├── mapServices.js │ └── picturesService.js └── views ├── home.html ├── map.html ├── modules.html └── pictures.html /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Directory created by tools 2 | node_modules/ 3 | bower_components/ 4 | www/js/lib/ 5 | build/ 6 | 7 | # Ignore css sourced from bower 8 | www/css/prettify.css 9 | 10 | # Other 11 | phantomjsdriver.log 12 | 13 | 14 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | /* Jasmine */ 4 | "describe" : false, 5 | "it" : false, 6 | "before" : false, 7 | "beforeEach" : false, 8 | "after" : false, 9 | "afterEach" : false, 10 | /* RequireJS */ 11 | "require" : false, 12 | "define" : false, 13 | /* AngularJS */ 14 | "angular" : false 15 | }, 16 | "bitwise" : true, 17 | "curly" : true, 18 | "eqeqeq" : true, 19 | "immed" : true, 20 | "latedef" : true, 21 | "newcap" : false, 22 | "noarg" : true, 23 | "quotmark" : "single", 24 | "undef" : true, 25 | "unused" : true, 26 | "strict" : true, 27 | "browser" : true 28 | } 29 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - 0.10 5 | 6 | branches: 7 | only: 8 | - master 9 | 10 | before_script: 11 | - npm update -g npm 12 | - npm install grunt-cli 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # 0.2.1 (2014-08-08) 3 | 4 | ## Bug Fixes 5 | 6 | - **AngularJS 1.3.x**: Animation unit test now works and added fix for `.processQueue` 7 | ([e0ded6f](https://github.com/marcoslin/angularAMD/commit/e0ded6ffd290299322a721f7c669867151e9d435) 8 | [#80](https://github.com/marcoslin/angularAMD/issues/80)) 9 | 10 | - **IE8**: Removed the use of `.const` breaking IE8. 11 | ([e0ded6f](https://github.com/marcoslin/angularAMD/commit/e0ded6ffd290299322a721f7c669867151e9d435) 12 | [#84](https://github.com/marcoslin/angularAMD/issues/84)) 13 | 14 | 15 | # 0.2.0 (2014-07-23) 16 | *0.2.0-rc.1 (2014-06-28)* 17 | 18 | ## Bug Fixes 19 | 20 | - **ngload**: ngload now correctly load module that creates submodules internally 21 | ([e8b74af](https://github.com/marcoslin/angularAMD/commit/e8b74afd8e8de40accd15d40ea58b2d4fbb53ca5) 22 | [#67](https://github.com/marcoslin/angularAMD/issues/67)) 23 | 24 | ## Features 25 | 26 | - **angularAMD.<>**: modules now can be defined before bootstrap using `angularAMD.factory` 27 | ([81a5495](https://github.com/marcoslin/angularAMD/commit/81a54955080cff296118321ee993eb66d95507a5) 28 | [#14](https://github.com/marcoslin/angularAMD/issues/14)) 29 | 30 | - **.config**: New method allowing changing of configuration post bootstrap 31 | ([d3ebfed](https://github.com/marcoslin/angularAMD/commit/d3ebfed13bc83fea326ef1dca98a82977782cd40) 32 | [#71](https://github.com/marcoslin/angularAMD/issues/71)) 33 | 34 | - **.route**: `controller` parameter can now be omitted if module specified in `controllerUrl` returns 35 | a function. 36 | ([550cd28](https://github.com/marcoslin/angularAMD/commit/550cd2832d0bdeac73d9c50b1ec68e7c087e9d93) 37 | [#72](https://github.com/marcoslin/angularAMD/issues/72)) 38 | 39 | ## Breaking Changes 40 | 41 | - **app.<>**: `app.register` is now deprecated so a factory creation using `app.register.factory` 42 | is now `app.factory`. To support this, `.bootstrap` creates a alternate `app` that need to be used 43 | instead of the `app` created using `angular.module(...)`. 44 | ([54b5ec2](https://github.com/marcoslin/angularAMD/commit/54b5ec2d2553b30cff60dde94e9b06b0be5bf435) 45 | [1c7922b](https://github.com/marcoslin/angularAMD/commit/1c7922b9ecd6b04f80f97085edf8debb346c83b8) 46 | [#63](https://github.com/marcoslin/angularAMD/pull/63) 47 | [#70](https://github.com/marcoslin/angularAMD/issues/70)) 48 | 49 | 50 | 51 | 52 | # 0.1.1 (2014-05-28) 53 | 54 | ## Bug Fixes 55 | 56 | - **ngload**: On-demand loading of animation now works 57 | ([eae2e56](https://github.com/marcoslin/angularAMD/commit/eae2e5623d742bc0ef1e6c10eb74da7ae9e367a0) 58 | [#31](https://github.com/marcoslin/angularAMD/issues/31)) 59 | - **.bootstrap** Fixed Firefox Right-Click bug in Html5Mode 60 | [as per this post](https://groups.google.com/d/msg/angular/LAk9oZqRx24/mPXPj495WlEJ) 61 | ([9ce8ca1](https://github.com/marcoslin/angularAMD/commit/9ce8ca18d2b69b4779714bb6a49feee784450458) 62 | [#41](https://github.com/marcoslin/angularAMD/issues/41)) 63 | - **www**: As result of 64 | [40a6dd0](https://github.com/marcoslin/angularAMD/commit/40a6dd0c89f49926fc4f5be4c5450f9eb61dcd42), 65 | removed `$rootScope` from `.config` block and replaced with custom `configValue` provider. 66 | ([6e4f7d1](https://github.com/marcoslin/angularAMD/commit/6e4f7d154879abd11c8292ded2e947e55e580347) 67 | [#60](https://github.com/marcoslin/angularAMD/issues/60)) 68 | - **www**: Sample Plunk doesn't execute due to MIME type error. Switched to use `jsdelivr` instead of `raw.github.com` and from Plunker to `bl.ocks.org` 69 | ([cf8c0ff](https://github.com/marcoslin/angularAMD/commit/cf8c0fffdc47fe2e85d36dfaf46365ed2d6ec66f) 70 | [#59](https://github.com/marcoslin/angularAMD/issues/59) 71 | [#54](https://github.com/marcoslin/angularAMD/issues/54)) 72 | 73 | ## Features 74 | 75 | - **unittest** Unit Test now include Registered Controller with Registered Factory 76 | ([c40bc19](https://github.com/marcoslin/angularAMD/commit/c40bc194983cc7d34b4c38e43405acf6591a1f6c) 77 | [#27](https://github.com/marcoslin/angularAMD/issues/27)) 78 | - **e2e**: Added Protractor e2e testing. Need to run manually using `grunt test-e2e` and excluded from Travis-CI for now. 79 | ([4efc87d](https://github.com/marcoslin/angularAMD/commit/4efc87d9c4ce4db8ac9f67752a448536e08d56af) 80 | [5f746ef](https://github.com/marcoslin/angularAMD/commit/5f746ef3f7554a8e068557b0e8b79e52cc7d114d) 81 | [6299430](https://github.com/marcoslin/angularAMD/commit/6299430877278dfb8e919bc5a872d0974543743d)) 82 | 83 | 84 | ## Breaking Changes 85 | 86 | - **ngload**: `ngload` did not correctly execute the `.config` block causing `.provider` to fail 87 | ([40a6dd0](https://github.com/marcoslin/angularAMD/commit/40a6dd0c89f49926fc4f5be4c5450f9eb61dcd42) 88 | [#28](https://github.com/marcoslin/angularAMD/issues/28) 89 | [#21](https://github.com/marcoslin/angularAMD/issues/21)). 90 | Prior to this fix, module loaded using `ngload` allowed for `.config` block to load instance such as `$rootScope`. Loading these module now will result in error. 91 | 92 | 93 | # 0.1.0 (2013-11-13) 94 | 95 | ### Initial Public Release with `ngload` 96 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | // Gruntfile 2 | /*jslint devel: true, node: true, white:true */ 3 | 4 | module.exports = function (grunt) { 5 | 'use strict'; 6 | require('load-grunt-tasks')(grunt); 7 | 8 | // Config variables 9 | var configVars = { 10 | 'build': 'build', 11 | 'dist': 'dist', 12 | 'dist_www': '../gh-pages', 13 | 'dist_bower': '../bower-repo', 14 | 'www_server': 'localhost', 15 | 'www_port': '9768', 16 | 'e2e_port': '9769' 17 | }; 18 | 19 | // Read version and banner files 20 | configVars.proj_version = grunt.file.read('src/version.txt'); 21 | configVars.proj_banner = grunt.file.read('src/banner.txt'); 22 | 23 | 24 | grunt.initConfig({ 25 | cvars: configVars, 26 | bower: { 27 | setup: { 28 | options: { install: true, copy: false } 29 | } 30 | }, 31 | shell: { 32 | 'webdriver-manager-update': { 33 | command: 'node_modules/protractor/bin/webdriver-manager update', 34 | options: { 35 | async: false 36 | } 37 | } 38 | }, 39 | copy: { 40 | 'setup-www': { 41 | files: [ 42 | { 43 | expand: true, cwd: 'src/', 44 | src: 'angularAMD.js', dest: 'www/js/lib/requirejs/' 45 | }, 46 | { 47 | expand: true, cwd: 'src/', 48 | src: 'ngload.js', dest: 'www/js/lib/requirejs/' 49 | }, 50 | { 51 | expand: true, cwd: 'bower_components/angular/', 52 | src: 'angular.js', dest: 'www/js/lib/angular/' 53 | }, 54 | { 55 | expand: true, cwd: 'bower_components/angular-route/', 56 | src: 'angular-route.js', dest: 'www/js/lib/angular/' 57 | }, 58 | { 59 | expand: true, cwd: 'bower_components/angular-ui-bootstrap-bower/', 60 | src: ['ui-bootstrap-tpls.js'], dest: 'www/js/lib/angular-ui-bootstrap/' 61 | }, 62 | { 63 | expand: true, cwd: 'bower_components/requirejs/', 64 | src: 'require.js', dest: 'www/js/lib/requirejs/' 65 | }, 66 | { 67 | expand: true, cwd: 'bower_components/requirejs-plugins/src/', 68 | src: 'async.js', dest: 'www/js/lib/requirejs/' 69 | }, 70 | { 71 | expand: true, cwd: 'bower_components/google-code-prettify/src/', 72 | src: 'prettify.js', dest: 'www/js/lib/google-code-prettify/' 73 | }, 74 | { 75 | expand: true, cwd: 'bower_components/google-code-prettify/src/', 76 | src: 'prettify.css', dest: 'www/css/' 77 | } 78 | ] 79 | }, 80 | 'build-www': { 81 | files: [ 82 | { 83 | expand: true, cwd: 'www/', 84 | src: ['index.html','css/**', 'views/**', 'js/main.js', 'js/scripts/**'], 85 | dest: '<%= cvars.build %>/www/' 86 | } 87 | ] 88 | }, 89 | 'dist-www': { 90 | files: [ 91 | { 92 | src: '<%= cvars.build %>/angularAMD.min.js', 93 | dest: '<%= cvars.dist_www %>/js/lib/requirejs/angularAMD.js' 94 | }, 95 | { 96 | src: '<%= cvars.build %>/ngload.min.js', 97 | dest: '<%= cvars.dist_www %>/js/lib/requirejs/ngload.js' 98 | }, 99 | { 100 | src: 'bower_components/angular/angular.min.js', 101 | dest: '<%= cvars.dist_www %>/js/lib/angular/angular.js' 102 | }, 103 | { 104 | src: 'bower_components/angular-route/angular-route.min.js', 105 | dest: '<%= cvars.dist_www %>/js/lib/angular/angular-route.js' 106 | }, 107 | { 108 | src: 'bower_components/angular-ui-bootstrap-bower/ui-bootstrap-tpls.min.js', 109 | dest: '<%= cvars.dist_www %>/js/lib/angular-ui-bootstrap/ui-bootstrap-tpls.js' 110 | }, 111 | { 112 | expand: true, cwd: 'bower_components/google-code-prettify/src/', 113 | src: 'prettify.js', dest: '<%= cvars.dist_www %>/js/lib/google-code-prettify/' 114 | } 115 | ] 116 | }, 117 | 'dist-bower' : { 118 | files: [ 119 | { 120 | expand: true, cwd: '<%= cvars.build %>', 121 | src: ['*.js','*.map'], 122 | dest: '<%= cvars.dist %>/' 123 | }, 124 | { 125 | expand: true, cwd: '<%= cvars.build %>', 126 | src: ['*.js','*.map'], 127 | dest: '<%= cvars.dist_bower %>/' 128 | } 129 | ] 130 | } 131 | 132 | }, 133 | connect: { 134 | // URL should be: http://localhost:9768/www/ to simulate github pages 135 | options : { 136 | hostname: '<%= cvars.www_server %>' 137 | }, 138 | 'serve-www': { 139 | options : { 140 | port: '<%= cvars.www_port %>', 141 | base: '.', 142 | keepalive: true 143 | } 144 | }, 145 | 'e2e-www': { 146 | options : { 147 | port: '<%= cvars.e2e_port %>', 148 | base: './www', 149 | keepalive: false 150 | } 151 | } 152 | }, 153 | open: { 154 | 'serve-www': { 155 | path: 'http://<%= cvars.www_server %>:<%= cvars.www_port %>/www/', 156 | app: 'Google Chrome' 157 | } 158 | }, 159 | karma: { 160 | 'unit': { 161 | configFile: '<%= cvars.build %>/test/conf/karma.unit.js', 162 | singleRun: false 163 | }, 164 | 'unit-no-ngload': { 165 | configFile: 'test/conf/karma.unit.no_ngload.js', 166 | singleRun: false 167 | }, 168 | 'build': { 169 | configFile: '<%= cvars.build %>/test/conf/karma.unit.js', 170 | browsers: ['PhantomJS','Chrome','Firefox'] 171 | }, 172 | 'build-min': { 173 | configFile: '<%= cvars.build %>/test/conf/karma.unit.min.js', 174 | browsers: ['PhantomJS','Chrome','Firefox'] 175 | }, 176 | 'build-travis': { 177 | configFile: '<%= cvars.build %>/test/conf/karma.unit.min.js' 178 | } 179 | }, 180 | protractor: { 181 | options: { 182 | configFile: 'test/conf/protractor.e2e.js' 183 | }, 184 | 'e2e-www': { 185 | options: { 186 | keepAlive: true, 187 | args: { 188 | browser: 'chrome', 189 | baseUrl: 'http://<%= cvars.www_server %>:<%= cvars.e2e_port %>' 190 | } 191 | } 192 | }, 193 | 'build-travis': { 194 | options: { 195 | keepAlive: false, 196 | args: { 197 | browser: 'phantomjs', 198 | baseUrl: 'http://<%= cvars.www_server %>:<%= cvars.e2e_port %>' 199 | } 200 | } 201 | } 202 | }, 203 | template: { 204 | 'main-js': { 205 | src: 'test/unit/lib/main.mustache', 206 | dest: '<%= cvars.build %>/test/unit/lib/main.js', 207 | variables: { 208 | 'angularAMD-js-file': 'src/angularAMD', 209 | 'ngload-js-file': 'src/ngload' 210 | } 211 | }, 212 | 'main-min-js': { 213 | src: 'test/unit/lib/main.mustache', 214 | dest: '<%= cvars.build %>/test/unit/lib/main.min.js', 215 | variables: { 216 | 'angularAMD-js-file': '<%= cvars.build %>/angularAMD.min', 217 | 'ngload-js-file': '<%= cvars.build %>/ngload.min' 218 | } 219 | }, 220 | 'karma-js': { 221 | src: 'test/conf/karma.unit.mustache', 222 | dest: '<%= cvars.build %>/test/conf/karma.unit.js', 223 | variables: { 224 | 'main-js-file': '<%= cvars.build %>/test/unit/lib/main.js' 225 | } 226 | }, 227 | 'karma-min-js': { 228 | src: 'test/conf/karma.unit.mustache', 229 | dest: '<%= cvars.build %>/test/conf/karma.unit.min.js', 230 | variables: { 231 | 'main-js-file': '<%= cvars.build %>/test/unit/lib/main.min.js' 232 | } 233 | } 234 | }, 235 | concat: { 236 | 'build': { 237 | options: { 238 | 'banner': configVars.proj_banner, 239 | 'stripBanners': true 240 | }, 241 | files: { 242 | '<%= cvars.build %>/angularAMD.js': ['src/angularAMD.js'], 243 | '<%= cvars.build %>/ngload.js': ['src/ngload.js'] 244 | } 245 | } 246 | }, 247 | ngAnnotate: { 248 | 'dist-www': { 249 | files: [{ 250 | expand: true, 251 | cwd: 'www/js/scripts/', 252 | src: '**/*.js', 253 | dest: '<%= cvars.dist_www %>/js/scripts/' 254 | }] 255 | } 256 | }, 257 | uglify: { 258 | 'build': { 259 | options: { 260 | 'report': true, 261 | 'banner': configVars.proj_banner, 262 | 'stripBanners': true, 263 | 'sourceMap': true 264 | }, 265 | files: { 266 | '<%= cvars.build %>/angularAMD.min.js': ['src/angularAMD.js'], 267 | '<%= cvars.build %>/ngload.min.js': ['src/ngload.js'] 268 | } 269 | }, 270 | 'dist-www': { 271 | files: [ 272 | { 273 | expand: true, cwd: 'www/js/', 274 | src: 'main.js', dest: '<%= cvars.dist_www %>/js/' 275 | }, 276 | { 277 | expand: true, cwd: '<%= cvars.dist_www %>/js/scripts/', 278 | src: '**/*.js', dest: '<%= cvars.dist_www %>/js/scripts/' 279 | }, 280 | { 281 | src: 'bower_components/requirejs/require.js', 282 | dest: '<%= cvars.dist_www %>/js/lib/requirejs/require.js' 283 | }, 284 | { 285 | expand: true, cwd: 'bower_components/requirejs-plugins/src/', 286 | src: 'async.js', dest: '<%= cvars.dist_www %>/js/lib/requirejs/' 287 | } 288 | ] 289 | } 290 | }, 291 | cssmin: { 292 | 'dist-www': { 293 | files: { 294 | '<%= cvars.dist_www %>/css/style.css': 'www/css/style.css', 295 | '<%= cvars.dist_www %>/css/prettify.css': 'www/css/prettify.css', 296 | '<%= cvars.dist_www %>/css/sons-of-obsidian.css': 'www/css/sons-of-obsidian.css' 297 | } 298 | } 299 | }, 300 | htmlmin : { 301 | 'dist-www': { 302 | options: { 303 | removeComments: true, 304 | collapseWhitespace: true 305 | }, 306 | files: [{ 307 | expand: true, cwd: 'www/', 308 | src: '**/*.html', dest: '<%= cvars.dist_www %>/' 309 | }] 310 | } 311 | } 312 | }); 313 | 314 | 315 | /* BASIC TASKS */ 316 | grunt.registerTask('setup', [ 317 | 'bower:setup', 318 | 'shell:webdriver-manager-update' 319 | ]); 320 | grunt.registerTask('genTestTemplates', [ 321 | 'template:main-js','template:karma-js', 322 | 'template:main-min-js','template:karma-min-js' 323 | ]); 324 | 325 | 326 | /* 327 | Designed to be used during the dev and contains 2 distinct tests: 328 | - unit 329 | - unit-no-ngload 330 | 331 | The `grunt test` will kick off the `unit` test first, and after 332 | `ctrl-c` will kick off `unit-no-ngload` test. This is needed as 333 | it was dificult to load 2 different instance of angularAMD. The 334 | race condition was redering subsequent test with both ngload and 335 | no-ngload version unpredictable. 336 | */ 337 | grunt.registerTask('test-base', [ 338 | 'setup' 339 | ]); 340 | grunt.registerTask('test-unit', [ 341 | 'test-base', 342 | 'genTestTemplates', 343 | 'karma:unit', 344 | 'karma:unit-no-ngload' 345 | ]); 346 | grunt.registerTask('test-e2e', [ 347 | 'test-base', 348 | 'setup-www', 349 | 'connect:e2e-www', 350 | 'protractor:e2e-www' 351 | ]); 352 | 353 | 354 | /* Done with dev, build it by creating a minified version */ 355 | grunt.registerTask('build', [ 356 | 'setup', 357 | 'genTestTemplates', 358 | 'karma:build', 359 | 'uglify:build', 360 | 'concat:build', 361 | 'karma:build-min' 362 | ]); 363 | grunt.registerTask('build-travis', [ 364 | 'setup', 365 | 'genTestTemplates', 366 | 'uglify:build', 367 | 'concat:build', 368 | 'karma:build-travis', 369 | 'setup-www', 370 | 'connect:e2e-www' 371 | // 'protractor:build-travis' 372 | ]); 373 | 374 | /* Run sample website */ 375 | grunt.registerTask('setup-www', ['copy:setup-www']); 376 | grunt.registerTask('serve-www', [ 377 | 'setup-www', 'open', 378 | 'connect:serve-www' 379 | ]); 380 | 381 | /* Create github pages */ 382 | grunt.registerTask('dist-www', [ 383 | 'test-e2e', 384 | 'ngAnnotate:dist-www', 385 | 'cssmin:dist-www', 386 | 'uglify:dist-www', 387 | 'htmlmin:dist-www', 388 | 'copy:dist-www' 389 | ]); 390 | 391 | /* Update bower repository -- must run build manually before this */ 392 | grunt.registerTask('dist-bower', [ 393 | 'copy:dist-bower' 394 | ]); 395 | 396 | }; 397 | 398 | 399 | 400 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2013-2014 Marcos Lin https://github.com/marcoslin/ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | angularAMD v0.2.1 [![Build Status](https://travis-ci.org/marcoslin/angularAMD.png)](https://travis-ci.org/marcoslin/angularAMD) 2 | ========== 3 | angularAMD is an utility that facilitates the use of RequireJS in AngularJS applications supporting on-demand loading 4 | of 3rd party modules such as [angular-ui](git@github.com:marcoslin/bower-angularAMD.git). 5 | 6 | Installation 7 | ========== 8 | 9 | ### bower 10 | bower install angularAMD 11 | 12 | ### cdn 13 | //cdn.jsdelivr.net/angular.amd/0.2/angularAMD.min.js 14 | 15 | 16 | Usage 17 | ========== 18 | 19 | http://marcoslin.github.io/angularAMD/ has been created as a working demo for `angularAMD`. The source code 20 | can be found in the `www/` directory of this project. 21 | 22 | Additional, the following project has been created to illustrate how to build an `r.js` optmized distribution: 23 | * https://github.com/marcoslin/angularAMD-sample 24 | 25 | ### RequireJS data-main 26 | 27 | Starting point for any RequireJS app is a `main.js`, which should be used to define the components and their 28 | dependencies. Use `deps` to kick off `app.js`: 29 | 30 | ```Javascript 31 | require.config({ 32 | baseUrl: "js", 33 | paths: { 34 | 'angular': '//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min', 35 | 'angularAMD': 'lib/angularAMD.min', 36 | 'ngload': 'lib/ngload.min' 37 | }, 38 | shim: { 39 | 'angularAMD': ['angular'], 40 | 'ngload': ['angularAMD'] 41 | }, 42 | deps: ['app'] 43 | }); 44 | ``` 45 | 46 | ### Bootstrapping AngularJS 47 | 48 | Once all component dependencies have been defined, use a `app.js` to create the AngularJS application and perform the bootstrapping: 49 | 50 | ```Javascript 51 | define(['angularAMD'], function (angularAMD) { 52 | var app = angular.module(app_name, ['webapp']); 53 | ... // Setup app here. E.g.: run .config with $routeProvider 54 | return angularAMD.bootstrap(app); 55 | }); 56 | ``` 57 | 58 | Since bootstrapping is taking place manually, `ng-app` should not be used in HTML. `angularAMD.bootstrap(app);` will take care of bootstraping AngularJS. 59 | 60 | ### On-Demand Loading of Controllers 61 | 62 | Use `angularAMD.route` when configuring routes using `$routeProvider` to enable the on-demand loading of controllers: 63 | 64 | ```Javascript 65 | app.config(function ($routeProvider) { 66 | $routeProvider.when( 67 | "/home", 68 | angularAMD.route({ 69 | templateUrl: 'views/home.html', 70 | controller: 'HomeController', 71 | controllerUrl: 'scripts/controller' 72 | }) 73 | ); 74 | }); 75 | ``` 76 | 77 | The primary purpose of `angularAMD.route` is to set the `.resolve` property to load controller using a `require` statement. 78 | Any attribute you pass into this method will simply be returned, with exception of `controllerUrl`. 79 | 80 | #### route without `controllerUrl` 81 | 82 | You can avoid passing of `controllerUrl` if you define it in your `main.js` as: 83 | 84 | ```Javascript 85 | paths: { 'HomeController': 'scripts/controller' } 86 | ``` 87 | 88 | 89 | #### route without `controller` 90 | 91 | When the `controller` option is omitted, `angularAMD.route` assumes that a function will be returned from the module defined 92 | by `controllerUrl`. As a result, you can avoid giving an explicit name to your controller by doing: 93 | 94 | ```Javascript 95 | define(['app'], function (app) { 96 | return ["$scope", function ($scope) { 97 | ... 98 | }]; 99 | }); 100 | ``` 101 | 102 | 103 | ### Creating a Module 104 | 105 | Any subsequent module definitions would simply need to require `app` to create the desired AngularJS services: 106 | 107 | ```Javascript 108 | define(['app'], function (app) { 109 | app.factory('Pictures', function (...) { 110 | ... 111 | }); 112 | }); 113 | ``` 114 | 115 | Here is the list of methods supported: 116 | 117 | * `.provider` ** 118 | * `.controller` 119 | * `.factory` 120 | * `.service` 121 | * `.constant` 122 | * `.value` 123 | * `.directive` 124 | * `.filter` 125 | * `.animation` 126 | 127 | ** Only as of 0.2.x 128 | 129 | #### Loading Application Wide Module 130 | 131 | Normally, application wide features are created as independent modules and added as dependency to your `app`. 132 | 3rd party packages such as [ui-bootstrap](http://angular-ui.github.io/bootstrap/) is a perfect example. However, 133 | what if you have a single directive? `angularAMD` simplifies such tasks by exposing the provider recipe so you can do something like: 134 | 135 | **directive/navMenu.js** 136 | ```Javascript 137 | define(['angularAMD'], function (angularAMD) { 138 | angularAMD.directive('navMenu', function (...) { 139 | ... 140 | }); 141 | }); 142 | ``` 143 | 144 | **app.js** 145 | ```Javascript 146 | define(['angularAMD', 'directive/navMenu'], function (angularAMD) { 147 | var app = angular.module(app_name, ['webapp']); 148 | ... 149 | // `navMenu` is automatically registered bootstrap 150 | return angularAMD.bootstrap(app); 151 | }); 152 | ``` 153 | 154 | In this case, `angularAMD.directive` will detect that boostraping hasn't taken place yet and it will queue up the 155 | directive creation request and apply that request directly on the `app` object passed to `angularAMD.bootstrap`. If bootstraping has taken place already, it will essentially do the same thing `app.directive`. As a result, services created using `angularAMD.<>` can be loaded before and after bootstraping. 156 | 157 | ### 3rd Party AngularJS Modules 158 | 159 | 3rd party AngularJS modules, meaning any module created using `angular.module` syntax, can be loaded as any normal 160 | JavaScript file *before* `angularAMD.bootstrap` is called. After bootstraping, any AngularJS module must be loaded 161 | using the included `ngload` RequireJS plugin. 162 | 163 | ```Javascript 164 | define(['app', 'ngload!dataServices'], function (app) {...}); 165 | ``` 166 | 167 | In case you need to load your module using the RequireJS plugin or if you have complex dependecies, you can create a wrapper RequireJS module as below: 168 | 169 | ```Javascript 170 | define(['angularAMD', 'ui-bootstrap'], function (angularAMD) { 171 | angularAMD.processQueue(); 172 | }); 173 | ``` 174 | 175 | In this case, all dependencies will be queued up and when `.processQueue()` is called, it will go through the queue and copy them into current app using `app.register`: 176 | 177 | #### Module without `.run` or `.config` 178 | 179 | If you have your own module that does not use `.run` or `.config`, you can avoid the use of `ngload` as any module 180 | created after bootstrap will support on-demand loading. For example: 181 | 182 | **common.js** 183 | ```Javascript 184 | define(['ngload!restangular'], function() { 185 | return angular.module('common', ['restangular']); 186 | }); 187 | ``` 188 | 189 | **user.js** 190 | ```Javascript 191 | define(['common'], function(common) { 192 | common.factory("User", function () { ... }); 193 | }); 194 | ``` 195 | 196 | **controller/home_ctrl** 197 | ```Javascript 198 | define(['app', 'user'], function(app) { 199 | app.controller("HomeCtrl", ["$scope", "User", function ($scope, User) { 200 | ... 201 | }]); 202 | }); 203 | ``` 204 | 205 | In this example, the `user` package does not need to be loaded in the `app.js` as it's loaded on demand when `HomeCtrl` is called. 206 | 207 | Running Sample Project 208 | ========== 209 | 210 | Prerequisites: 211 | * node and npm 212 | * grunt-cli installed globally as per [Grunt Getting started](http://gruntjs.com/getting-started). 213 | 214 | Run the following command after cloning this project: 215 | 216 | ```bash 217 | npm install 218 | grunt build 219 | grunt serve-www 220 | ``` 221 | * The default build will test angularAMD using following browsers: 'PhantomJS', 'Chrome' and 'Firefox' 222 | 223 | History 224 | ========== 225 | This project was inpired by [Dan Wahlin's blog](http://weblogs.asp.net/dwahlin/archive/2013/05/22/dynamically-loading-controllers-and-views-with-angularjs-and-requirejs.aspx) 226 | where he explained the core concept of what is needed to make RequireJS works with AngularJS. It is a *must* read 227 | if you wish to better understand implementation detail of `angularAMD`. 228 | 229 | As I started to implement RequireJS in my own project, I got stuck trying to figure out how to load my existing modules without re-writting them. After exhausive search with no satisfactory answer, I posted following question on [StackOverflow](http://stackoverflow.com/questions/19134023/lazy-loading-angularjs-modules-with-requirejs). 230 | [Nikos Paraskevopoulos](http://stackoverflow.com/users/2764255/nikos-paraskevopoulos) was kind enough to share his 231 | solution with me but his implementation did not handle `.config` method calls and out of order definition in modules. However, his implementation gave me the foundation I needed to create `angularAMD` and his project is where the idea for `alt_angular` came from. 232 | 233 | 234 | References 235 | ========== 236 | 237 | * [Dynamically Loading Controllers and Views with AngularJS and RequireJS](http://weblogs.asp.net/dwahlin/archive/2013/05/22/dynamically-loading-controllers-and-views-with-angularjs-and-requirejs.aspx) by Dan Wahlin 238 | * [Dependency Injection using RequireJS & AngularJS](http://solutionoptimist.com/2013/09/30/requirejs-angularjs-dependency-injection/) by Thomas Burleson 239 | * [Lazy loading AngularJS modules with RequireJS](http://stackoverflow.com/questions/19134023/lazy-loading-angularjs-modules-with-requirejs) stackoverflow 240 | * [angular-require-lazy](https://github.com/nikospara/angular-require-lazy) by Nikos Paraskevopoulos 241 | -------------------------------------------------------------------------------- /bin/karma: -------------------------------------------------------------------------------- 1 | ../node_modules/karma/bin/karma -------------------------------------------------------------------------------- /bin/protractor: -------------------------------------------------------------------------------- 1 | ../node_modules/protractor/bin/protractor -------------------------------------------------------------------------------- /bin/webdriver-manager: -------------------------------------------------------------------------------- 1 | ../node_modules/protractor/bin/webdriver-manager -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angularAMD", 3 | "version": "0.2.1", 4 | "dependencies": { 5 | "angular": "^1.3.0", 6 | "requirejs": "~2.1.9" 7 | }, 8 | "devDependencies": { 9 | "angular-route": "^1.3.0", 10 | "angular-animate": "^1.3.0", 11 | "angular-mocks": "^1.3.0", 12 | "angular-ui-bootstrap-bower": "~0.6.0", 13 | "requirejs-plugins": "~1.0.2", 14 | "google-code-prettify": "~1.0.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /dist/angularAMD.js: -------------------------------------------------------------------------------- 1 | /* 2 | angularAMD v<%= cvars.proj_version %> 3 | (c) 2013-2014 Marcos Lin https://github.com/marcoslin/ 4 | License: MIT 5 | */ 6 | 7 | define(function () { 8 | 'use strict'; 9 | var bootstrapped = false, 10 | 11 | // Used in .bootstrap 12 | app_name, 13 | orig_app, 14 | alt_app, 15 | run_injector, 16 | config_injector, 17 | app_cached_providers = {}, 18 | 19 | // Used in setAlternateAngular(), alt_angular is set to become angular.module 20 | orig_angular, 21 | alt_angular, 22 | 23 | // Object that wrap the provider methods that enables lazy loading 24 | onDemandLoader = {}, 25 | preBootstrapLoaderQueue = [], 26 | 27 | // Used in setAlternateAngular() and .processQueue 28 | alternate_modules = {}, 29 | alternate_modules_tracker = {}, 30 | alternate_queue = []; 31 | 32 | // Private method to check if angularAMD has been initialized 33 | function checkBootstrapped() { 34 | if ( !bootstrapped ) { 35 | throw new Error('angularAMD not initialized. Need to call angularAMD.bootstrap(app) first.'); 36 | } 37 | } 38 | 39 | /** 40 | * Create an alternate angular so that subsequent call to angular.module will queue up 41 | * the module created for later processing via the .processQueue method. 42 | * 43 | * This delaying processing is needed as angular does not recognize any newly created 44 | * module after angular.bootstrap has ran. The only way to add new objects to angular 45 | * post bootstrap is using cached provider. 46 | * 47 | * Once the modules has been queued, processQueue would then use each module's _invokeQueue 48 | * and _runBlock to recreate object using cached $provider. In essence, creating a duplicate 49 | * object into the current ng-app. As result, if there are subsequent call to retrieve the 50 | * module post processQueue, it would retrieve a module that is not integrated into the ng-app. 51 | * 52 | * Therefore, any subsequent to call to angular.module after processQueue should return undefined 53 | * to prevent obtaining a duplicated object. However, it is critical that angular.module return 54 | * appropriate object *during* processQueue. 55 | */ 56 | function setAlternateAngular() { 57 | // This method cannot be called more than once 58 | if (alt_angular) { 59 | throw new Error('setAlternateAngular can only be called once.'); 60 | } else { 61 | alt_angular = {}; 62 | } 63 | 64 | // Make sure that bootstrap has been called 65 | checkBootstrapped(); 66 | 67 | // Create a a copy of orig_angular with on demand loading capability 68 | orig_angular.extend(alt_angular, orig_angular); 69 | 70 | // Custom version of angular.module used as cache 71 | alt_angular.module = function (name, requires) { 72 | if (typeof requires === 'undefined') { 73 | // Return module from alternate_modules if it was created using the alt_angular 74 | if (alternate_modules_tracker.hasOwnProperty(name)) { 75 | return alternate_modules[name]; 76 | } else { 77 | return orig_angular.module(name); 78 | } 79 | } else { 80 | var orig_mod = orig_angular.module.apply(null, arguments), 81 | item = { name: name, module: orig_mod}; 82 | alternate_queue.push(item); 83 | orig_angular.extend(orig_mod, onDemandLoader); 84 | 85 | /* 86 | Use `alternate_modules_tracker` to track which module has been created by alt_angular 87 | but use `alternate_modules` to cache the module created. This is to simplify the 88 | removal of cached modules after .processQueue. 89 | */ 90 | alternate_modules_tracker[name] = true; 91 | alternate_modules[name] = orig_mod; 92 | 93 | // Return created module 94 | return orig_mod; 95 | } 96 | }; 97 | 98 | window.angular = alt_angular; 99 | 100 | if (require.defined('angular')) { 101 | require.undef('angular'); 102 | define('angular', [], alt_angular); 103 | } 104 | } 105 | 106 | // Constructor 107 | function AngularAMD() {} 108 | 109 | 110 | /** 111 | * Helper function to generate angular's $routeProvider.route. 'config' input param must be an object. 112 | * 113 | * Populate the resolve attribute using either 'controllerUrl' or 'controller'. If 'controllerUrl' 114 | * is passed, it will attempt to load the Url using requirejs and remove the attribute from the config 115 | * object. Otherwise, it will attempt to populate resolve by loading what's been passed in 'controller'. 116 | * If neither is passed, resolve is not populated. 117 | * 118 | * This function works as a pass-through, meaning what ever is passed in as 'config' will be returned, 119 | * except for 'controllerUrl' attribute. 120 | * 121 | */ 122 | AngularAMD.prototype.route = function (config) { 123 | // Initialization not necessary to call this method. 124 | var load_controller; 125 | 126 | /* 127 | If `controllerUrl` is provided, load the provided Url using requirejs. If `controller` is not provided 128 | but `controllerUrl` is, assume that module to be loaded will return a function to act as controller. 129 | 130 | Otherwise, attempt to load the controller using the controller name. In the later case, controller name 131 | is expected to be defined as one of 'paths' in main.js. 132 | */ 133 | if ( config.hasOwnProperty('controllerUrl') ) { 134 | load_controller = config.controllerUrl; 135 | delete config.controllerUrl; 136 | if (typeof config.controller === 'undefined') { 137 | // Only controllerUrl is defined. Attempt to set the controller to return value of package loaded. 138 | config.controller = [ 139 | '$scope', '__AAMDCtrl', '$injector', 140 | function ($scope, __AAMDCtrl, $injector) { 141 | if (typeof __AAMDCtrl !== 'undefined' ) { 142 | $injector.invoke(__AAMDCtrl, this, { '$scope': $scope }); 143 | } 144 | } 145 | ]; 146 | } 147 | } else if (typeof config.controller === 'string') { 148 | load_controller = config.controller; 149 | } 150 | 151 | // If controller needs to be loaded, append to the resolve property 152 | if (load_controller) { 153 | var resolve = config.resolve || {}; 154 | resolve['__AAMDCtrl'] = ['$q', '$rootScope', function ($q, $rootScope) { // jshint ignore:line 155 | var defer = $q.defer(); 156 | require([load_controller], function (ctrl) { 157 | defer.resolve(ctrl); 158 | $rootScope.$apply(); 159 | }); 160 | return defer.promise; 161 | }]; 162 | config.resolve = resolve; 163 | } 164 | 165 | return config; 166 | }; 167 | 168 | 169 | /** 170 | * Expose name of the app that has been bootstrapped 171 | */ 172 | AngularAMD.prototype.appname = function () { 173 | checkBootstrapped(); 174 | return app_name; 175 | }; 176 | 177 | 178 | /** 179 | * Recreate the modules created by alternate angular in ng-app using cached $provider. 180 | * As AMD loader does not guarantee the order of dependency in a require([...],...) 181 | * clause, user must make sure that dependecies are clearly setup in shim in order 182 | * for this to work. 183 | * 184 | * HACK ALERT: 185 | * This method relay on inner working of angular.module code, and access _invokeQueue 186 | * and _runBlock private variable. Must test carefully with each release of angular. 187 | * 188 | * As of AngularJS 1.3.x, there is new _configBlocks that get populated with configuration 189 | * blocks, thus replacing the need for "provider === '$injector' && method === 'invoke'" 190 | * logic. 191 | */ 192 | AngularAMD.prototype.processQueue = function () { 193 | checkBootstrapped(); 194 | 195 | if (typeof alt_angular === 'undefined') { 196 | throw new Error('Alternate angular not set. Make sure that `enable_ngload` option has been set when calling angularAMD.bootstrap'); 197 | } 198 | 199 | // Process alternate queue in FIFO fashion 200 | function processRunBlock(block) { 201 | //console.info('"' + item.name + '": executing run block: ', run_block); 202 | run_injector.invoke(block); 203 | } 204 | 205 | // Process the config blocks 206 | for (var i=0;i 0) { 455 | for (var iq = 0; iq < preBootstrapLoaderQueue.length; iq += 1) { 456 | var item = preBootstrapLoaderQueue[iq]; 457 | orig_app[item.recipe](item.name, item.constructor); 458 | } 459 | preBootstrapLoaderQueue = []; 460 | } 461 | 462 | // Create a app.register object to keep backward compatibility 463 | orig_app.register = onDemandLoader; 464 | 465 | // Bootstrap Angular 466 | orig_angular.element(document).ready(function () { 467 | orig_angular.bootstrap(elem, [app_name]); 468 | // Indicate bootstrap completed 469 | bootstrapped = true; 470 | 471 | // Replace angular.module 472 | if (enable_ngload) { 473 | //console.info('Setting alternate angular'); 474 | setAlternateAngular(); 475 | } 476 | }); 477 | 478 | // Return app 479 | return alt_app; 480 | }; 481 | 482 | // Define provider 483 | function executeProvider(providerRecipe) { 484 | return function (name, constructor) { 485 | if (bootstrapped) { 486 | onDemandLoader[providerRecipe](name, constructor); 487 | } else { 488 | // Queue up the request to be used during .bootstrap 489 | preBootstrapLoaderQueue.push({ 490 | 'recipe': providerRecipe, 491 | 'name': name, 492 | 'constructor': constructor 493 | }); 494 | } 495 | return this; 496 | }; 497 | } 498 | 499 | // .provider 500 | AngularAMD.prototype.provider = executeProvider('provider'); 501 | // .controller 502 | AngularAMD.prototype.controller = executeProvider('controller'); 503 | // .directive 504 | AngularAMD.prototype.directive = executeProvider('directive'); 505 | // .filter 506 | AngularAMD.prototype.filter = executeProvider('filter'); 507 | // .factory 508 | AngularAMD.prototype.factory = executeProvider('factory'); 509 | // .service 510 | AngularAMD.prototype.service = executeProvider('service'); 511 | // .constant 512 | AngularAMD.prototype.constant = executeProvider('constant'); 513 | // .value 514 | AngularAMD.prototype.value = executeProvider('value'); 515 | // .animation 516 | AngularAMD.prototype.animation = executeProvider('animation'); 517 | // .component 518 | AngularAMD.prototype.component = executeProvider('component'); 519 | 520 | // Create a new instance and return 521 | return new AngularAMD(); 522 | 523 | }); 524 | -------------------------------------------------------------------------------- /dist/angularAMD.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | angularAMD v0.2.1 3 | (c) 2013-2014 Marcos Lin https://github.com/marcoslin/ 4 | License: MIT 5 | */ 6 | 7 | define(function(){"use strict";function a(){if(!l)throw new Error("angularAMD not initialized. Need to call angularAMD.bootstrap(app) first.")}function b(){if(k)throw new Error("setAlternateAngular can only be called once.");k={},a(),j.extend(k,j),k.module=function(a,b){if("undefined"==typeof b)return q.hasOwnProperty(a)?p[a]:j.module(a);var c=j.module.apply(null,arguments),d={name:a,module:c};return r.push(d),j.extend(c,n),q[a]=!0,p[a]=c,c},window.angular=k}function c(){}function d(a){return function(b,c){return l?n[a](b,c):o.push({recipe:a,name:b,constructor:c}),this}}var e,f,g,h,i,j,k,l=!1,m={},n={},o=[],p={},q={},r=[];return c.prototype.route=function(a){var b;if(a.hasOwnProperty("controllerUrl")?(b=a.controllerUrl,delete a.controllerUrl,"undefined"==typeof a.controller&&(a.controller=["$scope","__AAMDCtrl","$injector",function(a,b,c){"undefined"!=typeof b&&c.invoke(b,this,{$scope:a})}])):"string"==typeof a.controller&&(b=a.controller),b){var c=a.resolve||{};c.__AAMDCtrl=["$q","$rootScope",function(a,c){var d=a.defer();return require([b],function(a){d.resolve(a),c.$apply()}),d.promise}],a.resolve=c}return a},c.prototype.appname=function(){return a(),e},c.prototype.processQueue=function(){function b(a){h.invoke(a)}if(a(),"undefined"==typeof k)throw new Error("Alternate angular not set. Make sure that `enable_ngload` option has been set when calling angularAMD.bootstrap");for(;r.length;){var c,d=r.shift(),e=d.module._invokeQueue;for(c=0;c0){for(var k=0;k 3 | (c) 2013-2014 Marcos Lin https://github.com/marcoslin/ 4 | License: MIT 5 | */ 6 | 7 | define(function () { 8 | 'use strict'; 9 | var bootstrapped = false, 10 | 11 | // Used in .bootstrap 12 | app_name, 13 | orig_app, 14 | alt_app, 15 | run_injector, 16 | config_injector, 17 | app_cached_providers = {}, 18 | 19 | // Used in setAlternateAngular(), alt_angular is set to become angular.module 20 | orig_angular, 21 | alt_angular, 22 | 23 | // Object that wrap the provider methods that enables lazy loading 24 | onDemandLoader = {}, 25 | preBootstrapLoaderQueue = [], 26 | 27 | // Used in setAlternateAngular() and .processQueue 28 | alternate_modules = {}, 29 | alternate_modules_tracker = {}, 30 | alternate_queue = []; 31 | 32 | // Private method to check if angularAMD has been initialized 33 | function checkBootstrapped() { 34 | if ( !bootstrapped ) { 35 | throw new Error('angularAMD not initialized. Need to call angularAMD.bootstrap(app) first.'); 36 | } 37 | } 38 | 39 | /** 40 | * Create an alternate angular so that subsequent call to angular.module will queue up 41 | * the module created for later processing via the .processQueue method. 42 | * 43 | * This delaying processing is needed as angular does not recognize any newly created 44 | * module after angular.bootstrap has ran. The only way to add new objects to angular 45 | * post bootstrap is using cached provider. 46 | * 47 | * Once the modules has been queued, processQueue would then use each module's _invokeQueue 48 | * and _runBlock to recreate object using cached $provider. In essence, creating a duplicate 49 | * object into the current ng-app. As result, if there are subsequent call to retrieve the 50 | * module post processQueue, it would retrieve a module that is not integrated into the ng-app. 51 | * 52 | * Therefore, any subsequent to call to angular.module after processQueue should return undefined 53 | * to prevent obtaining a duplicated object. However, it is critical that angular.module return 54 | * appropriate object *during* processQueue. 55 | */ 56 | function setAlternateAngular() { 57 | // This method cannot be called more than once 58 | if (alt_angular) { 59 | throw new Error('setAlternateAngular can only be called once.'); 60 | } else { 61 | alt_angular = {}; 62 | } 63 | 64 | // Make sure that bootstrap has been called 65 | checkBootstrapped(); 66 | 67 | // Create a a copy of orig_angular with on demand loading capability 68 | orig_angular.extend(alt_angular, orig_angular); 69 | 70 | // Custom version of angular.module used as cache 71 | alt_angular.module = function (name, requires) { 72 | if (typeof requires === 'undefined') { 73 | // Return module from alternate_modules if it was created using the alt_angular 74 | if (alternate_modules_tracker.hasOwnProperty(name)) { 75 | return alternate_modules[name]; 76 | } else { 77 | return orig_angular.module(name); 78 | } 79 | } else { 80 | var orig_mod = orig_angular.module.apply(null, arguments), 81 | item = { name: name, module: orig_mod}; 82 | alternate_queue.push(item); 83 | orig_angular.extend(orig_mod, onDemandLoader); 84 | 85 | /* 86 | Use `alternate_modules_tracker` to track which module has been created by alt_angular 87 | but use `alternate_modules` to cache the module created. This is to simplify the 88 | removal of cached modules after .processQueue. 89 | */ 90 | alternate_modules_tracker[name] = true; 91 | alternate_modules[name] = orig_mod; 92 | 93 | // Return created module 94 | return orig_mod; 95 | } 96 | }; 97 | 98 | window.angular = alt_angular; 99 | 100 | if (require.defined('angular')) { 101 | require.undef('angular'); 102 | define('angular', [], alt_angular); 103 | } 104 | } 105 | 106 | // Constructor 107 | function AngularAMD() {} 108 | 109 | 110 | /** 111 | * Helper function to generate angular's $routeProvider.route. 'config' input param must be an object. 112 | * 113 | * Populate the resolve attribute using either 'controllerUrl' or 'controller'. If 'controllerUrl' 114 | * is passed, it will attempt to load the Url using requirejs and remove the attribute from the config 115 | * object. Otherwise, it will attempt to populate resolve by loading what's been passed in 'controller'. 116 | * If neither is passed, resolve is not populated. 117 | * 118 | * This function works as a pass-through, meaning what ever is passed in as 'config' will be returned, 119 | * except for 'controllerUrl' attribute. 120 | * 121 | */ 122 | AngularAMD.prototype.route = function (config) { 123 | // Initialization not necessary to call this method. 124 | var load_controller; 125 | 126 | /* 127 | If `controllerUrl` is provided, load the provided Url using requirejs. If `controller` is not provided 128 | but `controllerUrl` is, assume that module to be loaded will return a function to act as controller. 129 | 130 | Otherwise, attempt to load the controller using the controller name. In the later case, controller name 131 | is expected to be defined as one of 'paths' in main.js. 132 | */ 133 | if ( config.hasOwnProperty('controllerUrl') ) { 134 | load_controller = config.controllerUrl; 135 | delete config.controllerUrl; 136 | if (typeof config.controller === 'undefined') { 137 | // Only controllerUrl is defined. Attempt to set the controller to return value of package loaded. 138 | config.controller = [ 139 | '$scope', '__AAMDCtrl', '$injector', 140 | function ($scope, __AAMDCtrl, $injector) { 141 | if (typeof __AAMDCtrl !== 'undefined' ) { 142 | $injector.invoke(__AAMDCtrl, this, { '$scope': $scope }); 143 | } 144 | } 145 | ]; 146 | } 147 | } else if (typeof config.controller === 'string') { 148 | load_controller = config.controller; 149 | } 150 | 151 | // If controller needs to be loaded, append to the resolve property 152 | if (load_controller) { 153 | var resolve = config.resolve || {}; 154 | resolve['__AAMDCtrl'] = ['$q', '$rootScope', function ($q, $rootScope) { // jshint ignore:line 155 | var defer = $q.defer(); 156 | require([load_controller], function (ctrl) { 157 | defer.resolve(ctrl); 158 | $rootScope.$apply(); 159 | }); 160 | return defer.promise; 161 | }]; 162 | config.resolve = resolve; 163 | } 164 | 165 | return config; 166 | }; 167 | 168 | 169 | /** 170 | * Expose name of the app that has been bootstrapped 171 | */ 172 | AngularAMD.prototype.appname = function () { 173 | checkBootstrapped(); 174 | return app_name; 175 | }; 176 | 177 | 178 | /** 179 | * Recreate the modules created by alternate angular in ng-app using cached $provider. 180 | * As AMD loader does not guarantee the order of dependency in a require([...],...) 181 | * clause, user must make sure that dependecies are clearly setup in shim in order 182 | * for this to work. 183 | * 184 | * HACK ALERT: 185 | * This method relay on inner working of angular.module code, and access _invokeQueue 186 | * and _runBlock private variable. Must test carefully with each release of angular. 187 | * 188 | * As of AngularJS 1.3.x, there is new _configBlocks that get populated with configuration 189 | * blocks, thus replacing the need for "provider === '$injector' && method === 'invoke'" 190 | * logic. 191 | */ 192 | AngularAMD.prototype.processQueue = function () { 193 | checkBootstrapped(); 194 | 195 | if (typeof alt_angular === 'undefined') { 196 | throw new Error('Alternate angular not set. Make sure that `enable_ngload` option has been set when calling angularAMD.bootstrap'); 197 | } 198 | 199 | // Process alternate queue in FIFO fashion 200 | function processRunBlock(block) { 201 | //console.info('"' + item.name + '": executing run block: ', run_block); 202 | run_injector.invoke(block); 203 | } 204 | 205 | // Process the config blocks 206 | for (var i=0;i 0) { 455 | for (var iq = 0; iq < preBootstrapLoaderQueue.length; iq += 1) { 456 | var item = preBootstrapLoaderQueue[iq]; 457 | orig_app[item.recipe](item.name, item.constructor); 458 | } 459 | preBootstrapLoaderQueue = []; 460 | } 461 | 462 | // Create a app.register object to keep backward compatibility 463 | orig_app.register = onDemandLoader; 464 | 465 | // Bootstrap Angular 466 | orig_angular.element(document).ready(function () { 467 | orig_angular.bootstrap(elem, [app_name]); 468 | // Indicate bootstrap completed 469 | bootstrapped = true; 470 | 471 | // Replace angular.module 472 | if (enable_ngload) { 473 | //console.info('Setting alternate angular'); 474 | setAlternateAngular(); 475 | } 476 | }); 477 | 478 | // Return app 479 | return alt_app; 480 | }; 481 | 482 | // Define provider 483 | function executeProvider(providerRecipe) { 484 | return function (name, constructor) { 485 | if (bootstrapped) { 486 | onDemandLoader[providerRecipe](name, constructor); 487 | } else { 488 | // Queue up the request to be used during .bootstrap 489 | preBootstrapLoaderQueue.push({ 490 | 'recipe': providerRecipe, 491 | 'name': name, 492 | 'constructor': constructor 493 | }); 494 | } 495 | return this; 496 | }; 497 | } 498 | 499 | // .provider 500 | AngularAMD.prototype.provider = executeProvider('provider'); 501 | // .controller 502 | AngularAMD.prototype.controller = executeProvider('controller'); 503 | // .directive 504 | AngularAMD.prototype.directive = executeProvider('directive'); 505 | // .filter 506 | AngularAMD.prototype.filter = executeProvider('filter'); 507 | // .factory 508 | AngularAMD.prototype.factory = executeProvider('factory'); 509 | // .service 510 | AngularAMD.prototype.service = executeProvider('service'); 511 | // .constant 512 | AngularAMD.prototype.constant = executeProvider('constant'); 513 | // .value 514 | AngularAMD.prototype.value = executeProvider('value'); 515 | // .animation 516 | AngularAMD.prototype.animation = executeProvider('animation'); 517 | // .component 518 | AngularAMD.prototype.component = executeProvider('component'); 519 | 520 | // Create a new instance and return 521 | return new AngularAMD(); 522 | 523 | }); 524 | -------------------------------------------------------------------------------- /src/banner.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | angularAMD v<%= cvars.proj_version %> 3 | (c) 2013-2014 Marcos Lin https://github.com/marcoslin/ 4 | License: MIT 5 | */ 6 | -------------------------------------------------------------------------------- /src/jshintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "nonew": true, 3 | "plusplus": true, 4 | "curly": true, 5 | "noarg": true, 6 | "trailing": true, 7 | "forin": true, 8 | "noempty": true, 9 | "eqeqeq": true, 10 | "undef": true, 11 | "bitwise": true, 12 | "browser": true, 13 | "node": true, 14 | "globals": { 15 | /* AngularJS */ 16 | "angular": false, 17 | /* RequireJS */ 18 | "define": false, 19 | "require": false, 20 | /* Jasmine */ 21 | "describe" : false, 22 | "it" : false, 23 | "before" : false, 24 | "beforeEach" : false, 25 | "after" : false, 26 | "afterEach" : false, 27 | "expect" : false 28 | } 29 | } -------------------------------------------------------------------------------- /src/ngload.js: -------------------------------------------------------------------------------- 1 | /* 2 | angularAMD v<%= cvars.proj_version %> 3 | (c) 2013-2014 Marcos Lin https://github.com/marcoslin/ 4 | License: MIT 5 | */ 6 | 7 | define({ 8 | load: function (name, req, onload) { 9 | 'use strict'; 10 | //console.log("ngamd loaded: ", req.toUrl(name)); 11 | req(['angularAMD', name], function (angularAMD, value) { 12 | //console.log("Processing queues."); 13 | angularAMD.processQueue(); 14 | onload(value); 15 | }); 16 | } 17 | }); 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/version.txt: -------------------------------------------------------------------------------- 1 | 0.2.1 -------------------------------------------------------------------------------- /test/conf/karma.unit.mustache: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // http://karma-runner.github.io/0.10/config/configuration-file.html 3 | /*jslint node: true */ 4 | 5 | module.exports = function (config) { 6 | config.set({ 7 | // base path, note that this is expected to be ran under build directory 8 | basePath: '../../..', 9 | 10 | // testing framework to use (jasmine/mocha/qunit/...) 11 | frameworks: ['jasmine', 'requirejs'], 12 | 13 | // list of files / patterns to load in the browser 14 | files: [ 15 | {pattern: 'bower_components/**/*.js', watched: false, included: false}, 16 | {pattern: 'build/*.js', watched: true, included: false}, 17 | {pattern: 'src/*.js', watched: true, included: false}, 18 | {pattern: 'test/unit/*.spec.js', watched: true, included: false}, 19 | {pattern: 'test/unit/factory/*.js', watched: true, included: false}, 20 | {pattern: 'test/unit/lib/*.js', watched: true, included: false}, 21 | 'bower_components/requirejs/require.js', 22 | 'node_modules/phantomjs-polyfill/bind-polyfill.js', 23 | '{{{main-js-file}}}' 24 | ], 25 | 26 | // files: ['test/conf/testSpec.js'], 27 | 28 | // list of files / patterns to exclude 29 | exclude: [], 30 | 31 | // web server port 32 | port: 9898, 33 | 34 | // level of logging 35 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 36 | logLevel: config.LOG_INFO, 37 | 38 | 39 | // enable / disable watching file and executing tests whenever any file changes 40 | autoWatch: true, 41 | 42 | 43 | // Start these browsers, currently available: 44 | // - Chrome 45 | // - ChromeCanary 46 | // - Firefox 47 | // - Opera 48 | // - Safari (only Mac) 49 | // - PhantomJS 50 | // - IE (only Windows) 51 | browsers: ['PhantomJS'], 52 | 53 | 54 | // Continuous Integration mode 55 | // if true, it capture browsers, run tests and exit 56 | singleRun: true 57 | }); 58 | }; 59 | -------------------------------------------------------------------------------- /test/conf/karma.unit.no_ngload.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // http://karma-runner.github.io/0.10/config/configuration-file.html 3 | /*jslint node: true */ 4 | 5 | module.exports = function (config) { 6 | config.set({ 7 | // base path, note that this is expected to be ran under build directory 8 | basePath: '../..', 9 | 10 | // testing framework to use (jasmine/mocha/qunit/...) 11 | frameworks: ['jasmine', 'requirejs'], 12 | 13 | // list of files / patterns to load in the browser 14 | files: [ 15 | {pattern: 'bower_components/angular/*.js', watched: false, included: false}, 16 | {pattern: 'src/*.js', watched: true, included: false}, 17 | {pattern: 'test/unit/app_no_ngload.spec.js', watched: true, included: false}, 18 | {pattern: 'test/unit/lib/app_no_ngload.js', watched: true, included: false}, 19 | 'bower_components/requirejs/require.js', 20 | 'node_modules/phantomjs-polyfill/bind-polyfill.js', 21 | 'test/unit/lib/main.no_ngload.js' 22 | ], 23 | 24 | // files: ['test/conf/testSpec.js'], 25 | 26 | // list of files / patterns to exclude 27 | exclude: [], 28 | 29 | // web server port 30 | port: 9898, 31 | 32 | // level of logging 33 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 34 | logLevel: config.LOG_INFO, 35 | 36 | 37 | // enable / disable watching file and executing tests whenever any file changes 38 | autoWatch: true, 39 | 40 | 41 | // Start these browsers, currently available: 42 | // - Chrome 43 | // - ChromeCanary 44 | // - Firefox 45 | // - Opera 46 | // - Safari (only Mac) 47 | // - PhantomJS 48 | // - IE (only Windows) 49 | browsers: ['PhantomJS'], 50 | 51 | 52 | // Continuous Integration mode 53 | // if true, it capture browsers, run tests and exit 54 | singleRun: true 55 | }); 56 | }; 57 | -------------------------------------------------------------------------------- /test/conf/protractor.e2e.js: -------------------------------------------------------------------------------- 1 | // An example configuration file. 2 | exports.config = { 3 | // Remove definition for `seleniumAddress` so that selenium is launched by protractor. 4 | // seleniumAddress: 'http://localhost:4444/wd/hub', 5 | 6 | //Capabilities to be passed to the webdriver instance. 7 | capabilities: { 8 | 'browserName': 'phantomjs', 9 | 'phantomjs.binary.path': './node_modules/karma-phantomjs-launcher/node_modules/phantomjs/bin/phantomjs' 10 | }, 11 | 12 | //baseUrl: 'http://localhost:8444', 13 | 14 | // Spec patterns are relative to the current working directly when 15 | // protractor is called. 16 | specs: ['../e2e/*.spec.js'], 17 | 18 | // Options to be passed to Jasmine-node. 19 | jasmineNodeOpts: { 20 | showColors: true, 21 | defaultTimeoutInterval: 30000 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /test/e2e/www.spec.js: -------------------------------------------------------------------------------- 1 | var url = require("url"); 2 | 3 | describe('angularAMD', function() { 4 | var ptor = protractor.getInstance(), 5 | default_wait_ms = 15000; 6 | 7 | /** 8 | * Function used to in place of `ptor.get` as the native version will not wait for manual bootstrapping. 9 | * It adds an 0.5 sec wait before checking that url has been correctly set. 10 | */ 11 | function ptor_get(rel_path) { 12 | ptor.driver.get(url.resolve(ptor.baseUrl, rel_path)); 13 | ptor.wait(function () { 14 | return ptor.driver.getCurrentUrl().then(function(in_url) { 15 | var re = new RegExp(rel_path, "i"); 16 | return re.test(in_url); 17 | }); 18 | }, default_wait_ms, "Taking too long to load " + rel_path); 19 | ptor.sleep(1000); 20 | } 21 | 22 | function ptor_waitForElementById(nameId) { 23 | ptor.wait(function () { 24 | //console.log("Checking if " + nameId + " exists"); 25 | return ptor.driver.isElementPresent(by.id(nameId)).then(function (is_present) { 26 | return is_present; 27 | }); 28 | }, default_wait_ms, "Taking too long waiting for element id '" + nameId + "' to be present."); 29 | return element(by.id(nameId)); 30 | } 31 | 32 | describe("home", function () { 33 | it('tab should be active', function() { 34 | ptor_get('#/home'); 35 | var navElem = ptor_waitForElementById("nav-home"); 36 | expect(navElem.getAttribute("class")).toBe("active"); 37 | expect(ptor.getCurrentUrl()).toContain('#/home'); 38 | }); 39 | 40 | it("View on GitHub button should exists", function () { 41 | expect($("#view-on-github").getAttribute('class')).toBe("btn btn-large"); 42 | }); 43 | 44 | it("View on GitHub button should exists", function () { 45 | expect($("#view-on-github").getAttribute('class')).toBe("btn btn-large"); 46 | expect($("#view-on-github i").getAttribute('class')).toBe("icon-github-sign"); 47 | }); 48 | }); 49 | 50 | describe("map", function () { 51 | // As map tab takes a bit longer to become active, probably due to the work to load google map 52 | it("tab should be active", function () { 53 | ptor_get('#/map'); 54 | var navElem = ptor_waitForElementById("nav-map"); 55 | ptor.wait(function () { 56 | return navElem.getAttribute("class").then(function (class_value) { 57 | return class_value == "active"; 58 | }); 59 | }, default_wait_ms, "Taking too long for map tab to become active"); 60 | }); 61 | 62 | // As map takes a bit to load, give it up to 5 secs for it to load 63 | it("should be loaded", function () { 64 | ptor_waitForElementById("map-canvas"); 65 | expect($('#map-canvas .gm-style').getAttribute("style")).toBeDefined(); 66 | }); 67 | }) 68 | 69 | describe("module", function () { 70 | it("modules tab should be active", function () { 71 | ptor_get('#/modules'); 72 | var navElem = ptor_waitForElementById("nav-modules"); 73 | ptor.wait(function () { 74 | return navElem.getAttribute("class").then(function (class_value) { 75 | return class_value == "active"; 76 | }); 77 | }, default_wait_ms, "Taking too long for map tab to become active"); 78 | }); 79 | 80 | it("ng-write to output correct value", function () { 81 | expect($("#output-ng-write").getText()).toBe("Output from Directive"); 82 | }); 83 | 84 | it("DeferredString to output correct value", function () { 85 | expect($("#output-deferred-string").getText()).toBe("Show case ngWrite with promise"); 86 | }); 87 | 88 | it("DeferredObject to output correct value", function () { 89 | expect($("#output-deferred-object").getText()).toBe("This is defered response"); 90 | }); 91 | 92 | it(".run to output correct value", function () { 93 | expect($("#output-run").getText()).toBe("Greetings from .run"); 94 | }); 95 | 96 | it(".config to output correct value", function () { 97 | expect($("#output-config").getText()).toBe("And config works"); 98 | }); 99 | 100 | 101 | }); 102 | 103 | // This has to be the last as ignoreSynchronization seems to impact remaining tests 104 | describe("pictures", function () { 105 | it("tab should be active", function () { 106 | ptor_get('#/pictures'); 107 | var navElem = ptor_waitForElementById("nav-pictures"); 108 | ptor.wait(function () { 109 | return navElem.getAttribute("class").then(function (class_value) { 110 | return class_value == "active"; 111 | }); 112 | }, default_wait_ms, "Taking too long for pictures tab to become active"); 113 | }); 114 | 115 | // Ignoring sync due to use of $timer in ui-bootstrap to change pictures 116 | describe("ignore sync", function () { 117 | beforeEach(function () { 118 | ptor.ignoreSynchronization = true; 119 | }); 120 | afterEach(function () { 121 | ptor.ignoreSynchronization = false; 122 | }); 123 | 124 | it("london clicked", function () { 125 | var btn_london = $('#btn-london'), 126 | pictures = element.all(by.repeater('row in rows')), 127 | pictures_count = 0; 128 | 129 | btn_london.click().then(function () { 130 | expect(btn_london.getAttribute("class")).toMatch('active'); 131 | 132 | // Get the pictures_count using repeater 133 | ptor.wait(function () { 134 | return pictures.count().then( function (row_count) { 135 | if (row_count > 0) { 136 | pictures_count = row_count; 137 | return true; 138 | } else { 139 | return false; 140 | } 141 | }); 142 | }, default_wait_ms, "Taking too long to load pictures"); 143 | 144 | // Make sure that number of `slide_image` is the same as pictures_count 145 | var slideimages = element.all(by.css('.slide-image')); 146 | slideimages.count().then(function (row_count) { 147 | expect(row_count).toBe(pictures_count); 148 | }) 149 | }); 150 | }); 151 | 152 | it("rome clicked", function () { 153 | var btn_rome = $('#btn-rome'); 154 | btn_rome.click().then(function () { 155 | expect(btn_rome.getAttribute("class")).toMatch('active'); 156 | }); 157 | }); 158 | 159 | }); 160 | }) 161 | 162 | }); 163 | -------------------------------------------------------------------------------- /test/unit/app.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Test focusing on setup of ngAMD making sure that cached provider are defined. 3 | */ 4 | define(['app','angularAMD', 'test/unit/factory/utestProvider'], function (app, angularAMD, utestProvider) { 5 | console.log("* Running app.spec.js"); 6 | 7 | describe('angularAMD', function () { 8 | 9 | describe("basics", function () { 10 | it('should be defined.', function () { 11 | expect(angularAMD).toBeDefined(); 12 | }); 13 | it('app should be defined.', function () { 14 | expect(app.name).toBe(angularAMD.appname()); 15 | }); 16 | it('app.__origAngular should be defined.', function () { 17 | var orig_angular = angularAMD.getCachedProvider('__orig_angular'); 18 | expect(app.__origAngular).toBeDefined(); 19 | expect(app.__origAngular).toBe(orig_angular); 20 | }); 21 | it('app.__preServiceResult should be defined.', function () { 22 | expect(app.__preServiceResult).toBeDefined(); 23 | }); 24 | it('alternate angular should be defined.', function () { 25 | var alt_angular = angularAMD.getCachedProvider('__alt_angular'); 26 | expect(alt_angular).toBeDefined(); 27 | expect(window.angular).toBe(alt_angular); 28 | }); 29 | }); 30 | 31 | describe('cached property', function () { 32 | it('controllerProvider', function () { 33 | expect(angularAMD.getCachedProvider("$controllerProvider")).toBeDefined(); 34 | }); 35 | it('compileProvider', function () { 36 | expect(angularAMD.getCachedProvider("$compileProvider")).toBeDefined(); 37 | }); 38 | it('filterProvider', function () { 39 | expect(angularAMD.getCachedProvider("$filterProvider")).toBeDefined(); 40 | }); 41 | it('animateProvider', function () { 42 | expect(angularAMD.getCachedProvider("$animateProvider")).toBeDefined(); 43 | }); 44 | it('provide', function () { 45 | expect(angularAMD.getCachedProvider("$provide")).toBeDefined(); 46 | }); 47 | it('injector', function () { 48 | expect(angularAMD.getCachedProvider("$injector")).toBeDefined(); 49 | }); 50 | it('orig_angular', function () { 51 | // Access the 'hidden' provider created for unit test purpose 52 | expect(angularAMD.getCachedProvider("__orig_angular")).toBe(app.__origAngular); 53 | }); 54 | }); 55 | 56 | describe('.router', function () { 57 | 58 | it('should be simple pass-through', function () { 59 | var utestConfig = { one: "m45awKLtbM", two: "IZh0o0almb", make: "FB7T7WefnD" }; 60 | expect(angularAMD.route(utestConfig)).toBe(utestConfig); 61 | }); 62 | 63 | describe('with controllerURL param:', function () { 64 | var tabName = "6oO33kWCB2", r; 65 | beforeEach(function () { 66 | r = angularAMD.route({ controllerUrl: 'test/unit/lib/controller', navtab: tabName }); 67 | }); 68 | 69 | it('navtab should be defined.', function () { 70 | expect(r.navtab).toBe(tabName); 71 | }); 72 | 73 | it('controllerUrl should have been deleted.', function () { 74 | expect(r.controllerUrl).toBeUndefined(); 75 | }); 76 | 77 | it('resolve should have been populated.', function () { 78 | expect(r.resolve["__AAMDCtrl"]).toBeDefined(); //jshint ignore:line 79 | }); 80 | }); 81 | 82 | describe('with controller param: ', function () { 83 | var tabName = "ioOc7ZIofT", r; 84 | beforeEach(function () { 85 | r = angularAMD.route({ controller: 'controller' }); 86 | }); 87 | 88 | it('controller should be defined.', function () { 89 | expect(r.controller).toBeDefined(); 90 | }); 91 | 92 | it('resolve should have been populated.', function () { 93 | expect(r.resolve["__AAMDCtrl"]).toBeDefined(); //jshint ignore:line 94 | }); 95 | }); 96 | }); 97 | 98 | // Perform test to ensure preService loaded before bootstrap works. 99 | utestProvider(app.__preServiceResult); 100 | 101 | }); 102 | 103 | }); 104 | -------------------------------------------------------------------------------- /test/unit/app_no_ngload.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Test focusing on making sure that window.angular has not been modified and 3 | * that .processQueue raises error when enable_ngload is set to false in 4 | * .bootstrap 5 | */ 6 | define(['app_no_ngload','angularAMD'], function (app, angularAMD) { 7 | console.log("* Running app_no_ngload.spec.js"); 8 | describe('angularAMD_no_ngload', function () { 9 | //console.log("Running app_no_ngload.spec.js"); 10 | 11 | it('is created.', function () { 12 | expect(angularAMD).toBeDefined(); 13 | }); 14 | it('app is defined.', function () { 15 | expect(app.name).toBe(angularAMD.appname()); 16 | }); 17 | it('original angular should defined.', function () { 18 | var orig_angular = angularAMD.getCachedProvider('__orig_angular'); 19 | expect(orig_angular).toBeDefined(); 20 | expect(window.angular).toBe(orig_angular); 21 | }); 22 | it('alternate angular should not be set.', function () { 23 | expect(angularAMD.getCachedProvider('__alt_angular')).toBeUndefined(); 24 | }); 25 | 26 | it('.processQueue should throw an error.', function () { 27 | expect(angularAMD.processQueue).toThrow(); 28 | }); 29 | 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/unit/controller.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Testing declaration of controller following require.js spec and make sure 3 | * it's dependecies are loaded. 4 | */ 5 | define(['app', 'controller', 'angularAMD'], function (app, controller, angularAMD) { 6 | console.log("* Running controller.spec.js"); 7 | describe('Utest Controller', function () { 8 | //console.log("Running controllerSpec.js"); 9 | var ctrl_name = controller.ctrl_name, 10 | service_results = controller.result, 11 | scope, ctrl; 12 | 13 | angularAMD.inject(function ($rootScope, $controller) { 14 | scope = $rootScope.$new(); 15 | ctrl = $controller(ctrl_name, { $scope: scope }); 16 | }); 17 | 18 | it("service_results should exists.", function () { 19 | expect(service_results).toBeDefined(); 20 | }); 21 | 22 | it("scope.ctrl_name check", function () { 23 | expect(scope.ctrl_name).toBe(ctrl_name); 24 | }); 25 | 26 | it("scope.utest_factory check", function () { 27 | var f = scope.utest_factory; 28 | expect(f.name).toBe(service_results.factory_name); 29 | expect(f.const_name).toBe(service_results.constant_name); 30 | }); 31 | 32 | it("scope.utest_service check", function () { 33 | var s = scope.utest_service; 34 | expect(s.name).toBe(service_results.service_name); 35 | expect(s.val_name).toBe(service_results.value_name); 36 | }); 37 | 38 | it("scope.subModule check", function () { 39 | // console.log("scope.subModule check", service_results.sub_module); 40 | expect(scope.utest_sub_module.get()).toBe(service_results.sub_module); 41 | }); 42 | 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /test/unit/controllerFn.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Testing declaration of controller following require.js spec and make sure 3 | * it's dependecies are loaded. 4 | */ 5 | define(['angularAMD'], function (angularAMD) { 6 | console.log("* Running controllerFn.spec.js"); 7 | describe('Utest Controller', function () { 8 | //console.log("Running controllerSpec.js"); 9 | var route, location, rootScope; 10 | 11 | angularAMD.inject(function ($route, $location, $rootScope) { 12 | route = $route; 13 | location = $location; 14 | rootScope = $rootScope; 15 | }); 16 | 17 | it("route should be undefined.", function () { 18 | expect(route.current).toBeUndefined(); 19 | }); 20 | 21 | it("controller should load for /controllerFn route.", function () { 22 | location.path('/controllerFn'); 23 | rootScope.$digest(); 24 | 25 | // Make sure that route has changed 26 | expect(route.current.utestParam).toBe('controllerFn'); 27 | 28 | // Controller should be defined 29 | expect(route.current.controller).toBeDefined(); 30 | 31 | // Make sure that controller has been set to custom function 32 | expect(route.current.controller.toString()).toMatch(/^\$scope,__AAMDCtrl,\$injector,function/); 33 | }); 34 | 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/unit/decoServices.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Test to ensure that services can be created using app.register method. 3 | */ 4 | define(['app', 'decoServices', 'test/unit/factory/utestProvider' ], function (app, decoServices, unitestFactory) { 5 | console.log("* Running decoServices.spec.js"); 6 | unitestFactory(decoServices); 7 | }); 8 | -------------------------------------------------------------------------------- /test/unit/factory/module.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A standard set of code used to simulate creation of independent modules, leveraging `provider` factory. 3 | * Basically, it will do everything `provider` factory do and setup .run and .config blocks. 4 | */ 5 | define(["test/unit/factory/provider"], function (providerFactory) { 6 | /** 7 | * A function that will create different AngularJS provider for the given module. 8 | * All module name created will has a suffix 9 | */ 10 | return function (module, suffix) { 11 | var result = providerFactory(module, suffix); 12 | 13 | /* 14 | Use the .config block to set the test provider's value 15 | Use the .run block to set value for $rootScope.run_name 16 | */ 17 | result.config_name = "module.config YzmACpEVW9" + suffix; 18 | result.run_name = "module.run YzmACpEVW9" + suffix; 19 | module 20 | .config([result.UtestStoreProvider, function (UtestStoreProvider) { 21 | UtestStoreProvider.configureValue(result.config_name); 22 | }]) 23 | .run(function ($rootScope) { 24 | $rootScope.run_name = result.run_name; 25 | }); 26 | 27 | /* 28 | Return result 29 | */ 30 | return result; 31 | }; 32 | 33 | }); 34 | 35 | -------------------------------------------------------------------------------- /test/unit/factory/provider.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A standard set of code used to create different AngularJS provider. They considerations: 3 | * 1. Out of sequence creation: .value and .constant used by .factory and .service are created later 4 | * 5 | * `module` object is used to create provider and `suffix` is used as part of the name of provider to 6 | * make it unique. A `result` object is returned with name of provider created and the expected value 7 | * associated with that provider. 8 | */ 9 | define(function () { 10 | /** 11 | * A function that will create different AngularJS provider for the given module. 12 | * All module name created will has a suffix 13 | */ 14 | return function (module, suffix) { 15 | var result = { suffix: suffix }; 16 | 17 | /* 18 | Create Test Provider to store configuration value and a associated .factory 19 | to return the value stored in the .config block. 20 | 21 | Also test chained call 22 | */ 23 | result.UtestStore = "UtestStore" + suffix; 24 | result.UtestStoreProvider = "UtestStore" + suffix + "Provider"; 25 | result.UtestStoreResult = 'UtestStoreResult' + suffix; 26 | module 27 | .provider(result.UtestStore, function () { 28 | var config_value; 29 | 30 | this.configureValue = function(value) { 31 | config_value = value; 32 | }; 33 | 34 | this.$get = function () { 35 | return { 36 | getValue: function () { 37 | return config_value; 38 | } 39 | }; 40 | }; 41 | }) 42 | .factory(result.UtestStoreResult, [result.UtestStore, function (UtestStore) { 43 | return UtestStore.getValue(); 44 | }]); 45 | 46 | /* 47 | Create a .factory using the UtestConstant declared later 48 | */ 49 | result.UtestConstant = "UtestConstant" + suffix; 50 | result.UtestFactory = "UtestFactory" + suffix; 51 | result.factory_name = "UtestFactory.name nSg56eHrWo" + suffix; 52 | module.factory(result.UtestFactory, [result.UtestConstant, function (UtestConstant) { 53 | // Make sure that constant_name is setup after this factory. 54 | return { name: result.factory_name, const_name: UtestConstant }; 55 | }]); 56 | 57 | /* 58 | Create a .service using the UtestValue declared later 59 | */ 60 | result.UtestValue = "UtestValue" + suffix; 61 | result.UtestService = "UtestService" + suffix; 62 | result.service_name = "UtestService.name ySg56eHrWo" + suffix; 63 | module.service(result.UtestService, ["UtestValue" + suffix, function (UtestValue) { 64 | // Make sure that value_name is defined after this service 65 | this.name = result.service_name; 66 | this.val_name = UtestValue; 67 | }]); 68 | 69 | /* 70 | Define out of sequence constant and value, used by .factory and .service earlier 71 | 72 | Also test chained call 73 | */ 74 | result.constant_name = "utestmod.constant_name xHf71eVzxd" + suffix; 75 | result.value_name = "utestmod.value_name ih3zRvZofo" + suffix; 76 | module 77 | .constant(result.UtestConstant, result.constant_name) 78 | .value(result.UtestValue, result.value_name); 79 | 80 | /* 81 | Create .directive. The name of directive is different as for consumer, it has to 82 | be converted to - based from the Camel case. 83 | */ 84 | result.utestDirective = "utest-directive-" + suffix.toLowerCase(); 85 | result.directive_name = "utestmod.directive_name Krloe7G1CH" + suffix; 86 | module.directive('utestDirective' + suffix, function () { 87 | return { 88 | restrict: 'A', 89 | link: function (scope, elm, attr) { 90 | elm.text(result.directive_name); 91 | } 92 | }; 93 | }); 94 | 95 | /* 96 | Create .filter 97 | */ 98 | result.utestFilter = 'utestFilter' + suffix; 99 | result.filter_name = "utestmod.filter_name 0WWb0usFCB" + suffix; 100 | module.filter(result.utestFilter, function () { 101 | return function (input) { 102 | return input + " " + result.filter_name; 103 | }; 104 | }); 105 | 106 | 107 | /* 108 | Create .animation 109 | For some reason, addClass and removeClass no longer works in the unit test 110 | in AngularJS 13.0.x (tested under 1.3.0-beta.17). As the intent of this 111 | test is to ensure that animation is loaded, changed to before* version. 112 | 113 | Created following plunker to ensure that addClass/removeClass works inder 1.3.x 114 | http://plnkr.co/edit/VsWfqqbAnqrr7cE3toYn 115 | */ 116 | result.utestAnimation = ".animation-" + suffix.toLowerCase(); 117 | module.animation(result.utestAnimation, function ($log, $interval) { 118 | return { 119 | beforeAddClass : function(element, className, done) { 120 | if ( className === "custom-hide") { 121 | element.css('opacity',0); 122 | done(); 123 | } 124 | }, 125 | beforeRemoveClass : function(element, className, done) { 126 | if ( className === "custom-hide") { 127 | element.css('opacity',1); 128 | done(); 129 | } 130 | } 131 | }; 132 | }); 133 | 134 | /* 135 | Return result 136 | */ 137 | return result; 138 | }; 139 | 140 | }); 141 | 142 | -------------------------------------------------------------------------------- /test/unit/factory/utestModule.js: -------------------------------------------------------------------------------- 1 | define(['angularAMD', 'test/unit/factory/utestProvider'], function (angularAMD, utestProvider) { 2 | 3 | return function (result) { 4 | var inject = angularAMD.inject; 5 | 6 | utestProvider(result); 7 | 8 | /* 9 | There is a problem with having a describe outside of utestProvider 10 | If there is an error, it will always use the title of the last 11 | test in the `utestProvider` 12 | */ 13 | describe("Utest " + result.suffix + " Modules", function () { 14 | it(".config check.", inject([result.UtestStoreResult, function (UtestStoreResult) { 15 | if (UtestStoreResult === "undefined") { 16 | console.log("### ERROR on " + result.suffix + ": ", result.config_name); 17 | } 18 | expect(UtestStoreResult).toBe(result.config_name); 19 | }])); 20 | 21 | it(".run check.", inject(function ($rootScope) { 22 | expect($rootScope.run_name).toBe(result.run_name); 23 | })); 24 | }); 25 | 26 | 27 | 28 | }; 29 | 30 | }); -------------------------------------------------------------------------------- /test/unit/factory/utestProvider.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Companion test for providerFactory. 3 | */ 4 | define(['angularAMD', 'angular-mocks', 'component'], function (angularAMD) { 5 | var inject = angularAMD.inject; 6 | 7 | return function (result) { 8 | describe("Utest " + result.suffix + " Services", function () { 9 | 10 | it(".constant check.", function () { 11 | inject([result.UtestConstant, function (UtestConstant) { 12 | expect(UtestConstant).toBe(result.constant_name); 13 | }]); 14 | }); 15 | 16 | it(".factory check.", function () { 17 | inject([result.UtestFactory, function (UtestFactory) { 18 | // Testing for out of order dependency. constant_name should be defined after UtestRegFactory 19 | expect(UtestFactory.name).toBe(result.factory_name); 20 | expect(UtestFactory.const_name).toBe(result.constant_name); 21 | }]); 22 | }); 23 | 24 | it(".value check.", function () { 25 | inject([result.UtestValue, function (UtestValue) { 26 | expect(UtestValue).toBe(result.value_name); 27 | }]); 28 | }); 29 | 30 | it(".service check.", function () { 31 | inject([result.UtestService, function (UtestService) { 32 | // Testing for out of order dependency. constant_name should be defined after UtestRegFactory 33 | expect(UtestService.name).toBe(result.service_name); 34 | expect(UtestService.val_name).toBe(result.value_name); 35 | }]); 36 | }); 37 | 38 | it(".directive check.", function () { 39 | inject(function ($rootScope, $compile) { 40 | var scope = $rootScope.$new(), 41 | elm = angular.element("
"); 42 | $compile(elm)(scope); 43 | expect(elm.text()).toBe(result.directive_name); 44 | }); 45 | }); 46 | 47 | it(".filter check.", function () { 48 | inject(function ($filter) { 49 | var ufilter = $filter(result.utestFilter); 50 | expect(ufilter).toBeDefined(); 51 | expect(ufilter("hello")).toBe("hello " + result.filter_name); 52 | }); 53 | }); 54 | }); 55 | 56 | describe("Utest " + result.suffix + " Animation", function () { 57 | var scope, animate, elem; 58 | 59 | beforeEach(function () { 60 | module("ngAnimateMock"); 61 | inject(function ($rootScope, $compile, $rootElement, $animate) { 62 | var html_text = "
"; 63 | scope = $rootScope; 64 | animate = $animate; 65 | elem = $compile(html_text)(scope); 66 | $rootElement.append(elem); 67 | }); 68 | }); 69 | 70 | //TODO: fix animate test 71 | it(".animation check.", function () { 72 | animate.addClass(elem, "custom-hide"); 73 | scope.$digest(); 74 | expect(elem.css("opacity")).toBe("");//.toBe("0"); 75 | 76 | animate.removeClass(elem, "custom-hide"); 77 | scope.$digest(); 78 | expect(elem.css("opacity")).toBe("");//.toBe("1"); 79 | }); 80 | }); 81 | 82 | describe("UTest " + result.suffix + " Component", function() { 83 | var element, scope; 84 | beforeEach(function() { 85 | inject(function($rootScope, $compile) { 86 | scope = $rootScope.$new(); 87 | element = angular.element(''); 88 | element = $compile(element)(scope); 89 | scope.prop = 'loaded'; 90 | scope.$digest(); 91 | }) 92 | }); 93 | 94 | it(".component check.", function() { 95 | var h3 = element.find('h3'); 96 | expect(h3.text()).toBe('Component Title loaded'); 97 | }); 98 | }) 99 | }; 100 | }); -------------------------------------------------------------------------------- /test/unit/lib/app.js: -------------------------------------------------------------------------------- 1 | /*jslint browser: true, devel: true, node: true, vars: true, nomen: true */ 2 | /*globals define, angular */ 3 | 4 | define(['angularAMD', 'test/unit/lib/preService', 'angular-route', 'angular-animate'], function (angularAMD, preService) { 5 | 'use strict'; 6 | 7 | /** 8 | * BOOTSTRAP ANGULAR 9 | * Replicating what would normally take place in app.js 10 | */ 11 | var app_name = "unitest-app", 12 | app = angular.module(app_name, ['ngRoute','ngAnimate']); 13 | 14 | // Add property for unit test 15 | app.__appname = app_name; 16 | app.__origAngular = window.angular; 17 | app.__preServiceResult = preService; 18 | 19 | // Define route for unit test 20 | app.config(function ($routeProvider) { 21 | $routeProvider.when("/controllerFn", angularAMD.route({ 22 | utestParam: "controllerFn", 23 | template: "
{{message}}
", 24 | controllerUrl: "test/unit/lib/controllerFn" 25 | })); 26 | }); 27 | 28 | /* 29 | var elem = document.body; 30 | angularAMD.bootstrap(app, elem); 31 | */ 32 | return angularAMD.bootstrap(app); 33 | }); 34 | -------------------------------------------------------------------------------- /test/unit/lib/app_no_ngload.js: -------------------------------------------------------------------------------- 1 | /*jslint browser: true, devel: true, node: true, vars: true, nomen: true */ 2 | /*globals define, angular */ 3 | 4 | define(["angularAMD"], function (angularAMD) { 5 | 'use strict'; 6 | /** 7 | * BOOTSTRAP ANGULAR 8 | * Replicating what would normally take place in app.js 9 | */ 10 | var app_name = "unitest-no-ngload", 11 | app = angular.module(app_name, []); 12 | 13 | // Add property for unit test 14 | app.__appname = app_name; 15 | 16 | var elem = document.createElement('div'); 17 | angularAMD.bootstrap(app, false, elem); 18 | return app; 19 | }); 20 | -------------------------------------------------------------------------------- /test/unit/lib/component.js: -------------------------------------------------------------------------------- 1 | define(['app', 'angularAMD'], function (app, angularAMD) { 2 | var comp_name = "mainComponent"; 3 | 4 | // console.log('component called'); 5 | 6 | app.component(comp_name, { 7 | bindings: { 8 | compBind: '@' 9 | }, 10 | controller: function() { 11 | this.title = 'Component Title'; 12 | }, 13 | template: '

{{ $ctrl.title }} {{ $ctrl.compBind }}

' 14 | }); 15 | 16 | return this; 17 | }); 18 | -------------------------------------------------------------------------------- /test/unit/lib/controller.js: -------------------------------------------------------------------------------- 1 | define(['app', 'angularAMD', 'ngload!services'], function (app, angularAMD) { 2 | var ctrl_name = "MainController", 3 | inject = angularAMD.inject, 4 | utestResult; 5 | 6 | inject(function (UtestServiceResult) { 7 | utestResult = UtestServiceResult; 8 | }); 9 | 10 | app.controller(ctrl_name, ["$scope", utestResult.UtestFactory, utestResult.UtestService, utestResult.UtestSubModule, function ($scope, UtestFactory, UtestService, UtestSubModule) { 11 | $scope.ctrl_name = ctrl_name; 12 | $scope.utest_factory = UtestFactory; 13 | $scope.utest_service = UtestService; 14 | $scope.utest_sub_module = UtestSubModule; 15 | }]); 16 | 17 | return { 18 | "ctrl_name": ctrl_name, 19 | "result": utestResult 20 | }; 21 | }); 22 | -------------------------------------------------------------------------------- /test/unit/lib/controllerFn.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | /*globals define, angular */ 3 | 4 | define(['app'], function (app) { 5 | 'use strict'; 6 | return ["$scope", function ($scope) { 7 | $scope.message = "Message from controllerFn"; 8 | }]; 9 | }); 10 | -------------------------------------------------------------------------------- /test/unit/lib/decoServices.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Simulate the creation of provider using app.register. 3 | * `app.register` is expected to be decommissioned in the future release. 4 | */ 5 | define(['angularAMD', 'test/unit/factory/provider'], function(angularAMD, providerFactory) { 6 | var app = angularAMD.getCachedProvider("__orig_app"); 7 | return providerFactory(app.register, "Deco"); 8 | }); 9 | -------------------------------------------------------------------------------- /test/unit/lib/main.mustache: -------------------------------------------------------------------------------- 1 | /*jslint browser: true, node: true, nomen: true */ 2 | 3 | !function () { 4 | // Create basic requirejs config 5 | var rconfig = { 6 | // The baseUrl depends on what is defined in karma.unit.js:basePath 7 | baseUrl: "/base", 8 | 9 | // alias libraries paths 10 | paths: { 11 | 'angular': 'bower_components/angular/angular', 12 | 'angular-route': 'bower_components/angular-route/angular-route', 13 | 'angular-animate': 'bower_components/angular-animate/angular-animate', 14 | 'angular-mocks': 'bower_components/angular-mocks/angular-mocks', 15 | 'angularAMD': '{{{angularAMD-js-file}}}', 16 | 'ngload': '{{{ngload-js-file}}}', 17 | 'app': 'test/unit/lib/app', 18 | 'app_no_ngload': 'test/unit/lib/app_no_ngload', 19 | 'services': 'test/unit/lib/services', 20 | 'regServices': 'test/unit/lib/regServices', 21 | 'controller': 'test/unit/lib/controller', 22 | 'component' : 'test/unit/lib/component', 23 | 'regController': 'test/unit/lib/regController', 24 | 'decoServices': 'test/unit/lib/decoServices' 25 | }, 26 | 27 | shim: { 28 | 'app': ['angular'], 29 | 'angularAMD': ['angular'], 30 | 'angular-route': ['angular'], 31 | 'angular-animate': ['angular'], 32 | 'angular-mocks': ['angular'], 33 | 34 | /* 35 | 'services' in this case is a regular angular.js module which calls moduleFactory using 36 | requirejs' sync method. As result, test/unit/factory/module must be called first. 37 | */ 38 | 'services': ['test/unit/factory/module'] 39 | }, 40 | 41 | // start test run, once Require.js is done 42 | callback: window.__karma__.start 43 | }; 44 | 45 | 46 | // Look for .spec.js files and created linked dependencies starting with app.spec to one spec at a time 47 | var prevSpec = "test/unit/app.spec", 48 | reSkip = new RegExp("app\\.spec\\.js|app_no_ngload\\.spec\\.js"), 49 | reSpec = new RegExp("^\\/base\\/(\\S+\\.spec)\\.js$"); 50 | 51 | console.log("* Setting unit test sequence:"); 52 | for (var file in window.__karma__.files) { 53 | // Ignore the app and app_no_ngload files 54 | if (reSkip.test(file)) { 55 | continue; 56 | } 57 | 58 | // Find the *.spec.js files and extract just the name without initial "/base/" and ending ".js" 59 | if (window.__karma__.files.hasOwnProperty(file)) { 60 | var matches = reSpec.exec(file); 61 | if(matches && matches.length > 1) { 62 | var curSpec = matches[1]; 63 | rconfig.shim[curSpec] = [prevSpec]; 64 | console.log(" - " + curSpec + ": " + prevSpec); 65 | prevSpec = curSpec; 66 | } 67 | } 68 | } 69 | 70 | // Kick off the test from the last spec.js file 71 | rconfig.deps = [prevSpec]; 72 | console.log(" - " + prevSpec + " [KICK-OFF]"); 73 | 74 | // Update requirejs config 75 | require.config(rconfig); 76 | }(); 77 | 78 | 79 | -------------------------------------------------------------------------------- /test/unit/lib/main.no_ngload.js: -------------------------------------------------------------------------------- 1 | /*jslint browser: true, node: true, nomen: true */ 2 | 3 | require.config({ 4 | // The baseUrl depends on what is defined in karma.unit.js:basePath 5 | baseUrl: "/base", 6 | 7 | // alias libraries paths 8 | paths: { 9 | 'angular': 'bower_components/angular/angular', 10 | 'angular-mocks': 'bower_components/angular-mocks/angular-mocks', 11 | 'angularAMD': 'src/angularAMD', 12 | 'ngload': 'src/ngload', 13 | 'app_no_ngload': 'test/unit/lib/app_no_ngload' 14 | }, 15 | 16 | shim: { 17 | 'angularAMD': ['angular'] 18 | }, 19 | 20 | // controllerSpec is at the end of dependency tree so kicking it off will start entire test 21 | deps: ['test/unit/app_no_ngload.spec'], 22 | 23 | // start test run, once Require.js is done 24 | callback: window.__karma__.start 25 | }); 26 | -------------------------------------------------------------------------------- /test/unit/lib/preService.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Simulate the creation of provider using angularAMD. Used before bootstrap 3 | */ 4 | define(['angularAMD', 'test/unit/factory/provider'], function(angularAMD, providerFactory) { 5 | return providerFactory(angularAMD, "Pre"); 6 | }); 7 | -------------------------------------------------------------------------------- /test/unit/lib/providerFactory.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A standard set of code used to create different AngularJS provider. They considerations: 3 | * 1. Out of sequence creation: .value and .constant used by .factory and .service are created later 4 | * 2. config block is tested by using a custom provider UtestStore and result given by a UtestStoreResult 5 | * 6 | * `module` object is used to create provider and `suffix` is used as part of the name of provider to 7 | * make it unique. A `result` object is returned with name of provider created and the expected value 8 | * associated with that provider. 9 | */ 10 | define(function () { 11 | /** 12 | * A function that will create different AngularJS provider for the given module. 13 | * All module name created will has a suffix 14 | */ 15 | return function (module, suffix) { 16 | var result = { suffix: suffix }; 17 | 18 | /* 19 | Create Test Provider to store configuration value and a associated .factory 20 | to return the value stored in the .config block 21 | */ 22 | result.UtestStore = "UtestStore" + suffix; 23 | result.UtestStoreProvider = "UtestStore" + suffix + "Provider"; 24 | module.provider(result.UtestStore, function () { 25 | var config_value; 26 | 27 | this.configureValue = function(value) { 28 | config_value = value; 29 | }; 30 | 31 | this.$get = function () { 32 | return { 33 | getValue: function () { 34 | return config_value; 35 | } 36 | }; 37 | }; 38 | }); 39 | 40 | result.UtestStoreResult = 'UtestStoreResult' + suffix; 41 | module.factory(result.UtestStoreResult, [result.UtestStore, function (UtestStore) { 42 | return UtestStore.getValue(); 43 | }]); 44 | 45 | /* 46 | Use the .config block to set the test provider's value 47 | */ 48 | result.config_name = "module.config SDkWRXOgII" + suffix; 49 | module.config([result.UtestStoreProvider, function (UtestStoreProvider) { 50 | UtestStoreProvider.configureValue(result.config_name); 51 | }]); 52 | 53 | /* 54 | Use the .run block to set value for $rootScope.run_name 55 | */ 56 | result.run_name = "module.run sOdq6GNsaW" + suffix; 57 | module.run(function ($rootScope) { 58 | $rootScope.run_name = result.run_name; 59 | }); 60 | 61 | /* 62 | Create a .factory using the UtestConstant declared later 63 | */ 64 | result.UtestConstant = "UtestConstant" + suffix; 65 | result.UtestFactory = "UtestFactory" + suffix; 66 | result.factory_name = "UtestFactory.name nSg56eHrWo" + suffix; 67 | module.factory(result.UtestFactory, [result.UtestConstant, function (UtestConstant) { 68 | // Make sure that constant_name is setup after this factory. 69 | return { name: result.factory_name, const_name: UtestConstant }; 70 | }]); 71 | 72 | /* 73 | Create a .service using the UtestValue declared later 74 | */ 75 | result.UtestValue = "UtestValue" + suffix; 76 | result.UtestService = "UtestService" + suffix; 77 | result.service_name = "UtestService.name ySg56eHrWo" + suffix; 78 | module.service(result.UtestService, ["UtestValue" + suffix, function (UtestValue) { 79 | // Make sure that value_name is defined after this service 80 | this.name = result.service_name; 81 | this.val_name = UtestValue; 82 | }]); 83 | 84 | /* 85 | Define out of sequence constant and value, used by .factory and .service earlier 86 | */ 87 | result.constant_name = "utestmod.constant_name xHf71eVzxd" + suffix; 88 | module.constant(result.UtestConstant, result.constant_name); 89 | 90 | result.value_name = "utestmod.value_name ih3zRvZofo" + suffix; 91 | module.value(result.UtestValue, result.value_name); 92 | 93 | /* 94 | Create .directive. The name of directive is different as for consumer, it has to 95 | be converted to - based from the Camel case. 96 | */ 97 | result.utestDirective = "utest-directive-" + suffix.toLowerCase(); 98 | result.directive_name = "utestmod.directive_name Krloe7G1CH" + suffix; 99 | module.directive('utestDirective' + suffix, function () { 100 | return { 101 | restrict: 'A', 102 | link: function (scope, elm, attr) { 103 | elm.text(result.directive_name); 104 | } 105 | }; 106 | }); 107 | 108 | /* 109 | Create .filter 110 | */ 111 | result.utestFilter = 'utestFilter' + suffix; 112 | result.filter_name = "utestmod.filter_name 0WWb0usFCB" + suffix; 113 | module.filter(result.utestFilter, function () { 114 | return function (input) { 115 | return input + " " + result.filter_name; 116 | }; 117 | }); 118 | 119 | 120 | /* 121 | Create .animation 122 | */ 123 | result.utestAnimation = ".animation-" + suffix.toLowerCase(); 124 | module.animation(result.utestAnimation, function ($log, $interval) { 125 | return { 126 | addClass : function(element, className, done) { 127 | if ( className === "custom-hide") { 128 | element.css('opacity',0); 129 | done(); 130 | } 131 | }, 132 | removeClass : function(element, className, done) { 133 | if ( className === "custom-hide") { 134 | element.css('opacity',1); 135 | done(); 136 | } 137 | } 138 | }; 139 | }); 140 | 141 | /* 142 | Return result 143 | */ 144 | return result; 145 | }; 146 | 147 | }); 148 | 149 | -------------------------------------------------------------------------------- /test/unit/lib/regController.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | /*globals define, angular */ 3 | 4 | define(['app','regServices'], function (app, utestResult) { 5 | 'use strict'; 6 | var ctrl_name = "RegController"; 7 | 8 | app.controller(ctrl_name, ['$scope', utestResult.UtestFactory, utestResult.UtestService, function ($scope, UtestFactory, UtestService) { 9 | $scope.ctrl_name = ctrl_name; 10 | $scope.utest_factory = UtestFactory; 11 | $scope.utest_service = UtestService; 12 | }]); 13 | 14 | return { 15 | "ctrl_name": ctrl_name, 16 | "result": utestResult 17 | }; 18 | }); 19 | -------------------------------------------------------------------------------- /test/unit/lib/regServices.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Simulate the creation of provider using app. 3 | */ 4 | define(['app', 'test/unit/factory/provider'], function(app, providerFactory) { 5 | return providerFactory(app, "Reg"); 6 | }); 7 | -------------------------------------------------------------------------------- /test/unit/lib/services.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Simulate a 3rd party service. This package loads the 'test/unit/factory/module' using requirejs' sync 3 | * call so it must be pre-loaded by caller first. 4 | */ 5 | 6 | !function () { 7 | // Services coded using regular angular approach 8 | var sub_module = angular.module("subModuleServices", []), 9 | services = angular.module("utestServices", ['subModuleServices']), 10 | moduleFactory = require("test/unit/factory/module"); 11 | 12 | // Define a simple subModule 13 | angular.module("subModuleServices").factory("SubConfigValue", function () { 14 | return "JwU9YSJMJS-HhRz4nhBuY"; 15 | }); 16 | 17 | 18 | // Load module factory 19 | var result = moduleFactory(services, "Ng"); 20 | 21 | // Add sub-module test 22 | result.UtestSubModule = "UtestSubModule" + result.suffix; 23 | result.sub_module = "utestServices.sub_config_value JwU9YSJMJS-HhRz4nhBuY"; 24 | services.factory(result.UtestSubModule, function (SubConfigValue) { 25 | return { 26 | get: function () { 27 | return "utestServices.sub_config_value " + SubConfigValue; 28 | } 29 | }; 30 | }); 31 | 32 | services.factory('UtestServiceResult', function () { 33 | return result; 34 | }); 35 | 36 | }(); 37 | 38 | -------------------------------------------------------------------------------- /test/unit/regController.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Testing declaration of controller following require.js spec and make sure 3 | * it's dependecies are loaded. 4 | */ 5 | define(['app', 'angularAMD', 'regController'], function (app, angularAMD, regController) { 6 | console.log("* Running regController.spec.js"); 7 | describe('Utest RegController', function () { 8 | //console.log("Running controllerSpec.js"); 9 | var ctrl_name = regController.ctrl_name, 10 | result = regController.result, 11 | scope, ctrl; 12 | 13 | angularAMD.inject(function ($rootScope, $controller) { 14 | scope = $rootScope.$new(); 15 | ctrl = $controller(ctrl_name, { $scope: scope }); 16 | }); 17 | 18 | it("utestResult should exists.", function () { 19 | expect(result).toBeDefined(); 20 | }); 21 | 22 | it("scope.ctrl_name check", function () { 23 | expect(scope.ctrl_name).toBe(ctrl_name); 24 | }); 25 | 26 | it("scope.utest_factory check", function () { 27 | var f = scope.utest_factory; 28 | expect(f.name).toBe(result.factory_name); 29 | expect(f.const_name).toBe(result.constant_name); 30 | }); 31 | 32 | it("scope.utest_service check", function () { 33 | var s = scope.utest_service; 34 | expect(s.name).toBe(result.service_name); 35 | expect(s.val_name).toBe(result.value_name); 36 | }); 37 | 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /test/unit/regServices.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Simulate 3rd party 3 | */ 4 | define(['app', 'regServices', 'test/unit/factory/utestProvider' ], function (app, regServices, unitestFactory) { 5 | console.log("* Running regServices.spec.js"); 6 | unitestFactory(regServices); 7 | }); 8 | -------------------------------------------------------------------------------- /test/unit/services.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Test with 2 main objectives: 3 | * 1. Make sure that ngAMD's cache provider works 4 | * 2. Make sure that independent angular.modules can be incorporated in the project 5 | * by using the ngAMD.processQueue() method to load these modules. 6 | */ 7 | define(['app', 'angularAMD', 'test/unit/factory/utestModule', 'ngload!services'], function (app, angularAMD, utestModule) { 8 | console.log("* Running services.spec.js"); 9 | var results, 10 | inject = angularAMD.inject; 11 | 12 | inject(function (UtestServiceResult) { 13 | results = UtestServiceResult; 14 | }); 15 | 16 | utestModule(results); 17 | 18 | describe('Utest Services', function () { 19 | it("sub module check.", inject([results.UtestSubModule, function (UtestSubModule) { 20 | expect(UtestSubModule.get()).toBe(results.sub_module); 21 | }])); 22 | 23 | it("sub module check module.", function () { 24 | expect(angular.module("subModuleServices")).toBeUndefined(); 25 | }); 26 | 27 | it("angularAMD.config check.", function () { 28 | var configVal = "B9NmTuDAeU-JO0S1yjKXc"; 29 | inject([results.UtestStore, function (UtestStore) { 30 | expect(UtestStore.getValue()).not.toBe(configVal); 31 | }]); 32 | angularAMD.config([results.UtestStoreProvider, function (UtestStoreProvider) { 33 | UtestStoreProvider.configureValue(configVal); 34 | }]); 35 | inject([results.UtestStore, function (UtestStore) { 36 | expect(UtestStore.getValue()).toBe(configVal); 37 | }]); 38 | 39 | }); 40 | 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /www/css/sons-of-obsidian.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Derived from einaros's Sons of Obsidian theme at 3 | * http://studiostyl.es/schemes/son-of-obsidian by 4 | * Alex Ford of CodeTunnel: 5 | * http://CodeTunnel.com/blog/post/71/google-code-prettify-obsidian-theme 6 | */ 7 | 8 | .str 9 | { 10 | color: #EC7600; 11 | } 12 | .kwd 13 | { 14 | color: #93C763; 15 | } 16 | .com 17 | { 18 | color: #66747B; 19 | } 20 | .typ 21 | { 22 | color: #678CB1; 23 | } 24 | .lit 25 | { 26 | color: #FACD22; 27 | } 28 | .pun 29 | { 30 | color: #F1F2F3; 31 | } 32 | .pln 33 | { 34 | color: #F1F2F3; 35 | } 36 | .tag 37 | { 38 | color: #8AC763; 39 | } 40 | .atn 41 | { 42 | color: #E0E2E4; 43 | } 44 | .atv 45 | { 46 | color: #EC7600; 47 | } 48 | .dec 49 | { 50 | color: purple; 51 | } 52 | pre.prettyprint 53 | { 54 | border: 0px solid #888; 55 | } 56 | ol.linenums 57 | { 58 | margin-top: 0; 59 | margin-bottom: 0; 60 | } 61 | .prettyprint { 62 | background: #000; 63 | } 64 | li.L0, li.L1, li.L2, li.L3, li.L4, li.L5, li.L6, li.L7, li.L8, li.L9 65 | { 66 | color: #555; 67 | list-style-type: decimal; 68 | } 69 | li.L1, li.L3, li.L5, li.L7, li.L9 { 70 | background: #111; 71 | } 72 | @media print 73 | { 74 | .str 75 | { 76 | color: #060; 77 | } 78 | .kwd 79 | { 80 | color: #006; 81 | font-weight: bold; 82 | } 83 | .com 84 | { 85 | color: #600; 86 | font-style: italic; 87 | } 88 | .typ 89 | { 90 | color: #404; 91 | font-weight: bold; 92 | } 93 | .lit 94 | { 95 | color: #044; 96 | } 97 | .pun 98 | { 99 | color: #440; 100 | } 101 | .pln 102 | { 103 | color: #000; 104 | } 105 | .tag 106 | { 107 | color: #006; 108 | font-weight: bold; 109 | } 110 | .atn 111 | { 112 | color: #404; 113 | } 114 | .atv 115 | { 116 | color: #060; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /www/css/style.css: -------------------------------------------------------------------------------- 1 | #map-canvas { 2 | height: 500px; 3 | } 4 | 5 | /* Google Map Fix: http://stackoverflow.com/questions/9141249/twitter-bootstrap-css-affecting-google-maps*/ 6 | #map-canvas img { 7 | max-width: none; 8 | } 9 | #map-canvas label { 10 | width: auto; display:inline; 11 | } 12 | 13 | #slide-carousel { 14 | height: 250px; 15 | width: 400px; 16 | } 17 | 18 | #slide-image { 19 | height: 250px; 20 | width: 400px; 21 | } 22 | 23 | .main-content { 24 | padding: 0 10% 0 10%; 25 | } 26 | 27 | .main-content .break { 28 | margin: 25px 0; 29 | } 30 | 31 | .text-justify { 32 | text-align:justify; 33 | } 34 | 35 | .vam { 36 | vertical-align: middle; 37 | } 38 | 39 | /** 40 | * Bootstrap's "Justified nav" Theme 41 | * URL: http://getbootstrap.com/2.3.2/examples/justified-nav.html 42 | */ 43 | body { 44 | padding-top: 20px; 45 | padding-bottom: 60px; 46 | } 47 | 48 | /* Custom container */ 49 | .container { 50 | margin: 0 auto; 51 | max-width: 1000px; 52 | } 53 | .container > hr { 54 | margin: 30px 0; 55 | } 56 | 57 | /* Main marketing message and sign up button */ 58 | .jumbotron { 59 | margin: 50px 0; 60 | text-align: center; 61 | } 62 | .jumbotron h1 { 63 | font-size: 100px; 64 | line-height: 1; 65 | } 66 | .jumbotron .lead { 67 | font-size: 24px; 68 | line-height: 1.25; 69 | } 70 | .jumbotron .btn { 71 | font-size: 21px; 72 | padding: 14px 24px; 73 | } 74 | 75 | /* Supporting marketing content */ 76 | .marketing { 77 | margin: 60px 0; 78 | } 79 | .marketing p + h4 { 80 | margin-top: 28px; 81 | } 82 | 83 | 84 | /* Customize the navbar links to be fill the entire space of the .navbar */ 85 | .navbar .navbar-inner { 86 | padding: 0; 87 | } 88 | .navbar .nav { 89 | margin: 0; 90 | display: table; 91 | width: 100%; 92 | } 93 | .navbar .nav li { 94 | display: table-cell; 95 | width: 1%; 96 | float: none; 97 | } 98 | .navbar .nav li a { 99 | font-weight: bold; 100 | text-align: center; 101 | border-left: 1px solid rgba(255,255,255,.75); 102 | border-right: 1px solid rgba(0,0,0,.1); 103 | } 104 | .navbar .nav li:first-child a { 105 | border-left: 0; 106 | border-radius: 3px 0 0 3px; 107 | } 108 | .navbar .nav li:last-child a { 109 | border-right: 0; 110 | border-radius: 0 3px 3px 0; 111 | } 112 | 113 | -------------------------------------------------------------------------------- /www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | angularAMD: The Simple Way to Integrate AngularJS and RequireJS 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 27 | 28 | 29 | 30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /www/js/main.js: -------------------------------------------------------------------------------- 1 | require.config({ 2 | 3 | baseUrl: "js/scripts", 4 | 5 | // alias libraries paths 6 | paths: { 7 | 'angular': '../lib/angular/angular', 8 | 'angular-route': '../lib/angular/angular-route', 9 | 'async': '../lib/requirejs/async', 10 | 'angularAMD': '../lib/requirejs/angularAMD', 11 | 'ngload': '../lib/requirejs/ngload', 12 | 'ui-bootstrap': '../lib/angular-ui-bootstrap/ui-bootstrap-tpls', 13 | 'prettify': '../lib/google-code-prettify/prettify', 14 | 15 | 'HomeController': 'controller/home_ctrl', 16 | 'MapController': 'controller/map_ctrl', 17 | 'ModulesController': 'controller/modules_ctrl' 18 | }, 19 | 20 | // Add angular modules that does not support AMD out of the box, put it in a shim 21 | shim: { 22 | 'angularAMD': ['angular'], 23 | 'angular-route': ['angular'] 24 | }, 25 | 26 | // kick start application 27 | deps: ['app'] 28 | }); 29 | -------------------------------------------------------------------------------- /www/js/scripts/app.js: -------------------------------------------------------------------------------- 1 | define(['angularAMD', 'angular-route'], function (angularAMD) { 2 | var app = angular.module("ngreq-app", ['ngRoute']); 3 | 4 | /** 5 | * Configure Angular ngApp with route and cache the needed providers 6 | */ 7 | app.config(function ($routeProvider) { 8 | $routeProvider 9 | .when("/home", angularAMD.route({ 10 | templateUrl: 'views/home.html', controller: 'HomeController', navTab: "home" 11 | })) 12 | .when("/pictures", angularAMD.route({ 13 | templateUrl: 'views/pictures.html', controllerUrl: 'controller/pictures_ctrl', navTab: "pictures" 14 | })) 15 | .when("/modules", angularAMD.route({ 16 | templateUrl: 'views/modules.html', controller: 'ModulesController', navTab: "modules" 17 | })) 18 | .when("/map", angularAMD.route({ 19 | templateUrl: 'views/map.html', controller: 'MapController', navTab: "map" 20 | })) 21 | .otherwise({redirectTo: '/home'}) 22 | ; 23 | }); 24 | 25 | // Define constant to be used by Google Analytics 26 | app.constant("SiteName", "/angularAMD"); 27 | 28 | 29 | // Create function to link to GitHub 30 | app.directive('ghLink', function () { 31 | return { 32 | restrict: 'A', 33 | scope: true, 34 | template: '{{filename}}', 35 | controller: function ($scope, $attrs) { 36 | var gh_root = "https://github.com/marcoslin/angularAMD/blob/master/www/", 37 | relfile = $attrs.ghLink, 38 | fullpath = gh_root + relfile; 39 | $scope.fullpath = fullpath; 40 | $scope.filename = relfile.replace(/^.*[\\\/]/, ''); 41 | } 42 | }; 43 | }); 44 | 45 | // Add support for pretty print 46 | app.directive('prettyprint', function() { 47 | return { 48 | restrict: 'C', 49 | link: function postLink(scope, element, attrs) { 50 | element.html(prettyPrint(scope.dom)); 51 | } 52 | }; 53 | }); 54 | 55 | // Bootstrap Angular when DOM is ready 56 | return angularAMD.bootstrap(app); 57 | 58 | }); 59 | -------------------------------------------------------------------------------- /www/js/scripts/controller/home_ctrl.js: -------------------------------------------------------------------------------- 1 | define(['app', 'directive/navMenu','prettify'], function (app) { 2 | app.controller('HomeController', function ($scope, $window) { 3 | $scope.title = "angularAMD"; 4 | $scope.openGitHubPage = function () { 5 | //console.log("trackEvent: ViewGitHub"); 6 | $window._gaq.push(['_trackEvent', 'angularAMD', 'ViewGitHub', 'Clicked on View on GitHub button']); 7 | $window.open("https://github.com/marcoslin/angularAMD", "_blank"); 8 | }; 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /www/js/scripts/controller/map_ctrl.js: -------------------------------------------------------------------------------- 1 | define(['app', 'service/mapServices', 'directive/navMenu'], function (app) { 2 | app.controller('MapController', function ($scope, MapService) { 3 | $scope.title = "Where is Colosseo?"; 4 | $scope.latitude = 41.8902; 5 | $scope.longitude = 12.4923; 6 | 7 | // Set the location to be Colosseum 8 | MapService.initialize($scope, "map-canvas"); 9 | }) 10 | }); 11 | -------------------------------------------------------------------------------- /www/js/scripts/controller/modules_ctrl.js: -------------------------------------------------------------------------------- 1 | define(['app', 'ngload!service/dataServices', 'directive/write', 'directive/navMenu'], function (app) { 2 | app.controller('ModulesController', function ($scope, $log, DeferredObject, DeferredString, $rootScope) { 3 | DeferredObject.get("This is defered response", 2000).then(function (result) { 4 | $scope.obj_response = result; 5 | }); 6 | $scope.str_response = DeferredString.get("Show case ngWrite with promise", 1000); 7 | }) 8 | }); 9 | -------------------------------------------------------------------------------- /www/js/scripts/controller/pictures_ctrl.js: -------------------------------------------------------------------------------- 1 | define(['app', 'service/picturesService', 'ngload!ui-bootstrap', 'directive/navMenu'], function (app) { 2 | 3 | return ['$scope', 'Pictures', function ($scope, Pictures) { 4 | $scope.slideChangeInterval = 4000; 5 | 6 | $scope.$watch('cityModel', function (newValue) { 7 | if (newValue) { 8 | Pictures.query(newValue).then(function (result) { 9 | $scope.rows = result; 10 | }); 11 | } 12 | }); 13 | 14 | }]; 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /www/js/scripts/directive/navMenu.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global define */ 3 | define(['app'], function (app) { 4 | app.controller("navMenuController", function ($scope, $route, SiteName, $window, $location) { 5 | var tab_name = $route.current.navTab, 6 | ga_path = SiteName + $location.path(); 7 | 8 | $scope.isTabActive = function (tabName) { 9 | if (tabName === tab_name) { 10 | return "active"; 11 | } 12 | }; 13 | 14 | //console.log("Sending to GA: " + ga_path); 15 | $window._gaq.push(['_trackPageview', ga_path]); 16 | 17 | }); 18 | 19 | app.directive('navMenu', function () { 20 | return { 21 | restrict: 'A', 22 | controller: 'navMenuController', 23 | templateUrl: 'js/scripts/directive/templates/navMenu.html' 24 | }; 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /www/js/scripts/directive/templates/navMenu.html: -------------------------------------------------------------------------------- 1 |

angularAMD

2 | 22 | -------------------------------------------------------------------------------- /www/js/scripts/directive/write.js: -------------------------------------------------------------------------------- 1 | // customDirectives 2 | define(['app'], function (app) { 3 | app.directive('ngWrite', function ($timeout, $log) { 4 | return { 5 | restrict: 'A', 6 | //require: "^ngController", 7 | link: function (scope, elm, attr) { 8 | // Attempt to read the attribe from scope. If not found, return what's pass to directive 9 | if (scope[attr.ngWrite]) { 10 | // ngWrite is found in scope. Check if promised has been returned 11 | var inval = scope[attr.ngWrite]; 12 | if ('then' in inval) { 13 | inval.then(function (data) { 14 | elm.text(data); 15 | }); 16 | } else { 17 | elm.text(inval); 18 | } 19 | } else { 20 | elm.text(attr.ngWrite); 21 | } 22 | } 23 | }; 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /www/js/scripts/service/dataServices.js: -------------------------------------------------------------------------------- 1 | // Services coded using regular angular approach 2 | angular.module("dataServices", []) 3 | .factory("DeferredObject", function ($timeout, $q, ServiceName) { 4 | return { 5 | get: function (message, delay_in_ms) { 6 | //console.log("DeferredObject.get ServiceName: ", ServiceName); 7 | var d = $q.defer(); 8 | $timeout(function () { 9 | d.resolve({label: ServiceName, message: message}); 10 | }, delay_in_ms); 11 | return d.promise; 12 | } 13 | } 14 | }) 15 | .provider("configValue", function () { 16 | var config_value; 17 | 18 | this.set = function(value) { 19 | config_value = value; 20 | }; 21 | 22 | this.$get = function () { 23 | return { 24 | get: function () { 25 | return config_value; 26 | } 27 | } 28 | }; 29 | }) 30 | .constant("ServiceName", "dataServices") 31 | .service("DeferredString", function ($timeout, $q) { 32 | this.get = function (message, delay_in_ms) { 33 | var d = $q.defer(); 34 | $timeout(function () { 35 | d.resolve(message); 36 | }, delay_in_ms); 37 | return d.promise; 38 | } 39 | }) 40 | .config(function (configValueProvider) { 41 | configValueProvider.set("And config works"); 42 | }) 43 | .run(function (configValue, $rootScope, $timeout) { 44 | $timeout(function () { 45 | $rootScope.run_block_message = "Greetings from .run"; 46 | }, 3000); 47 | $timeout(function () { 48 | $rootScope.config_block_message = configValue.get(); 49 | }, 4000); 50 | }) 51 | ; -------------------------------------------------------------------------------- /www/js/scripts/service/mapServices.js: -------------------------------------------------------------------------------- 1 | // convert Google Maps into an AMD module 2 | define(['app', 'async!//maps.google.com/maps/api/js?v=3&sensor=false'], function (app) { 3 | app.service('MapService', function () { 4 | var gmaps = window.google.maps, 5 | mapOptions = { 6 | zoom: 15, 7 | draggable: true, 8 | mapTypeId: gmaps.MapTypeId.ROADMAP, 9 | panControl: false, 10 | disableDoubleClickZoom: true, 11 | disableDefaultUI: true, 12 | zoomControl: false 13 | }, 14 | latLng; 15 | 16 | /** 17 | * Create map with initial position given by scope.latitude and scope.longitude. 18 | * Default to LatLng of Colosseo in Rome 19 | */ 20 | this.initialize = function (scope, elementName) { 21 | var lat = scope.latitude || 41.8902, 22 | lng = scope.longitude || 12.4923, 23 | map = new gmaps.Map(document.getElementById(elementName), mapOptions); 24 | 25 | // Set initial position 26 | if (!latLng) { 27 | latLng = new gmaps.LatLng(lat, lng); 28 | } else { 29 | scope.latitude = latLng.lat(); 30 | scope.longitude = latLng.lng(); 31 | } 32 | map.setCenter(latLng); 33 | var marker = new gmaps.Marker({position: latLng, draggable: true, map: map}); 34 | 35 | // Keep track of position of marker 36 | gmaps.event.addListener(marker, 'dragend', function (movedMarker) { 37 | latLng = movedMarker.latLng; 38 | scope.latitude = latLng.lat(); 39 | scope.longitude = latLng.lng(); 40 | scope.$apply(function () { 41 | marker.setPosition(latLng); 42 | }); 43 | }); 44 | }; 45 | 46 | }); 47 | }); 48 | // REF: http://blog.millermedeiros.com/requirejs-2-0-delayed-module-evaluation-and-google-maps/ -------------------------------------------------------------------------------- /www/js/scripts/service/picturesService.js: -------------------------------------------------------------------------------- 1 | // dataServices 2 | define(['app'], function (app) { 3 | app.factory('Pictures', function ($http, $q, $log) { 4 | var feed_url = "http://ycpi.api.flickr.com/services/feeds/photos_public.gne?format=json&jsoncallback=JSON_CALLBACK&tags="; 5 | return { 6 | query: function (tag_name) { 7 | var d = $q.defer(); 8 | if (typeof tag_name === "undefined" || tag_name === "") { 9 | $log.error("Pictures.query: missing tag_name."); 10 | d.reject("tag must be provided"); 11 | } else { 12 | $http.jsonp(feed_url + tag_name).success(function (data) { 13 | d.resolve(data.items); 14 | }).error(function (data, status, headers, config) { 15 | $log.error("Error: ", headers); 16 | d.reject(data); 17 | }); 18 | } 19 | 20 | return d.promise; 21 | } 22 | }; 23 | }); 24 | 25 | }); 26 | 27 | /* 28 | Another way to code this: 29 | ------------------------- 30 | // define(['app','angular-resource'], function (app) { 31 | angular.module("dataServices", ['ngResource']) 32 | .factory('Pictures', ['$http', '$q', '$log', '$resource', function ($http, $q, $log, $resource) { 33 | var feed_url = "http://ycpi.api.flickr.com/services/feeds/photos_public.gne?format=json&tags=London&jsoncallback=JSON_CALLBACK"; 34 | return { 35 | query: function () { 36 | var d = $q.defer(); 37 | 38 | $http.jsonp(feed_url).success(function (data) { 39 | d.resolve(data.items); 40 | }).error(function (data, status, headers, config) { 41 | $log.error("Error: ", headers); 42 | d.reject(data); 43 | }); 44 | 45 | return d.promise; 46 | } 47 | }; 48 | }]); 49 | 50 | Need to update require.config: 51 | ------------------------------ 52 | shim : { 53 | 'dataServices' : { 54 | exports: "dataServices", 55 | deps: ['angular-resource'] 56 | } 57 | } 58 | 59 | */ -------------------------------------------------------------------------------- /www/views/home.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |

{{title}}

5 |

6 | AngularJS + RequireJS: The Proper and Easier Way 7 |

8 |
9 | 10 |
11 |

12 | Integrating AngularJS and RequireJS shouldn't be complicated, and it isn't with angularAMD. 13 | To see it in action, checkout this website that show case key features. Make sure to 14 | load your favorite Developer Tools to see the on-demand loading of *.js files 15 | as you switch tabs. 16 |

17 | 18 |
19 | 20 |

A quick AngularJS + RequireJS how-to using angularAMD:

21 | 22 |

Step 1:

23 | Define components and dependencies in main.js: 24 | 25 |
require.config({
26 |     baseUrl: "js",    
27 |     paths: {
28 |         'angular': '.../angular.min',
29 |         'angular-route': '.../angular-route.min',
30 |         'angularAMD': '.../angularAMD.min'
31 |     },
32 |     shim: { 'angularAMD': ['angular'], 'angular-route': ['angular'] },
33 |     deps: ['app']
34 | });
35 | 36 | and load RequireJS, and only RequireJS, in index.html: 37 | 38 |
<head>
39 |     <script data-main="js/main.js" src=".../require.js"></script>
40 | </head>
41 | 42 |

Step 2:

43 | Create app.js using RequireJS's define statement: 44 |
define(['angularAMD', 'angular-route'], function (angularAMD) {
45 |     var app = angular.module("webapp", ['ngRoute']);
46 |     app.config(function ($routeProvider) {
47 |         $routeProvider.when("/home", angularAMD.route({
48 |             templateUrl: 'views/home.html', controller: 'HomeCtrl',
49 |             controllerUrl: 'ctrl/home'
50 |         }))
51 |     });
52 |     return angularAMD.bootstrap(app);
53 | });
54 | 55 |

Step 3:

56 | Create controller using app.register method: 57 |
define(['app'], function (app) {
58 |     app.controller('HomeCtrl', function ($scope) {
59 |         $scope.message = "Message from HomeCtrl"; 
60 |     });
61 | });
62 | 63 |

64 | Check out this Gist 65 | for a simple implementation. Then, head over to github for detail documentation. 66 |

67 |
68 | 69 |
70 | 71 | 77 | 78 | -------------------------------------------------------------------------------- /www/views/map.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | Third party Javascript API such as 5 | Google Maps API can be setup for on-demand loading using 6 | async.js 7 | RequireJS plugin as demonstrated in wrapper. 8 |

9 | 10 |
11 | 12 |
13 |
14 | {{title}} 15 | Move the marker to update latitude and logitude. 16 |
17 | 18 |
19 |
20 | 21 | 22 | latitude: {{latitude}} 23 | 24 | 25 | 26 | longitude: {{longitude}} 27 | 28 |
29 |
30 | 31 | 32 | -------------------------------------------------------------------------------- /www/views/modules.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | Demonstrate how to create on-demand loaded components such as 5 | and . 6 | Standard AngularJS module is supported 7 | via RequireJS plugin as used in 8 | . Notice that angularAMD 9 | is able to handle out of sequence calls of .factory and .constant in 10 | . 11 |

12 | 13 |
14 | 15 |
16 | 17 |
18 |
.ng-write directive:
19 |
ngWrite FAILED!!!
20 |
21 | 22 |
23 |
Value set in .config:
24 |
{{config_block_message}}
25 |
26 | 27 |
28 |
Value set in .run:
29 |
{{run_block_message}}
30 |
31 | 32 |
33 |
DeferredObject:
34 |
{{obj_response.message}}
35 |
.label:
36 |
{{obj_response.label}}
37 |
38 | 39 |
40 |
DeferredString:
41 |
42 |
43 | 44 |
45 | -------------------------------------------------------------------------------- /www/views/pictures.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | Show case on-demand loading of UI Bootstrap 5 | using RequireJS plugin. 6 |


7 | 8 |
9 | Show pictures from:  10 |
11 | 12 | 13 | 14 |
15 | 16 |

17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 |
29 | 30 | 31 | 32 | --------------------------------------------------------------------------------