diff --git a/js/angular/service/templateCache.js b/js/angular/service/templateCache.js new file mode 100644 index 0000000000..18d094dfca --- /dev/null +++ b/js/angular/service/templateCache.js @@ -0,0 +1,142 @@ +// closure to keep things neat +(function() { + var templatesToCache = []; + +/** + * @ngdoc service + * @name $ionicTemplateCache + * @module ionic + * @description An angular service that preemptively caches template files to reduce flicker and boost performance. + * @usage + * State templates are cached automatically, but you can optionally cache other templates. + * ```js + * $ionicTemplateCahce('myNgIncludeTemplate.html'); + * ``` + * + * Optionally disable all preemptive caching with the `$ionicConfigProvider` or individual states by setting `prefetchTemplate` + * in the $state definition + * ```js + * $ionicTemplateCahce('myNgIncludeTemplate.html'); + * ```js + * angular.module('myApp', ['ionic']) + * .config(function($stateProvider, $ionicConfigProvider) { + * + * // disable preemptive template caching globally + * $ionicConfigProvider.prefetchTemplates(false); + * + * // disable individual states + * $stateProvider + * .state('tabs', { + * url: "/tab", + * abstract: true, + * prefetchTemplate: false, + * templateUrl: "tabs-templates/tabs.html" + * }) + * .state('tabs.home', { + * url: "/home", + * views: { + * 'home-tab': { + * prefetchTemplate: false, + * templateUrl: "tabs-templates/home.html", + * controller: 'HomeTabCtrl' + * } + * } + * }); + * }); + * ``` + */ +IonicModule +.factory('$ionicTemplateCache', [ +'$http', +'$templateCache', +'$timeout', +'$ionicConfig', +function($http, $templateCache, $timeout, $ionicConfig) { + var toCache = templatesToCache, + hasRun = false; + /** + * @ngdoc method + * @name $ionicTemplateCache + * @description Add template(s) to be cached. + * @param {string | array} string or array of strings of templates to cache. + */ + function $ionicTemplateCache(templates){ + if(toCache.length > 500) return false; + if(typeof templates === 'undefined')return run(); + if(isString(templates))templates = [templates]; + forEach(templates, function(template){ + toCache.push(template); + }); + // is this is being called after the initial IonicModule.run() + if(hasRun) run(); + } + + // run through methods - internal method + var run = function(){ + if($ionicConfig.prefetchTemplates === false)return + //console.log('prefetching', toCache); + //for testing + $ionicTemplateCache._runCount++; + + hasRun = true; + // ignore if race condition already zeroed out array + if(toCache.length === 0)return; + //console.log(toCache); + var i = 0; + while ( i < 5 && (template = toCache.pop()) ) { + // note that inline templates are ignored by this request + if (isString(template)) $http.get(template, { cache: $templateCache }); + i++; + } + // only preload 5 templates a second + if(toCache.length)$timeout(function(){run()}, 1000); + }; + + // exposing for testing + $ionicTemplateCache._runCount = 0; + // default method + return $ionicTemplateCache; +}]) + +/** + * @ngdoc config + * @name $ionicTemplateCache + * @module ionic + * @private + * + * Intercepts the $stateprovider.state() command to look for templateUrls that can be cached + */ +.config([ +'$stateProvider', +'$ionicConfigProvider', +function($stateProvider, $ionicConfigProvider) { + var stateProviderState = $stateProvider.state; + $stateProvider.state = function(stateName, definition) { + // don't even bother if it's disabled. note, another config may run after this, so it's not a catch-all + if($ionicConfigProvider.prefetchTemplates() !== false){ + var enabled = definition.prefetchTemplate != false; + if(enabled && isString(definition.templateUrl))templatesToCache.push(definition.templateUrl); + if(angular.isObject(definition.views)){ + for (var key in definition.views){ + enabled = definition.views[key].prefetchTemplate != false; + if(enabled && isString(definition.views[key].templateUrl)) templatesToCache.push(definition.views[key].templateUrl); + } + } + } + return stateProviderState.call($stateProvider, stateName, definition); + }; +}]) + +/** + * @ngdoc run + * @name $ionicTemplateCache + * @module ionic + * @private + * + * process the templateUrls collected by the $stateProvider, adding them to the cache + */ +.run(function($ionicTemplateCache) { + $ionicTemplateCache(); +}); + +})(); diff --git a/test/html/tabs-cache.html b/test/html/tabs-cache.html new file mode 100644 index 0000000000..5dd66ff0f0 --- /dev/null +++ b/test/html/tabs-cache.html @@ -0,0 +1,112 @@ + + + + + + Tabs Example + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/html/tabs-templates/about.html b/test/html/tabs-templates/about.html new file mode 100644 index 0000000000..44fcbcf250 --- /dev/null +++ b/test/html/tabs-templates/about.html @@ -0,0 +1,10 @@ + + +

