Repository: ajoslin/angular-promise-tracker
Branch: master
Commit: 99e0064952a9
Files: 12
Total size: 28.3 KB
Directory structure:
gitextract_3ebtioif/
├── .editorconfig
├── .gitignore
├── .travis.yml
├── Gruntfile.js
├── README.md
├── bower.json
├── package.json
├── promise-tracker-http-interceptor.js
├── promise-tracker.js
└── test/
├── karma.conf.js
└── unit/
├── interceptor.spec.js
└── provider.spec.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
================================================
FILE: .gitignore
================================================
*.swp
npm-debug.log
.DS_Store
demo
bower_components
node_modules
dist
================================================
FILE: .travis.yml
================================================
---
language: node_js
node_js:
- 0.10
env:
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- npm install -g grunt-cli bower
- npm link
- bower install
script:
- grunt
================================================
FILE: Gruntfile.js
================================================
module.exports = function (grunt) {
require('load-grunt-tasks')(grunt);
grunt.initConfig({
dist: 'dist',
pkgFile: 'bower.json',
pkg: grunt.file.readJSON('bower.json'),
watch: {
scripts: {
files: ['src/**/*.js', 'test/unit/**/*.js'],
tasks: ['karma:watch:run']
},
gruntfile: {
files: ['Gruntfile.js'],
tasks: ['jshint']
}
},
jshint: {
all: ['Gruntfile.js', 'src/**/*.js'],
options: {
eqeqeq: true,
globals: {
angular: true
}
}
},
clean: ['demo/**/*'],
karma: {
watch: {
configFile: 'test/karma.conf.js',
background: true,
},
single: {
configFile: 'test/karma.conf.js',
singleRun: true,
browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome'],
}
},
changelog: {
options: {
dest: 'CHANGELOG.md'
}
},
});
grunt.registerTask('dev', ['karma:watch', 'watch']);
grunt.registerTask('default', ['jshint', 'test']);
grunt.registerTask('test', ['karma:single']);
};
================================================
FILE: README.md
================================================
angular-promise-tracker
=======================
> **Version**: 2.0
(note to users using version 1.x: upgrading has *many* breaking changes, see [the CHANGELOG](https://github.com/ajoslin/angular-promise-tracker/tree/master/CHANGELOG.md).)
[](https://travis-ci.org/ajoslin/angular-promise-tracker)
Small, feature filled library used to easily add spinners or general promise/request tracking to your angular app.
* [Quick Start](#quick-start)
* [API Documentation](#api-documentation)
* [Changes](https://github.com/ajoslin/angular-promise-tracker/tree/master/CHANGELOG.md)
* [License](#license)
## Quick Start
The basic idea: each time we add one or more promises to an instance of a `promiseTracker`, that instance's `active()` method will return true until all added promises are resolved. A common use case is showing some sort of loading spinner while some http requests are loading.
[Play with this example on plunkr](http://plnkr.co/edit/PrO2ou9b1uANbeGoX6eB?p=preview)
```sh
$ bower install angular-promise-tracker
```
```html
<body ng-app="myApp" ng-controller="MainCtrl">
<div class="my-super-awesome-loading-box" ng-show="loadingTracker.active()">
Loading...
</div>
<button ng-click="delaySomething()">Delay Something</button>
<button ng-click="fetchSomething()">Fetch Something</button>
<script src="angular.js"></script>
<script src="promise-tracker.js"></script>
<!-- optional for $http sugar -->
<script src="promise-tracker-http-interceptor.js"></script>
</body>
```
```js
angular.module('myApp', ['ajoslin.promise-tracker'])
.controller('MainCtrl', function($scope, $http, $timeout, promiseTracker) {
//Create a new tracker
$scope.loadingTracker = promiseTracker();
//use `addPromise` to add any old promise to our tracker
$scope.delaySomething = function() {
var promise = $timeout(function() {
alert('Delayed something!');
}, 1000);
$scope.loadingTracker.addPromise(promise);
};
//use `tracker:` shortcut in $http config to link our http promise to a tracker
//This shortcut is included in promise-tracker-http-interceptor.js
$scope.fetchSomething = function(id) {
return $http.get('/something', {
tracker: $scope.loadingTracker
}).then(function(response) {
alert('Fetched something! ' + response.data);
});
};
});
```
## API Documentation
### Service `promiseTracker`
* **`tracker` promiseTracker([options])**
Creates and returns a new promiseTracker.
Options can be given as an object, with the following allowed values:
- `activationDelay` `{Number}` - Number of milliseconds that an added promise needs to be pending before this tracker is active.
* Usage example: You have some http calls that sometimes return too quickly for a loading spinner to look good. You only want to show the tracker if a promise is pending for over 500ms. You put `{activationDelay: 500}` in options.
- `minDuration` `{Number}` - Minimum number of milliseconds that a tracker will stay active.
* Usage example: You want a loading spinner to always show up for at least 750ms. You put `{minDuration: 750}` in options.
Often you want a global promiseTracker (eg to show a loading screen); one easy way is to put the tracker on your $rootScope:
```js
app.run(function($rootScope, promiseTracker) {
$rootScope.loadingTracker = promiseTracker();
});
```
### Instantiated promiseTracker
Example: `var myTracker = promiseTracker({ activationDelay: 500, minDuration: 750 });`
* **`boolean` tracker.active()**
Returns whether this tracker is currently active. That is, whether any of the promises added to/created by this tracker are still pending. Note: if the `activationDelay` has not elapsed yet, this will return false.
* **`boolean` tracker.tracking()**
Returns whether this tracker is currently tracking a request. That is, whether any of the promises added to/created by this tracker are still pending. This method has no regard for `activationDelay`.
* **`number` tracker.trackingCount()**
The count of promises currently being tracked.
* **`promise` tracker.addPromise(promise)**
Add any arbitrary promise to tracker. `tracker.active()` will be true until `promise` is resolved or rejected.
- `promise` `{object}` - Promise to add
Usage Example:
```js
var promise = $timeout(doSomethingCool, 1000);
myTracker.addPromise(promise);
console.log(myTracker.active()); // => true
//1000 milliseconds later...
console.log(myTracker.active()); // => false
```
* **`promise` tracker.createPromise()**
Creates and returns a new deferred object that is tracked by our promiseTracker.
Usage Example:
```js
var deferred = myTracker.createPromise()
console.log(myTracker.active()); // => true
deferred.resolve();
console.log(myTracker.active()); // => false
```
* **`void` tracker.cancel()**
Causes a tracker to immediately become inactive and stop tracking all current promises.
### **`$http` Sugar**
**Requires promise-tracker-http-interceptor.js**
* **Any $http call's `config` parameter can have a `tracker` field. Examples:**
```js
//Add $http promise to tracker with id 'myTracker'
$http('/banana', { tracker: myPromiseTrackerInstance })
```
```js
//Add $http promise to both 'tracker1' and 'tracker2'
$http.post('/elephant', {some: 'data'}, { tracker: [myFirstTracker, mySecondTracker] })
```
## More Examples
* Do something whenever the tracker's active state changes
```js
angular.module('app', ['ajoslin.promise-tracker'])
.factory('myTracker', function (promiseTracker) {
return promiseTracker();
})
.controller('AppCtrl', function ($rootScope, myTracker) {
$rootScope.$watch(myTracker.active, function (isActive) {
//doSomething()
});
});
```
## Development
* Install karma & grunt with `npm install -g karma grunt-cli` to build & test
* Install local dependencies with `bower install && npm install`
* Run `grunt` to lint, test, build the code, and build the docs site
* Run `grunt dev` to watch and re-test on changes
#### New Versions
## <a id="license"></a>License
> <a rel="license" href="http://creativecommons.org/publicdomain/mark/1.0/"> <img src="http://i.creativecommons.org/p/mark/1.0/80x15.png" style="border-style: none;" alt="Public Domain Mark" /> </a> <span property="dct:title">angular-promise-tracker</span> by <a href="http://ajoslin.com" rel="dct:creator"><span property="dct:title">Andy Joslin</span></a> is free of known copyright restrictions.
================================================
FILE: bower.json
================================================
{
"author": "Andy Joslin",
"name": "promise-tracker",
"description": "Easily add spinners or general request tracking to your angular app.",
"version": "2.1.0",
"homepage": "http://github.com/ajoslin/angular-promise-tracker",
"repository": {
"type": "git",
"url": "git://github.com/ajoslin/angular-promise-tracker"
},
"license": "Public Domain",
"main": "./promise-tracker.js",
"ignore": [
"CHANGELOG.md",
"Gruntfile.js",
"bower.json",
"bower_components",
"dist",
"node_modules",
"package.json",
"src",
"test"
],
"files": [
"promise-tracker.js",
"promise-tracker-http-interceptor.js"
],
"dependencies": {
"angular": ">=1.3.0"
},
"devDependencies": {
"angular-mocks": ">=1.3.0",
"angular-resource": ">=1.3.0"
}
}
================================================
FILE: package.json
================================================
{
"name": "angular-promise-tracker",
"version": "2.2.2",
"main": "promise-tracker.js",
"author": {
"name": "Andy Joslin"
},
"repository": {
"url": "git://github.com/ajoslin/angular-promise-tracker.git"
},
"homepage": "https://github.com/ajoslin/angular-promise-tracker",
"dependencies": {
"angular": ">=1.3.0"
},
"devDependencies": {
"load-grunt-tasks": "~0.2.1",
"semver": "~2.2.1",
"grunt-contrib-clean": "~0.5.0",
"grunt-contrib-jshint": "~0.7.2",
"grunt": "~0.4.2",
"grunt-contrib-watch": "~0.5.3",
"grunt-contrib-uglify": "~0.2.7",
"karma-script-launcher": "~0.1.0",
"karma-chrome-launcher": "~0.1.2",
"karma-html2js-preprocessor": "~0.1.0",
"karma-firefox-launcher": "~0.1.2",
"karma-jasmine": "~0.1.5",
"requirejs": "~2.1.9",
"karma-requirejs": "~0.2.1",
"karma-coffee-preprocessor": "~0.1.1",
"karma-phantomjs-launcher": "~0.1.1",
"karma": "~0.10.8",
"grunt-karma": "~0.6.2",
"grunt-conventional-changelog": "~1.1.0"
}
}
================================================
FILE: promise-tracker-http-interceptor.js
================================================
/*
* promise-tracker - v2.1.0 - 2014-11-15
* http://github.com/ajoslin/angular-promise-tracker
* Created by Andy Joslin; Licensed under Public Domain
*/
(function() {
angular.module('ajoslin.promise-tracker')
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push(['$q', 'promiseTracker', function($q, promiseTracker) {
return {
request: function(config) {
if (config.tracker) {
if (!angular.isArray(config.tracker)) {
config.tracker = [config.tracker];
}
config.$promiseTrackerDeferred = config.$promiseTrackerDeferred || [];
angular.forEach(config.tracker, function(tracker) {
var deferred = tracker.createPromise();
config.$promiseTrackerDeferred.push(deferred);
});
}
return $q.when(config);
},
response: function(response) {
if (response.config && response.config.$promiseTrackerDeferred) {
angular.forEach(response.config.$promiseTrackerDeferred, function(deferred) {
deferred.resolve(response);
});
}
return $q.when(response);
},
responseError: function(response) {
if (response.config && response.config.$promiseTrackerDeferred) {
angular.forEach(response.config.$promiseTrackerDeferred, function(deferred) {
deferred.reject(response);
});
}
return $q.reject(response);
}
};
}]);
}]);
}());
================================================
FILE: promise-tracker.js
================================================
angular.module('ajoslin.promise-tracker', [])
.provider('promiseTracker', function() {
var trackers = {};
this.$get = ['$q', '$timeout', function($q, $timeout) {
function cancelTimeout(promise) {
if (promise) {
$timeout.cancel(promise);
}
}
return function PromiseTracker(options) {
options = options || {};
//Array of promises being tracked
var tracked = [];
var self = {};
//Allow an optional "minimum duration" that the tracker has to stay active for.
var minDuration = options.minDuration;
//Allow a delay that will stop the tracker from activating until that time is reached
var activationDelay = options.activationDelay;
var minDurationPromise;
var activationDelayPromise;
self.active = function() {
//Even if we have a promise in our tracker, we aren't active until delay is elapsed
if (activationDelayPromise) {
return false;
}
return tracked.length > 0;
};
self.tracking = function() {
//Even if we aren't active, we could still have a promise in our tracker
return tracked.length > 0;
};
self.trackingCount = function() {
return tracked.length;
};
self.destroy = self.cancel = function() {
minDurationPromise = cancelTimeout(minDurationPromise);
activationDelayPromise = cancelTimeout(activationDelayPromise);
for (var i=tracked.length-1; i>=0; i--) {
tracked[i].resolve();
}
tracked.length = 0;
};
//Create a promise that will make our tracker active until it is resolved.
// @return deferred - our deferred object that is being tracked
self.createPromise = function() {
var deferred = $q.defer();
tracked.push(deferred);
//If the tracker was just inactive and this the first in the list of
//promises, we reset our delay and minDuration
//again.
if (tracked.length === 1) {
if (activationDelay) {
activationDelayPromise = $timeout(function() {
activationDelayPromise = cancelTimeout(activationDelayPromise);
startMinDuration();
}, activationDelay);
} else {
startMinDuration();
}
}
deferred.promise.then(onDone(false), onDone(true));
return deferred;
function startMinDuration() {
if (minDuration) {
minDurationPromise = $timeout(angular.noop, minDuration);
}
}
//Create a callback for when this promise is done. It will remove our
//tracked promise from the array if once minDuration is complete
function onDone(isError) {
return function(value) {
(minDurationPromise || $q.when()).then(function() {
var index = tracked.indexOf(deferred);
tracked.splice(index, 1);
//If this is the last promise, cleanup the timeouts
//for activationDelay
if (tracked.length === 0) {
activationDelayPromise = cancelTimeout(activationDelayPromise);
}
});
};
}
};
self.addPromise = function(promise) {
if (Array.isArray(promise)) {
return $q.all(promise.map(self.addPromise));
}
promise = promise && (promise.$promise || promise) || {};
if (!promise.then) {
throw new Error("promiseTracker#addPromise expects a promise object!");
}
var deferred = self.createPromise();
//When given promise is done, resolve our created promise
//Allow $then for angular-resource objects
promise.then(function success(value) {
deferred.resolve(value);
return value;
}, function error(value) {
deferred.reject(value);
return $q.reject(value);
});
return deferred;
};
return self;
};
}];
});
================================================
FILE: test/karma.conf.js
================================================
// Karma configuration
// Generated on Mon Dec 23 2013 08:15:21 GMT-0500 (EST)
module.exports = function(config) {
config.set({
// base path, that will be used to resolve files and exclude
basePath: '../',
// frameworks to use
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: [
'bower_components/angular/angular.js',
'bower_components/angular-mocks/angular-mocks.js',
'bower_components/angular-resource/angular-resource.js',
'promise-tracker.js',
'promise-tracker-http-interceptor.js',
'test/unit/**/*.js'
],
// list of files to exclude
exclude: [
],
// test results reporter to use
// possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
reporters: ['dots'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// Start these browsers, currently available:
// - Chrome
// - ChromeCanary
// - Firefox
// - Opera (has to be installed with `npm install karma-opera-launcher`)
// - Safari (only Mac; has to be installed with `npm install karma-safari-launcher`)
// - PhantomJS
// - IE (only Windows; has to be installed with `npm install karma-ie-launcher`)
browsers: ['Chrome'],
// If browser does not capture in given timeout [ms], kill it
captureTimeout: 60000,
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun: false
});
};
================================================
FILE: test/unit/interceptor.spec.js
================================================
describe('http interceptor', function() {
beforeEach(module('ajoslin.promise-tracker'));
var http, promiseTracker, backend, q;
beforeEach(inject(function($http, _promiseTracker_, $httpBackend, $q) {
http = $http;
promiseTracker = _promiseTracker_;
$httpBackend.whenGET('/ok').respond(200);
$httpBackend.whenGET('/error').respond(404);
backend = $httpBackend;
q = $q;
}));
function digest() {
inject(function($rootScope) { $rootScope.$digest(); });
}
it('should add a promise to tracking with http config option', function() {
var tracker = promiseTracker();
var tracker2 = promiseTracker();
spyOn(tracker, 'createPromise').andCallThrough();
spyOn(tracker2, 'createPromise').andCallThrough();
http.get('/ok', { tracker: tracker });
digest();
expect(tracker.createPromise).toHaveBeenCalled();
tracker.createPromise.reset();
http.get('/ok', { tracker: [tracker,tracker2] });
digest();
expect(tracker.createPromise).toHaveBeenCalled();
expect(tracker2.createPromise).toHaveBeenCalled();
});
it('should resolve on good response', function(){
var tracker = promiseTracker();
var deferred = q.defer();
spyOn(tracker, 'createPromise').andCallFake(function() {
return deferred;
});
spyOn(deferred, 'resolve');
http.get('/ok', { tracker: tracker });
digest();
backend.flush();
expect(deferred.resolve).toHaveBeenCalled();
expect(deferred.resolve.mostRecentCall.args[0].status).toBe(200);
});
it('should reject on error response', function(){
var tracker = promiseTracker();
var deferred = q.defer();
spyOn(tracker, 'createPromise').andCallFake(function() {
return deferred;
});
spyOn(deferred, 'reject');
http.get('/error', { tracker: tracker });
digest();
backend.flush();
expect(deferred.reject).toHaveBeenCalled();
expect(deferred.reject.mostRecentCall.args[0].status).toBe(404);
});
});
================================================
FILE: test/unit/provider.spec.js
================================================
describe('promiseTracker provider', function() {
beforeEach(module('ajoslin.promise-tracker'));
var promiseTracker, timeout, q;
beforeEach(inject(function(_promiseTracker_, $timeout, $q) {
promiseTracker = _promiseTracker_;
timeout = $timeout;
q = $q;
}));
function digest() {
inject(function($rootScope) { $rootScope.$digest(); });
}
it('should create a tracker with api', function() {
var tracker = new promiseTracker();
expect(typeof tracker.addPromise).toBe('function');
expect(typeof tracker.createPromise).toBe('function');
expect(typeof tracker.destroy).toBe('function');
expect(typeof tracker.cancel).toBe('function');
});
it('should create a tracker even if no `new`', function() {
var tracker = promiseTracker();
expect(typeof tracker.addPromise).toBe('function');
});
it('should not be active by default', function() {
expect(promiseTracker().active()).toBe(false);
});
it('should not be tracking by default', function() {
expect(promiseTracker().tracking()).toBe(false);
});
describe('addPromise', function() {
it('should error with object', function() {
expect(function() { promiseTracker().addPromise({}); }).toThrow();
});
it('should error with deferred', function() {
expect(function() { promiseTracker().addPromise(q.defer()); }).toThrow();
});
it('should not error with then, $then, $promise.then', function() {
promiseTracker().addPromise(q.defer().promise);
promiseTracker().addPromise({ $promise: q.defer().promise } );
});
it('should return promise from createPromise', function() {
var tracker = promiseTracker();
var promise = q.defer().promise;
var created = q.defer();
spyOn(tracker, 'createPromise').andCallFake(function() {
return created;
});
var ret = tracker.addPromise(promise);
expect(ret).toBe(created);
});
it('should resolve returned promise when passed in promise is resolved', function() {
var tracker = promiseTracker();
var deferred = q.defer();
var trackerPromise = tracker.addPromise(deferred.promise);
spyOn(trackerPromise, 'resolve');
deferred.resolve(1);
digest();
expect(trackerPromise.resolve).toHaveBeenCalledWith(1);
});
it('should reject returned promise when passed in promise is rejected', function() {
var tracker = promiseTracker();
var deferred = q.defer();
var trackerPromise = tracker.addPromise(deferred.promise);
spyOn(trackerPromise, 'reject');
deferred.reject(2);
digest();
expect(trackerPromise.reject).toHaveBeenCalledWith(2);
});
it('should start tracking with then, $then, $promise.then', function() {
var tracker = promiseTracker();
tracker.addPromise(q.defer().promise);
expect(tracker.tracking()).toBe(true);
tracker = promiseTracker();
tracker.addPromise(q.defer().promise);
expect(tracker.tracking()).toBe(true);
tracker = promiseTracker();
tracker.addPromise({ $promise: q.defer().promise });
expect(tracker.tracking()).toBe(true);
});
});
describe('createPromise', function() {
it('should return a deferred', function() {
expect(promiseTracker().createPromise().promise.then).toBeTruthy();
});
it('should set active to true when promise is added', function() {
var tracker = promiseTracker();
tracker.createPromise();
expect(tracker.active()).toBe(true);
});
it('should set active to true when promises are added', function() {
var tracker = promiseTracker();
tracker.createPromise();
tracker.createPromise();
expect(tracker.active()).toBe(true);
});
it('should set active to false when promises are added and resolved/rejected', function() {
var tracker = promiseTracker();
var p1 = tracker.createPromise();
var p2 = tracker.createPromise();
expect(tracker.active()).toBe(true);
p1.resolve();
digest();
expect(tracker.active()).toBe(true);
p2.reject();
digest();
expect(tracker.active()).toBe(false);
});
it('should set tracking to true when promise is added', function() {
var tracker = promiseTracker();
tracker.createPromise();
expect(tracker.tracking()).toBe(true);
});
it('should set tracking to true when promises are added', function() {
var tracker = promiseTracker();
tracker.createPromise();
tracker.createPromise();
expect(tracker.tracking()).toBe(true);
});
it('should set tracking to false when promises are added and resolved/rejected', function() {
var tracker = promiseTracker();
var p1 = tracker.createPromise();
var p2 = tracker.createPromise();
expect(tracker.tracking()).toBe(true);
p1.resolve();
digest();
expect(tracker.tracking()).toBe(true);
p2.reject();
digest();
expect(tracker.tracking()).toBe(false);
});
});
it('cancel should deactivate and resolve all promises', function() {
var tracker = promiseTracker();
var p1 = tracker.createPromise();
expect(tracker.active()).toBe(true);
spyOn(p1, 'resolve');
tracker.cancel();
expect(p1.resolve).toHaveBeenCalled();
expect(tracker.active()).toBe(false);
expect(tracker.tracking()).toBe(false);
});
it('destroy should be cancel', function() {
var tracker = promiseTracker();
expect(tracker.destroy).toBe(tracker.cancel);
});
describe('activationDelay', function() {
it('should not be active() until delay is over', function() {
var tracker = promiseTracker({ activationDelay: 1000 });
tracker.createPromise();
//Should not be active due to delay
expect(tracker.active()).toBe(false);
tracker.createPromise();
expect(tracker.active()).toBe(false);
//Flush, it should be active
timeout.flush();
expect(tracker.active()).toBe(true);
});
it('should be tracking irrespective of the activation delay', function() {
var tracker = promiseTracker({ activationDelay: 1000 });
tracker.createPromise();
//Should be tracking
expect(tracker.tracking()).toBe(true);
tracker.createPromise();
expect(tracker.tracking()).toBe(true);
//Flush, it should be tracking
timeout.flush();
expect(tracker.tracking()).toBe(true);
});
});
describe('minDuration', function() {
it('should be active() for at least minDuration', function() {
var tracker = promiseTracker({ minDuration: 1000 });
var p1 = tracker.createPromise();
expect(tracker.active()).toBe(true);
p1.resolve();
digest();
//Should still be active until minDuration timeout elapses
expect(tracker.active()).toBe(true);
timeout.flush();
expect(tracker.active()).toBe(false);
});
it('should not deactivate if there is still another promise active', function() {
var tracker = promiseTracker({ minDuration: 1000 });
var p1 = tracker.createPromise();
expect(tracker.active()).toBe(true);
p1.resolve();
digest();
//Should still be active until minDuration timeout elapses
expect(tracker.active()).toBe(true);
var p2 = tracker.createPromise();
timeout.flush();
expect(tracker.active()).toBe(true);
p2.resolve();
digest();
expect(tracker.active()).toBe(false);
});
it('should be tracking for at least minDuration', function() {
var tracker = promiseTracker({ minDuration: 1000 });
var p1 = tracker.createPromise();
expect(tracker.tracking()).toBe(true);
p1.resolve();
digest();
//Should still be tracking until minDuration timeout elapses
expect(tracker.tracking()).toBe(true);
timeout.flush();
expect(tracker.tracking()).toBe(false);
});
it('should continue tracking if there is still another promise active', function() {
var tracker = promiseTracker({ minDuration: 1000 });
var p1 = tracker.createPromise();
expect(tracker.tracking()).toBe(true);
p1.resolve();
digest();
//Should still be tracking until minDuration timeout elapses
expect(tracker.tracking()).toBe(true);
var p2 = tracker.createPromise();
timeout.flush();
expect(tracker.tracking()).toBe(true);
p2.resolve();
digest();
expect(tracker.tracking()).toBe(false);
});
});
describe('minDuration + activationDelay', function() {
it('should delay, be active, wait until duration, then be inactive', function() {
var tracker = promiseTracker({ minDuration: 500, activationDelay: 250 });
var p1 = tracker.createPromise();
expect(tracker.active()).toBe(false);
timeout.flush();
expect(tracker.active()).toBe(true);
p1.resolve();
digest();
expect(tracker.active()).toBe(true);
timeout.flush();
expect(tracker.active()).toBe(false);
});
it('should delay, be tracking, wait until duration, then be not tracking', function() {
var tracker = promiseTracker({ minDuration: 500, activationDelay: 250 });
expect(tracker.tracking()).toBe(false);
var p1 = tracker.createPromise();
expect(tracker.tracking()).toBe(true);
timeout.flush();
expect(tracker.tracking()).toBe(true);
p1.resolve();
digest();
expect(tracker.tracking()).toBe(true);
timeout.flush();
expect(tracker.tracking()).toBe(false);
});
});
});
gitextract_3ebtioif/
├── .editorconfig
├── .gitignore
├── .travis.yml
├── Gruntfile.js
├── README.md
├── bower.json
├── package.json
├── promise-tracker-http-interceptor.js
├── promise-tracker.js
└── test/
├── karma.conf.js
└── unit/
├── interceptor.spec.js
└── provider.spec.js
SYMBOL INDEX (5 symbols across 3 files)
FILE: promise-tracker.js
function cancelTimeout (line 7) | function cancelTimeout(promise) {
function startMinDuration (line 78) | function startMinDuration() {
function onDone (line 86) | function onDone(isError) {
FILE: test/unit/interceptor.spec.js
function digest (line 15) | function digest() {
FILE: test/unit/provider.spec.js
function digest (line 11) | function digest() {
Condensed preview — 12 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (31K chars).
[
{
"path": ".editorconfig",
"chars": 147,
"preview": "root = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ni"
},
{
"path": ".gitignore",
"chars": 70,
"preview": "*.swp\nnpm-debug.log\n.DS_Store\ndemo\nbower_components\nnode_modules\ndist\n"
},
{
"path": ".travis.yml",
"chars": 192,
"preview": "---\nlanguage: node_js\nnode_js:\n- 0.10\nenv:\n\nbefore_script:\n- export DISPLAY=:99.0\n- sh -e /etc/init.d/xvfb start\n- npm i"
},
{
"path": "Gruntfile.js",
"chars": 1113,
"preview": "module.exports = function (grunt) {\n\n require('load-grunt-tasks')(grunt);\n\n grunt.initConfig({\n dist: 'dist',\n p"
},
{
"path": "README.md",
"chars": 6583,
"preview": "angular-promise-tracker\n=======================\n\n> **Version**: 2.0\n\n(note to users using version 1.x: upgrading has *ma"
},
{
"path": "bower.json",
"chars": 810,
"preview": "{\n \"author\": \"Andy Joslin\",\n \"name\": \"promise-tracker\",\n \"description\": \"Easily add spinners or general request track"
},
{
"path": "package.json",
"chars": 1041,
"preview": "{\n \"name\": \"angular-promise-tracker\",\n \"version\": \"2.2.2\",\n \"main\": \"promise-tracker.js\",\n \"author\": {\n \"name\": \""
},
{
"path": "promise-tracker-http-interceptor.js",
"chars": 1505,
"preview": "/*\n * promise-tracker - v2.1.0 - 2014-11-15\n * http://github.com/ajoslin/angular-promise-tracker\n * Created by Andy Josl"
},
{
"path": "promise-tracker.js",
"chars": 4043,
"preview": "angular.module('ajoslin.promise-tracker', [])\n\n.provider('promiseTracker', function() {\n var trackers = {};\n\n this.$ge"
},
{
"path": "test/karma.conf.js",
"chars": 1841,
"preview": "// Karma configuration\n// Generated on Mon Dec 23 2013 08:15:21 GMT-0500 (EST)\n\nmodule.exports = function(config) {\n co"
},
{
"path": "test/unit/interceptor.spec.js",
"chars": 1983,
"preview": "describe('http interceptor', function() {\n\n beforeEach(module('ajoslin.promise-tracker'));\n\n var http, promiseTracker,"
},
{
"path": "test/unit/provider.spec.js",
"chars": 9619,
"preview": "describe('promiseTracker provider', function() {\n beforeEach(module('ajoslin.promise-tracker'));\n\n var promiseTracker,"
}
]
About this extraction
This page contains the full source code of the ajoslin/angular-promise-tracker GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 12 files (28.3 KB), approximately 7.1k tokens, and a symbol index with 5 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.