[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n"
  },
  {
    "path": ".gitignore",
    "content": "*.swp\nnpm-debug.log\n.DS_Store\ndemo\nbower_components\nnode_modules\ndist\n"
  },
  {
    "path": ".travis.yml",
    "content": "---\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 install -g grunt-cli bower\n- npm link\n- bower install\n\nscript:\n  - grunt\n"
  },
  {
    "path": "Gruntfile.js",
    "content": "module.exports = function (grunt) {\n\n  require('load-grunt-tasks')(grunt);\n\n  grunt.initConfig({\n    dist: 'dist',\n    pkgFile: 'bower.json',\n    pkg: grunt.file.readJSON('bower.json'),\n\n    watch: {\n      scripts: {\n        files: ['src/**/*.js', 'test/unit/**/*.js'],\n        tasks: ['karma:watch:run']\n      },\n      gruntfile: {\n        files: ['Gruntfile.js'],\n        tasks: ['jshint']\n      }\n    },\n\n    jshint: {\n      all: ['Gruntfile.js', 'src/**/*.js'],\n      options: {\n        eqeqeq: true,\n        globals: {\n          angular: true\n        }\n      }\n    },\n\n    clean: ['demo/**/*'],\n\n    karma: {\n      watch: {\n        configFile: 'test/karma.conf.js',\n        background: true,\n      },\n      single: {\n        configFile: 'test/karma.conf.js',\n        singleRun: true,\n        browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome'],\n      }\n    },\n\n    changelog: {\n      options: {\n        dest: 'CHANGELOG.md'\n      }\n    },\n  });\n\n  grunt.registerTask('dev', ['karma:watch', 'watch']);\n\n  grunt.registerTask('default', ['jshint', 'test']);\n  grunt.registerTask('test', ['karma:single']);\n};\n"
  },
  {
    "path": "README.md",
    "content": "angular-promise-tracker\n=======================\n\n> **Version**: 2.0\n\n(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).)\n\n[![Build Status](https://travis-ci.org/ajoslin/angular-promise-tracker.png)](https://travis-ci.org/ajoslin/angular-promise-tracker)\n\nSmall, feature filled library used to easily add spinners or general promise/request tracking to your angular app.\n\n* [Quick Start](#quick-start)\n* [API Documentation](#api-documentation)\n* [Changes](https://github.com/ajoslin/angular-promise-tracker/tree/master/CHANGELOG.md)\n* [License](#license)\n\n## Quick Start\n\nThe 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.\n\n[Play with this example on plunkr](http://plnkr.co/edit/PrO2ou9b1uANbeGoX6eB?p=preview)\n\n```sh\n$ bower install angular-promise-tracker\n```\n```html\n<body ng-app=\"myApp\" ng-controller=\"MainCtrl\">\n  <div class=\"my-super-awesome-loading-box\" ng-show=\"loadingTracker.active()\">\n    Loading...\n  </div>\n  <button ng-click=\"delaySomething()\">Delay Something</button>\n  <button ng-click=\"fetchSomething()\">Fetch Something</button>\n\n  <script src=\"angular.js\"></script>\n  <script src=\"promise-tracker.js\"></script>\n\n  <!-- optional for $http sugar -->\n  <script src=\"promise-tracker-http-interceptor.js\"></script>\n</body>\n```\n```js\nangular.module('myApp', ['ajoslin.promise-tracker'])\n.controller('MainCtrl', function($scope, $http, $timeout, promiseTracker) {\n  //Create a new tracker\n  $scope.loadingTracker = promiseTracker();\n\n  //use `addPromise` to add any old promise to our tracker\n  $scope.delaySomething = function() {\n    var promise = $timeout(function() {\n      alert('Delayed something!');\n    }, 1000);\n    $scope.loadingTracker.addPromise(promise);\n  };\n\n  //use `tracker:` shortcut in $http config to link our http promise to a tracker\n  //This shortcut is included in promise-tracker-http-interceptor.js\n  $scope.fetchSomething = function(id) {\n    return $http.get('/something', {\n      tracker: $scope.loadingTracker\n    }).then(function(response) {\n      alert('Fetched something! ' + response.data);\n    });\n  };\n});\n```\n\n## API Documentation\n\n### Service `promiseTracker`\n\n* **`tracker` promiseTracker([options])**\n\n  Creates and returns a new promiseTracker.\n\n  Options can be given as an object, with the following allowed values:\n\n  - `activationDelay` `{Number}` - Number of milliseconds that an added promise needs to be pending before this tracker is active.\n      * 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.\n  - `minDuration` `{Number}` - Minimum number of milliseconds that a tracker will stay active.\n      * Usage example: You want a loading spinner to always show up for at least 750ms. You put `{minDuration: 750}` in options.\n\n  Often you want a global promiseTracker (eg to show a loading screen); one easy way is to put the tracker on your $rootScope:\n\n  ```js\n  app.run(function($rootScope, promiseTracker) {\n    $rootScope.loadingTracker = promiseTracker();\n  });\n  ```\n\n### Instantiated promiseTracker\n\nExample: `var myTracker = promiseTracker({ activationDelay: 500, minDuration: 750 });`\n\n* **`boolean` tracker.active()**\n\n  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.\n\n* **`boolean` tracker.tracking()**\n\n  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`.\n\n* **`number` tracker.trackingCount()**\n\n  The count of promises currently being tracked.\n\n* **`promise` tracker.addPromise(promise)**\n\n  Add any arbitrary promise to tracker. `tracker.active()` will be true until `promise` is resolved or rejected.\n\n  - `promise` `{object}` - Promise to add\n\n  Usage Example:\n\n  ```js\n  var promise = $timeout(doSomethingCool, 1000);\n  myTracker.addPromise(promise);\n  console.log(myTracker.active()); // => true\n  //1000 milliseconds later...\n  console.log(myTracker.active()); // => false\n  ```\n\n* **`promise` tracker.createPromise()**\n\n  Creates and returns a new deferred object that is tracked by our promiseTracker.\n\n  Usage Example:\n\n  ```js\n  var deferred = myTracker.createPromise()\n  console.log(myTracker.active()); // => true\n  deferred.resolve();\n  console.log(myTracker.active()); // => false\n  ```\n\n* **`void` tracker.cancel()**\n\n  Causes a tracker to immediately become inactive and stop tracking all current promises.\n\n### **`$http` Sugar**\n\n  **Requires promise-tracker-http-interceptor.js**\n\n  * **Any $http call's `config` parameter can have a `tracker` field. Examples:**\n\n  ```js\n  //Add $http promise to tracker with id 'myTracker'\n  $http('/banana', { tracker: myPromiseTrackerInstance })\n  ```\n  ```js\n  //Add $http promise to both 'tracker1' and 'tracker2'\n  $http.post('/elephant', {some: 'data'}, { tracker: [myFirstTracker, mySecondTracker] })\n  ```\n\n## More Examples\n\n* Do something whenever the tracker's active state changes\n\n```js\nangular.module('app', ['ajoslin.promise-tracker'])\n\n.factory('myTracker', function (promiseTracker) {\n  return promiseTracker();\n})\n\n.controller('AppCtrl', function ($rootScope, myTracker) {\n  $rootScope.$watch(myTracker.active, function (isActive) {\n    //doSomething()\n  });\n});\n```\n\n## Development\n\n* Install karma & grunt with `npm install -g karma grunt-cli` to build & test\n* Install local dependencies with `bower install && npm install`\n* Run `grunt` to lint, test, build the code, and build the docs site\n* Run `grunt dev` to watch and re-test on changes\n\n#### New Versions\n\n## <a id=\"license\"></a>License\n\n> <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.\n"
  },
  {
    "path": "bower.json",
    "content": "{\n  \"author\": \"Andy Joslin\",\n  \"name\": \"promise-tracker\",\n  \"description\": \"Easily add spinners or general request tracking to your angular app.\",\n  \"version\": \"2.1.0\",\n  \"homepage\": \"http://github.com/ajoslin/angular-promise-tracker\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/ajoslin/angular-promise-tracker\"\n  },\n  \"license\": \"Public Domain\",\n  \"main\": \"./promise-tracker.js\",\n  \"ignore\": [\n    \"CHANGELOG.md\",\n    \"Gruntfile.js\",\n    \"bower.json\",\n    \"bower_components\",\n    \"dist\",\n    \"node_modules\",\n    \"package.json\",\n    \"src\",\n    \"test\"\n  ],\n  \"files\": [\n    \"promise-tracker.js\",\n    \"promise-tracker-http-interceptor.js\"\n  ],\n  \"dependencies\": {\n    \"angular\": \">=1.3.0\"\n  },\n  \"devDependencies\": {\n    \"angular-mocks\": \">=1.3.0\",\n    \"angular-resource\": \">=1.3.0\"\n  }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"angular-promise-tracker\",\n  \"version\": \"2.2.2\",\n  \"main\": \"promise-tracker.js\",\n  \"author\": {\n    \"name\": \"Andy Joslin\"\n  },\n  \"repository\": {\n    \"url\": \"git://github.com/ajoslin/angular-promise-tracker.git\"\n  },\n  \"homepage\": \"https://github.com/ajoslin/angular-promise-tracker\",\n  \"dependencies\": {\n    \"angular\": \">=1.3.0\"\n  },\n  \"devDependencies\": {\n    \"load-grunt-tasks\": \"~0.2.1\",\n    \"semver\": \"~2.2.1\",\n    \"grunt-contrib-clean\": \"~0.5.0\",\n    \"grunt-contrib-jshint\": \"~0.7.2\",\n    \"grunt\": \"~0.4.2\",\n    \"grunt-contrib-watch\": \"~0.5.3\",\n    \"grunt-contrib-uglify\": \"~0.2.7\",\n    \"karma-script-launcher\": \"~0.1.0\",\n    \"karma-chrome-launcher\": \"~0.1.2\",\n    \"karma-html2js-preprocessor\": \"~0.1.0\",\n    \"karma-firefox-launcher\": \"~0.1.2\",\n    \"karma-jasmine\": \"~0.1.5\",\n    \"requirejs\": \"~2.1.9\",\n    \"karma-requirejs\": \"~0.2.1\",\n    \"karma-coffee-preprocessor\": \"~0.1.1\",\n    \"karma-phantomjs-launcher\": \"~0.1.1\",\n    \"karma\": \"~0.10.8\",\n    \"grunt-karma\": \"~0.6.2\",\n    \"grunt-conventional-changelog\": \"~1.1.0\"\n  }\n}\n"
  },
  {
    "path": "promise-tracker-http-interceptor.js",
    "content": "/*\n * promise-tracker - v2.1.0 - 2014-11-15\n * http://github.com/ajoslin/angular-promise-tracker\n * Created by Andy Joslin; Licensed under Public Domain\n */\n\n(function() {\n\nangular.module('ajoslin.promise-tracker')\n.config(['$httpProvider', function($httpProvider) {\n  $httpProvider.interceptors.push(['$q', 'promiseTracker', function($q, promiseTracker) {\n    return {\n      request: function(config) {\n        if (config.tracker) {\n          if (!angular.isArray(config.tracker)) {\n            config.tracker = [config.tracker];\n          }\n          config.$promiseTrackerDeferred = config.$promiseTrackerDeferred || [];\n\n          angular.forEach(config.tracker, function(tracker) {\n            var deferred = tracker.createPromise();\n            config.$promiseTrackerDeferred.push(deferred);\n          });\n        }\n        return $q.when(config);\n      },\n      response: function(response) {\n        if (response.config && response.config.$promiseTrackerDeferred) {\n          angular.forEach(response.config.$promiseTrackerDeferred, function(deferred) {\n            deferred.resolve(response);\n          });\n        }\n        return $q.when(response);\n      },\n      responseError: function(response) {\n        if (response.config && response.config.$promiseTrackerDeferred) {\n          angular.forEach(response.config.$promiseTrackerDeferred, function(deferred) {\n            deferred.reject(response);\n          });\n        }\n        return $q.reject(response);\n      }\n    };\n  }]);\n}]);\n\n}());"
  },
  {
    "path": "promise-tracker.js",
    "content": "angular.module('ajoslin.promise-tracker', [])\n\n.provider('promiseTracker', function() {\n  var trackers = {};\n\n  this.$get = ['$q', '$timeout', function($q, $timeout) {\n    function cancelTimeout(promise) {\n      if (promise) {\n        $timeout.cancel(promise);\n      }\n    }\n\n    return function PromiseTracker(options) {\n      options = options || {};\n\n      //Array of promises being tracked\n      var tracked = [];\n      var self = {};\n\n      //Allow an optional \"minimum duration\" that the tracker has to stay active for.\n      var minDuration = options.minDuration;\n      //Allow a delay that will stop the tracker from activating until that time is reached\n      var activationDelay = options.activationDelay;\n\n      var minDurationPromise;\n      var activationDelayPromise;\n\n      self.active = function() {\n        //Even if we have a promise in our tracker, we aren't active until delay is elapsed\n        if (activationDelayPromise) {\n          return false;\n        }\n        return tracked.length > 0;\n      };\n\n      self.tracking = function() {\n        //Even if we aren't active, we could still have a promise in our tracker\n        return tracked.length > 0;\n      };\n\n      self.trackingCount = function() {\n        return tracked.length;\n      };\n\n      self.destroy = self.cancel = function() {\n        minDurationPromise = cancelTimeout(minDurationPromise);\n        activationDelayPromise = cancelTimeout(activationDelayPromise);\n        for (var i=tracked.length-1; i>=0; i--) {\n          tracked[i].resolve();\n        }\n        tracked.length = 0;\n      };\n\n      //Create a promise that will make our tracker active until it is resolved.\n      // @return deferred - our deferred object that is being tracked\n      self.createPromise = function() {\n        var deferred = $q.defer();\n        tracked.push(deferred);\n\n        //If the tracker was just inactive and this the first in the list of\n        //promises, we reset our delay and minDuration\n        //again.\n        if (tracked.length === 1) {\n          if (activationDelay) {\n            activationDelayPromise = $timeout(function() {\n              activationDelayPromise = cancelTimeout(activationDelayPromise);\n              startMinDuration();\n            }, activationDelay);\n          } else {\n            startMinDuration();\n          }\n        }\n\n        deferred.promise.then(onDone(false), onDone(true));\n\n        return deferred;\n\n        function startMinDuration() {\n          if (minDuration) {\n            minDurationPromise = $timeout(angular.noop, minDuration);\n          }\n        }\n\n        //Create a callback for when this promise is done. It will remove our\n        //tracked promise from the array if once minDuration is complete\n        function onDone(isError) {\n          return function(value) {\n            (minDurationPromise || $q.when()).then(function() {\n              var index = tracked.indexOf(deferred);\n              tracked.splice(index, 1);\n\n              //If this is the last promise, cleanup the timeouts\n              //for activationDelay\n              if (tracked.length === 0) {\n                activationDelayPromise = cancelTimeout(activationDelayPromise);\n              }\n            });\n          };\n        }\n      };\n\n      self.addPromise = function(promise) {\n        if (Array.isArray(promise)) {\n          return $q.all(promise.map(self.addPromise));\n        }\n\n        promise = promise && (promise.$promise || promise) || {};\n        if (!promise.then) {\n          throw new Error(\"promiseTracker#addPromise expects a promise object!\");\n        }\n\n        var deferred = self.createPromise();\n\n        //When given promise is done, resolve our created promise\n        //Allow $then for angular-resource objects\n        promise.then(function success(value) {\n          deferred.resolve(value);\n          return value;\n        }, function error(value) {\n          deferred.reject(value);\n          return $q.reject(value);\n        });\n\n        return deferred;\n      };\n\n      return self;\n    };\n  }];\n});\n"
  },
  {
    "path": "test/karma.conf.js",
    "content": "// Karma configuration\n// Generated on Mon Dec 23 2013 08:15:21 GMT-0500 (EST)\n\nmodule.exports = function(config) {\n  config.set({\n\n    // base path, that will be used to resolve files and exclude\n    basePath: '../',\n\n\n    // frameworks to use\n    frameworks: ['jasmine'],\n\n\n    // list of files / patterns to load in the browser\n    files: [\n      'bower_components/angular/angular.js',\n      'bower_components/angular-mocks/angular-mocks.js',\n      'bower_components/angular-resource/angular-resource.js',\n      'promise-tracker.js',\n      'promise-tracker-http-interceptor.js',\n      'test/unit/**/*.js'\n    ],\n\n\n    // list of files to exclude\n    exclude: [\n\n    ],\n\n\n    // test results reporter to use\n    // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'\n    reporters: ['dots'],\n\n\n    // web server port\n    port: 9876,\n\n\n    // enable / disable colors in the output (reporters and logs)\n    colors: true,\n\n\n    // level of logging\n    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG\n    logLevel: config.LOG_INFO,\n\n\n    // enable / disable watching file and executing tests whenever any file changes\n    autoWatch: true,\n\n\n    // Start these browsers, currently available:\n    // - Chrome\n    // - ChromeCanary\n    // - Firefox\n    // - Opera (has to be installed with `npm install karma-opera-launcher`)\n    // - Safari (only Mac; has to be installed with `npm install karma-safari-launcher`)\n    // - PhantomJS\n    // - IE (only Windows; has to be installed with `npm install karma-ie-launcher`)\n    browsers: ['Chrome'],\n\n\n    // If browser does not capture in given timeout [ms], kill it\n    captureTimeout: 60000,\n\n\n    // Continuous Integration mode\n    // if true, it capture browsers, run tests and exit\n    singleRun: false\n  });\n};\n"
  },
  {
    "path": "test/unit/interceptor.spec.js",
    "content": "describe('http interceptor', function() {\n\n  beforeEach(module('ajoslin.promise-tracker'));\n\n  var http, promiseTracker, backend, q;\n  beforeEach(inject(function($http, _promiseTracker_, $httpBackend, $q) {\n    http = $http;\n    promiseTracker = _promiseTracker_;\n    $httpBackend.whenGET('/ok').respond(200);\n    $httpBackend.whenGET('/error').respond(404);\n    backend = $httpBackend;\n    q = $q;\n  }));\n\n  function digest() {\n    inject(function($rootScope) { $rootScope.$digest(); });\n  }\n\n  it('should add a promise to tracking with http config option', function() {\n    var tracker = promiseTracker();\n    var tracker2 = promiseTracker();\n    spyOn(tracker, 'createPromise').andCallThrough();\n    spyOn(tracker2, 'createPromise').andCallThrough();\n\n    http.get('/ok', { tracker: tracker });\n    digest();\n    expect(tracker.createPromise).toHaveBeenCalled();\n\n    tracker.createPromise.reset();\n    http.get('/ok', { tracker: [tracker,tracker2] });\n    digest();\n    expect(tracker.createPromise).toHaveBeenCalled();\n    expect(tracker2.createPromise).toHaveBeenCalled();\n  });\n\n  it('should resolve on good response', function(){\n    var tracker = promiseTracker();\n    var deferred = q.defer();\n    spyOn(tracker, 'createPromise').andCallFake(function() {\n      return deferred;\n    });\n    spyOn(deferred, 'resolve');\n\n    http.get('/ok', { tracker: tracker });\n    digest();\n    backend.flush();\n    expect(deferred.resolve).toHaveBeenCalled();\n    expect(deferred.resolve.mostRecentCall.args[0].status).toBe(200);\n  });\n\n  it('should reject on error response', function(){\n    var tracker = promiseTracker();\n    var deferred = q.defer();\n    spyOn(tracker, 'createPromise').andCallFake(function() {\n      return deferred;\n    });\n    spyOn(deferred, 'reject');\n\n    http.get('/error', { tracker: tracker });\n    digest();\n    backend.flush();\n    expect(deferred.reject).toHaveBeenCalled();\n    expect(deferred.reject.mostRecentCall.args[0].status).toBe(404);\n  });\n});\n"
  },
  {
    "path": "test/unit/provider.spec.js",
    "content": "describe('promiseTracker provider', function() {\n  beforeEach(module('ajoslin.promise-tracker'));\n\n  var promiseTracker, timeout, q;\n  beforeEach(inject(function(_promiseTracker_, $timeout, $q) {\n    promiseTracker = _promiseTracker_;\n    timeout = $timeout;\n    q = $q;\n  }));\n\n  function digest() {\n    inject(function($rootScope) { $rootScope.$digest(); });\n  }\n\n  it('should create a tracker with api', function() {\n    var tracker = new promiseTracker();\n    expect(typeof tracker.addPromise).toBe('function');\n    expect(typeof tracker.createPromise).toBe('function');\n    expect(typeof tracker.destroy).toBe('function');\n    expect(typeof tracker.cancel).toBe('function');\n  });\n\n  it('should create a tracker even if no `new`', function() {\n    var tracker = promiseTracker();\n    expect(typeof tracker.addPromise).toBe('function');\n  });\n\n  it('should not be active by default', function() {\n    expect(promiseTracker().active()).toBe(false);\n  });\n\n  it('should not be tracking by default', function() {\n    expect(promiseTracker().tracking()).toBe(false);\n  });\n\n  describe('addPromise', function() {\n\n    it('should error with object', function() {\n      expect(function() { promiseTracker().addPromise({}); }).toThrow();\n    });\n\n    it('should error with deferred', function() {\n      expect(function() { promiseTracker().addPromise(q.defer()); }).toThrow();\n    });\n\n    it('should not error with then, $then, $promise.then', function() {\n      promiseTracker().addPromise(q.defer().promise);\n      promiseTracker().addPromise({ $promise: q.defer().promise } );\n    });\n\n    it('should return promise from createPromise', function() {\n      var tracker = promiseTracker();\n      var promise = q.defer().promise;\n      var created = q.defer();\n      spyOn(tracker, 'createPromise').andCallFake(function() {\n        return created;\n      });\n      var ret = tracker.addPromise(promise);\n      expect(ret).toBe(created);\n    });\n\n    it('should resolve returned promise when passed in promise is resolved', function() {\n      var tracker = promiseTracker();\n      var deferred = q.defer();\n      var trackerPromise = tracker.addPromise(deferred.promise);\n      spyOn(trackerPromise, 'resolve');\n      deferred.resolve(1);\n      digest();\n      expect(trackerPromise.resolve).toHaveBeenCalledWith(1);\n    });\n\n    it('should reject returned promise when passed in promise is rejected', function() {\n      var tracker = promiseTracker();\n      var deferred = q.defer();\n      var trackerPromise = tracker.addPromise(deferred.promise);\n      spyOn(trackerPromise, 'reject');\n      deferred.reject(2);\n      digest();\n      expect(trackerPromise.reject).toHaveBeenCalledWith(2);\n    });\n\n    it('should start tracking with then, $then, $promise.then', function() {\n      var tracker = promiseTracker();\n      tracker.addPromise(q.defer().promise);\n      expect(tracker.tracking()).toBe(true);\n\n      tracker = promiseTracker();\n      tracker.addPromise(q.defer().promise);\n      expect(tracker.tracking()).toBe(true);\n\n      tracker = promiseTracker();\n      tracker.addPromise({ $promise: q.defer().promise });\n      expect(tracker.tracking()).toBe(true);\n    });\n\n  });\n\n  describe('createPromise', function() {\n\n    it('should return a deferred', function() {\n      expect(promiseTracker().createPromise().promise.then).toBeTruthy();\n    });\n\n    it('should set active to true when promise is added', function() {\n      var tracker = promiseTracker();\n      tracker.createPromise();\n      expect(tracker.active()).toBe(true);\n    });\n\n    it('should set active to true when promises are added', function() {\n      var tracker = promiseTracker();\n      tracker.createPromise();\n      tracker.createPromise();\n      expect(tracker.active()).toBe(true);\n    });\n\n    it('should set active to false when promises are added and resolved/rejected', function() {\n      var tracker = promiseTracker();\n      var p1 = tracker.createPromise();\n      var p2 = tracker.createPromise();\n      expect(tracker.active()).toBe(true);\n      p1.resolve();\n      digest();\n      expect(tracker.active()).toBe(true);\n      p2.reject();\n      digest();\n      expect(tracker.active()).toBe(false);\n    });\n\n    it('should set tracking to true when promise is added', function() {\n      var tracker = promiseTracker();\n      tracker.createPromise();\n      expect(tracker.tracking()).toBe(true);\n    });\n\n    it('should set tracking to true when promises are added', function() {\n      var tracker = promiseTracker();\n      tracker.createPromise();\n      tracker.createPromise();\n      expect(tracker.tracking()).toBe(true);\n    });\n\n    it('should set tracking to false when promises are added and resolved/rejected', function() {\n      var tracker = promiseTracker();\n      var p1 = tracker.createPromise();\n      var p2 = tracker.createPromise();\n      expect(tracker.tracking()).toBe(true);\n      p1.resolve();\n      digest();\n      expect(tracker.tracking()).toBe(true);\n      p2.reject();\n      digest();\n      expect(tracker.tracking()).toBe(false);\n    });\n\n  });\n\n  it('cancel should deactivate and resolve all promises', function() {\n    var tracker = promiseTracker();\n    var p1 = tracker.createPromise();\n    expect(tracker.active()).toBe(true);\n    spyOn(p1, 'resolve');\n    tracker.cancel();\n    expect(p1.resolve).toHaveBeenCalled();\n    expect(tracker.active()).toBe(false);\n    expect(tracker.tracking()).toBe(false);\n  });\n\n  it('destroy should be cancel', function() {\n    var tracker = promiseTracker();\n    expect(tracker.destroy).toBe(tracker.cancel);\n  });\n\n  describe('activationDelay', function() {\n\n    it('should not be active() until delay is over', function() {\n      var tracker = promiseTracker({ activationDelay: 1000 });\n      tracker.createPromise();\n\n      //Should not be active due to delay\n      expect(tracker.active()).toBe(false);\n      tracker.createPromise();\n      expect(tracker.active()).toBe(false);\n\n      //Flush, it should be active\n      timeout.flush();\n      expect(tracker.active()).toBe(true);\n    });\n\n    it('should be tracking irrespective of the activation delay', function() {\n      var tracker = promiseTracker({ activationDelay: 1000 });\n      tracker.createPromise();\n\n      //Should be tracking\n      expect(tracker.tracking()).toBe(true);\n      tracker.createPromise();\n      expect(tracker.tracking()).toBe(true);\n\n      //Flush, it should be tracking\n      timeout.flush();\n      expect(tracker.tracking()).toBe(true);\n    });\n\n  });\n\n  describe('minDuration', function() {\n\n    it('should be active() for at least minDuration', function() {\n      var tracker = promiseTracker({ minDuration: 1000 });\n      var p1 = tracker.createPromise();\n      expect(tracker.active()).toBe(true);\n      p1.resolve();\n      digest();\n      //Should still be active until minDuration timeout elapses\n      expect(tracker.active()).toBe(true);\n      timeout.flush();\n      expect(tracker.active()).toBe(false);\n    });\n\n    it('should not deactivate if there is still another promise active', function() {\n      var tracker = promiseTracker({ minDuration: 1000 });\n      var p1 = tracker.createPromise();\n      expect(tracker.active()).toBe(true);\n      p1.resolve();\n      digest();\n      //Should still be active until minDuration timeout elapses\n      expect(tracker.active()).toBe(true);\n      var p2 = tracker.createPromise();\n      timeout.flush();\n      expect(tracker.active()).toBe(true);\n      p2.resolve();\n      digest();\n      expect(tracker.active()).toBe(false);\n    });\n\n    it('should be tracking for at least minDuration', function() {\n      var tracker = promiseTracker({ minDuration: 1000 });\n      var p1 = tracker.createPromise();\n      expect(tracker.tracking()).toBe(true);\n      p1.resolve();\n      digest();\n      //Should still be tracking until minDuration timeout elapses\n      expect(tracker.tracking()).toBe(true);\n      timeout.flush();\n      expect(tracker.tracking()).toBe(false);\n    });\n\n    it('should continue tracking if there is still another promise active', function() {\n      var tracker = promiseTracker({ minDuration: 1000 });\n      var p1 = tracker.createPromise();\n      expect(tracker.tracking()).toBe(true);\n      p1.resolve();\n      digest();\n      //Should still be tracking until minDuration timeout elapses\n      expect(tracker.tracking()).toBe(true);\n      var p2 = tracker.createPromise();\n      timeout.flush();\n      expect(tracker.tracking()).toBe(true);\n      p2.resolve();\n      digest();\n      expect(tracker.tracking()).toBe(false);\n    });\n\n  });\n\n  describe('minDuration + activationDelay', function() {\n\n    it('should delay, be active, wait until duration, then be inactive', function() {\n      var tracker = promiseTracker({ minDuration: 500, activationDelay: 250 });\n      var p1 = tracker.createPromise();\n      expect(tracker.active()).toBe(false);\n      timeout.flush();\n      expect(tracker.active()).toBe(true);\n      p1.resolve();\n      digest();\n      expect(tracker.active()).toBe(true);\n      timeout.flush();\n      expect(tracker.active()).toBe(false);\n    });\n\n    it('should delay, be tracking, wait until duration, then be not tracking', function() {\n      var tracker = promiseTracker({ minDuration: 500, activationDelay: 250 });\n      expect(tracker.tracking()).toBe(false);\n      var p1 = tracker.createPromise();\n      expect(tracker.tracking()).toBe(true);\n      timeout.flush();\n      expect(tracker.tracking()).toBe(true);\n      p1.resolve();\n      digest();\n      expect(tracker.tracking()).toBe(true);\n      timeout.flush();\n      expect(tracker.tracking()).toBe(false);\n    });\n\n  });\n\n});\n"
  }
]