Create hybrid mobile apps with the web technologies you love.

+

Free and open source, Ionic offers a library of mobile-optimized HTML, CSS and JS components for building highly interactive apps.

+

Built with Sass and optimized for AngularJS.

+

+ Tabs Nav Stack +

+
+
\ No newline at end of file diff --git a/test/html/tabs-templates/facts.html b/test/html/tabs-templates/facts.html new file mode 100644 index 0000000000..e2cdde198c --- /dev/null +++ b/test/html/tabs-templates/facts.html @@ -0,0 +1,13 @@ + + +

Banging your head against a wall uses 150 calories an hour.

+

Dogs have four toes on their hind feet, and five on their front feet.

+

The ant can lift 50 times its own weight, can pull 30 times its own weight and always falls over on its right side when intoxicated.

+

A cockroach will live nine days without it's head, before it starves to death.

+

Polar bears are left handed.

+

+ Home + More Facts +

+
+
\ No newline at end of file diff --git a/test/html/tabs-templates/facts2.html b/test/html/tabs-templates/facts2.html new file mode 100644 index 0000000000..101ddd1436 --- /dev/null +++ b/test/html/tabs-templates/facts2.html @@ -0,0 +1,14 @@ + + +

111,111,111 x 111,111,111 = 12,345,678,987,654,321

+

1 in every 4 Americans has appeared on T.V.

+

11% of the world is left-handed.

+

1 in 8 Americans has worked at a McDonalds restaurant.

+

$283,200 is the absolute highest amount of money you can win on Jeopardy.

+

101 Dalmatians, Peter Pan, Lady and the Tramp, and Mulan are the only Disney cartoons where both parents are present and don't die throughout the movie.

+

+ Home + Scientific Facts +

+
+
\ No newline at end of file diff --git a/test/html/tabs-templates/home.html b/test/html/tabs-templates/home.html new file mode 100644 index 0000000000..173828e6b2 --- /dev/null +++ b/test/html/tabs-templates/home.html @@ -0,0 +1,10 @@ + + +

Example of Ionic tabs. Navigate to each tab, and + navigate to child views of each tab and notice how + each tab has its own navigation history.

+

+ Scientific Facts +

+
+
\ No newline at end of file diff --git a/test/html/tabs-templates/nav-stack.html b/test/html/tabs-templates/nav-stack.html new file mode 100644 index 0000000000..b0509d1b35 --- /dev/null +++ b/test/html/tabs-templates/nav-stack.html @@ -0,0 +1,5 @@ + + +

+
+
\ No newline at end of file diff --git a/test/html/tabs-templates/tabs.html b/test/html/tabs-templates/tabs.html new file mode 100644 index 0000000000..f80f5965c8 --- /dev/null +++ b/test/html/tabs-templates/tabs.html @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/unit/angular/service/templateCache.unit.js b/test/unit/angular/service/templateCache.unit.js new file mode 100644 index 0000000000..3cfb236f72 --- /dev/null +++ b/test/unit/angular/service/templateCache.unit.js @@ -0,0 +1,38 @@ +describe('$ionicTemplateCache', function() { + beforeEach(module('ionic')); + + beforeEach(inject(function($ionicTemplateCache, $templateCache) { + ionicTemplateCache = $ionicTemplateCache; + templateCache = $templateCache; + })); + + it('should run during initial startup', function () { + var info = templateCache.info(); + expect(info.size).toBe(0); + expect(ionicTemplateCache._runCount).toBe(1); + }); + + it('should run immediately after a new addition after initial run', inject(function ($templateCache, $httpBackend, $timeout) { + $httpBackend.whenGET("/test").respond([{hello:"world"}]); + ionicTemplateCache('/test'); + $timeout.flush(); + var info = templateCache.info(); + expect(info.size).toBe(1); + expect(ionicTemplateCache._runCount).toBe(2); + })); + + it('should cache 5 templates at a time', inject(function ($httpBackend, $timeout) { + $httpBackend.whenGET("/test1").respond([{hello:"world"}]); + $httpBackend.whenGET("/test2").respond([{hello:"world"}]); + $httpBackend.whenGET("/test3").respond([{hello:"world"}]); + $httpBackend.whenGET("/test4").respond([{hello:"world"}]); + $httpBackend.whenGET("/test5").respond([{hello:"world"}]); + $httpBackend.whenGET("/test6").respond([{hello:"world"}]); + $httpBackend.whenGET("/test7").respond([{hello:"world"}]); + ionicTemplateCache(['/test1','/test2','/test2','/test3','/test4','/test5','/test6','/test7']); + $timeout.flush(); + var info = templateCache.info(); + expect(info.size).toBe(7); + expect(ionicTemplateCache._runCount).toBe(3); + })); +}); \ No newline at end of file