text: 'use strict';
  
  /**
   * @ngdoc service
   * @name $cacheFactory
   * @this
   *
   * @description
   * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
   * them.
   *
   * ```js
   *
   *  var cache = $cacheFactory('cacheId');
   *  expect($cacheFactory.get('cacheId')).toBe(cache);
   *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
   *
   *  cache.put("key", "value");
   *  cache.put("another key", "another value");
   *
   *  // We've specified no options on creation
   *  expect(cache.info()).toEqual({id: 'cacheId', size: 2});
   *
   * ```
   *
   *
   * @param {string} cacheId Name or id of the newly created cache.
   * @param {object=} options Options object that specifies the cache behavior. Properties:
   *
   *   - `{number=}` `capacity` — turns the cache into LRU cache.
   *
   * @returns {object} Newly created cache object with the following set of methods:
   *
   * - `{object}` `info()` — Returns id, size, and options of cache.
   * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
   *   it.
   * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
   * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
   * - `{void}` `removeAll()` — Removes all cached values.
   * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
   *
   * @example
     <example module="cacheExampleApp" name="cache-factory">
       <file name="index.html">
         <div ng-controller="CacheController">
           <input ng-model="newCacheKey" placeholder="Key">
           <input ng-model="newCacheValue" placeholder="Value">
           <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
  
           <p ng-if="keys.length">Cached Values</p>
           <div ng-repeat="key in keys">
             <span ng-bind="key"></span>
             <span>: </span>
             <b ng-bind="cache.get(key)"></b>
           </div>
  
           <p>Cache Info</p>
           <div ng-repeat="(key, value) in cache.info()">
             <span ng-bind="key"></span>
             <span>: </span>
             <b ng-bind="value"></b>
           </div>
         </div>
       </file>
       <file name="script.js">
         angular.module('cacheExampleApp', []).
           controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
             $scope.keys = [];
             $scope.cache = $cacheFactory('cacheId');
             $scope.put = function(key, value) {
               if (angular.isUndefined($scope.cache.get(key))) {
                 $scope.keys.push(key);
               }
               $scope.cache.put(key, angular.isUndefined(value) ? null : value);
             };
           }]);
       </file>
       <file name="style.css">
         p {
           margin: 10px 0 3px;
         }
       </file>
     </example>
   */
  function $CacheFactoryProvider() {
  
    this.$get = function() {
      var caches = {};
  
      function cacheFactory(cacheId, options) {
        if (cacheId in caches) {
          throw minErr('$cacheFactory')('iid', 'CacheId \'{0}\' is already taken!', cacheId);
        }
  
        var size = 0,
            stats = extend({}, options, {id: cacheId}),
            data = createMap(),
            capacity = (options && options.capacity) || Number.MAX_VALUE,
            lruHash = createMap(),
            freshEnd = null,
            staleEnd = null;
  
        /**
         * @ngdoc type
         * @name $cacheFactory.Cache
         *
         * @description
         * A cache object used to store and retrieve data, primarily used by
         * {@link $http $http} and the {@link ng.directive:script script} directive to cache
         * templates and other data.
         *
         * ```js
         *  angular.module('superCache')
         *    .factory('superCache', ['$cacheFactory', function($cacheFactory) {
         *      return $cacheFactory('super-cache');
         *    }]);
         * ```
         *
         * Example test:
         *
         * ```js
         *  it('should behave like a cache', inject(function(superCache) {
         *    superCache.put('key', 'value');
         *    superCache.put('another key', 'another value');
         *
         *    expect(superCache.info()).toEqual({
         *      id: 'super-cache',
         *      size: 2
         *    });
         *
         *    superCache.remove('another key');
         *    expect(superCache.get('another key')).toBeUndefined();
         *
         *    superCache.removeAll();
         *    expect(superCache.info()).toEqual({
         *      id: 'super-cache',
         *      size: 0
         *    });
         *  }));
         * ```
         */
        return (caches[cacheId] = {
  
          /**
           * @ngdoc method
           * @name $cacheFactory.Cache#put
           * @kind function
           *
           * @description
           * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
           * retrieved later, and incrementing the size of the cache if the key was not already
           * present in the cache. If behaving like an LRU cache, it will also remove stale
           * entries from the set.
           *
           * It will not insert undefined values into the cache.
           *
           * @param {string} key the key under which the cached data is stored.
           * @param {*} value the value to store alongside the key. If it is undefined, the key
           *    will not be stored.
           * @returns {*} the value stored.
           */
          put: function(key, value) {
            if (isUndefined(value)) return;
            if (capacity < Number.MAX_VALUE) {
              var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
  
              refresh(lruEntry);
            }
  
            if (!(key in data)) size++;
            data[key] = value;
  
            if (size > capacity) {
              this.remove(staleEnd.key);
            }
  
            return value;
          },
  
          /**
           * @ngdoc method
           * @name $cacheFactory.Cache#get
           * @kind function
           *
           * @description
           * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
           *
           * @param {string} key the key of the data to be retrieved
           * @returns {*} the value stored.
           */
          get: function(key) {
            if (capacity < Number.MAX_VALUE) {
              var lruEntry = lruHash[key];
  
              if (!lruEntry) return;
  
              refresh(lruEntry);
            }
  
            return data[key];
          },
  
  
          /**
           * @ngdoc method
           * @name $cacheFactory.Cache#remove
           * @kind function
           *
           * @description
           * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
           *
           * @param {string} key the key of the entry to be removed
           */
          remove: function(key) {
            if (capacity < Number.MAX_VALUE) {
              var lruEntry = lruHash[key];
  
              if (!lruEntry) return;
  
              if (lruEntry === freshEnd) freshEnd = lruEntry.p;
              if (lruEntry === staleEnd) staleEnd = lruEntry.n;
              link(lruEntry.n,lruEntry.p);
  
              delete lruHash[key];
            }
  
            if (!(key in data)) return;
  
            delete data[key];
            size--;
          },
  
  
          /**
           * @ngdoc method
           * @name $cacheFactory.Cache#removeAll
           * @kind function
           *
           * @description
           * Clears the cache object of any entries.
           */
          removeAll: function() {
            data = createMap();
            size = 0;
            lruHash = createMap();
            freshEnd = staleEnd = null;
          },
  
  
          /**
           * @ngdoc method
           * @name $cacheFactory.Cache#destroy
           * @kind function
           *
           * @description
           * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
           * removing it from the {@link $cacheFactory $cacheFactory} set.
           */
          destroy: function() {
            data = null;
            stats = null;
            lruHash = null;
            delete caches[cacheId];
          },
  
  
          /**
           * @ngdoc method
           * @name $cacheFactory.Cache#info
           * @kind function
           *
           * @description
           * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
           *
           * @returns {object} an object with the following properties:
           *   <ul>
           *     <li>**id**: the id of the cache instance</li>
           *     <li>**size**: the number of entries kept in the cache instance</li>
           *     <li>**...**: any additional properties from the options object when creating the
           *       cache.</li>
           *   </ul>
           */
          info: function() {
            return extend({}, stats, {size: size});
          }
        });
  
  
        /**
         * makes the `entry` the freshEnd of the LRU linked list
         */
        function refresh(entry) {
          if (entry !== freshEnd) {
            if (!staleEnd) {
              staleEnd = entry;
            } else if (staleEnd === entry) {
              staleEnd = entry.n;
            }
  
            link(entry.n, entry.p);
            link(entry, freshEnd);
            freshEnd = entry;
            freshEnd.n = null;
          }
        }
  
  
        /**
         * bidirectionally links two entries of the LRU linked list
         */
        function link(nextEntry, prevEntry) {
          if (nextEntry !== prevEntry) {
            if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
            if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
          }
        }
      }
  
  
    /**
     * @ngdoc method
     * @name $cacheFactory#info
     *
     * @description
     * Get information about all the caches that have been created
     *
     * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
     */
      cacheFactory.info = function() {
        var info = {};
        forEach(caches, function(cache, cacheId) {
          info[cacheId] = cache.info();
        });
        return info;
      };
  
  
    /**
     * @ngdoc method
     * @name $cacheFactory#get
     *
     * @description
     * Get access to a cache object by the `cacheId` used when it was created.
     *
     * @param {string} cacheId Name or id of a cache to access.
     * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
     */
      cacheFactory.get = function(cacheId) {
        return caches[cacheId];
      };
  
  
      return cacheFactory;
    };
  }
  
  /**
   * @ngdoc service
   * @name $templateCache
   * @this
   *
   * @description
   * The first time a template is used, it is loaded in the template cache for quick retrieval. You
   * can load templates directly into the cache in a `script` tag, or by consuming the
   * `$templateCache` service directly.
   *
   * Adding via the `script` tag:
   *
   * ```html
   *   <script type="text/ng-template" id="templateId.html">
   *     <p>This is the content of the template</p>
   *   </script>
   * ```
   *
   * **Note:** the `script` tag containing the template does not need to be included in the `head` of
   * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
   * element with ng-app attribute), otherwise the template will be ignored.
   *
   * Adding via the `$templateCache` service:
   *
   * ```js
   * var myApp = angular.module('myApp', []);
   * myApp.run(function($templateCache) {
   *   $templateCache.put('templateId.html', 'This is the content of the template');
   * });
   * ```
   *
   * To retrieve the template later, simply use it in your component:
   * ```js
   * myApp.component('myComponent', {
   *    templateUrl: 'templateId.html'
   * });
   * ```
   *
   * or get it via the `$templateCache` service:
   * ```js
   * $templateCache.get('templateId.html')
   * ```
   *
   * See {@link ng.$cacheFactory $cacheFactory}.
   *
   */
  function $TemplateCacheProvider() {
    this.$get = ['$cacheFactory', function($cacheFactory) {
      return $cacheFactory('templates');
    }];
  }
  