123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- /*
- * angular-loading-bar
- *
- * intercepts XHR requests and creates a loading bar.
- * Based on the excellent nprogress work by rstacruz (more info in readme)
- *
- * (c) 2013 Wes Cruver
- * License: MIT
- */
- (function() {
- 'use strict';
- // Alias the loading bar for various backwards compatibilities since the project has matured:
- angular.module('angular-loading-bar', ['cfp.loadingBarInterceptor']);
- angular.module('chieffancypants.loadingBar', ['cfp.loadingBarInterceptor']);
- /**
- * loadingBarInterceptor service
- *
- * Registers itself as an Angular interceptor and listens for XHR requests.
- */
- 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;
- }
- /**
- * Determine if the response has already been cached
- * @param {Object} config the config option from the request
- * @return {Boolean} retrns true if cached, otherwise false
- */
- function isCached(config) {
- var cache;
- var defaultCache = $cacheFactory.get('$http');
- var defaults = $httpProvider.defaults;
- // Choose the proper cache source. Borrowed from angular: $http service
- if ((config.cache || defaults.cache) && config.cache !== false &&
- (config.method === 'GET' || config.method === 'JSONP')) {
- cache = angular.isObject(config.cache) ? config.cache
- : angular.isObject(defaults.cache) ? defaults.cache
- : defaultCache;
- }
- var cached = cache !== undefined ?
- cache.get(config.url) !== undefined : false;
- if (config.cached !== undefined && cached !== config.cached) {
- return config.cached;
- }
- config.cached = cached;
- return cached;
- }
- return {
- 'request': function(config) {
- // 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);
- }
- return config;
- },
- 'response': function(response) {
- if (!response || !response.config) {
- $log.error('Broken interceptor detected: Config object not supplied in response:\n https://github.com/chieffancypants/angular-loading-bar/pull/50');
- return response;
- }
- if (!response.config.ignoreLoadingBar && !isCached(response.config)) {
- reqsCompleted++;
- $rootScope.$broadcast('cfpLoadingBar:loaded', {url: response.config.url, result: response});
- if (reqsCompleted >= reqsTotal) {
- setComplete();
- } else {
- cfpLoadingBar.set(reqsCompleted / reqsTotal);
- }
- }
- return response;
- },
- 'responseError': function(rejection) {
- if (!rejection || !rejection.config) {
- $log.error('Broken interceptor detected: Config object not supplied in rejection:\n https://github.com/chieffancypants/angular-loading-bar/pull/50');
- return $q.reject(rejection);
- }
- if (!rejection.config.ignoreLoadingBar && !isCached(rejection.config)) {
- reqsCompleted++;
- $rootScope.$broadcast('cfpLoadingBar:loaded', {url: rejection.config.url, result: rejection});
- if (reqsCompleted >= reqsTotal) {
- setComplete();
- } else {
- cfpLoadingBar.set(reqsCompleted / reqsTotal);
- }
- }
- return $q.reject(rejection);
- }
- };
- }];
- $httpProvider.interceptors.push(interceptor);
- }]);
- /**
- * Loading Bar
- *
- * This service handles adding and removing the actual element in the DOM.
- * Generally, best practices for DOM manipulation is to take place in a
- * directive, but because the element itself is injected in the DOM only upon
- * XHR requests, and it's likely needed on every view, the best option is to
- * use a service.
- */
- angular.module('cfp.loadingBar', [])
- .provider('cfpLoadingBar', function() {
- this.includeSpinner = true;
- this.includeBar = true;
- this.latencyThreshold = 100;
- this.startSize = 0.02;
- this.parentSelector = 'body';
- this.spinnerTemplate = '<div id="loading-bar-spinner"><div class="spinner-icon"></div></div>';
- this.loadingBarTemplate = '<div id="loading-bar"><div class="bar"><div class="peg"></div></div></div>';
- this.$get = ['$injector', '$document', '$timeout', '$rootScope', function ($injector, $document, $timeout, $rootScope) {
- var $animate;
- var $parentSelector = this.parentSelector,
- loadingBarContainer = angular.element(this.loadingBarTemplate),
- loadingBar = loadingBarContainer.find('div').eq(0),
- spinner = angular.element(this.spinnerTemplate);
- var incTimeout,
- completeTimeout,
- started = false,
- status = 0;
- var includeSpinner = this.includeSpinner;
- var includeBar = this.includeBar;
- var startSize = this.startSize;
- /**
- * Inserts the loading bar element into the dom, and sets it to 2%
- */
- function _start() {
- if (!$animate) {
- $animate = $injector.get('$animate');
- }
- var $parent = $document.find($parentSelector).eq(0);
- $timeout.cancel(completeTimeout);
- // do not continually broadcast the started event:
- if (started) {
- return;
- }
- $rootScope.$broadcast('cfpLoadingBar:started');
- started = true;
- if (includeBar) {
- $animate.enter(loadingBarContainer, $parent, angular.element($parent[0].lastChild));
- }
- if (includeSpinner) {
- $animate.enter(spinner, $parent, angular.element($parent[0].lastChild));
- }
- _set(startSize);
- }
- /**
- * Set the loading bar's width to a certain percent.
- *
- * @param n any value between 0 and 1
- */
- function _set(n) {
- if (!started) {
- return;
- }
- var pct = (n * 100) + '%';
- loadingBar.css('width', pct);
- status = n;
- // increment loadingbar to give the illusion that there is always
- // progress but make sure to cancel the previous timeouts so we don't
- // have multiple incs running at the same time.
- $timeout.cancel(incTimeout);
- incTimeout = $timeout(function() {
- _inc();
- }, 250);
- }
- /**
- * Increments the loading bar by a random amount
- * but slows down as it progresses
- */
- function _inc() {
- if (_status() >= 1) {
- return;
- }
- var rnd = 0;
- // TODO: do this mathmatically instead of through conditions
- var stat = _status();
- if (stat >= 0 && stat < 0.25) {
- // Start out between 3 - 6% increments
- rnd = (Math.random() * (5 - 3 + 1) + 3) / 100;
- } else if (stat >= 0.25 && stat < 0.65) {
- // increment between 0 - 3%
- rnd = (Math.random() * 3) / 100;
- } else if (stat >= 0.65 && stat < 0.9) {
- // increment between 0 - 2%
- rnd = (Math.random() * 2) / 100;
- } else if (stat >= 0.9 && stat < 0.99) {
- // finally, increment it .5 %
- rnd = 0.005;
- } else {
- // after 99%, don't increment:
- rnd = 0;
- }
- var pct = _status() + rnd;
- _set(pct);
- }
- function _status() {
- return status;
- }
- function _completeAnimation() {
- status = 0;
- started = false;
- }
- function _complete() {
- if (!$animate) {
- $animate = $injector.get('$animate');
- }
- $rootScope.$broadcast('cfpLoadingBar:completed');
- _set(1);
- $timeout.cancel(completeTimeout);
- // Attempt to aggregate any start/complete calls within 500ms:
- completeTimeout = $timeout(function() {
- var promise = $animate.leave(loadingBarContainer, _completeAnimation);
- if (promise && promise.then) {
- promise.then(_completeAnimation);
- }
- $animate.leave(spinner);
- }, 500);
- }
- return {
- start : _start,
- set : _set,
- status : _status,
- inc : _inc,
- complete : _complete,
- includeSpinner : this.includeSpinner,
- latencyThreshold : this.latencyThreshold,
- parentSelector : this.parentSelector,
- startSize : this.startSize
- };
- }]; //
- }); // wtf javascript. srsly
- })(); //
|