From e1d6cc4d4872b6a658a1b8de7d9f29d73374712c Mon Sep 17 00:00:00 2001 From: chieffancypants Date: Mon, 9 Sep 2013 22:26:39 -0400 Subject: [PATCH] commit message goes here --- .editorconfig | 21 ++++ .gitignore | 0 README.md | 13 +++ src/loading-bar-interceptor.js | 142 ++++++++++++++++++++++++++++ src/loading-bar.js | 74 +++++++++++++++ test/loading-bar-interceptor.coffee | 27 ++++++ 6 files changed, 277 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 src/loading-bar-interceptor.js create mode 100644 src/loading-bar.js create mode 100644 test/loading-bar-interceptor.coffee diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c2cdfb8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + + +[*] + +# Change these settings to your own preference +indent_style = space +indent_size = 2 + +# We recommend you to keep these unchanged +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md index f15ce65..ee27313 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,16 @@ angular-loading-bar =================== Automatically add a loading bar to your angular apps, automatically. Automatic. + + +## Why I created this + +I made this for two reasons: +1. To make sure it was automatic +2. To make sure it was fully tested + +### Automated +There are a couple projects similar to this out there, but none are ideal. All implementations I've seen are based on the excellent [nprogress](https://github.com/rstacruz/nprogress) by rstacruz, which requires that you maintain state on behalf of the loading bar. In other words, you're setting the value of the loading/progress bar manually from potentially many different locations. This becomes complicated when you have a very large application with several services all making independant XHR requests. + +### Tested +The basis of Angular is that it's easy to test. So it pains me to see angular modules without tests. This loading bar aims for 100% code coverage. diff --git a/src/loading-bar-interceptor.js b/src/loading-bar-interceptor.js new file mode 100644 index 0000000..44c6833 --- /dev/null +++ b/src/loading-bar-interceptor.js @@ -0,0 +1,142 @@ +/* + * angular-loading-bar + * + * intercepts XHR requests and creates a loading bar when that shit happens. + * Based on the excellent nprogress work by rstacruz (more info in readme) + * + * (c) 2013 Wes Cruver + * License: MIT + */ + + + +/** + * loadingBarInterceptor service + * + * Registers itself as an Angular interceptor and listens for XHR requests. + */ +angular.module('chieffancypants.loadingBar', []) + .factory('loadingBarInterceptor', function($q, loadingBar) { + + /** + * The total number of requests made + * @type {Number} + */ + var reqsTotal = 0; + + /** + * The number of requests completed (either successfully or not) + * @type {Number} + */ + var reqsCompleted = 0; + + + /** + * increments the reqsCompleted, and checks if all XHR requests have + * completed. If so, it calls loadingBar.complete() which removes the + * loading bar from the DOM. + */ + function checkComplete() { + reqsCompleted++; + if (reqsCompleted === reqsTotal) { + loadingBar.complete(); + } + } + + return { + 'request': function(config) { + reqsTotal++; + return config; + }, + + 'response': function(response) { + checkComplete(); + loadingBar.set(reqsCompleted / reqsTotal); + return response; + }, + + 'responseError': function(rejection) { + checkComplete(); + loadingBar.set(reqsCompleted / reqsTotal); + return $q.reject(rejection); + } + + }; + }) + + /** + * Loading Bar + * + * This service handles actually adding and removing the element from the DOM. + * Because this is such a light-weight element, the + */ + .factory('cfpLoadingBar', ['$document', '$timeout', function ($document, $timeout) { + 'use strict'; + + var $body = $document.find('body'), + loadingBarContainer = angular.element('
'), + loadingBar = loadingBarContainer.find('div').eq(0); + + var started = false, + status = 0; + + + /** + * Inserts the loading bar element into the dom, and sets it to 1% + */ + function _start() { + started = true; + $body.append(loadingBarContainer); + loadingBar.css('width', '1%').css('opacity', 1); + } + + /** + * Set the loading bar's width to a certain percent. + * + * @param n any value between 0 and 1 + */ + function _set(n) { + if (!started) { + _start(); + } + var pct = (n * 100) + '%'; + loadingBar.css('width', pct); + status = n; + + // give the illusion that there is always progress... + $timeout(function() { + _inc(); + }, 500); + } + + /** + * Increments the loading bar by a random amount between .1% and .9% + */ + function _inc() { + if (_status() >= 1) { + return; + } + var pct = _status() + (Math.random() / 100); + _set(pct); + console.log('status is', _status()); + } + + function _status() { + return status; + } + + return { + start: _start, + set: _set, + status: _status, + + complete: function () { + $timeout(function() { + loadingBar.css('opacity', 0); + started = false; + }, 500); + } + + }; + }]); + diff --git a/src/loading-bar.js b/src/loading-bar.js new file mode 100644 index 0000000..5709e22 --- /dev/null +++ b/src/loading-bar.js @@ -0,0 +1,74 @@ +angular.module('kzo.services') + + /** + * Metric service + */ + .factory('loadingBar', ['$document', '$timeout', function ($document, $timeout) { + 'use strict'; + + var $body = $document.find('body'), + loadingBarContainer = angular.element('
'), + loadingBar = loadingBarContainer.find('div').eq(0); + + var started = false, + status = 0; + + + /** + * Inserts the loading bar element into the dom, and sets it to 1% + */ + function _start() { + started = true; + $body.append(loadingBarContainer); + loadingBar.css('width', '1%').css('opacity', 1); + } + + /** + * Set the loading bar's width to a certain percent. + * + * @param n any value between 0 and 1 + */ + function _set(n) { + if (!started) { + _start(); + } + var pct = (n * 100) + '%'; + loadingBar.css('width', pct); + status = n; + + // give the illusion that there is always progress... + $timeout(function() { + _inc(); + }, 500); + } + + /** + * Increments the loading bar by a random amount between .1% and .9% + */ + function _inc() { + if (_status() >= 1) { + return; + } + var pct = _status() + (Math.random() / 100); + _set(pct); + console.log('status is', _status()); + } + + function _status() { + return status; + } + + return { + start: _start, + set: _set, + status: _status, + + complete: function () { + $timeout(function() { + loadingBar.css('opacity', 0); + started = false; + }, 500); + } + + }; + }]); diff --git a/test/loading-bar-interceptor.coffee b/test/loading-bar-interceptor.coffee new file mode 100644 index 0000000..e373345 --- /dev/null +++ b/test/loading-bar-interceptor.coffee @@ -0,0 +1,27 @@ +describe 'loadingBarInterceptor Service', -> + + $http = $httpBackend = $document = result = null + response = {message:'OK'} + endpoint = '/service' + + beforeEach -> + module 'kzo.services' + result = null + inject (_$http_, _$httpBackend_, _$document_) -> + $http = _$http_ + $httpBackend = _$httpBackend_ + $document = _$document_ + + it 'should insert the loadingbar when a request is sent', -> + $httpBackend.expectGET(endpoint).respond response + $http.get(endpoint).then (data) -> + result = data + + + $httpBackend.flush() + expect(result.data.message).toBe 'OK' + + + + + # $httpBackend.expectGET(endpoint).respond 401, {result: 'failure'}