├── .gitignore ├── .jshintrc ├── Gruntfile.js ├── LICENSE ├── README.md ├── bower.json ├── dist ├── angular-couch-potato.js ├── dependencies │ ├── angular-route.js │ ├── angular-ui-router.js │ ├── angular.js │ └── requirejs.js ├── docs │ ├── css │ │ ├── animations.css │ │ ├── bootstrap.min.css │ │ ├── doc_widgets.css │ │ ├── docs.css │ │ ├── font-awesome.css │ │ └── style.css │ ├── font │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ └── fontawesome-webfont.woff │ ├── index.html │ ├── js │ │ ├── angular-bootstrap-prettify.min.js │ │ ├── angular-bootstrap.min.js │ │ ├── angular.min.js │ │ ├── docs-setup.js │ │ └── docs.js │ └── partials │ │ ├── api │ │ ├── index.html │ │ ├── scs.couch-potato.$couchPotato.html │ │ ├── scs.couch-potato.$couchPotatoProvider.html │ │ └── scs.couch-potato.html │ │ └── guide │ │ ├── 03-faq.html │ │ ├── 05-conceptual.01-how-it-works.html │ │ ├── 05-conceptual.html │ │ ├── 11-architecture.01-components.html │ │ ├── 11-architecture.11-app-module-setup.html │ │ ├── 11-architecture.html │ │ ├── 21-lazy-loading-via-routing.html │ │ ├── index.html │ │ └── templateProvider.html └── samples │ ├── README.md │ ├── index.html │ ├── requirejs-ui-router │ ├── app │ │ ├── app-init.js │ │ ├── app.js │ │ ├── config.js │ │ ├── controllers │ │ │ ├── contactsController.js │ │ │ ├── contactsDetailController.js │ │ │ ├── contactsDetailItemController.js │ │ │ └── contactsDetailItemEditController.js │ │ ├── routeDefs.js │ │ ├── run.js │ │ └── services │ │ │ └── findById.js │ ├── css │ │ ├── bootstrap.min.css │ │ └── styles.css │ ├── index.html │ └── partials │ │ ├── contacts.detail.html │ │ ├── contacts.detail.item.edit.html │ │ ├── contacts.detail.item.html │ │ ├── contacts.html │ │ └── contacts.list.html │ └── script-tags-ngroute │ ├── css │ └── app.css │ ├── index.html │ ├── js │ ├── app.js │ ├── controllers │ │ ├── myCtrl1.js │ │ └── myCtrl2.js │ ├── directives │ │ └── appVersion.js │ ├── filters │ │ └── interpolator.js │ └── services │ │ ├── myService2.js │ │ └── version.js │ └── partials │ ├── partial1.html │ └── partial2.html ├── docs ├── content │ ├── api │ │ └── index.ngdoc │ └── guide │ │ ├── 03-faq.ngdoc │ │ ├── 05-conceptual.01-how-it-works.ngdoc │ │ ├── 05-conceptual.ngdoc │ │ ├── 11-architecture.01-components.ngdoc │ │ ├── 11-architecture.11-app-module-setup.ngdoc │ │ ├── 11-architecture.ngdoc │ │ ├── 21-lazy-loading-via-routing.ngdoc │ │ ├── index.ngdoc │ │ └── templateProvider.ngdoc ├── css │ └── style.css └── html │ ├── favicon.ico │ └── nav.html ├── package.json ├── samples ├── README.md ├── index.html ├── requirejs-ui-router │ ├── app │ │ ├── app-init.js │ │ ├── app.js │ │ ├── config.js │ │ ├── controllers │ │ │ ├── contactsController.js │ │ │ ├── contactsDetailController.js │ │ │ ├── contactsDetailItemController.js │ │ │ └── contactsDetailItemEditController.js │ │ ├── routeDefs.js │ │ ├── run.js │ │ └── services │ │ │ └── findById.js │ ├── css │ │ ├── bootstrap.min.css │ │ └── styles.css │ ├── index.html │ └── partials │ │ ├── contacts.detail.html │ │ ├── contacts.detail.item.edit.html │ │ ├── contacts.detail.item.html │ │ ├── contacts.html │ │ └── contacts.list.html └── script-tags-ngroute │ ├── css │ └── app.css │ ├── index.html │ ├── js │ ├── app.js │ ├── controllers │ │ ├── myCtrl1.js │ │ └── myCtrl2.js │ ├── directives │ │ └── appVersion.js │ ├── filters │ │ └── interpolator.js │ └── services │ │ ├── myService2.js │ │ └── version.js │ └── partials │ ├── partial1.html │ └── partial2.html ├── server.js └── src └── couchPotato.js /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.bak 4 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "boss": true, 3 | "quotmark": true, 4 | "laxcomma" : true, 5 | "laxbreak": true, 6 | "curly":true, 7 | "eqeqeq":true, 8 | "immed":true, 9 | "latedef":true, 10 | "newcap":true, 11 | "noarg":true, 12 | "sub":true, 13 | "eqnull":true, 14 | 15 | "globals" : { 16 | "angular" : false 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | grunt.loadNpmTasks('grunt-bower-task'); 4 | grunt.renameTask('bower', 'bowerTask'); 5 | grunt.loadNpmTasks('grunt-bower'); 6 | grunt.renameTask('bower', 'gruntBower'); 7 | grunt.loadNpmTasks('grunt-contrib-copy'); 8 | // grunt.loadNpmTasks('grunt-contrib-jshint'); 9 | grunt.loadNpmTasks('grunt-contrib-clean'); 10 | grunt.loadNpmTasks('grunt-contrib-concat'); 11 | // grunt.loadNpmTasks('grunt-contrib-uglify'); 12 | grunt.loadNpmTasks('grunt-ngdocs'); 13 | grunt.loadNpmTasks('grunt-gh-pages'); 14 | 15 | // grunt.registerTask('bower', ['bowerTask', 'gruntBower']); 16 | grunt.registerTask('bower', ['bowerTask', 'gruntBower']); 17 | grunt.registerTask('default', ['build']); 18 | grunt.registerTask('build', ['clean', 'bower', 'concat']); 19 | grunt.registerTask('release', ['build','copy:samples', 'ngdocs']); 20 | grunt.registerTask('docs', ['ngdocs']); 21 | 22 | // Print a timestamp (useful for when watching) 23 | grunt.registerTask('timestamp', function() { 24 | grunt.log.subhead(Date()); 25 | }); 26 | 27 | // Project configuration. 28 | grunt.initConfig({ 29 | dirs: { 30 | dist: 'dist', 31 | src: { 32 | js: ['src/**/couchPotato.js'] 33 | } 34 | }, 35 | 'gh-pages': { 36 | options: { 37 | base: 'dist' 38 | }, 39 | src: ['**'] 40 | }, 41 | pkg: grunt.file.readJSON('package.json'), 42 | banner: 43 | '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' + 44 | '<%= pkg.homepage ? " * " + pkg.homepage + "\\n" : "" %>' + 45 | ' * Copyright (c) <%= grunt.template.today(\'yyyy\') %> <%= pkg.author.name %>;\n' + 46 | ' * Uses software code originally found at https://github.com/szhanginrhythm/angular-require-lazyload\n' + 47 | ' * Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %>\n */\n', 48 | ngdocs: { 49 | options: { 50 | dest: 'dist/docs', 51 | title: 'angular-couch-potato', 52 | startPage: '/guide', 53 | styles: ['docs/css/style.css'], 54 | navTemplate: 'docs/html/nav.html', 55 | html5Mode: false 56 | }, 57 | guide: { 58 | src: ['docs/content/guide/**/*.ngdoc'], 59 | title: 'Guide' 60 | }, 61 | api: { 62 | src: ['src/**/*.js', 'docs/content/api/**/*.ngdoc'], 63 | title: 'API Reference' 64 | } 65 | }, 66 | clean: ['<%= dirs.dist %>/*'], 67 | gruntBower: { 68 | dev: { 69 | dest: '<%= dirs.dist %>/dependencies' 70 | } 71 | }, 72 | bowerTask: { 73 | install: { 74 | options: { 75 | copy: false 76 | } 77 | } 78 | }, 79 | copy: { 80 | samples: { 81 | expand: true, 82 | cwd: './', 83 | src: 'samples/**/*', 84 | dest: 'dist' 85 | } 86 | }, 87 | concat: { 88 | dist: { 89 | options: { 90 | banner: '<%= banner %>', 91 | stripBanners: true 92 | }, 93 | src:['<%= dirs.src.js %>'], 94 | dest:'<%= dirs.dist %>/<%= pkg.name %>.js' 95 | } 96 | } 97 | }); 98 | }; 99 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2013 Daphne Maddox 4 | Based on, and includes substantial portions of 5 | szhanginrhythm/angular-require-lazyload 6 | (https://github.com/szhanginrhythm/angular-require-lazyload), 7 | which at the time of this writing bears no copyright information. 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## angular-couch-potato 2 | > Lazy-Load and Register Components in AngularJS Applications 3 | 4 | For documentation, please see 5 | http://laurelnaiad.github.io/angular-couch-potato/docs/. The documentation is a first and incomplete draft. 6 | 7 | To see the samples live, go to 8 | http://laurelnaiad.github.io/angular-couch-potato/samples/ 9 | 10 | #### Installing Source/Running Samples Locally 11 | 12 | See the README in the [sample apps](https://github.com/laurelnaiad/ng-couch-potato/tree/master/samples) for basic demonstrations. 13 | 14 | #### History/Attribution 15 | 16 | Couch Potato has its roots in [angular-require-lazyload](https://github.com/szhanginrhythm/angular-require-lazyload) but has grown into something that supports hierarchical dependencies and features ease-of-use and efficiency optimizations to make lazy loading vs. compiling into your app easy. 17 | 18 | #### License 19 | 20 | See the [LICENSE file](https://github.com/laurelnaiad/ng-couch-potato/blob/master/LICENSE). 21 | 22 | #### Questions/Comments/Concerns 23 | 24 | See the [Issues List](https://github.com/laurelnaiad/ng-couch-potato/issues). Yes, couch-potato really is just small a page of code **before** minimization. Short and sweet. It's about the pattern... 25 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-couch-potato", 3 | "version": "0.2.3", 4 | "homepage": "https://github.com/laurelnaiad/angular-couch-potato", 5 | "main": [ 6 | "dist/angular-couch-potato.js" 7 | ], 8 | "description": "Provider/Service to Lazy-Load Components in AngularJS Applications", 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/laurelnaiad/angular-couch-potato.git" 12 | }, 13 | "ignore": [ 14 | "dist/docs", 15 | "docs", 16 | "src", 17 | "./*.js", 18 | "./*.jshintrc", 19 | "./*.gitignore", 20 | "package.json" 21 | ], 22 | "dependencies": { 23 | }, 24 | "devDependencies": { 25 | "requirejs": "~2.1.8", 26 | "angular-ui-router": "~0.2.0", 27 | "angular": "~1.2.0", 28 | "angular-route": "~1.2.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /dist/angular-couch-potato.js: -------------------------------------------------------------------------------- 1 | /*! angular-couch-potato - v0.2.3 - 2014-11-06 2 | * https://github.com/laurelnaiad/angular-couch-potato 3 | * Copyright (c) 2013 Daphne Maddox; 4 | * Uses software code originally found at https://github.com/szhanginrhythm/angular-require-lazyload 5 | * Licensed MIT 6 | */ 7 | (function() { 8 | 9 | var CouchPotato = function(angular) { 10 | //Self-invoking anonymous function keeps global scope clean. 11 | 12 | //Register the module. 13 | //Getting angular onto the global scope is the client's responsibility. 14 | 15 | /** 16 | * 17 | * @ngdoc overview 18 | * @name scs.couch-potato 19 | * 20 | * @description 21 | * 22 | * ## scs.couch-potato module 23 | * 24 | * ### Loading the Script 25 | * 26 | * Couch Potato needs RequireJS in order to be useful. However, it is not 27 | * necessary for your application to be bootstrapped using AMD. You have 28 | * two options: 29 | * 30 | * #### A. Use Traditional <script> Tags 31 | * 32 | * If you use traditional script tags to load the module (i.e. you aren't 33 | * using AMD to structure your the non-lazy portion of your application, 34 | * you **must** load the following three scripts in this order (other 35 | * modules can be loaded wherever it makes sense for you, but these three 36 | * must follow the order): 37 | * 38 | * 1. Angular 39 | * 2. Couch Potato 40 | * 3. RequireJS 41 | * 42 | *
43 | * 44 | * 45 | * 46 | * 47 | *48 | * 49 | * #### B. Use RequireJS 50 | * 51 | * If you use RequireJS, Couch Potato will first try to use an AMD module 52 | * that is defined with the name ```'angular'```. If it does not find 53 | * that, it will try to use an angular object defined as 54 | * ```window.angular```. This flexibility allows you to load angular 55 | * from a script tag (if you do so before your require.js script tag) 56 | * or from RequireJS -- the distinction will be critical if you are 57 | * using multiple instances of angular (in which case I pity you for 58 | * needing to, even though I understand that there are edge cases 59 | * where it is necessary) -- it must be very painful. 60 | * 61 | * ### Adding Couch Potato as a Dependency 62 | * 63 | * Reference Couch Potato as a Dependency as follows: 64 | *
65 | * var myModule = angular.module('myApp', ['myOtherDep', 'scs.couch-potato']); 66 | *67 | * 68 | * See also the {@link scs.couch-potato.$couchPotatoProvider 69 | * $couchPotatoProvider} documentation. 70 | */ 71 | var module = angular.module('scs.couch-potato', ['ng']); 72 | 73 | function CouchPotatoProvider( 74 | $controllerProvider, 75 | $compileProvider, 76 | $provide, 77 | $filterProvider 78 | ) { 79 | 80 | var rootScope = null; 81 | 82 | //Expose each provider's functionality as single-argument functions. 83 | //The component-definining functions that are passed as parameters 84 | //should bear their own names. If apply is true, call apply on the 85 | //root scope. This allows clients that are manually registering 86 | //components (outside of the promise-based methods) to force registration 87 | //to be applied, even if they are not doing so in an angular context. 88 | 89 | function registerValue(value, apply) { 90 | $provide.value.apply(null, value); 91 | if (apply) { 92 | rootScope.$apply(); 93 | } 94 | } 95 | 96 | function registerConstant(value, apply) { 97 | $provide.value.apply(null, value); 98 | if (apply) { 99 | rootScope.$apply(); 100 | } 101 | } 102 | 103 | function registerFactory(factory, apply) { 104 | $provide.factory.apply(null, factory); 105 | if (apply) { 106 | rootScope.$apply(); 107 | } 108 | } 109 | 110 | function registerService(service, apply) { 111 | $provide.service.apply(null, service); 112 | if (apply) { 113 | rootScope.$apply(); 114 | } 115 | } 116 | 117 | function registerFilter(filter, apply) { 118 | $filterProvider.register.apply(null, filter); 119 | if (apply) { 120 | rootScope.$apply(); 121 | } 122 | } 123 | 124 | function registerDirective(directive, apply) { 125 | $compileProvider.directive.apply(null, directive); 126 | if (apply) { 127 | rootScope.$apply(); 128 | } 129 | } 130 | 131 | function registerController(controller, apply) { 132 | $controllerProvider.register.apply(null, controller); 133 | if (apply) { 134 | rootScope.$apply(); 135 | } 136 | } 137 | 138 | function registerDecorator(decorator, apply) { 139 | $provide.decorator.apply(null, decorator); 140 | if (apply) { 141 | rootScope.$apply(); 142 | } 143 | } 144 | 145 | function registerProvider(service, apply) { 146 | $provide.provider.apply(null, service); 147 | if (apply) { 148 | rootScope.$apply(); 149 | } 150 | } 151 | 152 | function resolve(dependencies, returnIndex, returnSubId) { 153 | if (dependencies.dependencies) { 154 | return resolveDependenciesProperty( 155 | dependencies, 156 | returnIndex, 157 | returnSubId 158 | ); 159 | } 160 | else { 161 | return resolveDependencies(dependencies, returnIndex, returnSubId); 162 | } 163 | } 164 | this.resolve = resolve; 165 | 166 | function resolveDependencies(dependencies, returnIndex, returnSubId) { 167 | function delay($q, $rootScope) { 168 | 169 | var defer = $q.defer(); 170 | 171 | require(dependencies, function() { 172 | var args = Array.prototype.slice(arguments); 173 | 174 | var out; 175 | 176 | if (returnIndex === undefined) { 177 | out = arguments[arguments.length - 1]; 178 | } 179 | else { 180 | argForOut = arguments[returnIndex]; 181 | if (returnSubId === undefined) { 182 | out = argForOut; 183 | } 184 | else { 185 | out = argForOut[returnSubId]; 186 | } 187 | } 188 | 189 | defer.resolve(out); 190 | $rootScope.$apply(); 191 | 192 | }); 193 | 194 | return defer.promise; 195 | } 196 | 197 | delay.$inject = ['$q', '$rootScope']; 198 | return delay; 199 | 200 | } 201 | this.resolveDependencies = resolveDependencies; 202 | 203 | function resolveDependenciesProperty(configProperties) { 204 | if (configProperties.dependencies) { 205 | var resolveConfig = configProperties; 206 | var deps = configProperties.dependencies; 207 | delete resolveConfig['dependencies']; 208 | 209 | resolveConfig.resolve = {}; 210 | resolveConfig.resolve.delay = resolveDependencies(deps); 211 | return resolveConfig; 212 | } 213 | else 214 | { 215 | return configProperties; 216 | } 217 | 218 | } 219 | this.resolveDependenciesProperty = resolveDependenciesProperty; 220 | 221 | /** 222 | * 223 | * @ngdoc object 224 | * @name scs.couch-potato.$couchPotato 225 | * 226 | * @description 227 | * 228 | * == 229 | * 230 | * **Important:** you must inject the 231 | * {@link scs.couch-potato.$couchPotatoProvider $couchPotatoProvider} 232 | * at config-time to use the service. 233 | * 234 | */ 235 | this.$get = function ($rootScope) { 236 | var svc = {}; 237 | 238 | rootScope = $rootScope; 239 | 240 | svc.registerValue = registerValue; 241 | svc.registerConstant = registerConstant; 242 | svc.registerFactory = registerFactory; 243 | svc.registerService = registerService; 244 | svc.registerFilter = registerFilter; 245 | svc.registerDirective = registerDirective; 246 | svc.registerController = registerController; 247 | svc.registerDecorator = registerDecorator; 248 | svc.registerProvider = registerProvider; 249 | 250 | svc.resolveDependenciesProperty = resolveDependenciesProperty; 251 | svc.resolveDependencies = resolveDependencies; 252 | svc.resolve = resolve; 253 | 254 | return svc; 255 | }; 256 | this.$get.$inject = ['$rootScope']; 257 | 258 | } 259 | CouchPotatoProvider.$inject = [ 260 | '$controllerProvider', 261 | '$compileProvider', 262 | '$provide', 263 | '$filterProvider' 264 | ]; //inject the providers into CouchPotatoProvider 265 | 266 | /** 267 | * 268 | * @ngdoc object 269 | * @name scs.couch-potato.$couchPotatoProvider 270 | * 271 | * @description 272 | * Injects and retains references to providers that will be used 273 | * by the {@link scs.couch-potato.$couchPotato $couchPotato service} 274 | * at run-time. 275 | * 276 | * It is **mandatory** that you inject the provider before 277 | * your app's module.run is called (e.g. in module.config). 278 | * 279 | * @example 280 | *
281 | * myModule.config( 282 | * [ 283 | * '$couchPotatoProvider', 'myOtherProvider', 284 | * function($couchPotatoProvider, myOtherProvider) { 285 | * myOtherProvider.config = { someParam: 'demo' }; 286 | * // $couchPotatoProvider needs no specific configuration 287 | * } 288 | * ] 289 | * ); 290 | *291 | * 292 | * See the {@link scs.couch-potato couch-potato module documentation} to learn 293 | * how to load the module. 294 | * 295 | * @requires $controllerProvider 296 | * @requires $compileProvider 297 | * @requires $filterProvider 298 | * @requires $provide 299 | * 300 | */ 301 | module.provider('$couchPotato', CouchPotatoProvider); 302 | 303 | this.configureApp = function(app) { 304 | app.registerController = function(name, controller) { 305 | if (app.lazy) { 306 | app.lazy.registerController([name, controller]); 307 | } 308 | else { 309 | app.controller(name, controller); 310 | } 311 | return app; 312 | }; 313 | 314 | app.registerFactory = function(name, factory) { 315 | if (app.lazy) { 316 | app.lazy.registerFactory([name, factory]); 317 | } 318 | else { 319 | app.factory(name, factory); 320 | } 321 | return app; 322 | }; 323 | 324 | 325 | app.registerService = function(name, service) { 326 | if (app.lazy) { 327 | app.lazy.registerService([name, service]); 328 | } 329 | else { 330 | app.service(name, service); 331 | } 332 | return app; 333 | }; 334 | 335 | app.registerDirective = function(name, directive) { 336 | if (app.lazy) { 337 | app.lazy.registerDirective([name, directive]); 338 | } 339 | else { 340 | app.directive(name, directive); 341 | } 342 | return app; 343 | }; 344 | 345 | app.registerDecorator = function(name, decorator) { 346 | if (app.lazy) { 347 | app.lazy.registerDecorator([name, decorator]); 348 | } 349 | else { 350 | app.decorator(name, decorator); 351 | } 352 | return app; 353 | }; 354 | 355 | app.registerProvider = function(name, provider) { 356 | if (app.lazy) { 357 | app.lazy.registerProvider([name, provider]); 358 | } 359 | else { 360 | app.provider(name, provider); 361 | } 362 | return app; 363 | }; 364 | 365 | app.registerValue = function(name, value) { 366 | if (app.lazy) { 367 | app.lazy.registerValue([name, value]); 368 | } 369 | else { 370 | app.value(name, value); 371 | } 372 | return app; 373 | }; 374 | 375 | app.registerConstant = function(name, value) { 376 | if (app.lazy) { 377 | app.lazy.registerConstant([name, value]); 378 | } 379 | else { 380 | app.constant(name, value); 381 | } 382 | return app; 383 | }; 384 | 385 | 386 | app.registerFilter = function(name, filter) { 387 | if (app.lazy) { 388 | app.lazy.registerFilter([name, filter]); 389 | } 390 | else { 391 | app.filter(name, filter); 392 | } 393 | return app; 394 | }; 395 | 396 | /** 397 | * extendInjectable Prototypically extends an injectable object from 398 | * another injectable object. Supports $inject-property-style injections 399 | * (e.g. CtrlFunc.$inject = ['$scope'];) and array notation 400 | * (e.g. ['$scope', function($scope) {...}]). 401 | * 402 | * @param object parent Parent object from which to extend. 403 | * @param object child Child object to receive into. 404 | * @return object The prototypically extended object. 405 | */ 406 | app.extendInjectable = function(parent, child) { 407 | 408 | // split up injections and constructor 409 | function disassembleInjected(object) { 410 | if (angular.isArray(object)) { 411 | var func = object.slice(object.length - 1)[0]; 412 | return [func, object.slice(0, object.length - 1)]; 413 | } 414 | else { 415 | var injections = object.$inject; 416 | return [object, injections || []]; 417 | } 418 | } 419 | 420 | parentPieces = disassembleInjected(parent); 421 | childPieces = disassembleInjected(child); 422 | 423 | // combined constructor. 424 | function CombinedConstructor() { 425 | var args = Array.prototype.slice.call(arguments); 426 | 427 | parentPieces[0].apply(this, args.slice(0, parentPieces[1].length)); 428 | childPieces[0].apply(this, args.slice(parentPieces[1].length)); 429 | } 430 | 431 | // combined object target 432 | function Inherit() {} 433 | // child's prototype will already be present 434 | Inherit.prototype = parentPieces[0].prototype; 435 | 436 | // instantiate it without calling constructor 437 | CombinedConstructor.prototype = new Inherit(); 438 | 439 | // ask for everything. 440 | CombinedConstructor.$inject = 441 | [].concat(parentPieces[1]).concat(childPieces[1]); 442 | 443 | return CombinedConstructor; 444 | }; 445 | 446 | 447 | }; 448 | 449 | }; 450 | 451 | 452 | if ( typeof(define) === 'function' && define.amd) { 453 | // expose couch potato as an AMD module depending on 'angular' 454 | // since we use angular from window, apps are not required 455 | // to export the angular object from a shim. 456 | define(['angular'], function() { return new CouchPotato(window.angular); }); 457 | } 458 | else { 459 | window.couchPotato = new CouchPotato(angular); 460 | } 461 | }()); 462 | -------------------------------------------------------------------------------- /dist/dependencies/angular-ui-router.js: -------------------------------------------------------------------------------- 1 | /** 2 | * State-based routing for AngularJS 3 | * @version v0.2.3 4 | * @link http://angular-ui.github.com/ 5 | * @license MIT License, http://www.opensource.org/licenses/MIT 6 | */ 7 | (function(r,t,e){"use strict";function n(r,t){return P(new(P(function(){},{prototype:r})),t)}function a(r){return y(arguments,function(t){t!==r&&y(t,function(t,e){r.hasOwnProperty(e)||(r[e]=t)})}),r}function o(r,t){var e=[];for(var n in r.path)if(""!==r.path[n]){if(!t.path[n])break;e.push(r.path[n])}return e}function i(r,t,e,n){var a,i=o(e,n),u={},s=[];for(var l in i)if(i[l].params&&i[l].params.length){a=i[l].params;for(var c in a)s.indexOf(a[c])>=0||(s.push(a[c]),u[a[c]]=r[a[c]])}return P({},u,t)}function u(r,t){var n=1,o=2,i={},u=[],s=i,l=P(r.when(i),{$$promises:i,$$values:i});this.study=function(i){function c(r,e){if(v[e]!==o){if(p.push(e),v[e]===n)throw p.splice(0,p.indexOf(e)),Error("Cyclic dependency: "+p.join(" -> "));if(v[e]=n,b(r))h.push(e,[function(){return t.get(e)}],u);else{var a=t.annotate(r);y(a,function(r){r!==e&&i.hasOwnProperty(r)&&c(i[r],r)}),h.push(e,r,a)}p.pop(),v[e]=o}}function f(r){return E(r)&&r.then&&r.$$promises}if(!E(i))throw Error("'invocables' must be an object");var h=[],p=[],v={};return y(i,c),i=p=v=null,function(n,o,i){function u(){--w||(b||a(d,o.$$values),$.$$values=d,$.$$promises=!0,v.resolve(d))}function c(r){$.$$failure=r,v.reject(r)}function p(e,a,o){function s(r){f.reject(r),c(r)}function l(){if(!g($.$$failure))try{f.resolve(t.invoke(a,i,d)),f.promise.then(function(r){d[e]=r,u()},s)}catch(r){s(r)}}var f=r.defer(),h=0;o.forEach(function(r){m.hasOwnProperty(r)&&!n.hasOwnProperty(r)&&(h++,m[r].then(function(t){d[r]=t,--h||l()},s))}),h||l(),m[e]=f.promise}if(f(n)&&i===e&&(i=o,o=n,n=null),n){if(!E(n))throw Error("'locals' must be an object")}else n=s;if(o){if(!f(o))throw Error("'parent' must be a promise returned by $resolve.resolve()")}else o=l;var v=r.defer(),$=v.promise,m=$.$$promises={},d=P({},n),w=1+h.length/3,b=!1;if(g(o.$$failure))return c(o.$$failure),$;o.$$values?(b=a(d,o.$$values),u()):(P(m,o.$$promises),o.then(u,c));for(var x=0,y=h.length;y>x;x+=3)n.hasOwnProperty(h[x])?u():p(h[x],h[x+1],h[x+2]);return $}},this.resolve=function(r,t,e,n){return this.study(r)(t,e,n)}}function s(r,t,e){this.fromConfig=function(r,t,e){return g(r.template)?this.fromString(r.template,t):g(r.templateUrl)?this.fromUrl(r.templateUrl,t):g(r.templateProvider)?this.fromProvider(r.templateProvider,t,e):null},this.fromString=function(r,t){return w(r)?r(t):r},this.fromUrl=function(e,n){return w(e)&&(e=e(n)),null==e?null:r.get(e,{cache:t}).then(function(r){return r.data})},this.fromProvider=function(r,t,n){return e.invoke(r,null,n||{params:t})}}function l(r){function t(t){if(!/^\w+(-+\w+)*$/.test(t))throw Error("Invalid parameter name '"+t+"' in pattern '"+r+"'");if(o[t])throw Error("Duplicate parameter name '"+t+"' in pattern '"+r+"'");o[t]=!0,l.push(t)}function e(r){return r.replace(/[\\\[\]\^$*+?.()|{}]/g,"\\$&")}var n,a=/([:*])(\w+)|\{(\w+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,o={},i="^",u=0,s=this.segments=[],l=this.params=[];this.source=r;for(var c,f,h;(n=a.exec(r))&&(c=n[2]||n[3],f=n[4]||("*"==n[1]?".*":"[^/]*"),h=r.substring(u,n.index),!(h.indexOf("?")>=0));)i+=e(h)+"("+f+")",t(c),s.push(h),u=a.lastIndex;h=r.substring(u);var p=h.indexOf("?");if(p>=0){var v=this.sourceSearch=h.substring(p);h=h.substring(0,p),this.sourcePath=r.substring(0,u+p),y(v.substring(1).split(/[&?]/),t)}else this.sourcePath=r,this.sourceSearch="";i+=e(h)+"$",s.push(h),this.regexp=RegExp(i),this.prefix=s[0]}function c(){this.compile=function(r){return new l(r)},this.isMatcher=function(r){return E(r)&&w(r.exec)&&w(r.format)&&w(r.concat)},this.$get=function(){return this}}function f(r){function t(r){var t=/^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(r.source);return null!=t?t[1].replace(/\\(.)/g,"$1"):""}function e(r,t){return r.replace(/\$(\$|\d{1,2})/,function(r,e){return t["$"===e?0:Number(e)]})}function n(r,t,e){if(!e)return!1;var n=r.invoke(t,t,{$match:e});return g(n)?n:!0}var a=[],o=null;this.rule=function(r){if(!w(r))throw Error("'rule' must be a function");return a.push(r),this},this.otherwise=function(r){if(b(r)){var t=r;r=function(){return t}}else if(!w(r))throw Error("'rule' must be a function");return o=r,this},this.when=function(a,o){var i,u=b(o);if(b(a)&&(a=r.compile(a)),!u&&!w(o)&&!x(o))throw Error("invalid 'handler' in when()");var s={matcher:function(t,e){return u&&(i=r.compile(e),e=["$match",function(r){return i.format(r)}]),P(function(r,a){return n(r,e,t.exec(a.path(),a.search()))},{prefix:b(t.prefix)?t.prefix:""})},regex:function(r,a){if(r.global||r.sticky)throw Error("when() RegExp must not be global or sticky");return u&&(i=a,a=["$match",function(r){return e(i,r)}]),P(function(t,e){return n(t,a,r.exec(e.path()))},{prefix:t(r)})}},l={matcher:r.isMatcher(a),regex:a instanceof RegExp};for(var c in l)if(l[c])return this.rule(s[c](a,o));throw Error("invalid 'what' in when()")},this.$get=["$location","$rootScope","$injector",function(r,t,e){function n(){function t(t){var n=t(e,r);return n?(b(n)&&r.replace().url(n),!0):!1}var n,i=a.length;for(n=0;i>n;n++)if(t(a[n]))return;o&&t(o)}return t.$on("$locationChangeSuccess",n),{}}]}function h(r,a,o){function u(r,t){var n=b(r),a=n?r:r.name,o=0===a.indexOf(".")||0===a.indexOf("^");if(o){if(!t)throw Error("No reference point given for path '"+a+"'");for(var i=a.split("."),u=0,s=i.length,l=t;s>u;u++)if(""!==i[u]||0!==u){if("^"!==i[u])break;if(!l.parent)throw Error("Path '"+a+"' not valid for state '"+t.name+"'");l=l.parent}else l=t;i=i.slice(u).join("."),a=l.name+(l.name&&i?".":"")+i}var c=m[a];return!c||!n&&(n||c!==r&&c.self!==r)?e:c}function s(t){t=n(t,{self:t,resolve:t.resolve||{},toString:function(){return this.name}});var e=t.name;if(!b(e)||e.indexOf("@")>=0)throw Error("State must have a valid name");if(m[e])throw Error("State '"+e+"'' is already defined");for(var a in d)t[a]=d[a](t);return m[e]=t,!t["abstract"]&&t.url&&r.when(t.url,["$match","$stateParams",function(r,e){$.$current.navigable==t&&h(r,e)||$.transitionTo(t,r,!1)}]),t}function l(r,t){return E(r)?t=r:t.name=r,s(t),this}function c(r,t,a,s,l,c,m){function d(r,e,n,o,i){var u=n?e:p(r.params,e),s={$stateParams:u};i.resolve=l.resolve(r.resolve,s,i.resolve,r);var c=[i.resolve.then(function(r){i.globals=r})];return o&&c.push(o),y(r.views,function(t,e){var n=t.resolve&&t.resolve!==r.resolve?t.resolve:{};n.$template=[function(){return a.load(e,{view:t,locals:s,params:u,notify:!1})||""}],c.push(l.resolve(n,s,i.resolve,r).then(function(n){n.$$controller=t.controller,n.$$state=r,i[e]=n}))}),t.all(c).then(function(){return i})}var w=t.reject(Error("transition superseded")),b=t.reject(Error("transition prevented"));return v.locals={resolve:null,globals:{$stateParams:{}}},$={params:{},current:v.self,$current:v,transition:null},$.go=function(r,t,e){return this.transitionTo(r,t,P({inherit:!0,relative:$.$current},e))},$.transitionTo=function(e,a,o){g(o)||(o=o===!0||o===!1?{location:o}:{}),a=a||{},o=P({location:!0,inherit:!1,relative:null},o);var l=u(e,o.relative);if(!g(l))throw Error("No such state "+l);if(l["abstract"])throw Error("Cannot transition to abstract state '"+e+"'");o.inherit&&(a=i(c,a||{},$.$current,l)),e=l;var p,E,x=e.path,y=$.$current,C=$.params,S=y.path,O=v.locals,k=[];for(p=0,E=x[p];E&&E===S[p]&&h(a,C,E.ownParams);p++,E=x[p])O=k[p]=E.locals;if(e===y&&O===y.locals)return $.transition=null,t.when($.current);a=f(e.params,a||{});var R=r.$broadcast("$stateChangeStart",e.self,a,y.self,C);if(R.defaultPrevented)return b;for(var I=t.when(O),M=p;x.length>M;M++,E=x[M])O=k[M]=n(O),I=d(E,a,E===e,I,O);var U=$.transition=I.then(function(){var t,n,i;if($.transition!==U)return w;for(t=S.length-1;t>=p;t--)i=S[t],i.self.onExit&&s.invoke(i.self.onExit,i.self,i.locals.globals),i.locals=null;for(t=p;x.length>t;t++)n=x[t],n.locals=k[t],n.self.onEnter&&s.invoke(n.self.onEnter,n.self,n.locals.globals);$.$current=e,$.current=e.self,$.params=a,j($.params,c),$.transition=null;var u=e.navigable;return o.location&&u&&m.url(u.url.format(u.locals.globals.$stateParams)),r.$broadcast("$stateChangeSuccess",e.self,a,y.self,C),$.current},function(n){return $.transition!==U?w:($.transition=null,r.$broadcast("$stateChangeError",e.self,a,y.self,C,n),t.reject(n))});return U},$.is=function(r){var t=u(r);return g(t)?$.$current===t:e},$.includes=function(r){var t=u(r);return g(t)?g($.$current.includes[t.name]):e},$.href=function(r,t,e){e=P({lossy:!0,inherit:!1,relative:$.$current},e||{});var n=u(r,e.relative);if(!g(n))return null;t=i(c,t||{},$.$current,n);var a=n&&e.lossy?n.navigable:n,s=a&&a.url?a.url.format(f(n.params,t||{})):null;return!o.html5Mode()&&s?"#"+s:s},$.get=function(r){var t=u(r);return t&&t.self?t.self:null},$}function f(r,t){var e={};return y(r,function(r){var n=t[r];e[r]=null!=n?n+"":null}),e}function h(r,t,e){if(!e){e=[];for(var n in r)e.push(n)}for(var a=0;e.length>a;a++){var o=e[a];if(r[o]!=t[o])return!1}return!0}function p(r,t){var e={};return y(r,function(r){e[r]=t[r]}),e}var v,$,m={},d={parent:function(r){if(g(r.parent)&&r.parent)return u(r.parent);var t=/^(.+)\.[^.]+$/.exec(r.name);return t?u(t[1]):v},data:function(r){return r.parent&&r.parent.data&&(r.data=r.self.data=t.extend({},r.parent.data,r.data)),r.data},url:function(r){var t=r.url;if(b(t))return"^"==t.charAt(0)?a.compile(t.substring(1)):(r.parent.navigable||v).url.concat(t);if(a.isMatcher(t)||null==t)return t;throw Error("Invalid url '"+t+"' in state '"+r+"'")},navigable:function(r){return r.url?r:r.parent?r.parent.navigable:null},params:function(r){if(!r.params)return r.url?r.url.parameters():r.parent.params;if(!x(r.params))throw Error("Invalid params in state '"+r+"'");if(r.url)throw Error("Both params and url specicified in state '"+r+"'");return r.params},views:function(r){var t={};return y(g(r.views)?r.views:{"":r},function(e,n){0>n.indexOf("@")&&(n+="@"+r.parent.name),t[n]=e}),t},ownParams:function(r){if(!r.parent)return r.params;var t={};y(r.params,function(r){t[r]=!0}),y(r.parent.params,function(e){if(!t[e])throw Error("Missing required parameter '"+e+"' in state '"+r.name+"'");t[e]=!1});var e=[];return y(t,function(r,t){r&&e.push(t)}),e},path:function(r){return r.parent?r.parent.path.concat(r):[]},includes:function(r){var t=r.parent?P({},r.parent.includes):{};return t[r.name]=!0,t}};v=s({name:"",url:"^",views:null,"abstract":!0}),v.navigable=null,this.state=l,this.$get=c,c.$inject=["$rootScope","$q","$view","$injector","$resolve","$stateParams","$location","$urlRouter"]}function p(){function r(r,t){return{load:function(e,n){var a,o={template:null,controller:null,view:null,locals:null,notify:!0,async:!0,params:{}};return n=P(o,n),n.view&&(a=t.fromConfig(n.view,n.params,n.locals)),a&&n.notify&&r.$broadcast("$viewContentLoading",n),a}}}this.$get=r,r.$inject=["$rootScope","$templateFactory"]}function v(r,e,n,a,o){var i;try{i=a.get("$animator")}catch(u){}var s=!1,l={restrict:"ECA",terminal:!0,transclude:!0,compile:function(a,u,c){return function(a,u,f){function h(t){var i=r.$current&&r.$current.locals[$];if(i!==v){var s=w(d&&t);if(s.remove(u),p&&(p.$destroy(),p=null),!i)return v=null,E.state=null,s.restore(c(a),u);v=i,E.state=i.$$state;var l=e(s.populate(i.$template,u));if(p=a.$new(),i.$$controller){i.$scope=p;var f=n(i.$$controller,i);u.children().data("$ngControllerController",f)}l(p),p.$emit("$viewContentLoaded"),m&&p.$eval(m),o()}}var p,v,$=f[l.name]||f.name||"",m=f.onload||"",d=g(i)&&i(a,f),w=function(r){return{"true":{remove:function(r){d.leave(r.contents(),r)},restore:function(r,t){d.enter(r,t)},populate:function(r,e){var n=t.element("").html(r).contents();return d.enter(n,e),n}},"false":{remove:function(r){r.html("")},restore:function(r,t){t.append(r)},populate:function(r,t){return t.html(r),t.contents()}}}[""+r]};u.append(c(a));var b=u.parent().inheritedData("$uiView");0>$.indexOf("@")&&($=$+"@"+(b?b.state.name:""));var E={name:$,state:null};u.data("$uiView",E);var x=function(){if(!s){s=!0;try{h(!0)}catch(r){throw s=!1,r}s=!1}};a.$on("$stateChangeSuccess",x),a.$on("$viewContentLoading",x),h(!1)}}};return l}function $(r){var t=r.match(/^([^(]+?)\s*(\((.*)\))?$/);if(!t||4!==t.length)throw Error("Invalid state ref '"+r+"'");return{state:t[1],paramExpr:t[3]||null}}function m(r){return{restrict:"A",link:function(t,n,a){var o=$(a.uiSref),i=null,u=r.$current,s="FORM"===n[0].nodeName,l=s?"action":"href",c=!0,f=n.parent().inheritedData("$uiView");f&&f.state&&f.state.name&&(u=f.state);var h=function(t){if(t&&(i=t),c){var a=r.href(o.state,i,{relative:u});return a?(n[0][l]=a,e):(c=!1,!1)}};o.paramExpr&&(t.$watch(o.paramExpr,function(r,t){r!==t&&h(r)},!0),i=t.$eval(o.paramExpr)),h(),s||n.bind("click",function(e){1!=e.which||e.ctrlKey||e.metaKey||e.shiftKey||(r.go(o.state,i,{relative:u}),t.$apply(),e.preventDefault())})}}}function d(r,t){function a(r){this.locals=r.locals.globals,this.params=this.locals.$stateParams}function o(){this.locals=null,this.params=null}function i(e,i){if(null!=i.redirectTo){var u,l=i.redirectTo;if(b(l))u=l;else{if(!w(l))throw Error("Invalid 'redirectTo' in when()");u=function(r,t){return l(r,t.path(),t.search())}}t.when(e,u)}else r.state(n(i,{parent:null,name:"route:"+encodeURIComponent(e),url:e,onEnter:a,onExit:o}));return s.push(i),this}function u(r,t,n){function a(r){return""!==r.name?r:e}var o={routes:s,params:n,current:e};return t.$on("$stateChangeStart",function(r,e,n,o){t.$broadcast("$routeChangeStart",a(e),a(o))}),t.$on("$stateChangeSuccess",function(r,e,n,i){o.current=a(e),t.$broadcast("$routeChangeSuccess",a(e),a(i)),j(n,o.params)}),t.$on("$stateChangeError",function(r,e,n,o,i,u){t.$broadcast("$routeChangeError",a(e),a(o),u)}),o}var s=[];a.$inject=["$$state"],this.when=i,this.$get=u,u.$inject=["$state","$rootScope","$routeParams"]}var g=t.isDefined,w=t.isFunction,b=t.isString,E=t.isObject,x=t.isArray,y=t.forEach,P=t.extend,j=t.copy;t.module("ui.router.util",["ng"]),t.module("ui.router.router",["ui.router.util"]),t.module("ui.router.state",["ui.router.router","ui.router.util"]),t.module("ui.router",["ui.router.state"]),t.module("ui.router.compat",["ui.router"]),u.$inject=["$q","$injector"],t.module("ui.router.util").service("$resolve",u),s.$inject=["$http","$templateCache","$injector"],t.module("ui.router.util").service("$templateFactory",s),l.prototype.concat=function(r){return new l(this.sourcePath+r+this.sourceSearch)},l.prototype.toString=function(){return this.source},l.prototype.exec=function(r,t){var e=this.regexp.exec(r);if(!e)return null;var n,a=this.params,o=a.length,i=this.segments.length-1,u={};if(i!==e.length-1)throw Error("Unbalanced capture group in route '"+this.source+"'");for(n=0;i>n;n++)u[a[n]]=e[n+1];for(;o>n;n++)u[a[n]]=t[a[n]];return u},l.prototype.parameters=function(){return this.params},l.prototype.format=function(r){var t=this.segments,e=this.params;if(!r)return t.join("");var n,a,o,i=t.length-1,u=e.length,s=t[0];for(n=0;i>n;n++)o=r[e[n]],null!=o&&(s+=encodeURIComponent(o)),s+=t[n+1];for(;u>n;n++)o=r[e[n]],null!=o&&(s+=(a?"&":"?")+e[n]+"="+encodeURIComponent(o),a=!0);return s},t.module("ui.router.util").provider("$urlMatcherFactory",c),f.$inject=["$urlMatcherFactoryProvider"],t.module("ui.router.router").provider("$urlRouter",f),h.$inject=["$urlRouterProvider","$urlMatcherFactoryProvider","$locationProvider"],t.module("ui.router.state").value("$stateParams",{}).provider("$state",h),p.$inject=[],t.module("ui.router.state").provider("$view",p),v.$inject=["$state","$compile","$controller","$injector","$anchorScroll"],t.module("ui.router.state").directive("uiView",v),m.$inject=["$state"],t.module("ui.router.state").directive("uiSref",m),d.$inject=["$stateProvider","$urlRouterProvider"],t.module("ui.router.compat").provider("$route",d).directive("ngView",v)})(window,window.angular); 8 | -------------------------------------------------------------------------------- /dist/docs/css/animations.css: -------------------------------------------------------------------------------- 1 | .reveal { 2 | -webkit-transition:1s linear all; 3 | -moz-transition:1s linear all; 4 | -o-transition:1s linear all; 5 | transition:1s linear all; 6 | 7 | opacity:0; 8 | } 9 | .reveal.reveal-active { 10 | opacity:1; 11 | } 12 | 13 | .slide-reveal { 14 | -webkit-transition:0.5s linear all; 15 | -moz-transition:0.5s linear all; 16 | -o-transition:0.5s linear all; 17 | transition:0.5s linear all; 18 | opacity:0.5; 19 | 20 | position:relative; 21 | opacity:0; 22 | top:10px; 23 | } 24 | .slide-reveal.slide-reveal-active { 25 | top:0; 26 | opacity:1; 27 | } 28 | 29 | .expand-enter { 30 | -webkit-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all; 31 | -moz-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all; 32 | -o-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all; 33 | transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all; 34 | 35 | opacity:0; 36 | max-height:0; 37 | overflow:hidden; 38 | } 39 | .expand-enter.expand-enter-active { 40 | opacity:1; 41 | max-height:40px; 42 | } 43 | 44 | .expand-leave { 45 | -webkit-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all; 46 | -moz-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all; 47 | -o-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all; 48 | transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all; 49 | 50 | opacity:1; 51 | max-height:40px; 52 | overflow:hidden; 53 | } 54 | .expand-leave.expand-leave-active { 55 | opacity:0; 56 | max-height:0; 57 | } 58 | 59 | .example-animate-container { 60 | position:relative; 61 | background:white; 62 | border:1px solid black; 63 | height:40px; 64 | overflow:hidden; 65 | } 66 | 67 | .example-animate-container > div { 68 | padding:1em; 69 | } 70 | 71 | .animator-container.animations-off * { 72 | -webkit-transition: none; 73 | -moz-transition: none; 74 | -o-transition: color 0 ease-in; /* opera is special :) */ 75 | transition: none; 76 | } 77 | -------------------------------------------------------------------------------- /dist/docs/css/doc_widgets.css: -------------------------------------------------------------------------------- 1 | ul.doc-example { 2 | list-style-type: none; 3 | position: relative; 4 | font-size: 14px; 5 | } 6 | 7 | ul.doc-example > li { 8 | border: 2px solid gray; 9 | border-radius: 5px; 10 | -moz-border-radius: 5px; 11 | -webkit-border-radius: 5px; 12 | background-color: white; 13 | margin-bottom: 20px; 14 | } 15 | 16 | ul.doc-example > li.doc-example-heading { 17 | border: none; 18 | border-radius: none; 19 | margin-bottom: -10px; 20 | } 21 | 22 | span.nojsfiddle { 23 | float: right; 24 | font-size: 14px; 25 | margin-right:10px; 26 | margin-top: 10px; 27 | } 28 | 29 | form.jsfiddle { 30 | position: absolute; 31 | right: 0; 32 | z-index: 1; 33 | height: 14px; 34 | } 35 | 36 | form.jsfiddle button { 37 | cursor: pointer; 38 | padding: 4px 10px; 39 | margin: 10px; 40 | background-color: #FFF; 41 | font-weight: bold; 42 | color: #7989D6; 43 | border-color: #7989D6; 44 | -moz-border-radius: 8px; 45 | -webkit-border-radius:8px; 46 | border-radius: 8px; 47 | } 48 | 49 | form.jsfiddle textarea, form.jsfiddle input { 50 | display: none; 51 | } 52 | 53 | li.doc-example-live { 54 | padding: 10px; 55 | font-size: 1.2em; 56 | } 57 | 58 | div.syntaxhighlighter { 59 | padding-bottom: 1px !important; /* fix to remove unnecessary scrollbars http://is.gd/gSMgC */ 60 | } 61 | 62 | /* TABS - tutorial environment navigation */ 63 | 64 | div.tabs-nav { 65 | height: 25px; 66 | position: relative; 67 | } 68 | 69 | div.tabs-nav ul li { 70 | list-style: none; 71 | display: inline-block; 72 | padding: 5px 10px; 73 | } 74 | 75 | div.tabs-nav ul li.current a { 76 | color: white; 77 | text-decoration: none; 78 | } 79 | 80 | div.tabs-nav ul li.current { 81 | background: #7989D6; 82 | -moz-box-shadow: 4px 4px 6px #48577D; 83 | -moz-border-radius-topright: 8px; 84 | -moz-border-radius-topleft: 8px; 85 | box-shadow: 4px 4px 6px #48577D; 86 | border-radius-topright: 8px; 87 | border-radius-topleft: 8px; 88 | -webkit-box-shadow: 4px 4px 6px #48577D; 89 | -webkit-border-top-right-radius: 8px; 90 | -webkit-border-top-left-radius: 8px; 91 | border-top-right-radius: 8px; 92 | border-top-left-radius: 8px; 93 | } 94 | 95 | div.tabs-content { 96 | padding: 4px; 97 | position: relative; 98 | background: #7989D6; 99 | -moz-border-radius: 8px; 100 | border-radius: 8px; 101 | -webkit-border-radius: 8px; 102 | } 103 | 104 | div.tabs-content-inner { 105 | margin: 1px; 106 | padding: 10px; 107 | background: white; 108 | border-radius: 6px; 109 | -moz-border-radius: 6px; 110 | -webkit-border-radius: 6px; 111 | } 112 | 113 | 114 | /* Tutorial Nav Bar */ 115 | 116 | #tutorial-nav { 117 | margin: 0.5em 0 1em 0; 118 | padding: 0; 119 | list-style-type: none; 120 | background: #7989D6; 121 | 122 | -moz-border-radius: 15px; 123 | -webkit-border-radius: 15px; 124 | border-radius: 15px; 125 | 126 | -moz-box-shadow: 4px 4px 6px #48577D; 127 | -webkit-box-shadow: 4px 4px 6px #48577D; 128 | box-shadow: 4px 4px 6px #48577D; 129 | } 130 | 131 | 132 | #tutorial-nav li { 133 | display: inline; 134 | } 135 | 136 | 137 | #tutorial-nav a:link, #tutorial-nav a:visited { 138 | font-size: 1.2em; 139 | color: #FFF; 140 | text-decoration: none; 141 | text-align: center; 142 | display: inline-block; 143 | width: 11em; 144 | padding: 0.2em 0; 145 | } 146 | 147 | 148 | #tutorial-nav a:hover { 149 | color: #000; 150 | } 151 | -------------------------------------------------------------------------------- /dist/docs/css/docs.css: -------------------------------------------------------------------------------- 1 | img.AngularJS-small { 2 | width: 95px; 3 | height: 25px; 4 | } 5 | 6 | /* this is here to avoid the display=block shuffling of ngShow */ 7 | .breadcrumb li > * { 8 | float:left; 9 | margin:0 2px 0 0; 10 | } 11 | 12 | .breadcrumb { 13 | padding-top: 6px; 14 | padding-bottom: 0; 15 | line-height: 18px 16 | } 17 | 18 | .header img { 19 | max-height: 40px; 20 | padding-right: 20px; 21 | } 22 | 23 | .header img+.brand { 24 | padding: 10px 20px 10px 10px; 25 | } 26 | 27 | .clear-navbar { 28 | margin-top: 60px; 29 | } 30 | 31 | .footer { 32 | padding-top: 2em; 33 | background-color: #333; 34 | color: white; 35 | padding-bottom: 2em; 36 | } 37 | 38 | .spacer { 39 | height: 1em; 40 | } 41 | 42 | 43 | .icon-cog { 44 | line-height: 13px; 45 | } 46 | 47 | /* =============================== */ 48 | 49 | .form-search { 50 | margin-right: 10px; 51 | } 52 | 53 | .form-search .search-query { 54 | width: 180px; 55 | width: 200px \9; 56 | } 57 | 58 | .form-search .dropdown-menu { 59 | margin-left: 10px; 60 | } 61 | 62 | .form-search .code { 63 | font-family: monospace; 64 | font-weight: bold; 65 | font-size: 13px; 66 | color: black; 67 | } 68 | 69 | .form-search > ul.nav > li > a { 70 | margin: 0; 71 | } 72 | 73 | .form-search > ul.nav > li.module { 74 | background-color: #d3d3d3; 75 | } 76 | 77 | .form-search > ul.nav > li.section { 78 | background-color: #ebebeb; 79 | min-height: 14px; 80 | } 81 | 82 | .form-search > ul.nav > li.first { 83 | padding-top: 6px; 84 | } 85 | 86 | .form-search > ul.nav > li.last { 87 | padding-bottom: 6px; 88 | } 89 | 90 | .form-search > ul.nav > li.last + li.api-list-item { 91 | margin-top: -6px; 92 | padding-bottom: 6px; 93 | } 94 | 95 | .form-search .well { 96 | border-color: #d3d3d3; 97 | padding: 0; 98 | margin-bottom: 15px; 99 | } 100 | 101 | .form-search .well .nav-header { 102 | text-transform: none; 103 | padding: 3px 1px; 104 | margin: 0; 105 | } 106 | 107 | .form-search .well .nav-header a { 108 | text-transform: none; 109 | color: black; 110 | } 111 | .form-search .well .nav-header a:hover { 112 | background-color: inherit; 113 | } 114 | 115 | .form-search .well li { 116 | line-height: 14px; 117 | } 118 | 119 | .form-search .well li a:focus { 120 | outline: none; 121 | } 122 | 123 | .form-search .well .guide { 124 | float: right; 125 | padding-top: 0; 126 | color: gray; 127 | } 128 | 129 | .form-search .module .guide { 130 | line-height: 20px; 131 | } 132 | 133 | .match > a, .nav > .match > a:hover { 134 | background-color: #dbeaf4; 135 | } 136 | 137 | /* =============================== */ 138 | /* Content */ 139 | /* =============================== */ 140 | 141 | .improve-docs { 142 | float: right; 143 | } 144 | 145 | .hint { 146 | font-size: .6em; 147 | color: #c0c0c0; 148 | } 149 | 150 | .content code { 151 | background-color: inherit; 152 | color: inherit; 153 | border: none; 154 | padding: 0; 155 | font-size: inherit; 156 | font-family: monospace; 157 | } 158 | 159 | .content h2, 160 | .content h3, 161 | .content h4, 162 | .content h5 { 163 | margin: 1em 0 5px; 164 | } 165 | 166 | .content h1 { 167 | font-size: 30px; 168 | line-height: 36px; 169 | } 170 | 171 | .content h2 { 172 | font-size: 24px; 173 | line-height: 36px; 174 | } 175 | 176 | .content h3 { 177 | line-height: 27px; 178 | font-size: 18px; 179 | } 180 | 181 | .content h4 { 182 | font-size: 15px; 183 | } 184 | 185 | ul.parameters > li > p, 186 | .returns > p { 187 | display: inline; 188 | } 189 | 190 | ul.methods > li, 191 | ul.properties > li, 192 | ul.events > li { 193 | list-style: none; 194 | min-height: 20px; 195 | padding: 19px; 196 | margin-bottom: 20px; 197 | background-color: #f5f5f5; 198 | border: 1px solid #eee; 199 | border: 1px solid rgba(0, 0, 0, 0.05); 200 | -webkit-border-radius: 4px; 201 | -moz-border-radius: 4px; 202 | border-radius: 4px; 203 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); 204 | -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); 205 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); 206 | } 207 | 208 | .member.method > h2, 209 | .member.property > h2, 210 | .member.event > h2 { 211 | margin-bottom: .5em; 212 | } 213 | 214 | ul.methods > li > h3, 215 | ul.properties > li > h3, 216 | ul.events > li > h3 { 217 | margin: -19px -19px 1em -19px; 218 | padding: .25em 19px; 219 | background-color: #d3d3d3; 220 | font-family: monospace; 221 | } 222 | 223 | .center { 224 | display: block; 225 | margin: 2em auto; 226 | } 227 | 228 | .diagram { 229 | display: block; 230 | margin: 2em auto; 231 | padding: 1em; 232 | border: 1px solid black; 233 | 234 | -moz-box-shadow: 4px 4px 6px #48577D; 235 | -webkit-box-shadow: 4px 4px 6px #48577D; 236 | box-shadow: 4px 4px 6px #48577D; 237 | 238 | -moz-border-radius: 15px; 239 | -webkit-border-radius: 15px; 240 | border-radius: 15px; 241 | } 242 | 243 | .tutorial-nav { 244 | margin-left: 175px; 245 | color: black; 246 | margin-top: 2em; 247 | margin-bottom: 2em; 248 | } 249 | 250 | .tutorial-nav a { 251 | color: white; 252 | } 253 | 254 | .tutorial-nav a:hover { 255 | color: white; 256 | text-decoration: none; 257 | } 258 | 259 | .clear { 260 | clear: both; 261 | } 262 | -------------------------------------------------------------------------------- /dist/docs/css/style.css: -------------------------------------------------------------------------------- 1 | div[role=main] div.row div.span3 { 2 | width: 240px; 3 | } 4 | div[role=main] div.row div.span9 { 5 | width: 680px; 6 | } 7 | -------------------------------------------------------------------------------- /dist/docs/font/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laurelnaiad/angular-couch-potato/fea40f7547890d2914c673e18dd1e752b4b110b3/dist/docs/font/FontAwesome.otf -------------------------------------------------------------------------------- /dist/docs/font/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laurelnaiad/angular-couch-potato/fea40f7547890d2914c673e18dd1e752b4b110b3/dist/docs/font/fontawesome-webfont.eot -------------------------------------------------------------------------------- /dist/docs/font/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laurelnaiad/angular-couch-potato/fea40f7547890d2914c673e18dd1e752b4b110b3/dist/docs/font/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /dist/docs/font/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laurelnaiad/angular-couch-potato/fea40f7547890d2914c673e18dd1e752b4b110b3/dist/docs/font/fontawesome-webfont.woff -------------------------------------------------------------------------------- /dist/docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | 14 |
2 |
3 |
4 | You can use the navbar on the left to search (see footnote), or 7 | click an item from the table of contents.
8 | 9 |If you're looking for an overview of Couch Potato or want to know how to 10 | get started, try the guide.
11 | 12 |Footnote: Well, you should be able to use the search, but it's broken in this version of Angular docs -- you can't (usefully) click on the results it finds :(
$couchPotato
2 | (service in module scs.couch-potato
3 | )
4 | ==
7 | 8 |Important: you must inject the 9 | $couchPotatoProvider 10 | at config-time to use the service.
$couchPotatoProvider
2 | (service in module scs.couch-potato
3 | )
4 | Injects and retains references to providers that will be used 7 | by the $couchPotato service 8 | at run-time.
9 | 10 |It is mandatory that you inject the provider before 11 | your app's module.run is called (e.g. in module.config).
$controllerProvider
14 | $compileProvider
16 | $filterProvider
18 | $provide
20 | 24 | myModule.config( 25 | [ 26 | '$couchPotatoProvider', 'myOtherProvider', 27 | function($couchPotatoProvider, myOtherProvider) { 28 | myOtherProvider.config = { someParam: 'demo' }; 29 | // $couchPotatoProvider needs no specific configuration 30 | } 31 | ] 32 | ); 33 |34 | 35 |
See the couch-potato module documentation to learn 36 | how to load the module.
2 |
3 | Couch Potato needs RequireJS in order to be useful. However, it is not 9 | necessary for your application to be bootstrapped using AMD. You have 10 | two options:
11 | 12 |If you use traditional script tags to load the module (i.e. you aren't 15 | using AMD to structure your the non-lazy portion of your application, 16 | you must load the following three scripts in this order (other 17 | modules can be loaded wherever it makes sense for you, but these three 18 | must follow the order):
19 | 20 |RequireJS
24 | 25 |26 | <!-- in index.html --> 27 | <script src="/js/angular.min.js"></script> 28 | <script src="/js/angular-couch-potato.min.js"></script> 29 | <script src="/js/require.min.js"></script> 30 |
If you use RequireJS, Couch Potato will first try to use an AMD module
36 | that is defined with the name 'angular'
. If it does not find
37 | that, it will try to use an angular object defined as
38 | window.angular
. This flexibility allows you to load angular
39 | from a script tag (if you do so before your require.js script tag)
40 | or from RequireJS -- the distinction will be critical if you are
41 | using multiple instances of angular (in which case I pity you for
42 | needing to, even though I understand that there are edge cases
43 | where it is necessary) -- it must be very painful.
Reference Couch Potato as a Dependency as follows: 48 |
49 | var myModule = angular.module('myApp', ['myOtherDep', 'scs.couch-potato']); 50 |51 | 52 |
See also the $couchPotatoProvider documentation.
2 |
3 | Couch Potato is useful if you want to delay a few, some or most of your Angular components for speed or scale reasons. It allows you to author all components in one framework, and choose through configuration which components are "compiled in" to your application and which ones are lazy loaded, and to have fine-grained control over when that lazy loading takes place. It can help you organize dependencies for your routes.
9 | 10 |No, Couch Potato isn't a router. It's meant to be used with a router. It is known to work with with current versions of angular-ui-router (recommended) and Angular's stock router (and ngRoute). It is built into angular-detour. It should work in any context where promises are resolved.
13 | 14 |It can also be used outside the context of routing.
15 | 16 |At this time, you cannot undefine or redefine components at run-time.
19 | 20 |No. At least one experiment is known to have established lazy-loading modules to be possible, but it involves hacking Angular.
23 | 24 |What does work: If you have a component that you want to lazy-load and it depends on 25 | a non-Angular-based script library, such as d3 or a jQuery plugin, you can depend on that 26 | library in the Couch Potato context and defer loading it until your component is loaded.
27 | 28 |What doesn't work: If you have a component that you want to lazy-load and it depends on an Angular-based module, e.g. angular-ui/bootstrap, then your application itself must depend on bootstrap at declaration time.
29 | 30 |No. At least one experiment is known to have established lazy-loading modules to be
33 | 34 |It's not.
37 | 38 |For one, Couch Potato doesn't operate at the module level by design. Operating at the component level within application components is the intent of Couch Potato. Module-level dependencies are at a higher-level.
39 | 40 |Two, run-time loading of modules isn't here, yet. Time will tell whether/how Couch Potato will fit into the future AnguarJS landscape.
41 | 42 |Couch Potato tests: the project does not have any tests, yet. It should have tests 46 | before version 1.0.0.
Testing components that are authored as Couch Potato components: guidelines/examples 48 | for testing apps built with Couch Potato. Will probably be based on protractor (new Angular testing framework).
Undefining/redefining components (for scale). RequireJS has an undef function 50 | that may enable this, but it may also be quite complicated. This is pie-in-the-sky stuff 51 | for now.
If the documentation isn't enough and/or you're having problems, use the issues list of the github repository.
57 | 58 |Absolutely. Pull requests are welcome.
2 |
3 | Couch Potato works by preparing itself -- at configuration-time to do -- at 9 | run-time -- the work of registering components.
10 | 11 |In a normal AngularJS application, when run-time starts (which is typically 12 | when module.run happens or when the application is bootstrapped), all providers 13 | become "uninjectable." As such, you lose the ability to configure components.
14 | 15 |By storing references to the key registration "vehicles", Couch Potato can perform 16 | registrations during run-time. The components Couch Potato maintains references 17 | to are:
18 | 19 |Lazy-registration would be of little use if you couldn't lazily load script.
27 | 28 |This is where AMD (Asynchronous Module Definition) comes in. Couch Potato uses RequireJS. If you are unfamiliar with 29 | RequireJS, you'll want to read up a little. The general idea is that an AMD 30 | module definition states a list of dependencies and then specifies its 31 | definition.
32 | 33 |When a module is requested ("required" in AMD terms), all of it dependencies 34 | are fetched (or retrieved from the cache if they've already been required), and 35 | then passed to the module as function parameters. Continue by reading about 36 | Couch Potato Components.
37 | 38 |This guide is about to get heavy. If you haven't already done so, it's highly 39 | recommended that you stop and look at the samples 40 | before you proceed.
2 |
3 | In a traditional AngularJS application, you define and configure all components 7 | before the application runs. The seperation between "configuration time" and 8 | "run time", along with the dependency injection system, enables the programmer 9 | to define all components in any order and to know that they will all be 10 | configured prior to any of them being run. This might sound like a truism!
11 | 12 |There may be application where defining and configuring every component that the 13 | application will use is not optimal. A few reasons to delay are:
14 | 15 |In these cases, one may want to delay loading components. Couch Potato is 22 | intended to make the above cases possible.
23 | 24 |Additionally, you may have an application that, while developing it, you do 25 | not know which components will ultimately be used. For example, in a CMS 26 | (Content Management System), it is conceivable that components that did not 27 | even exist when the application was built are intended to be used within the 28 | application.
29 | 30 |In such a case, you most certainly need to delay loading the component!
31 | 32 |Couch Potato is intended to be part of a solution for this type of loose 33 | binding.
34 | 35 |How It Works provides a relatively high-level explanation 36 | of the methods that Couch Potato employs to allow for lazy loading and 37 | registration of components.
2 |
3 | A typical Couch Potato component might look something like this: 7 |
8 | define(['app', 'lodash', 'myService', function(app, _) { 9 | 10 | app.registerController('myController', ['$scope', '$timeout', '$myService', 11 | 12 | function($scope, $timeout, $myService) { 13 | 14 | $scope.concattedAlphas = ''; 15 | _.each(['a', 'b', 'c'], function(val) { 16 | $scope.concattedAlphas += val; 17 | }); 18 | 19 | $timeout( 20 | function() { 21 | $scope.aValueFromMyService = $myService.aValue; 22 | }, 23 | 1000 24 | ); 25 | } 26 | ]); 27 | }); 28 |29 | 30 |
That really silly controller depends on the AMD modules "lodash" and 31 | "myService".
32 | 33 |lodash is just the lodash utility library. It knows nothing 34 | about Couch Potato (or Angular) and doesn't need to. This is regular RequireJS here.
35 | 36 |On the other hand, "myService" is itself (or at least might be) a Couch Potato AMD module. It is 37 | defined similarly to the one we're defining here (i.e. 38 | app.registerService(...)). Note the use of the word "might". It's also 39 | entirely possible that myService was configured as a non-lazy component. In 40 | fact, when you use Couch Potato and you define components as modules like the 41 | one above, you are in control of whether they end up being loaded lazily or 42 | built into your application as part of a RequireJS compilation/minification 43 | build step (e.g in a grunt task).
44 | 45 |Both modules are required as part of requiring myController. In practical 46 | terms, this means that:
47 | 48 |In this way, Couch Potato components can depend on each other. This follows 55 | the "AMD way" of expressing dependencies, so that users of myController (e.g. 56 | routes or states) are not forced to know what components the controller requires 57 | -- the controller module is tracking that, 58 | and RequireJS is ensuring that everything it needs is prepped and ready to go.
59 | 60 |You may have noticed that we have not discussed the "app" dependency. It's the 61 | application module. It deserves some discussion.
62 | 63 |These AMD modules run outside the context of Angular. There needs to be a way 64 | to tie back into your app in order to register the component with Angular. The 65 | application module is the bridge.
66 | 67 |It turns out that in order to make it possible for Couch Potato to do its work, 68 | we've prepared the application module with some sugary functions that expose 69 | the Couch Potato services. Later, we'll see what those sugary functions look 70 | like. For now, just know that app.registerController is a function that's using 71 | Couch Potato to get the controller registered.
72 | 73 |The rest of the code in this module is just standard Angular stuff. The only 74 | real difference is the define function that wraps it and the call 75 | to app.registerController instead of app.controller.
76 | 77 |Ok, with components out of the way, let's loop back and look at the app module 78 | again to understand how it's enabling this app.registerController business. 79 | Read about setting up the app module.
2 |
3 | Yes, you were called impatient, hasty and quick if you wanted to look at the 9 | samples. Well it was a joke (perhaps a weak one). Or maybe it was deemed important 10 | to read the conceptual overview. In any case, the rest of this 11 | guide assumes you've looked at the samples. Using Couch Potato is actually 12 | pretty easy, and without seeing it in action it may feel more complicated 13 | and confusing than it needs to be or is.
14 | 15 |So have a look at the 16 | source and its readme 17 | and see them in action!
18 | 19 |Since Couch Potato component definitions run outside of the Angular context, 20 | there are two things we need to "do to" the app in order to be able to reach 21 | back into it to register components. Let's take them in order of when they 22 | occur in the lifecycle of the application.
23 | 24 |When the application module is declared, we want to define the registerXXX functions that Couch Potato components use.
25 | 26 |The easiest and best way is probably to let Couch Potato do it for us. When you 27 | declare your module and its dependencies, ask Couch Potato to configure the 28 | app.
29 | 30 |Two examples follow. The first assumes you've used <script> tags to 31 | load Angular and Couch Potato and the second uses the AMD-style. It's 32 | important to realize that the choice does not impact the way Couch Potato 33 | components are authored.
34 | 35 |Using <script> tags: 36 |
37 | var myApp = angular.module('myApp', ['scs.couch-potato']); 38 | 39 | couchPotato.configureApp(myApp); 40 |41 | 42 |
Using AMD: 43 |
44 | define(['angular','couch-potato'], function(angular, couchPotato) { 45 | var myApp = angular.module('myApp', ['scs.couch-potato']); 46 | 47 | couchPotato.configureApp(myApp); 48 | }); 49 |50 | 51 |
Pretty easy.
52 | 53 |So what just happened? Couch Potato has assigned some functions as variables 54 | on the app. Specifically:
55 | 56 |It is these functions that the Couch Potato components call to accomplish 66 | registration. More on their inner workings below.
67 | 68 |The second thing we want to do occurs when the application enters run-time.
69 | 70 |71 | myApp.run(['$couchPotato', function($couchPotato) { 72 | myApp.lazy = $couchPotato; //note that you need to use the name 'lazy' 73 | }); 74 |75 | 76 |
This little assignment gives those register functions that were assigned above 77 | the capability of asking Couch Potato to do its work. Remember that the RequireJS AMD 78 | modules do not run in an Angular context -- hence we wouldn't be able to use 79 | dependency injection to grab the $couchPotato service. Latching onto it in 80 | module.run works around this.
81 | 82 |So back to those registerXXX functions. In fact, they contain branches. 83 | Each one looks something like this (we'll use registerController but they're 84 | all doing the same thing). Keep in mind you don't write these, Couch Potato 85 | assigns them to the app module for you when you call configureApp.
86 | 87 |88 | function registerController(name, controller) { 89 | if (app.lazy) { 90 | app.lazy.registerController([name, controller]); 91 | } 92 | else { 93 | app.controller(name, controller); 94 | } 95 | } 96 |97 | 98 |
Why the branch? It enables calls to registerController (or registerDirective, 99 | etc.) to function at at configuration time or at run time. This is really 100 | handy. In practical terms, you can author all of your components as Couch 101 | Potato components, whether you want to compile them into your eventual app.js 102 | using r.js optimization or you want to load/register them lazily.
103 | 104 |What follows assumes you're using RequireJS to load 107 | your app and is relatively advanced. It is likely to be confusing to those who 108 | do not know RequireJS well. (If you're using <script> tags then you're probably 109 | compiling certain components directly into your application module anyway.)
110 | 111 |You've been warned. If you want to skip this section you can move straight on 112 | to Lazy Loading Via Routing.
113 | 114 |The distinction between whether r.js will compile Couch Potato components
115 | or whether they will be lazy-loaded/registered is effected by whether or not you require
(as in
116 | use the RequireJS require function on) them within your RequireJS main.js
117 | file (or wherever it is that you have your RequireJS entry point). An example is probably necessary to flesh out this concept.
Let's say your (over)simplified main.js looks like this -- relatively normal 120 | RequireJS configuration for an Angular app:
121 | 122 |123 | require.config({ 124 | paths: { 125 | 'angular': '//somecdn/angular.min.js', 126 | 'angular-ui-router': '//somecdn/angular-ui-router.min.js', 127 | 'couch-potato': '/js/angular-couch-potato.min.js' 128 | } 129 | }); 130 | 131 | require(['app', 'run'], function(app) { 132 | angular.element(document).ready(function() { 133 | angular.bootstrap($('html'), [ 134 | app['name'], function() { 135 | $('html').addClass('ng-app'); 136 | } 137 | ] 138 | }); 139 | }); 140 |141 | 142 |
'app' is your AMD module that defines the Angular app module.
143 | 144 |'run' is your AMD module that causes the Angular app to be run.
145 | 146 |This main.js just initializes the app and runs it, then attaches the 147 | bootstrap function to the document ready event. As said, this is basic 148 | RequireJS/Angular stuff. Uses jQuery, but that's not important.
149 | 150 |Everything in main.js will be compiled by r.js -- including the components 151 | that are required, and the ones they require, etc.
152 | 153 |So anything that 'app' depends on, and anything that 'run' depends on -- those 154 | will be compiled into your output from r.js (if they are not prevented by being 155 | loaded from a CDN or otherwise exluded from the r.js compile process).
156 | 157 |Let's look at app.js: 158 |
159 | define(['angular', 'couch-potato', 'ui-router'], 160 | function(angular, couchPotato) { 161 | var app = angular.module('myApp', [ 162 | 'ui.state' 163 | 'scs.couch-potato' 164 | ]); 165 | 166 | couchPotato.configureApp(app); 167 | 168 | // return app so you can access it using RequireJs 169 | return app; 170 | } 171 | ); 172 |173 | 174 |
Aside from configuring the app for Couch Potato, this is all still normal 175 | RequireJS/Angular. Don't get bored -- we're getting toward the point here...
176 | 177 |Let's look at run.js: 178 |
179 | define(['app', 'config', 'someService'], 180 | function(app) { 181 | app.run(['$couchPotato', 'someService', 182 | function($couchPotato, someService) { 183 | app.lazy = $couchPotato; 184 | } 185 | ]); 186 | } 187 | ); 188 |189 | 190 |
So this causes the app to go into run mode, and assigns $couchPotato to 191 | app.lazy. For the present exposition, what matters is that 'config' is 192 | required.
193 | 194 |Here's config.js: 195 |
196 | define(['app', 'couch-potato', 'compiled-components'], function(app) { 197 | app.config('$couchPotatoProvider', function($couchPotatoProvider) { 198 | // nothing really needed here except the rest of your app.config logic 199 | }); 200 | }); 201 |202 | 203 |
So, because it's necessary, $couchPotatoProvider is injected. But the point 204 | here is that 'compiled-components' is required. This is the module in which 205 | you specific which of your Couch Potato components will not be lazy loaded, 206 | and instead will be compiled into your app by r.js.
207 | 208 |One approach here is to bail on lazy loading filters and decorators. You may 209 | also have some directives that are used all over the place and not worth 210 | tracking as individual dependencies or lazy loading them.
211 | 212 |In which case, 'compiled-components.js' could look like: 213 |
214 | define( 215 | [ 216 | 'filters', 217 | 'decorators', 218 | 'common-directives' 219 | ], function() { 220 | }); 221 |222 | 223 |
Since all of the modules in this exposition are required down from main.js, 224 | r.js is going to build them into the compiled output.
225 | 226 |Components that are not required by this tree of dependencies will be lazy 227 | loaded as disussed in 228 | Lazy Loading Via Routing.
2 |
3 | You want to know about Couch Potato Components 5 | and App Module Setup.
2 |
3 | Many applications will use Couch Potato exclusively within the context of their 7 | routing configurations, whether they use ngRoute, ui-router, or some other router 8 | (e.g. angular-detour or the inbuilt router in pre-2.0 versions of angular).
9 | 10 |The reasoning is that you are trying to lazy-load and register components that 11 | are used in the context of something must happen when the user visits a certain 12 | route/state of the application. This page describes such usage in general. See Ad Hoc Lazy Loading 13 | for alternative usage.
14 | 15 |When routes or states are invoked, an angular promise can resolved, and the $couchPotato service can invoke the $couchPotatoProvider to use RequireJS 18 | to download and register dependencies in the context of the promise.
19 | 20 |Since each component registers itself, indirect dependencies specified in AMD form are downloaded and registered along with the directly stated dependencies of the route.
21 | 22 |Couch Potato does not care which dependencies are resolved when the 23 | route is invoked. It is an application's responsibility to specify directly or indirectly all requirements.
24 | 25 |For example, if a route depends on a Couch Potato component named 26 | "base-state-components", and that component depends on another component named "base-Services", 27 | then all base services will be registered when the state "base", which depends 28 | on the component "base-state-components" is activated.
29 | 30 |The documented configurations for Couch Potato are:
31 | 32 |
2 |
3 | Couch Potato aids in the lazy download and and run-time registration of:
7 | 8 |It supports hierarchies of dependencies within these components, expressed as 18 | RequireJS modules. Applications can use Couch Potato to download and register components when they are needed to satisfy the requirements of a given route, or can do so ad-hoc in response to other application events.
19 | 20 |For the impatient, the hasty and the quick, you can stop reading and take a look at the 21 | samples or check out their source code.
22 | 23 |For the reference learner, you can dive straight into the API.
24 | 25 |The Conceptual Overview section explains the purpose(s) for which 28 | Couch Potato is intended and how its usage differs from traditional AngularJS 29 | component definition and registration.
30 | 31 |Architecture using Couch Potato describes how applications and their components are organized and authored in a Couch Potato context.
32 | 33 |Lazy Loading Via Routing introduces the most common use case for Couch Potato -- loading and registering components in the context of route/state changes in the application.
34 | 35 |Ad Hoc Lazy Loading demonstrates how arbitrary application code can invoke the download and registration of Couch Potato components.
36 | 37 |Thoughts on Lazy Loading is a sort of postlude in which some challenges and ideas for the future are presented, hopefully in a manner that is somewhat more organized than a ramble.
38 | 39 |describes how applications and their components are organized and authored in a Couch Potato context.
44 | 45 |The various "use case" sections show how Couch Potato can be used in several 46 | contexts:
47 | 48 |Finally, some closing thoughts are presented with the 62 | hope of stimulating discussion and interest in participating in Couch Potato 63 | specifically and the development of tools and techniques for building big 64 | applications/sites with AngularJS in general.
2 |
3 | Testy
The first sample is the "low end" of Couch Potato usage. It totally 13 | works but you leave some value on the table. It's meant to shadow angular-seed 14 | and to demonstrate the variety of components that *can* be lazy-loaded. It does 15 | not follow the recommended source code layout (semantic), instead it follows 16 | the angular-seed layout (directories for controllers, partials, etc.). 17 | 18 | If you're building a big 19 | app -- which, of course, is one of the reasons to use Couch Potato -- then 20 | you should consider following the techniques in the second sample. 21 |
22 | -------------------------------------------------------------------------------- /dist/samples/requirejs-ui-router/app/app-init.js: -------------------------------------------------------------------------------- 1 | // Anything required here wil by default be combined/minified by r.js 2 | // if you use it. 3 | define(['app', 'routeDefs'], function(app) { 4 | 5 | app.config(['routeDefsProvider', function(routeDefsProvider) { 6 | 7 | // in large applications, you don't want to clutter up app.config 8 | // with routing particulars. You probably have enough going on here. 9 | // Use a service provider to manage your routing. 10 | 11 | }]); 12 | 13 | app.run([ 14 | '$couchPotato', '$state', '$stateParams', '$rootScope', 15 | function($couchPotato, $state, $stateParams, $rootScope) { 16 | 17 | // by assigning the couchPotato service to the lazy property, we 18 | // the register functions will know to run-time-register components 19 | // instead of config-time-registering them. 20 | app.lazy = $couchPotato; 21 | 22 | // angular-ui-project recommends assigning these services to the root 23 | // scope. Others have argued that doing so can lead to obscured 24 | // dependencies and that making services directly available to html and 25 | // directives is unclean. In any case, the ui-router demo assumes these 26 | // are available in the DOM, therefore they should be on $rootScope. 27 | $rootScope.$state = $state; 28 | $rootScope.$stateParams = $stateParams; 29 | 30 | } 31 | ]); 32 | 33 | 34 | }); 35 | -------------------------------------------------------------------------------- /dist/samples/requirejs-ui-router/app/app.js: -------------------------------------------------------------------------------- 1 | define( 2 | 3 | ['angular','angular-couch-potato', 'angular-ui-router'], 4 | function(angular, couchPotato) { 5 | 6 | var app = angular.module('app', ['scs.couch-potato', 'ui.router']); 7 | 8 | // have Couch Potato set up the registerXXX functions on the app so that 9 | // registration of components is as easy as can be 10 | couchPotato.configureApp(app); 11 | 12 | return app; 13 | 14 | } 15 | 16 | ); 17 | -------------------------------------------------------------------------------- /dist/samples/requirejs-ui-router/app/config.js: -------------------------------------------------------------------------------- 1 | // Anything required here wil by default be combined/minified by r.js 2 | // if you use it. 3 | define(['app', 'services/routeDefs'], function(app) { 4 | 5 | app.config(['routeDefsProvider', function(routeDefsProvider) { 6 | 7 | // in large applications, you don't want to clutter up app.config 8 | // with routing particulars. You probably have enough going on here. 9 | // Use a service provider to manage your routing. 10 | 11 | }]); 12 | 13 | }); 14 | -------------------------------------------------------------------------------- /dist/samples/requirejs-ui-router/app/controllers/contactsController.js: -------------------------------------------------------------------------------- 1 | define(['app'], function (app) { 2 | app.registerController( 3 | 'contactsController', 4 | [ '$scope', '$state', 5 | function ($scope, $state) { 6 | $scope.contacts = [{ 7 | id: 1, 8 | name: "Alice", 9 | items: [{ 10 | id: 'a', 11 | type: 'phone number', 12 | value: '555-1234-1234', 13 | },{ 14 | id: 'b', 15 | type: 'email', 16 | value: 'alice@mailinator.com', 17 | }], 18 | }, { 19 | id: 42, 20 | name: "Bob", 21 | items: [{ 22 | id: 'a', 23 | type: 'blog', 24 | value: 'http://bob.blogger.com', 25 | },{ 26 | id: 'b', 27 | type: 'fax', 28 | value: '555-999-9999', 29 | }], 30 | }, { 31 | id: 123, 32 | name: "Eve", 33 | items: [{ 34 | id: 'a', 35 | type: 'full name', 36 | value: 'Eve Adamsdottir', 37 | }], 38 | }]; 39 | 40 | $scope.goToRandom = function () { 41 | var contacts = $scope.contacts, id; 42 | do { 43 | id = contacts[Math.floor(contacts.length * Math.random())].id; 44 | } while (id == $state.params.contactId); 45 | $state.transitionTo('contacts.detail', { contactId: id }); 46 | }; 47 | }] 48 | ); 49 | }); 50 | -------------------------------------------------------------------------------- /dist/samples/requirejs-ui-router/app/controllers/contactsDetailController.js: -------------------------------------------------------------------------------- 1 | define(['app', 'services/findById'], function (app) { 2 | app.registerController( 3 | 'contactsDetailController', 4 | [ '$scope', '$stateParams', 'something', 'findById', 5 | function ($scope, $stateParams, something, findById) { 6 | $scope.something = something; 7 | $scope.contact = findById.find($scope.contacts, $stateParams.contactId); 8 | }] 9 | ); 10 | }); 11 | -------------------------------------------------------------------------------- /dist/samples/requirejs-ui-router/app/controllers/contactsDetailItemController.js: -------------------------------------------------------------------------------- 1 | define(['app', 'services/findById'], function (app) { 2 | app.registerController( 3 | 'contactsDetailItemController', 4 | [ '$scope', '$stateParams', '$state', 'findById', 5 | function ($scope, $stateParams, $state, findById) { 6 | $scope.item = findById.find($scope.contact.items, $stateParams.itemId); 7 | $scope.edit = function () { 8 | $state.transitionTo('contacts.detail.item.edit', $stateParams); 9 | }; 10 | }] 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /dist/samples/requirejs-ui-router/app/controllers/contactsDetailItemEditController.js: -------------------------------------------------------------------------------- 1 | define(['app', 'services/findById'], function (app) { 2 | app.registerController( 3 | 'contactsDetailItemEditController', 4 | [ '$scope', '$stateParams', '$state', 'findById', 5 | function ($scope, $stateParams, $state, findById) { 6 | $scope.item = findById.find($scope.contact.items, $stateParams.itemId); 7 | $scope.done = function () { 8 | $state.transitionTo('contacts.detail.item', $stateParams); 9 | }; 10 | }] 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /dist/samples/requirejs-ui-router/app/routeDefs.js: -------------------------------------------------------------------------------- 1 | define(['app'], function(app) { 2 | app.registerProvider( 3 | 'routeDefs', 4 | [ 5 | '$stateProvider', 6 | '$urlRouterProvider', 7 | '$couchPotatoProvider', 8 | function ( 9 | $stateProvider, 10 | $urlRouterProvider, 11 | $couchPotatoProvider 12 | ) { 13 | 14 | this.$get = function() { 15 | // this is a config-time-only provider 16 | // in a future sample it will expose runtime information to the app 17 | return {}; 18 | }; 19 | 20 | $urlRouterProvider 21 | .when('/c?id', '/contacts/:id') 22 | .when('', '/') 23 | .when('/user/:id', '/contacts/:id'); 24 | 25 | $stateProvider 26 | .state('home', { 27 | url: '/', 28 | template: 'Welcome to the ngStates sample
Use the menu above to navigate
' + 29 | 'Look at Alice or Bob to see a URL with a redirect in action.<' + '/p>' 30 | }) 31 | .state('contacts', { 32 | url: '/contacts', 33 | abstract: true, 34 | templateUrl: 'partials/contacts.html', 35 | controller: 'contactsController', 36 | resolve: { 37 | dummy: $couchPotatoProvider.resolveDependencies(['controllers/contactsController']) 38 | } 39 | }) 40 | .state('contacts.list', { 41 | // parent: 'contacts', 42 | url: '', 43 | templateUrl: 'partials/contacts.list.html' 44 | }) 45 | .state('contacts.detail', { 46 | // parent: 'contacts', 47 | url: '/{contactId}', 48 | resolve: { 49 | dummy: $couchPotatoProvider.resolveDependencies(['controllers/contactsDetailController']), 50 | something: 51 | [ '$timeout', 52 | function ($timeout) { 53 | return $timeout(function () { return 'Asynchronously resolved data'; }, 10); 54 | }] 55 | }, 56 | views: { 57 | '': { 58 | templateUrl: 'partials/contacts.detail.html', 59 | controller: 'contactsDetailController' 60 | }, 'hint@': { 61 | template: 'This is contacts.detail populating the view "hint@"' 62 | }, 63 | 'menu': { 64 | templateProvider: 65 | [ '$stateParams', 66 | function ($stateParams){ 67 | // This is just to demonstrate that $stateParams injection works for templateProvider 68 | // $stateParams are the parameters for the new state we're transitioning to, even 69 | // though the global '$stateParams' has not been updated yet. 70 | return '
61 | 62 | $state = {{$state.current.name}} 63 | $stateParams = {{$stateParams}} 64 | $state full url = {{ $state.$current.url.source }} 65 | 67 |68 | 69 | 70 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /dist/samples/requirejs-ui-router/partials/contacts.detail.html: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /dist/samples/requirejs-ui-router/partials/contacts.detail.item.edit.html: -------------------------------------------------------------------------------- 1 |
This is the partial for view 1.
2 |3 | Getting version from the scope: {{ scopedAppVersion }} 4 |
5 | 6 |7 | And the version from the directive: 8 |
9 | -------------------------------------------------------------------------------- /dist/samples/script-tags-ngroute/partials/partial2.html: -------------------------------------------------------------------------------- 1 |This is the partial for view 2.
2 |3 | {{ welcomeMessage }} 4 |
5 |6 | Showing of 'interpolator' filter: 7 | 11 | 12 | 13 |
14 | 15 | -------------------------------------------------------------------------------- /docs/content/api/index.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name Couch Potato API Reference 3 | @description 4 | 5 | ## Couch Potato API Reference 6 | 7 | You can use the navbar on the left to search (see footnote), or 8 | click an item from the table of contents. 9 | 10 | If you're looking for an overview of Couch Potato or want to know how to 11 | get started, try the [guide](#/guide). 12 | 13 | *Footnote: Well, you __should__ be able to use the search, but it's broken in this version of Angular docs -- you can't (usefully) click on the results it finds :(* 14 | -------------------------------------------------------------------------------- /docs/content/guide/03-faq.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name FAQ 3 | @description 4 | 5 | ## Fequently Asked Couch Potato Questions 6 | 7 | #### Why Use Couch Potato 8 | 9 | Couch Potato is useful if you want to delay a few, some or most of your Angular components for speed or scale reasons. It allows you to author all components in one framework, and choose through configuration which components are "compiled in" to your application and which ones are lazy loaded, and to have fine-grained control over when that lazy loading takes place. It can help you organize dependencies for your routes. 10 | 11 | #### Is Couch Potato a replacement for xyzRouter? 12 | 13 | No, Couch Potato isn't a router. It's meant to be used *with* a router. It is known to work with with current versions of angular-ui-router (recommended) and Angular's stock router (and ngRoute). It is built into angular-detour. It *should* work in any context where promises are resolved. 14 | 15 | It can also be used outside the context of routing. 16 | 17 | #### Can undefine or redefine components? 18 | 19 | At this time, you cannot undefine or redefine components at run-time. 20 | 21 | #### Can I lazy-depend on modules? 22 | 23 | No. At least one experiment is known to have established lazy-loading modules to be possible, but it involves hacking Angular. 24 | 25 | **What does work**: If you have a component that you want to lazy-load and it depends on 26 | a non-Angular-based script library, such as d3 or a jQuery plugin, you can depend on that 27 | library in the Couch Potato context and defer loading it until your component is loaded. 28 | 29 | **What doesn't work**: If you have a component that you want to lazy-load and it depends on an Angular-based module, e.g. angular-ui/bootstrap, then your application itself must depend on bootstrap at declaration time. 30 | 31 | #### Can I lazy-depend on modules? 32 | 33 | No. At least one experiment is known to have established lazy-loading modules to be 34 | 35 | #### Run-time Module Loading is Coming To Angular -- How is Couch Potato Related? 36 | 37 | It's not. 38 | 39 | For one, Couch Potato doesn't operate at the module level by design. Operating at the component level within application components is the intent of Couch Potato. Module-level dependencies are at a higher-level. 40 | 41 | Two, run-time loading of modules isn't here, yet. Time will tell whether/how Couch Potato will fit into the future AnguarJS landscape. 42 | 43 | #### What's in the future? 44 | 45 | * Couch Potato tests: the project does not have any tests, yet. It should have tests 46 | before version 1.0.0. 47 | 48 | * Testing components that are authored as Couch Potato components: guidelines/examples 49 | for testing apps built with Couch Potato. Will probably be based on protractor (new Angular testing framework). 50 | 51 | * Undefining/redefining components (for scale). RequireJS has an undef function 52 | that may enable this, but it may also be quite complicated. This is pie-in-the-sky stuff 53 | for now. 54 | 55 | #### Where can I ask questions? 56 | 57 | If the documentation isn't enough and/or you're having problems, use the issues list of the github repository. 58 | 59 | #### Can I contribute to Couch Potato? 60 | 61 | Absolutely. Pull requests are welcome. 62 | -------------------------------------------------------------------------------- /docs/content/guide/05-conceptual.01-how-it-works.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name How It Works 3 | @description 4 | 5 | ## Couch Potato: How It Works 6 | 7 | #### In General 8 | 9 | Couch Potato works by preparing itself -- at configuration-time to do -- at 10 | run-time -- the work of registering components. 11 | 12 | In a normal AngularJS application, when run-time starts (which is typically 13 | when module.run happens or when the application is bootstrapped), all providers 14 | become "uninjectable." As such, you lose the ability to configure components. 15 | 16 | By storing references to the key registration "vehicles", Couch Potato can perform 17 | registrations during run-time. The components Couch Potato maintains references 18 | to are: 19 | 20 | * $controllerProvider, 21 | * $compileProvider, 22 | * $provide, 23 | * $filterProvider 24 | 25 | Lazy-registration would be of little use if you couldn't lazily *load* script. 26 | 27 | This is where AMD (Asynchronous Module Definition) comes in. Couch Potato uses RequireJS. If you are unfamiliar with 28 | RequireJS, you'll want to read up a little. The general idea is that an AMD 29 | module definition states a list of dependencies and then specifies its 30 | definition. 31 | 32 | When a module is requested ("required" in AMD terms), all of it dependencies 33 | are fetched (or retrieved from the cache if they've already been required), and 34 | then passed to the module as function parameters. Continue by reading about 35 | {@link 17-couch-potato-components Couch Potato Components}. 36 | 37 | This guide is about to get heavy. If you haven't already done so, it's highly 38 | recommended that you stop and look at the samples 39 | before you proceed. 40 | -------------------------------------------------------------------------------- /docs/content/guide/05-conceptual.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name Conceptual Overview 3 | @description 4 | 5 | ## Couch Potato: Conceptual Overview 6 | 7 | In a traditional AngularJS application, you define and configure all components 8 | before the application runs. The seperation between "configuration time" and 9 | "run time", along with the dependency injection system, enables the programmer 10 | to define all components *in any order* and to know that they will all be 11 | configured prior to any of them being run. This might sound like a truism! 12 | 13 | There may be application where defining and configuring every component that the 14 | application will use is not optimal. A few reasons to delay are: 15 | 16 | * there are a very large number of components split up among many states 17 | * there are some very large individual components that are only needed for 18 | certain routes/states within the application 19 | 20 | In these cases, one may want to delay loading components. Couch Potato is 21 | intended to make the above cases possible. 22 | 23 | Additionally, you may have an application that, while developing it, you do 24 | not know which components will ultimately be used. For example, in a CMS 25 | (Content Management System), it is conceivable that components that did not 26 | even exist when the application was built are intended to be used within the 27 | application. 28 | 29 | In such a case, you most certainly need to delay loading the component! 30 | 31 | Couch Potato is intended to be *part* of a solution for this type of loose 32 | binding. 33 | 34 | {@link 01.conceptual.how-it-works How It Works} provides a relatively high-level explanation 35 | of the methods that Couch Potato employs to allow for lazy loading and 36 | registration of components. 37 | 38 | -------------------------------------------------------------------------------- /docs/content/guide/11-architecture.01-components.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name Couch Potato Components 3 | @description 4 | 5 | ## Couch Potato Components 6 | 7 | A typical Couch Potato component might look something like this: 8 |9 | define(['app', 'lodash', 'myService', function(app, _) { 10 | 11 | app.registerController('myController', ['$scope', '$timeout', '$myService', 12 | 13 | function($scope, $timeout, $myService) { 14 | 15 | $scope.concattedAlphas = ''; 16 | _.each(['a', 'b', 'c'], function(val) { 17 | $scope.concattedAlphas += val; 18 | }); 19 | 20 | $timeout( 21 | function() { 22 | $scope.aValueFromMyService = $myService.aValue; 23 | }, 24 | 1000 25 | ); 26 | } 27 | ]); 28 | }); 29 |30 | 31 | That really silly controller depends on the AMD modules "lodash" and 32 | "myService". 33 | 34 | lodash is just the lodash utility library. It knows nothing 35 | about Couch Potato (or Angular) and doesn't need to. This is regular RequireJS here. 36 | 37 | On the other hand, "myService" is itself (or at least might be) a Couch Potato AMD module. It is 38 | defined similarly to the one we're defining here (i.e. 39 | app.registerService(...)). Note the use of the word "might". It's also 40 | entirely possible that myService was configured as a non-lazy component. In 41 | fact, when you use Couch Potato and you define components as modules like the 42 | one above, you are in control of whether they end up being loaded lazily or 43 | built into your application as part of a RequireJS compilation/minification 44 | build step (e.g in a grunt task). 45 | 46 | Both modules are required as part of requiring myController. In practical 47 | terms, this means that: 48 | 49 | * lodash can be used within the controller; and 50 | * myService will be registered before anyone tries to use myController, thus 51 | it can be injected into myController. 52 | 53 | In this way, Couch Potato components can depend on each other. This follows 54 | the "AMD way" of expressing dependencies, so that users of myController (e.g. 55 | routes or states) are not forced to know what components the controller requires 56 | -- the controller module is tracking that, 57 | and RequireJS is ensuring that everything it needs is prepped and ready to go. 58 | 59 | You may have noticed that we have not discussed the "app" dependency. It's the 60 | application module. It deserves some discussion. 61 | 62 | These AMD modules run outside the context of Angular. There needs to be a way 63 | to tie back into your app in order to register the component with Angular. The 64 | application module is the bridge. 65 | 66 | It turns out that in order to make it possible for Couch Potato to do its work, 67 | we've prepared the application module with some sugary functions that expose 68 | the Couch Potato services. Later, we'll see what those sugary functions look 69 | like. For now, just know that app.registerController is a function that's using 70 | Couch Potato to get the controller registered. 71 | 72 | The rest of the code in this module is just standard Angular stuff. The only 73 | real difference is the define function that wraps it and the call 74 | to app.registerController instead of app.controller. 75 | 76 | Ok, with components out of the way, let's loop back and look at the app module 77 | again to understand how it's enabling this app.registerController business. 78 | Read about {@link 19-app-module-setup setting up the app module}. 79 | -------------------------------------------------------------------------------- /docs/content/guide/11-architecture.11-app-module-setup.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name App Module Setup 3 | @description 4 | 5 | ## Couch Potato: Application Module Setup 6 | 7 | ### Stop, Breathe and Look at the Samples 8 | 9 | Yes, you were called impatient, hasty and quick if you wanted to look at the 10 | samples. Well it was a joke (perhaps a weak one). Or maybe it was deemed important 11 | to read the conceptual overview. In any case, the rest of this 12 | guide **assumes** you've looked at the samples. Using Couch Potato is actually 13 | pretty easy, and without seeing it in action it may feel more complicated 14 | and confusing than it needs to be or is. 15 | 16 | So have a look at the 17 | source and its readme 18 | and see them in action! 19 | 20 | Since Couch Potato component definitions run outside of the Angular context, 21 | there are two things we need to "do to" the app in order to be able to reach 22 | back into it to register components. Let's take them in order of when they 23 | occur in the lifecycle of the application. 24 | 25 | When the application module is declared, we want to define the registerXXX functions that Couch Potato components use. 26 | 27 | The easiest and best way is probably to let Couch Potato do it for us. When you 28 | declare your module and its dependencies, ask Couch Potato to configure the 29 | app. 30 | 31 | Two examples follow. The first assumes you've used <script> tags to 32 | load Angular and Couch Potato and the second uses the AMD-style. It's 33 | important to realize that the choice *does not* impact the way Couch Potato 34 | components are authored. 35 | 36 | Using <script> tags: 37 |
38 | var myApp = angular.module('myApp', ['scs.couch-potato']); 39 | 40 | couchPotato.configureApp(myApp); 41 |42 | 43 | Using AMD: 44 |
45 | define(['angular','couch-potato'], function(angular, couchPotato) { 46 | var myApp = angular.module('myApp', ['scs.couch-potato']); 47 | 48 | couchPotato.configureApp(myApp); 49 | }); 50 |51 | 52 | Pretty easy. 53 | 54 | So what just happened? Couch Potato has assigned some functions as variables 55 | on the app. Specifically: 56 | 57 | * registerController(name, controller) 58 | * registerFactory(name, factory) 59 | * registerDirective(name, directive) 60 | * registerFilter(name, filter) 61 | * registerDecorator(name, decorator) 62 | * registerValue(name, value) 63 | 64 | It is these functions that the Couch Potato components call to accomplish 65 | registration. More on their inner workings below. 66 | 67 | The second thing we want to do occurs when the application enters run-time. 68 | 69 |
70 | myApp.run(['$couchPotato', function($couchPotato) { 71 | myApp.lazy = $couchPotato; //note that you need to use the name 'lazy' 72 | }); 73 |74 | 75 | This little assignment gives those register functions that were assigned above 76 | the capability of asking Couch Potato to do its work. Remember that the RequireJS AMD 77 | modules *do not* run in an Angular context -- hence we wouldn't be able to use 78 | dependency injection to grab the $couchPotato service. Latching onto it in 79 | module.run works around this. 80 | 81 | So back to those registerXXX functions. In fact, they contain branches. 82 | Each one looks something like this (we'll use registerController but they're 83 | all doing the same thing). Keep in mind you don't write these, Couch Potato 84 | assigns them to the app module for you when you call configureApp. 85 | 86 |
87 | function registerController(name, controller) { 88 | if (app.lazy) { 89 | app.lazy.registerController([name, controller]); 90 | } 91 | else { 92 | app.controller(name, controller); 93 | } 94 | } 95 |96 | 97 | Why the branch? It enables calls to registerController (or registerDirective, 98 | etc.) to function at at configuration time **or** at run time. This is really 99 | handy. In practical terms, you can author *all* of your components as Couch 100 | Potato components, whether you want to compile them into your eventual app.js 101 | using r.js optimization or you want to load/register them lazily. 102 | 103 | ### Advanced Setup 104 | 105 | What follows assumes you're using RequireJS to load 106 | your app and is relatively advanced. It is likely to be confusing to those who 107 | do not know RequireJS well. (If you're using <script> tags then you're probably 108 | compiling certain components directly into your application module anyway.) 109 | 110 | You've been warned. If you want to skip this section you can move straight on 111 | to {@link 25-lazy-loading-via-routing Lazy Loading Via Routing}. 112 | 113 | The distinction between whether r.js will compile Couch Potato components 114 | or whether they will be lazy-loaded/registered is effected by whether or not you ```require``` (as in 115 | use the RequireJS require function on) them within your RequireJS ```main.js``` 116 | file (or wherever it is that you have your RequireJS entry point). An example is probably necessary to flesh out this concept. 117 | 118 | Let's say your (over)simplified main.js looks like this -- relatively normal 119 | RequireJS configuration for an Angular app: 120 | 121 |
122 | require.config({ 123 | paths: { 124 | 'angular': '//somecdn/angular.min.js', 125 | 'angular-ui-router': '//somecdn/angular-ui-router.min.js', 126 | 'couch-potato': '/js/angular-couch-potato.min.js' 127 | } 128 | }); 129 | 130 | require(['app', 'run'], function(app) { 131 | angular.element(document).ready(function() { 132 | angular.bootstrap($('html'), [ 133 | app['name'], function() { 134 | $('html').addClass('ng-app'); 135 | } 136 | ] 137 | }); 138 | }); 139 |140 | 141 | 'app' is your AMD module that defines the Angular app module. 142 | 143 | 'run' is your AMD module that causes the Angular app to be run. 144 | 145 | This main.js just initializes the app and runs it, then attaches the 146 | bootstrap function to the document ready event. As said, this is basic 147 | RequireJS/Angular stuff. Uses jQuery, but that's not important. 148 | 149 | Everything in main.js will be compiled by r.js -- **including the components 150 | that are required, and the ones they require, etc.** 151 | 152 | So anything that 'app' depends on, and anything that 'run' depends on -- those 153 | will be compiled into your output from r.js (if they are not prevented by being 154 | loaded from a CDN or otherwise exluded from the r.js compile process). 155 | 156 | Let's look at app.js: 157 |
158 | define(['angular', 'couch-potato', 'ui-router'], 159 | function(angular, couchPotato) { 160 | var app = angular.module('myApp', [ 161 | 'ui.state' 162 | 'scs.couch-potato' 163 | ]); 164 | 165 | couchPotato.configureApp(app); 166 | 167 | // return app so you can access it using RequireJs 168 | return app; 169 | } 170 | ); 171 |172 | 173 | Aside from configuring the app for Couch Potato, this is all still normal 174 | RequireJS/Angular. Don't get bored -- we're getting toward the point here... 175 | 176 | Let's look at run.js: 177 |
178 | define(['app', 'config', 'someService'], 179 | function(app) { 180 | app.run(['$couchPotato', 'someService', 181 | function($couchPotato, someService) { 182 | app.lazy = $couchPotato; 183 | } 184 | ]); 185 | } 186 | ); 187 |188 | 189 | So this causes the app to go into run mode, and assigns $couchPotato to 190 | app.lazy. For the present exposition, what matters is that 'config' is 191 | required. 192 | 193 | Here's config.js: 194 |
195 | define(['app', 'couch-potato', 'compiled-components'], function(app) { 196 | app.config('$couchPotatoProvider', function($couchPotatoProvider) { 197 | // nothing really needed here except the rest of your app.config logic 198 | }); 199 | }); 200 |201 | 202 | So, because it's necessary, $couchPotatoProvider is injected. But the point 203 | here is that 'compiled-components' is required. This is the module in which 204 | you specific which of your Couch Potato components will *not* be lazy loaded, 205 | and instead will be compiled into your app by r.js. 206 | 207 | One approach here is to bail on lazy loading filters and decorators. You may 208 | also have some directives that are used all over the place and not worth 209 | tracking as individual dependencies or lazy loading them. 210 | 211 | In which case, 'compiled-components.js' could look like: 212 |
213 | define( 214 | [ 215 | 'filters', 216 | 'decorators', 217 | 'common-directives' 218 | ], function() { 219 | }); 220 |221 | 222 | Since all of the modules in this exposition are required down from main.js, 223 | r.js is going to build them into the compiled output. 224 | 225 | Components that are not required by this tree of dependencies will be lazy 226 | loaded as disussed in 227 | {@link 25-lazy-loading-via-routing Lazy Loading Via Routing}. 228 | -------------------------------------------------------------------------------- /docs/content/guide/11-architecture.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name Architecture using Couch Potato 3 | @description 4 | 5 | You want to know about {@link 02.authoring.components Couch Potato Components} 6 | and {@link 02.authoring.app-module-setup App Module Setup}. 7 | -------------------------------------------------------------------------------- /docs/content/guide/21-lazy-loading-via-routing.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name Lazy Loading Via Routing 3 | @description 4 | 5 | ## Couch Potato: Lazy Loading Via Routing 6 | 7 | Many applications will use Couch Potato exclusively within the context of their 8 | routing configurations, whether they use ngRoute, ui-router, or some other router 9 | (e.g. angular-detour or the inbuilt router in pre-2.0 versions of angular). 10 | 11 | The reasoning is that you are trying to lazy-load and register components that 12 | are used in the context of something must happen when the user visits a certain 13 | route/state of the application. This page describes such usage in general. *See {@link 45-adhoc-lazy-loading Ad Hoc Lazy Loading} 14 | for alternative usage.* 15 | 16 | #### Route/State-Based Lazy Loading In General 17 | 18 | When routes or states are *invoked*, an angular promise can resolved, and the $couchPotato *service* can invoke the *$couchPotatoProvider* to use RequireJS 19 | to download and register dependencies in the context of the promise. 20 | 21 | Since each component registers itself, *indirect* dependencies specified in AMD form are downloaded and registered along with the directly stated dependencies of the route. 22 | 23 | Couch Potato does not care which dependencies are resolved when the 24 | route is invoked. It is an application's responsibility to specify directly or indirectly all requirements. 25 | 26 | For example, if a route depends on a Couch Potato component named 27 | "base-state-components", and that component depends on another component named "base-Services", 28 | then all base services will be registered when the state "base", which depends 29 | on the component "base-state-components" is activated. 30 | 31 | The documented configurations for Couch Potato are: 32 | 33 | * {@link 31-use-ngrouter Use with ngRouter/stock pre-2.0 router} 34 | * {@link 33-use-ui-router Use with angular-ui-router} 35 | * {@link 35-use-detour Use with angular-detour} 36 | 37 | -------------------------------------------------------------------------------- /docs/content/guide/index.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name Couch Potato Overview and Guide 3 | @description 4 | 5 | ## Couch Potato Overview and Usage Guide 6 | 7 | Couch Potato aids in the lazy download and and run-time registration of: 8 | 9 | * controllers 10 | * factories 11 | * directives 12 | * values 13 | * filters 14 | * decorators 15 | 16 | It supports hierarchies of dependencies within these components, expressed as 17 | RequireJS modules. Applications can use Couch Potato to download and register components when they are needed to satisfy the requirements of a given *route*, or can do so ad-hoc in response to other application events. 18 | 19 | *For the impatient, the hasty and the quick, you can stop reading and take a look at the 20 | samples or check out their source code.* 21 | 22 | *For the reference learner, you can dive straight into the API.* 23 | 24 | ### This guide contains several sections: 25 | 26 | The {@link 05-conceptual Conceptual Overview} section explains the purpose(s) for which 27 | Couch Potato is intended and how its usage differs from traditional AngularJS 28 | component definition and registration. 29 | 30 | {@link 11-architecture Architecture using Couch Potato} describes how applications and their components are organized and authored in a Couch Potato context. 31 | 32 | {@link 21-lazy-loading-via-routing Lazy Loading Via Routing} introduces the most common use case for Couch Potato -- loading and registering components in the context of route/state changes in the application. 33 | 34 | {@link 31-lazy-loading-ad-hoc Ad Hoc Lazy Loading} demonstrates how arbitrary application code can invoke the download and registration of Couch Potato components. 35 | 36 | {@link 41-thoughts Thoughts on Lazy Loading} is a sort of postlude in which some challenges and ideas for the future are presented, hopefully in a manner that is somewhat more organized than a ramble. 37 | 38 | #### Of course, there is a {@link 03-faq FAQ} page. 39 | 40 | 41 | ## blah blah 42 | 43 | describes how applications and their components are organized and authored in a Couch Potato context. 44 | 45 | 46 | The various "use case" sections show how Couch Potato can be used in several 47 | contexts: 48 | 49 | * {@link 20a-with-ngRoute Use with ngRoute} demonstrates how Couch Potato can 50 | be configured to load components when a particular **route** is activated during 51 | a user's interaction with an application; 52 | * {@link 20b-with-resolve Use with resolve} demonstrates how 53 | Couch Potato can be configured to load *individual* components when a particular 54 | **ui-router state** is acvtivated; 55 | * {@link 20c-with-ui-templateProvider Use with templateProvider} demonstrates how Couch Potato can be configured to load a *set of components 56 | along with a template* when a particular **ui-router view** is activated; 57 | * {@link 20d-ad-hoc Use Ad Hoc} demonstrates how Couch Potato can be called 58 | within application code that is outside the scope of any router. 59 | 60 | Finally, some {@link 90-closing-thoughts closing thoughts} are presented with the 61 | hope of stimulating discussion and interest in participating in Couch Potato 62 | specifically and the development of tools and techniques for building big 63 | applications/sites with AngularJS in general. 64 | 65 | -------------------------------------------------------------------------------- /docs/content/guide/templateProvider.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name Use with templateProvider 3 | @description 4 | 5 | Testy 6 | -------------------------------------------------------------------------------- /docs/css/style.css: -------------------------------------------------------------------------------- 1 | div[role=main] div.row div.span3 { 2 | width: 240px; 3 | } 4 | div[role=main] div.row div.span9 { 5 | width: 680px; 6 | } 7 | -------------------------------------------------------------------------------- /docs/html/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laurelnaiad/angular-couch-potato/fea40f7547890d2914c673e18dd1e752b4b110b3/docs/html/favicon.ico -------------------------------------------------------------------------------- /docs/html/nav.html: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-couch-potato", 3 | "description": "Provider/Service to Lazy-Load Components in AngularJS Applications", 4 | "version": "0.2.3", 5 | "homepage": "https://github.com/laurelnaiad/angular-couch-potato", 6 | "author": { 7 | "name": "Daphne Maddox", 8 | "url": "https://github.com/laurelnaiad" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/laurelnaiad/angular-couch-potato.git" 13 | }, 14 | "licenses": [ 15 | { 16 | "type": "MIT", 17 | "url": "https://github.com/laurelnaiad/angular-couch-potato/blob/master/LICENSE" 18 | } 19 | ], 20 | "dependencies": {}, 21 | "devDependencies": { 22 | "express": "~3.4.4", 23 | "grunt": "~0.4.1", 24 | "grunt-bower": "~0.7.0", 25 | "grunt-bower-task": "~0.3.2", 26 | "grunt-contrib-clean": "~0.4.0", 27 | "grunt-ngdocs": "m7r/grunt-ngdocs", 28 | "grunt-contrib-copy": "~0.4.1", 29 | "grunt-contrib-concat": "~0.3.0", 30 | "grunt-gh-pages": "~0.8.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /samples/README.md: -------------------------------------------------------------------------------- 1 | To run the samples: 2 | 3 | * clone the repository 4 | * ```cd angular-couch-potato``` 5 | * ```npm install``` 6 | * ```node server.js``` 7 | * visit [http://localhost:3000/angular-couch-potato/samples/](http://localhost:3000/angular-couch-potato/samples/) 8 | -------------------------------------------------------------------------------- /samples/index.html: -------------------------------------------------------------------------------- 1 |
The first sample is the "low end" of Couch Potato usage. It totally 13 | works but you leave some value on the table. It's meant to shadow angular-seed 14 | and to demonstrate the variety of components that *can* be lazy-loaded. It does 15 | not follow the recommended source code layout (semantic), instead it follows 16 | the angular-seed layout (directories for controllers, partials, etc.). 17 | 18 | If you're building a big 19 | app -- which, of course, is one of the reasons to use Couch Potato -- then 20 | you should consider following the techniques in the second sample. 21 |
22 | -------------------------------------------------------------------------------- /samples/requirejs-ui-router/app/app-init.js: -------------------------------------------------------------------------------- 1 | // Anything required here wil by default be combined/minified by r.js 2 | // if you use it. 3 | define(['app', 'routeDefs'], function(app) { 4 | 5 | app.config(['routeDefsProvider', function(routeDefsProvider) { 6 | 7 | // in large applications, you don't want to clutter up app.config 8 | // with routing particulars. You probably have enough going on here. 9 | // Use a service provider to manage your routing. 10 | 11 | }]); 12 | 13 | app.run([ 14 | '$couchPotato', '$state', '$stateParams', '$rootScope', 15 | function($couchPotato, $state, $stateParams, $rootScope) { 16 | 17 | // by assigning the couchPotato service to the lazy property, we 18 | // the register functions will know to run-time-register components 19 | // instead of config-time-registering them. 20 | app.lazy = $couchPotato; 21 | 22 | // angular-ui-project recommends assigning these services to the root 23 | // scope. Others have argued that doing so can lead to obscured 24 | // dependencies and that making services directly available to html and 25 | // directives is unclean. In any case, the ui-router demo assumes these 26 | // are available in the DOM, therefore they should be on $rootScope. 27 | $rootScope.$state = $state; 28 | $rootScope.$stateParams = $stateParams; 29 | 30 | } 31 | ]); 32 | 33 | 34 | }); 35 | -------------------------------------------------------------------------------- /samples/requirejs-ui-router/app/app.js: -------------------------------------------------------------------------------- 1 | define( 2 | 3 | ['angular','angular-couch-potato', 'angular-ui-router'], 4 | function(angular, couchPotato) { 5 | 6 | var app = angular.module('app', ['scs.couch-potato', 'ui.router']); 7 | 8 | // have Couch Potato set up the registerXXX functions on the app so that 9 | // registration of components is as easy as can be 10 | couchPotato.configureApp(app); 11 | 12 | return app; 13 | 14 | } 15 | 16 | ); 17 | -------------------------------------------------------------------------------- /samples/requirejs-ui-router/app/config.js: -------------------------------------------------------------------------------- 1 | // Anything required here wil by default be combined/minified by r.js 2 | // if you use it. 3 | define(['app', 'services/routeDefs'], function(app) { 4 | 5 | app.config(['routeDefsProvider', function(routeDefsProvider) { 6 | 7 | // in large applications, you don't want to clutter up app.config 8 | // with routing particulars. You probably have enough going on here. 9 | // Use a service provider to manage your routing. 10 | 11 | }]); 12 | 13 | }); 14 | -------------------------------------------------------------------------------- /samples/requirejs-ui-router/app/controllers/contactsController.js: -------------------------------------------------------------------------------- 1 | define(['app'], function (app) { 2 | app.registerController( 3 | 'contactsController', 4 | [ '$scope', '$state', 5 | function ($scope, $state) { 6 | $scope.contacts = [{ 7 | id: 1, 8 | name: "Alice", 9 | items: [{ 10 | id: 'a', 11 | type: 'phone number', 12 | value: '555-1234-1234', 13 | },{ 14 | id: 'b', 15 | type: 'email', 16 | value: 'alice@mailinator.com', 17 | }], 18 | }, { 19 | id: 42, 20 | name: "Bob", 21 | items: [{ 22 | id: 'a', 23 | type: 'blog', 24 | value: 'http://bob.blogger.com', 25 | },{ 26 | id: 'b', 27 | type: 'fax', 28 | value: '555-999-9999', 29 | }], 30 | }, { 31 | id: 123, 32 | name: "Eve", 33 | items: [{ 34 | id: 'a', 35 | type: 'full name', 36 | value: 'Eve Adamsdottir', 37 | }], 38 | }]; 39 | 40 | $scope.goToRandom = function () { 41 | var contacts = $scope.contacts, id; 42 | do { 43 | id = contacts[Math.floor(contacts.length * Math.random())].id; 44 | } while (id == $state.params.contactId); 45 | $state.transitionTo('contacts.detail', { contactId: id }); 46 | }; 47 | }] 48 | ); 49 | }); 50 | -------------------------------------------------------------------------------- /samples/requirejs-ui-router/app/controllers/contactsDetailController.js: -------------------------------------------------------------------------------- 1 | define(['app', 'services/findById'], function (app) { 2 | app.registerController( 3 | 'contactsDetailController', 4 | [ '$scope', '$stateParams', 'something', 'findById', 5 | function ($scope, $stateParams, something, findById) { 6 | $scope.something = something; 7 | $scope.contact = findById.find($scope.contacts, $stateParams.contactId); 8 | }] 9 | ); 10 | }); 11 | -------------------------------------------------------------------------------- /samples/requirejs-ui-router/app/controllers/contactsDetailItemController.js: -------------------------------------------------------------------------------- 1 | define(['app', 'services/findById'], function (app) { 2 | app.registerController( 3 | 'contactsDetailItemController', 4 | [ '$scope', '$stateParams', '$state', 'findById', 5 | function ($scope, $stateParams, $state, findById) { 6 | $scope.item = findById.find($scope.contact.items, $stateParams.itemId); 7 | $scope.edit = function () { 8 | $state.transitionTo('contacts.detail.item.edit', $stateParams); 9 | }; 10 | }] 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /samples/requirejs-ui-router/app/controllers/contactsDetailItemEditController.js: -------------------------------------------------------------------------------- 1 | define(['app', 'services/findById'], function (app) { 2 | app.registerController( 3 | 'contactsDetailItemEditController', 4 | [ '$scope', '$stateParams', '$state', 'findById', 5 | function ($scope, $stateParams, $state, findById) { 6 | $scope.item = findById.find($scope.contact.items, $stateParams.itemId); 7 | $scope.done = function () { 8 | $state.transitionTo('contacts.detail.item', $stateParams); 9 | }; 10 | }] 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /samples/requirejs-ui-router/app/routeDefs.js: -------------------------------------------------------------------------------- 1 | define(['app'], function(app) { 2 | app.registerProvider( 3 | 'routeDefs', 4 | [ 5 | '$stateProvider', 6 | '$urlRouterProvider', 7 | '$couchPotatoProvider', 8 | function ( 9 | $stateProvider, 10 | $urlRouterProvider, 11 | $couchPotatoProvider 12 | ) { 13 | 14 | this.$get = function() { 15 | // this is a config-time-only provider 16 | // in a future sample it will expose runtime information to the app 17 | return {}; 18 | }; 19 | 20 | $urlRouterProvider 21 | .when('/c?id', '/contacts/:id') 22 | .when('', '/') 23 | .when('/user/:id', '/contacts/:id'); 24 | 25 | $stateProvider 26 | .state('home', { 27 | url: '/', 28 | template: 'Welcome to the ngStates sample
Use the menu above to navigate
' + 29 | 'Look at Alice or Bob to see a URL with a redirect in action.<' + '/p>' 30 | }) 31 | .state('contacts', { 32 | url: '/contacts', 33 | abstract: true, 34 | templateUrl: 'partials/contacts.html', 35 | controller: 'contactsController', 36 | resolve: { 37 | dummy: $couchPotatoProvider.resolveDependencies(['controllers/contactsController']) 38 | } 39 | }) 40 | .state('contacts.list', { 41 | // parent: 'contacts', 42 | url: '', 43 | templateUrl: 'partials/contacts.list.html' 44 | }) 45 | .state('contacts.detail', { 46 | // parent: 'contacts', 47 | url: '/{contactId}', 48 | resolve: { 49 | dummy: $couchPotatoProvider.resolveDependencies(['controllers/contactsDetailController']), 50 | something: 51 | [ '$timeout', 52 | function ($timeout) { 53 | return $timeout(function () { return 'Asynchronously resolved data'; }, 10); 54 | }] 55 | }, 56 | views: { 57 | '': { 58 | templateUrl: 'partials/contacts.detail.html', 59 | controller: 'contactsDetailController' 60 | }, 'hint@': { 61 | template: 'This is contacts.detail populating the view "hint@"' 62 | }, 63 | 'menu': { 64 | templateProvider: 65 | [ '$stateParams', 66 | function ($stateParams){ 67 | // This is just to demonstrate that $stateParams injection works for templateProvider 68 | // $stateParams are the parameters for the new state we're transitioning to, even 69 | // though the global '$stateParams' has not been updated yet. 70 | return '
61 | 62 | $state = {{$state.current.name}} 63 | $stateParams = {{$stateParams}} 64 | $state full url = {{ $state.$current.url.source }} 65 | 67 |68 | 69 | 70 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /samples/requirejs-ui-router/partials/contacts.detail.html: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /samples/requirejs-ui-router/partials/contacts.detail.item.edit.html: -------------------------------------------------------------------------------- 1 |
This is the partial for view 1.
2 |3 | Getting version from the scope: {{ scopedAppVersion }} 4 |
5 | 6 |7 | And the version from the directive: 8 |
9 | -------------------------------------------------------------------------------- /samples/script-tags-ngroute/partials/partial2.html: -------------------------------------------------------------------------------- 1 |This is the partial for view 2.
2 |3 | {{ welcomeMessage }} 4 |
5 |6 | Showing of 'interpolator' filter: 7 | 11 | 12 | 13 |
14 | 15 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var app = express(); 3 | 4 | app.use('/angular-couch-potato', express.static(__dirname + '/dist')); 5 | 6 | app.listen(3000); //the port you want to use 7 | -------------------------------------------------------------------------------- /src/couchPotato.js: -------------------------------------------------------------------------------- 1 | /* angular-couch-potato 2 | * https://github.com/laurelnaiad/angular-couch-potato 3 | * Copyright (c) 2013 Stuart Salsbury 4 | * Based on https://github.com/szhanginrhythm/angular-require-lazyload 5 | * Licensed MIT 6 | */ 7 | 8 | (function() { 9 | 10 | var CouchPotato = function(angular) { 11 | //Self-invoking anonymous function keeps global scope clean. 12 | 13 | //Register the module. 14 | //Getting angular onto the global scope is the client's responsibility. 15 | 16 | /** 17 | * 18 | * @ngdoc overview 19 | * @name scs.couch-potato 20 | * 21 | * @description 22 | * 23 | * ## scs.couch-potato module 24 | * 25 | * ### Loading the Script 26 | * 27 | * Couch Potato needs RequireJS in order to be useful. However, it is not 28 | * necessary for your application to be bootstrapped using AMD. You have 29 | * two options: 30 | * 31 | * #### A. Use Traditional <script> Tags 32 | * 33 | * If you use traditional script tags to load the module (i.e. you aren't 34 | * using AMD to structure your the non-lazy portion of your application, 35 | * you **must** load the following three scripts in this order (other 36 | * modules can be loaded wherever it makes sense for you, but these three 37 | * must follow the order): 38 | * 39 | * 1. Angular 40 | * 2. Couch Potato 41 | * 3. RequireJS 42 | * 43 | *44 | * 45 | * 46 | * 47 | * 48 | *49 | * 50 | * #### B. Use RequireJS 51 | * 52 | * If you use RequireJS, Couch Potato will first try to use an AMD module 53 | * that is defined with the name ```'angular'```. If it does not find 54 | * that, it will try to use an angular object defined as 55 | * ```window.angular```. This flexibility allows you to load angular 56 | * from a script tag (if you do so before your require.js script tag) 57 | * or from RequireJS -- the distinction will be critical if you are 58 | * using multiple instances of angular (in which case I pity you for 59 | * needing to, even though I understand that there are edge cases 60 | * where it is necessary) -- it must be very painful. 61 | * 62 | * ### Adding Couch Potato as a Dependency 63 | * 64 | * Reference Couch Potato as a Dependency as follows: 65 | *
66 | * var myModule = angular.module('myApp', ['myOtherDep', 'scs.couch-potato']); 67 | *68 | * 69 | * See also the {@link scs.couch-potato.$couchPotatoProvider 70 | * $couchPotatoProvider} documentation. 71 | */ 72 | var module = angular.module('scs.couch-potato', ['ng']); 73 | 74 | function CouchPotatoProvider( 75 | $controllerProvider, 76 | $compileProvider, 77 | $provide, 78 | $filterProvider 79 | ) { 80 | 81 | var rootScope = null; 82 | 83 | //Expose each provider's functionality as single-argument functions. 84 | //The component-definining functions that are passed as parameters 85 | //should bear their own names. If apply is true, call apply on the 86 | //root scope. This allows clients that are manually registering 87 | //components (outside of the promise-based methods) to force registration 88 | //to be applied, even if they are not doing so in an angular context. 89 | 90 | function registerValue(value, apply) { 91 | $provide.value.apply(null, value); 92 | if (apply) { 93 | rootScope.$apply(); 94 | } 95 | } 96 | 97 | function registerConstant(value, apply) { 98 | $provide.value.apply(null, value); 99 | if (apply) { 100 | rootScope.$apply(); 101 | } 102 | } 103 | 104 | function registerFactory(factory, apply) { 105 | $provide.factory.apply(null, factory); 106 | if (apply) { 107 | rootScope.$apply(); 108 | } 109 | } 110 | 111 | function registerService(service, apply) { 112 | $provide.service.apply(null, service); 113 | if (apply) { 114 | rootScope.$apply(); 115 | } 116 | } 117 | 118 | function registerFilter(filter, apply) { 119 | $filterProvider.register.apply(null, filter); 120 | if (apply) { 121 | rootScope.$apply(); 122 | } 123 | } 124 | 125 | function registerDirective(directive, apply) { 126 | $compileProvider.directive.apply(null, directive); 127 | if (apply) { 128 | rootScope.$apply(); 129 | } 130 | } 131 | 132 | function registerController(controller, apply) { 133 | $controllerProvider.register.apply(null, controller); 134 | if (apply) { 135 | rootScope.$apply(); 136 | } 137 | } 138 | 139 | function registerDecorator(decorator, apply) { 140 | $provide.decorator.apply(null, decorator); 141 | if (apply) { 142 | rootScope.$apply(); 143 | } 144 | } 145 | 146 | function registerProvider(service, apply) { 147 | $provide.provider.apply(null, service); 148 | if (apply) { 149 | rootScope.$apply(); 150 | } 151 | } 152 | 153 | function resolve(dependencies, returnIndex, returnSubId) { 154 | if (dependencies.dependencies) { 155 | return resolveDependenciesProperty( 156 | dependencies, 157 | returnIndex, 158 | returnSubId 159 | ); 160 | } 161 | else { 162 | return resolveDependencies(dependencies, returnIndex, returnSubId); 163 | } 164 | } 165 | this.resolve = resolve; 166 | 167 | function resolveDependencies(dependencies, returnIndex, returnSubId) { 168 | function delay($q, $rootScope) { 169 | 170 | var defer = $q.defer(); 171 | 172 | require(dependencies, function() { 173 | var args = Array.prototype.slice(arguments); 174 | 175 | var out; 176 | 177 | if (returnIndex === undefined) { 178 | out = arguments[arguments.length - 1]; 179 | } 180 | else { 181 | argForOut = arguments[returnIndex]; 182 | if (returnSubId === undefined) { 183 | out = argForOut; 184 | } 185 | else { 186 | out = argForOut[returnSubId]; 187 | } 188 | } 189 | 190 | defer.resolve(out); 191 | $rootScope.$apply(); 192 | 193 | }); 194 | 195 | return defer.promise; 196 | } 197 | 198 | delay.$inject = ['$q', '$rootScope']; 199 | return delay; 200 | 201 | } 202 | this.resolveDependencies = resolveDependencies; 203 | 204 | function resolveDependenciesProperty(configProperties) { 205 | if (configProperties.dependencies) { 206 | var resolveConfig = configProperties; 207 | var deps = configProperties.dependencies; 208 | delete resolveConfig['dependencies']; 209 | 210 | resolveConfig.resolve = {}; 211 | resolveConfig.resolve.delay = resolveDependencies(deps); 212 | return resolveConfig; 213 | } 214 | else 215 | { 216 | return configProperties; 217 | } 218 | 219 | } 220 | this.resolveDependenciesProperty = resolveDependenciesProperty; 221 | 222 | /** 223 | * 224 | * @ngdoc object 225 | * @name scs.couch-potato.$couchPotato 226 | * 227 | * @description 228 | * 229 | * == 230 | * 231 | * **Important:** you must inject the 232 | * {@link scs.couch-potato.$couchPotatoProvider $couchPotatoProvider} 233 | * at config-time to use the service. 234 | * 235 | */ 236 | this.$get = function ($rootScope) { 237 | var svc = {}; 238 | 239 | rootScope = $rootScope; 240 | 241 | svc.registerValue = registerValue; 242 | svc.registerConstant = registerConstant; 243 | svc.registerFactory = registerFactory; 244 | svc.registerService = registerService; 245 | svc.registerFilter = registerFilter; 246 | svc.registerDirective = registerDirective; 247 | svc.registerController = registerController; 248 | svc.registerDecorator = registerDecorator; 249 | svc.registerProvider = registerProvider; 250 | 251 | svc.resolveDependenciesProperty = resolveDependenciesProperty; 252 | svc.resolveDependencies = resolveDependencies; 253 | svc.resolve = resolve; 254 | 255 | return svc; 256 | }; 257 | this.$get.$inject = ['$rootScope']; 258 | 259 | } 260 | CouchPotatoProvider.$inject = [ 261 | '$controllerProvider', 262 | '$compileProvider', 263 | '$provide', 264 | '$filterProvider' 265 | ]; //inject the providers into CouchPotatoProvider 266 | 267 | /** 268 | * 269 | * @ngdoc object 270 | * @name scs.couch-potato.$couchPotatoProvider 271 | * 272 | * @description 273 | * Injects and retains references to providers that will be used 274 | * by the {@link scs.couch-potato.$couchPotato $couchPotato service} 275 | * at run-time. 276 | * 277 | * It is **mandatory** that you inject the provider before 278 | * your app's module.run is called (e.g. in module.config). 279 | * 280 | * @example 281 | *
282 | * myModule.config( 283 | * [ 284 | * '$couchPotatoProvider', 'myOtherProvider', 285 | * function($couchPotatoProvider, myOtherProvider) { 286 | * myOtherProvider.config = { someParam: 'demo' }; 287 | * // $couchPotatoProvider needs no specific configuration 288 | * } 289 | * ] 290 | * ); 291 | *292 | * 293 | * See the {@link scs.couch-potato couch-potato module documentation} to learn 294 | * how to load the module. 295 | * 296 | * @requires $controllerProvider 297 | * @requires $compileProvider 298 | * @requires $filterProvider 299 | * @requires $provide 300 | * 301 | */ 302 | module.provider('$couchPotato', CouchPotatoProvider); 303 | 304 | this.configureApp = function(app) { 305 | app.registerController = function(name, controller) { 306 | if (app.lazy) { 307 | app.lazy.registerController([name, controller]); 308 | } 309 | else { 310 | app.controller(name, controller); 311 | } 312 | return app; 313 | }; 314 | 315 | app.registerFactory = function(name, factory) { 316 | if (app.lazy) { 317 | app.lazy.registerFactory([name, factory]); 318 | } 319 | else { 320 | app.factory(name, factory); 321 | } 322 | return app; 323 | }; 324 | 325 | 326 | app.registerService = function(name, service) { 327 | if (app.lazy) { 328 | app.lazy.registerService([name, service]); 329 | } 330 | else { 331 | app.service(name, service); 332 | } 333 | return app; 334 | }; 335 | 336 | app.registerDirective = function(name, directive) { 337 | if (app.lazy) { 338 | app.lazy.registerDirective([name, directive]); 339 | } 340 | else { 341 | app.directive(name, directive); 342 | } 343 | return app; 344 | }; 345 | 346 | app.registerDecorator = function(name, decorator) { 347 | if (app.lazy) { 348 | app.lazy.registerDecorator([name, decorator]); 349 | } 350 | else { 351 | app.decorator(name, decorator); 352 | } 353 | return app; 354 | }; 355 | 356 | app.registerProvider = function(name, provider) { 357 | if (app.lazy) { 358 | app.lazy.registerProvider([name, provider]); 359 | } 360 | else { 361 | app.provider(name, provider); 362 | } 363 | return app; 364 | }; 365 | 366 | app.registerValue = function(name, value) { 367 | if (app.lazy) { 368 | app.lazy.registerValue([name, value]); 369 | } 370 | else { 371 | app.value(name, value); 372 | } 373 | return app; 374 | }; 375 | 376 | app.registerConstant = function(name, value) { 377 | if (app.lazy) { 378 | app.lazy.registerConstant([name, value]); 379 | } 380 | else { 381 | app.constant(name, value); 382 | } 383 | return app; 384 | }; 385 | 386 | 387 | app.registerFilter = function(name, filter) { 388 | if (app.lazy) { 389 | app.lazy.registerFilter([name, filter]); 390 | } 391 | else { 392 | app.filter(name, filter); 393 | } 394 | return app; 395 | }; 396 | 397 | /** 398 | * extendInjectable Prototypically extends an injectable object from 399 | * another injectable object. Supports $inject-property-style injections 400 | * (e.g. CtrlFunc.$inject = ['$scope'];) and array notation 401 | * (e.g. ['$scope', function($scope) {...}]). 402 | * 403 | * @param object parent Parent object from which to extend. 404 | * @param object child Child object to receive into. 405 | * @return object The prototypically extended object. 406 | */ 407 | app.extendInjectable = function(parent, child) { 408 | 409 | // split up injections and constructor 410 | function disassembleInjected(object) { 411 | if (angular.isArray(object)) { 412 | var func = object.slice(object.length - 1)[0]; 413 | return [func, object.slice(0, object.length - 1)]; 414 | } 415 | else { 416 | var injections = object.$inject; 417 | return [object, injections || []]; 418 | } 419 | } 420 | 421 | parentPieces = disassembleInjected(parent); 422 | childPieces = disassembleInjected(child); 423 | 424 | // combined constructor. 425 | function CombinedConstructor() { 426 | var args = Array.prototype.slice.call(arguments); 427 | 428 | parentPieces[0].apply(this, args.slice(0, parentPieces[1].length)); 429 | childPieces[0].apply(this, args.slice(parentPieces[1].length)); 430 | } 431 | 432 | // combined object target 433 | function Inherit() {} 434 | // child's prototype will already be present 435 | Inherit.prototype = parentPieces[0].prototype; 436 | 437 | // instantiate it without calling constructor 438 | CombinedConstructor.prototype = new Inherit(); 439 | 440 | // ask for everything. 441 | CombinedConstructor.$inject = 442 | [].concat(parentPieces[1]).concat(childPieces[1]); 443 | 444 | return CombinedConstructor; 445 | }; 446 | 447 | 448 | }; 449 | 450 | }; 451 | 452 | 453 | if ( typeof(define) === 'function' && define.amd) { 454 | // expose couch potato as an AMD module depending on 'angular' 455 | // since we use angular from window, apps are not required 456 | // to export the angular object from a shim. 457 | define(['angular'], function() { return new CouchPotato(window.angular); }); 458 | } 459 | else { 460 | window.couchPotato = new CouchPotato(angular); 461 | } 462 | }()); 463 | --------------------------------------------------------------------------------