diff --git a/README.md b/README.md index cb409bd..3cde38c 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,15 @@ $http.post('/save', data, { ``` +#### Auto Increment +By default, the loading bar will increment by a random amount that gets smaller as the bar fills, you can disable this: +```js +angular.module('myApp', ['angular-loading-bar']) + .config(['cfpLoadingBarProvider', function(cfpLoadingBarProvider) { + cfpLoadingBarProvider.autoIncrement = false; + }]) +``` ## How it works: @@ -190,7 +198,15 @@ cfpLoadingBar.status() // Returns the loading bar's progress. cfpLoadingBar.complete() // Set the loading bar's progress to 100%, and then remove it from the DOM. - +// +// If your requests don't go through the $http service and can't be automatically detected, +// use the following +cfpLoadingBar.push(x) +// signals the start of a request +// it will broadcast x on the 'cfpLoadingBar:loading' event when called +cfpLoadingBar.pop(y) +// signals the completion of a request +// if it is the last request to complete, broadcasts y on the 'cfpLoadingBar:loaded' event ``` ## Events diff --git a/build/loading-bar.css b/build/loading-bar.css index 72408d1..d547f5b 100644 --- a/build/loading-bar.css +++ b/build/loading-bar.css @@ -1,7 +1,7 @@ /*! * angular-loading-bar v0.9.0 * https://chieffancypants.github.io/angular-loading-bar - * Copyright (c) 2016 Wes Cruver + * Copyright (c) 2017 Wes Cruver * License: MIT */ diff --git a/build/loading-bar.js b/build/loading-bar.js index 01630c0..7b7c7e0 100644 --- a/build/loading-bar.js +++ b/build/loading-bar.js @@ -1,7 +1,7 @@ /*! * angular-loading-bar v0.9.0 * https://chieffancypants.github.io/angular-loading-bar - * Copyright (c) 2016 Wes Cruver + * Copyright (c) 2017 Wes Cruver * License: MIT */ /* @@ -32,39 +32,7 @@ angular.module('chieffancypants.loadingBar', ['cfp.loadingBarInterceptor']); angular.module('cfp.loadingBarInterceptor', ['cfp.loadingBar']) .config(['$httpProvider', function ($httpProvider) { - var interceptor = ['$q', '$cacheFactory', '$timeout', '$rootScope', '$log', 'cfpLoadingBar', function ($q, $cacheFactory, $timeout, $rootScope, $log, cfpLoadingBar) { - - /** - * The total number of requests made - */ - var reqsTotal = 0; - - /** - * The number of requests completed (either successfully or not) - */ - var reqsCompleted = 0; - - /** - * The amount of time spent fetching before showing the loading bar - */ - var latencyThreshold = cfpLoadingBar.latencyThreshold; - - /** - * $timeout handle for latencyThreshold - */ - var startTimeout; - - - /** - * calls cfpLoadingBar.complete() which removes the - * loading bar from the DOM. - */ - function setComplete() { - $timeout.cancel(startTimeout); - cfpLoadingBar.complete(); - reqsCompleted = 0; - reqsTotal = 0; - } + var interceptor = ['$q', '$cacheFactory', '$log', 'cfpLoadingBar', function ($q, $cacheFactory, $log, cfpLoadingBar) { /** * Determine if the response has already been cached @@ -100,14 +68,7 @@ angular.module('cfp.loadingBarInterceptor', ['cfp.loadingBar']) // Check to make sure this request hasn't already been cached and that // the requester didn't explicitly ask us to ignore this request: if (!config.ignoreLoadingBar && !isCached(config)) { - $rootScope.$broadcast('cfpLoadingBar:loading', {url: config.url}); - if (reqsTotal === 0) { - startTimeout = $timeout(function() { - cfpLoadingBar.start(); - }, latencyThreshold); - } - reqsTotal++; - cfpLoadingBar.set(reqsCompleted / reqsTotal); + cfpLoadingBar.push({url: config.url}); } return config; }, @@ -119,13 +80,7 @@ angular.module('cfp.loadingBarInterceptor', ['cfp.loadingBar']) } if (!response.config.ignoreLoadingBar && !isCached(response.config)) { - reqsCompleted++; - if (reqsCompleted >= reqsTotal) { - $rootScope.$broadcast('cfpLoadingBar:loaded', {url: response.config.url, result: response}); - setComplete(); - } else { - cfpLoadingBar.set(reqsCompleted / reqsTotal); - } + cfpLoadingBar.pop({url: response.config.url, result: response}); } return response; }, @@ -137,13 +92,7 @@ angular.module('cfp.loadingBarInterceptor', ['cfp.loadingBar']) } if (!rejection.config.ignoreLoadingBar && !isCached(rejection.config)) { - reqsCompleted++; - if (reqsCompleted >= reqsTotal) { - $rootScope.$broadcast('cfpLoadingBar:loaded', {url: rejection.config.url, result: rejection}); - setComplete(); - } else { - cfpLoadingBar.set(reqsCompleted / reqsTotal); - } + cfpLoadingBar.pop({url: rejection.config.url, result: rejection}); } return $q.reject(rejection); } @@ -192,6 +141,37 @@ angular.module('cfp.loadingBar', []) var includeBar = this.includeBar; var startSize = this.startSize; + /** + * The total number of requests made + */ + var reqsTotal = 0; + + /** + * The number of requests completed (either successfully or not) + */ + var reqsCompleted = 0; + + /** + * The amount of time spent fetching before showing the loading bar + */ + var latencyThreshold = this.latencyThreshold; + + /** + * $timeout handle for latencyThreshold + */ + var startTimeout; + + /** + * calls cfpLoadingBar.complete() which removes the + * loading bar from the DOM. + */ + function setComplete() { + $timeout.cancel(startTimeout); + _complete(); + reqsCompleted = 0; + reqsTotal = 0; + } + /** * Inserts the loading bar element into the dom, and sets it to 2% */ @@ -321,12 +301,35 @@ angular.module('cfp.loadingBar', []) }, 500); } + function _push(info) { + $rootScope.$broadcast('cfpLoadingBar:loading', info); + if (reqsTotal === 0) { + startTimeout = $timeout(function() { + _start(); + }, latencyThreshold); + } + reqsTotal++; + _set(reqsCompleted / reqsTotal); + } + + function _pop(info) { + reqsCompleted++; + if (reqsCompleted >= reqsTotal) { + $rootScope.$broadcast('cfpLoadingBar:loaded', info); + setComplete(); + } else { + _set(reqsCompleted / reqsTotal); + } + } + return { start : _start, set : _set, status : _status, inc : _inc, complete : _complete, + push : _push, + pop : _pop, autoIncrement : this.autoIncrement, includeSpinner : this.includeSpinner, latencyThreshold : this.latencyThreshold, diff --git a/build/loading-bar.min.css b/build/loading-bar.min.css index 0f9f106..75bfc7c 100644 --- a/build/loading-bar.min.css +++ b/build/loading-bar.min.css @@ -1 +1 @@ -#loading-bar,#loading-bar-spinner{pointer-events:none;-webkit-pointer-events:none;-webkit-transition:350ms linear all;-moz-transition:350ms linear all;-o-transition:350ms linear all;transition:350ms linear all}#loading-bar-spinner.ng-enter,#loading-bar-spinner.ng-leave.ng-leave-active,#loading-bar.ng-enter,#loading-bar.ng-leave.ng-leave-active{opacity:0}#loading-bar-spinner.ng-enter.ng-enter-active,#loading-bar-spinner.ng-leave,#loading-bar.ng-enter.ng-enter-active,#loading-bar.ng-leave{opacity:1}#loading-bar .bar{-webkit-transition:width 350ms;-moz-transition:width 350ms;-o-transition:width 350ms;transition:width 350ms;background:#29d;position:fixed;z-index:10002;top:0;left:0;width:100%;height:2px;border-bottom-right-radius:1px;border-top-right-radius:1px}#loading-bar .peg{position:absolute;width:70px;right:0;top:0;height:2px;opacity:.45;-moz-box-shadow:#29d 1px 0 6px 1px;-ms-box-shadow:#29d 1px 0 6px 1px;-webkit-box-shadow:#29d 1px 0 6px 1px;box-shadow:#29d 1px 0 6px 1px;-moz-border-radius:100%;-webkit-border-radius:100%;border-radius:100%}#loading-bar-spinner{display:block;position:fixed;z-index:10002;top:10px;left:10px}#loading-bar-spinner .spinner-icon{width:14px;height:14px;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:loading-bar-spinner 400ms linear infinite;-moz-animation:loading-bar-spinner 400ms linear infinite;-ms-animation:loading-bar-spinner 400ms linear infinite;-o-animation:loading-bar-spinner 400ms linear infinite;animation:loading-bar-spinner 400ms linear infinite}@-webkit-keyframes loading-bar-spinner{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes loading-bar-spinner{0%{-moz-transform:rotate(0);transform:rotate(0)}100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}@-o-keyframes loading-bar-spinner{0%{-o-transform:rotate(0);transform:rotate(0)}100%{-o-transform:rotate(360deg);transform:rotate(360deg)}}@-ms-keyframes loading-bar-spinner{0%{-ms-transform:rotate(0);transform:rotate(0)}100%{-ms-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes loading-bar-spinner{0%{transform:rotate(0)}100%{transform:rotate(360deg)}} \ No newline at end of file +#loading-bar,#loading-bar-spinner{pointer-events:none;-webkit-pointer-events:none;-webkit-transition:350ms linear all;-moz-transition:350ms linear all;-o-transition:350ms linear all;transition:350ms linear all}#loading-bar-spinner.ng-enter,#loading-bar-spinner.ng-leave.ng-leave-active,#loading-bar.ng-enter,#loading-bar.ng-leave.ng-leave-active{opacity:0}#loading-bar-spinner.ng-enter.ng-enter-active,#loading-bar-spinner.ng-leave,#loading-bar.ng-enter.ng-enter-active,#loading-bar.ng-leave{opacity:1}#loading-bar .bar{-webkit-transition:width 350ms;-moz-transition:width 350ms;-o-transition:width 350ms;transition:width 350ms;background:#29d;position:fixed;z-index:10002;top:0;left:0;width:100%;height:2px;border-bottom-right-radius:1px;border-top-right-radius:1px}#loading-bar .peg{position:absolute;width:70px;right:0;top:0;height:2px;opacity:.45;-moz-box-shadow:#29d 1px 0 6px 1px;-ms-box-shadow:#29d 1px 0 6px 1px;-webkit-box-shadow:#29d 1px 0 6px 1px;box-shadow:#29d 1px 0 6px 1px;-moz-border-radius:100%;-webkit-border-radius:100%;border-radius:100%}#loading-bar-spinner{display:block;position:fixed;z-index:10002;top:10px;left:10px}#loading-bar-spinner .spinner-icon{width:14px;height:14px;border:solid 2px transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:loading-bar-spinner .4s linear infinite;-moz-animation:loading-bar-spinner .4s linear infinite;-ms-animation:loading-bar-spinner .4s linear infinite;-o-animation:loading-bar-spinner .4s linear infinite;animation:loading-bar-spinner .4s linear infinite}@-webkit-keyframes loading-bar-spinner{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes loading-bar-spinner{0%{-moz-transform:rotate(0);transform:rotate(0)}100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}@-o-keyframes loading-bar-spinner{0%{-o-transform:rotate(0);transform:rotate(0)}100%{-o-transform:rotate(360deg);transform:rotate(360deg)}}@-ms-keyframes loading-bar-spinner{0%{-ms-transform:rotate(0);transform:rotate(0)}100%{-ms-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes loading-bar-spinner{0%{transform:rotate(0)}100%{transform:rotate(360deg)}} \ No newline at end of file diff --git a/build/loading-bar.min.js b/build/loading-bar.min.js index e4b18ad..06c97f6 100644 --- a/build/loading-bar.min.js +++ b/build/loading-bar.min.js @@ -1,7 +1,8 @@ /*! * angular-loading-bar v0.9.0 * https://chieffancypants.github.io/angular-loading-bar - * Copyright (c) 2016 Wes Cruver + * Copyright (c) 2017 Wes Cruver * License: MIT */ -!function(){"use strict";angular.module("angular-loading-bar",["cfp.loadingBarInterceptor"]),angular.module("chieffancypants.loadingBar",["cfp.loadingBarInterceptor"]),angular.module("cfp.loadingBarInterceptor",["cfp.loadingBar"]).config(["$httpProvider",function(a){var b=["$q","$cacheFactory","$timeout","$rootScope","$log","cfpLoadingBar",function(b,c,d,e,f,g){function h(){d.cancel(j),g.complete(),l=0,k=0}function i(b){var d,e=c.get("$http"),f=a.defaults;!b.cache&&!f.cache||b.cache===!1||"GET"!==b.method&&"JSONP"!==b.method||(d=angular.isObject(b.cache)?b.cache:angular.isObject(f.cache)?f.cache:e);var g=void 0!==d?void 0!==d.get(b.url):!1;return void 0!==b.cached&&g!==b.cached?b.cached:(b.cached=g,g)}var j,k=0,l=0,m=g.latencyThreshold;return{request:function(a){return a.ignoreLoadingBar||i(a)||(e.$broadcast("cfpLoadingBar:loading",{url:a.url}),0===k&&(j=d(function(){g.start()},m)),k++,g.set(l/k)),a},response:function(a){return a&&a.config?(a.config.ignoreLoadingBar||i(a.config)||(l++,l>=k?(e.$broadcast("cfpLoadingBar:loaded",{url:a.config.url,result:a}),h()):g.set(l/k)),a):(f.error("Broken interceptor detected: Config object not supplied in response:\n https://github.com/chieffancypants/angular-loading-bar/pull/50"),a)},responseError:function(a){return a&&a.config?(a.config.ignoreLoadingBar||i(a.config)||(l++,l>=k?(e.$broadcast("cfpLoadingBar:loaded",{url:a.config.url,result:a}),h()):g.set(l/k)),b.reject(a)):(f.error("Broken interceptor detected: Config object not supplied in rejection:\n https://github.com/chieffancypants/angular-loading-bar/pull/50"),b.reject(a))}}}];a.interceptors.push(b)}]),angular.module("cfp.loadingBar",[]).provider("cfpLoadingBar",function(){this.autoIncrement=!0,this.includeSpinner=!0,this.includeBar=!0,this.latencyThreshold=100,this.startSize=.02,this.parentSelector="body",this.spinnerTemplate='
',this.loadingBarTemplate='
',this.$get=["$injector","$document","$timeout","$rootScope",function(a,b,c,d){function e(){if(k||(k=a.get("$animate")),c.cancel(m),!r){var e=b[0],g=e.querySelector?e.querySelector(n):b.find(n)[0];g||(g=e.getElementsByTagName("body")[0]);var h=angular.element(g),i=g.lastChild&&angular.element(g.lastChild);d.$broadcast("cfpLoadingBar:started"),r=!0,v&&k.enter(o,h,i),u&&k.enter(q,h,o),f(w)}}function f(a){if(r){var b=100*a+"%";p.css("width",b),s=a,t&&(c.cancel(l),l=c(function(){g()},250))}}function g(){if(!(h()>=1)){var a=0,b=h();a=b>=0&&.25>b?(3*Math.random()+3)/100:b>=.25&&.65>b?3*Math.random()/100:b>=.65&&.9>b?2*Math.random()/100:b>=.9&&.99>b?.005:0;var c=h()+a;f(c)}}function h(){return s}function i(){s=0,r=!1}function j(){k||(k=a.get("$animate")),f(1),c.cancel(m),m=c(function(){var a=k.leave(o,i);a&&a.then&&a.then(i),k.leave(q),d.$broadcast("cfpLoadingBar:completed")},500)}var k,l,m,n=this.parentSelector,o=angular.element(this.loadingBarTemplate),p=o.find("div").eq(0),q=angular.element(this.spinnerTemplate),r=!1,s=0,t=this.autoIncrement,u=this.includeSpinner,v=this.includeBar,w=this.startSize;return{start:e,set:f,status:h,inc:g,complete:j,autoIncrement:this.autoIncrement,includeSpinner:this.includeSpinner,latencyThreshold:this.latencyThreshold,parentSelector:this.parentSelector,startSize:this.startSize}}]})}(); \ No newline at end of file + +!function(){"use strict";angular.module("angular-loading-bar",["cfp.loadingBarInterceptor"]),angular.module("chieffancypants.loadingBar",["cfp.loadingBarInterceptor"]),angular.module("cfp.loadingBarInterceptor",["cfp.loadingBar"]).config(["$httpProvider",function(e){var n=["$q","$cacheFactory","$log","cfpLoadingBar",function(n,t,a,r){function i(n){var a,r=t.get("$http"),i=e.defaults;!n.cache&&!i.cache||!1===n.cache||"GET"!==n.method&&"JSONP"!==n.method||(a=angular.isObject(n.cache)?n.cache:angular.isObject(i.cache)?i.cache:r);var c=void 0!==a&&void 0!==a.get(n.url);return void 0!==n.cached&&c!==n.cached?n.cached:(n.cached=c,c)}return{request:function(e){return e.ignoreLoadingBar||i(e)||r.push({url:e.url}),e},response:function(e){return e&&e.config?(e.config.ignoreLoadingBar||i(e.config)||r.pop({url:e.config.url,result:e}),e):(a.error("Broken interceptor detected: Config object not supplied in response:\n https://github.com/chieffancypants/angular-loading-bar/pull/50"),e)},responseError:function(e){return e&&e.config?(e.config.ignoreLoadingBar||i(e.config)||r.pop({url:e.config.url,result:e}),n.reject(e)):(a.error("Broken interceptor detected: Config object not supplied in rejection:\n https://github.com/chieffancypants/angular-loading-bar/pull/50"),n.reject(e))}}}];e.interceptors.push(n)}]),angular.module("cfp.loadingBar",[]).provider("cfpLoadingBar",function(){this.autoIncrement=!0,this.includeSpinner=!0,this.includeBar=!0,this.latencyThreshold=100,this.startSize=.02,this.parentSelector="body",this.spinnerTemplate='
',this.loadingBarTemplate='
',this.$get=["$injector","$document","$timeout","$rootScope",function(e,n,t,a){function r(){t.cancel(m),u(),z=0,q=0}function i(){if(p||(p=e.get("$animate")),t.cancel(f),!$){var r=n[0],i=r.querySelector?r.querySelector(v):n.find(v)[0];i||(i=r.getElementsByTagName("body")[0]);var o=angular.element(i),l=i.lastChild&&angular.element(i.lastChild);a.$broadcast("cfpLoadingBar:started"),$=!0,j&&p.enter(B,o,l),L&&p.enter(S,o,B),c(I)}}function c(e){if($){var n=100*e+"%";b.css("width",n),y=e,T&&(t.cancel(g),g=t(function(){o()},250))}}function o(){if(!(l()>=1)){var e=0,n=l();e=n>=0&&n<.25?(3*Math.random()+3)/100:n>=.25&&n<.65?3*Math.random()/100:n>=.65&&n<.9?2*Math.random()/100:n>=.9&&n<.99?.005:0,c(l()+e)}}function l(){return y}function d(){y=0,$=!1}function u(){p||(p=e.get("$animate")),c(1),t.cancel(f),f=t(function(){var e=p.leave(B,d);e&&e.then&&e.then(d),p.leave(S),a.$broadcast("cfpLoadingBar:completed")},500)}function s(e){a.$broadcast("cfpLoadingBar:loading",e),0===q&&(m=t(function(){i()},C)),c(z/++q)}function h(e){++z>=q?(a.$broadcast("cfpLoadingBar:loaded",e),r()):c(z/q)}var p,g,f,m,v=this.parentSelector,B=angular.element(this.loadingBarTemplate),b=B.find("div").eq(0),S=angular.element(this.spinnerTemplate),$=!1,y=0,T=this.autoIncrement,L=this.includeSpinner,j=this.includeBar,I=this.startSize,q=0,z=0,C=this.latencyThreshold;return{start:i,set:c,status:l,inc:o,complete:u,push:s,pop:h,autoIncrement:this.autoIncrement,includeSpinner:this.includeSpinner,latencyThreshold:this.latencyThreshold,parentSelector:this.parentSelector,startSize:this.startSize}}]})}(); \ No newline at end of file diff --git a/package.json b/package.json index 6529008..9942ee7 100644 --- a/package.json +++ b/package.json @@ -31,16 +31,17 @@ }, "homepage": "https://chieffancypants.github.io/angular-loading-bar", "devDependencies": { - "karma-jasmine": "^0.1.3", - "karma-coffee-preprocessor": "^0.2.0", - "karma-phantomjs-launcher": "^0.1.0", - "karma": "~0.12.0", - "karma-coverage": "^0.1.0", - "grunt": "~0.4.1", - "grunt-contrib-jshint": "~0.6.4", - "grunt-contrib-uglify": "^0.9.1", - "grunt-contrib-cssmin": "~0.12.0", - "grunt-karma": "~0.11.0", - "grunt-contrib-concat": "^0.5.0" + "grunt": "^1.0.1", + "grunt-contrib-concat": "^1.0.1", + "grunt-contrib-cssmin": "^2.2.0", + "grunt-contrib-jshint": "^1.1.0", + "grunt-contrib-uglify": "^3.0.0", + "grunt-karma": "^2.0.0", + "jasmine-core": "^2.6.2", + "karma": "^1.7.0", + "karma-coffee-preprocessor": "^1.0.1", + "karma-coverage": "^1.1.1", + "karma-jasmine": "^1.1.0", + "karma-phantomjs-launcher": "^1.0.4" } } diff --git a/src/loading-bar.js b/src/loading-bar.js index 44e092a..501a949 100644 --- a/src/loading-bar.js +++ b/src/loading-bar.js @@ -26,39 +26,7 @@ angular.module('chieffancypants.loadingBar', ['cfp.loadingBarInterceptor']); angular.module('cfp.loadingBarInterceptor', ['cfp.loadingBar']) .config(['$httpProvider', function ($httpProvider) { - var interceptor = ['$q', '$cacheFactory', '$timeout', '$rootScope', '$log', 'cfpLoadingBar', function ($q, $cacheFactory, $timeout, $rootScope, $log, cfpLoadingBar) { - - /** - * The total number of requests made - */ - var reqsTotal = 0; - - /** - * The number of requests completed (either successfully or not) - */ - var reqsCompleted = 0; - - /** - * The amount of time spent fetching before showing the loading bar - */ - var latencyThreshold = cfpLoadingBar.latencyThreshold; - - /** - * $timeout handle for latencyThreshold - */ - var startTimeout; - - - /** - * calls cfpLoadingBar.complete() which removes the - * loading bar from the DOM. - */ - function setComplete() { - $timeout.cancel(startTimeout); - cfpLoadingBar.complete(); - reqsCompleted = 0; - reqsTotal = 0; - } + var interceptor = ['$q', '$cacheFactory', '$log', 'cfpLoadingBar', function ($q, $cacheFactory, $log, cfpLoadingBar) { /** * Determine if the response has already been cached @@ -94,14 +62,7 @@ angular.module('cfp.loadingBarInterceptor', ['cfp.loadingBar']) // Check to make sure this request hasn't already been cached and that // the requester didn't explicitly ask us to ignore this request: if (!config.ignoreLoadingBar && !isCached(config)) { - $rootScope.$broadcast('cfpLoadingBar:loading', {url: config.url}); - if (reqsTotal === 0) { - startTimeout = $timeout(function() { - cfpLoadingBar.start(); - }, latencyThreshold); - } - reqsTotal++; - cfpLoadingBar.set(reqsCompleted / reqsTotal); + cfpLoadingBar.push({url: config.url}); } return config; }, @@ -113,13 +74,7 @@ angular.module('cfp.loadingBarInterceptor', ['cfp.loadingBar']) } if (!response.config.ignoreLoadingBar && !isCached(response.config)) { - reqsCompleted++; - if (reqsCompleted >= reqsTotal) { - $rootScope.$broadcast('cfpLoadingBar:loaded', {url: response.config.url, result: response}); - setComplete(); - } else { - cfpLoadingBar.set(reqsCompleted / reqsTotal); - } + cfpLoadingBar.pop({url: response.config.url, result: response}); } return response; }, @@ -131,13 +86,7 @@ angular.module('cfp.loadingBarInterceptor', ['cfp.loadingBar']) } if (!rejection.config.ignoreLoadingBar && !isCached(rejection.config)) { - reqsCompleted++; - if (reqsCompleted >= reqsTotal) { - $rootScope.$broadcast('cfpLoadingBar:loaded', {url: rejection.config.url, result: rejection}); - setComplete(); - } else { - cfpLoadingBar.set(reqsCompleted / reqsTotal); - } + cfpLoadingBar.pop({url: rejection.config.url, result: rejection}); } return $q.reject(rejection); } @@ -186,6 +135,37 @@ angular.module('cfp.loadingBar', []) var includeBar = this.includeBar; var startSize = this.startSize; + /** + * The total number of requests made + */ + var reqsTotal = 0; + + /** + * The number of requests completed (either successfully or not) + */ + var reqsCompleted = 0; + + /** + * The amount of time spent fetching before showing the loading bar + */ + var latencyThreshold = this.latencyThreshold; + + /** + * $timeout handle for latencyThreshold + */ + var startTimeout; + + /** + * calls cfpLoadingBar.complete() which removes the + * loading bar from the DOM. + */ + function setComplete() { + $timeout.cancel(startTimeout); + _complete(); + reqsCompleted = 0; + reqsTotal = 0; + } + /** * Inserts the loading bar element into the dom, and sets it to 2% */ @@ -315,12 +295,35 @@ angular.module('cfp.loadingBar', []) }, 500); } + function _push(info) { + $rootScope.$broadcast('cfpLoadingBar:loading', info); + if (reqsTotal === 0) { + startTimeout = $timeout(function() { + _start(); + }, latencyThreshold); + } + reqsTotal++; + _set(reqsCompleted / reqsTotal); + } + + function _pop(info) { + reqsCompleted++; + if (reqsCompleted >= reqsTotal) { + $rootScope.$broadcast('cfpLoadingBar:loaded', info); + setComplete(); + } else { + _set(reqsCompleted / reqsTotal); + } + } + return { start : _start, set : _set, status : _status, inc : _inc, complete : _complete, + push : _push, + pop : _pop, autoIncrement : this.autoIncrement, includeSpinner : this.includeSpinner, latencyThreshold : this.latencyThreshold, diff --git a/test/loading-bar-interceptor-config.coffee b/test/loading-bar-interceptor-config.coffee index f29ab1e..b33cd8d 100644 --- a/test/loading-bar-interceptor-config.coffee +++ b/test/loading-bar-interceptor-config.coffee @@ -44,28 +44,27 @@ describe 'loadingBarInterceptor Service - config options', -> cfpLoadingBar.complete() $timeout.flush() - it 'should not auto increment loadingBar if configured', (done) -> - module 'chieffancypants.loadingBar', (cfpLoadingBarProvider) -> - cfpLoadingBarProvider.autoIncrement = false - return - inject ($timeout, cfpLoadingBar) -> - flag = false - cfpLoadingBar.start() - cfpLoadingBar.set(.5) - runs -> + describe 'loadingBarInterceptor Service - config options, async tests', -> + beforeEach (done) -> + module 'chieffancypants.loadingBar', (cfpLoadingBarProvider) -> + cfpLoadingBarProvider.autoIncrement = false + return + inject ($timeout, cfpLoadingBar) -> + cfpLoadingBar.start() + cfpLoadingBar.set(.5) setTimeout -> - flag = true + done() , 500 - waitsFor -> - return flag - , "500ms timeout" - , 1000 + it 'should not auto increment loadingBar if configured', (done) -> + inject ($timeout, cfpLoadingBar) -> + setTimeout -> + expect(cfpLoadingBar.status()).toBe .5; + cfpLoadingBar.complete(); + $timeout.flush(); + done(); + , 500 - runs -> - expect(cfpLoadingBar.status()).toBe .5; - cfpLoadingBar.complete() - $timeout.flush() it 'should auto increment loadingBar if configured', -> module 'chieffancypants.loadingBar', (cfpLoadingBarProvider) -> diff --git a/test/loading-bar-interceptor.coffee b/test/loading-bar-interceptor.coffee index 241967f..cf0ab64 100644 --- a/test/loading-bar-interceptor.coffee +++ b/test/loading-bar-interceptor.coffee @@ -34,13 +34,21 @@ describe 'loadingBarInterceptor Service', -> $animate.triggerCallbacks && $animate.triggerCallbacks() beforeEach -> - this.addMatchers - toBeBetween: (high, low) -> - if low > high - temp = low - low = high - high = temp - return this.actual > low && this.actual < high + jasmine.addMatchers + toBeBetween: () -> + return compare: (actual, high, low) -> + if low > high + temp = low + low = high + high = temp + passed = actual > low && actual < high + result = + pass: + passed + message: + 'expected ' + actual + ' to ' + (if passed then 'not ' else '') + 'be between ' + low + ' and ' + high + return result + afterEach -> @@ -494,6 +502,28 @@ describe 'LoadingBar only', -> expect(isLoadingBarInjected($document.find(cfpLoadingBar.parentSelector))).toBe false + it 'should allow for explicit calls to push and pop', -> + cfpLoadingBar.start() + $timeout.flush() + + # pushing adds only to reqsTotal, and shouldn't set the status above 0, even with multiple calls + cfpLoadingBar.push({}) + expect(cfpLoadingBar.status()).toBe(0) + cfpLoadingBar.push({}) + cfpLoadingBar.push({}) + cfpLoadingBar.push({}) + expect(cfpLoadingBar.status()).toBe(0) + + # pushing adds to reqsCompleted, and should set the status to reqsCompleted/reqsTotal + cfpLoadingBar.pop({}) + expect(cfpLoadingBar.status()).toBe(0.25) + cfpLoadingBar.pop({}) + expect(cfpLoadingBar.status()).toBe(0.5) + cfpLoadingBar.pop({}) + expect(cfpLoadingBar.status()).toBe(0.75) + cfpLoadingBar.pop({}) + expect(cfpLoadingBar.status()).toBe(1) + describe 'Interceptor tests', -> provider = $http = $httpBackend = $log = null