/* * 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 so it can be included using a simpler // (and maybe more professional) module name: angular.module('angular-loading-bar', ['chieffancypants.loadingBar']); /** * loadingBarInterceptor service * * Registers itself as an Angular interceptor and listens for XHR requests. */ angular.module('chieffancypants.loadingBar', []) .config(['$httpProvider', function ($httpProvider) { var interceptor = ['$q', '$cacheFactory', '$timeout', '$rootScope', 'cfpLoadingBar', function ($q, $cacheFactory, $timeout, $rootScope, 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 defaults = $httpProvider.defaults; if (config.method !== 'GET' || config.cache === false) { config.cached = false; return false; } if (config.cache === true && defaults.cache === undefined) { cache = $cacheFactory.get('$http'); } else if (defaults.cache !== undefined) { cache = defaults.cache; } else { cache = config.cache; } 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 (!isCached(response.config)) { reqsCompleted++; $rootScope.$broadcast('cfpLoadingBar:loaded', {url: response.config.url}); if (reqsCompleted >= reqsTotal) { setComplete(); } else { cfpLoadingBar.set(reqsCompleted / reqsTotal); } } return response; }, 'responseError': function(rejection) { if (!isCached(rejection.config)) { reqsCompleted++; $rootScope.$broadcast('cfpLoadingBar:loaded', {url: rejection.config.url}); 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. */ .provider('cfpLoadingBar', function() { this.includeSpinner = true; this.includeBar = true; this.latencyThreshold = 100; this.startSize = 0.02; this.parentSelector = 'body'; this.$get = ['$document', '$timeout', '$animate', '$rootScope', function ($document, $timeout, $animate, $rootScope) { var $parentSelector = this.parentSelector, $parent = $document.find($parentSelector), loadingBarContainer = angular.element('