diff --git a/.travis.yml b/.travis.yml index 8b443b9..496d4f0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: node_js node_js: - - "0.8" - "0.10" + - "0.11" before_install: - npm install -g bower - bower install @@ -11,3 +11,4 @@ before_script: - sh -e /etc/init.d/xvfb start script: - grunt cli + - grunt coveralls || true diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c0a462..219e300 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,42 @@ +##### 2.4.1 - 18 October 2014 + +###### Backwards compatible bug fixes +- #133 - Fix ios privacy mode + +##### 2.4.0 - 14 October 2014 + +###### Backwards compatible API changes +- #131 - Added a flag to allow storing promises + +###### Backwards compatible bug fixes +- #128 - Fix for exception thrown in iOS privacy mode +- #130 - (Docs) Wrong value for cache flush interval +- #132 - stringifyNumber(0) fails to stringify + +##### 2.3.7 - 16 June 2014 + +###### Backwards compatible bug fixes +- Angular 1.2.18 with $http/localStorage #116 + +##### 2.3.6 - 15 June 2014 + +###### Backwards compatible bug fixes +- $http w/ cache is trying to store a promise, which dies on JSON.stringify #115 + +##### 2.3.5 - 15 June 2014 + +###### Backwards compatible bug fixes +- Page refresh retouches cache expiry times with defaults #114 + +##### 2.3.4 - 01 May 2014 + +###### Backwards compatible bug fixes +- Fix module definition and load sequencing #111 + ##### 2.3.3 - 24 February 2014 ###### Backwards compatible bug fixes -- *sigh Fixed #102 (regression from #100) +- *sigh* Fixed #102 (regression from #100) ##### 2.3.2 - 23 February 2014 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bbc8fb0..47bae77 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -68,7 +68,7 @@ dist/angular-cache-x.x.x.min.js ## Contributing/Submitting changes -- Checkout a new branch based on `develop` and name it to what you intend to do: +- Checkout a new branch based on `master` and name it to what you intend to do: - Example: ```` $ git checkout -b BRANCH_NAME @@ -82,5 +82,4 @@ dist/angular-cache-x.x.x.min.js - Please provide a git message which explains what you've done - Commit to the forked repository - Make a pull request - - Make sure you send the PR to the `develop` branch - - Travis CI is watching you! \ No newline at end of file + - Travis CI is watching you! diff --git a/Gruntfile.js b/Gruntfile.js index 5756d56..c508c40 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -6,242 +6,253 @@ * Licensed under the MIT license. */ module.exports = function (grunt) { - 'use strict'; + 'use strict'; - require('load-grunt-tasks')(grunt); - require('time-grunt')(grunt); + require('load-grunt-tasks')(grunt); + require('time-grunt')(grunt); - // Project configuration. - grunt.initConfig({ - pkg: grunt.file.readJSON('package.json'), - // these folders will no longer be checked into development branches - clean: { - pre: ['dist/', 'coverage/'], - post: ['coverage/'] - }, - jshint: { - all: ['Gruntfile.js', 'src/**/*.js', 'test/*.js'], - jshintrc: '.jshintrc' - }, - copy: { - options: { - processContent: function (contents) { - contents = contents.replace(/<%= pkg.version %>/g, grunt.file.readJSON('package.json').version); - return contents; - } - }, - dist: { - src: ['src/angular-cache.js'], - dest: 'dist/angular-cache.js' - } - }, - uglify: { - main: { - options: { - banner: '/**\n' + - '* @author Jason Dobry \n' + - '* @file angular-cache.min.js\n' + - '* @version <%= pkg.version %> - Homepage \n' + - '* @copyright (c) 2013 Jason Dobry \n' + - '* @license MIT \n' + - '*\n' + - '* @overview angular-cache is a very useful replacement for Angular\'s $cacheFactory.\n' + - '*/\n' - }, - files: { - 'dist/angular-cache.min.js': ['dist/angular-cache.js'] - } - } - }, - karma: { - options: { - configFile: './karma.conf.js' - }, - dev: { - browsers: ['Chrome'], - autoWatch: true, - singleRun: false - }, - '1.0.4': { - options: { - files: [ - 'bower_components/angular-1.0.4/angular.js', - 'bower_components/angular-mocks-1.0.4/angular-mocks.js', - 'src/angular-cache.js', - 'test/karma.start.js', - 'test/*.js' - ] - } - }, - '1.0.5': { - options: { - files: [ - 'bower_components/angular-1.0.5/angular.js', - 'bower_components/angular-mocks-1.0.5/angular-mocks.js', - 'src/angular-cache.js', - 'test/karma.start.js', - 'test/*.js' - ] - } - }, - '1.0.6': { - options: { - files: [ - 'bower_components/angular-1.0.6/angular.js', - 'bower_components/angular-mocks-1.0.6/angular-mocks.js', - 'src/angular-cache.js', - 'test/karma.start.js', - 'test/*.js' - ] - } - }, - '1.0.7': { - options: { - files: [ - 'bower_components/angular-1.0.7/angular.js', - 'bower_components/angular-mocks-1.0.7/angular-mocks.js', - 'src/angular-cache.js', - 'test/karma.start.js', - 'test/*.js' - ] - } - }, - '1.0.8': { - options: { - files: [ - 'bower_components/angular-1.0.8/angular.js', - 'bower_components/angular-mocks-1.0.8/angular-mocks.js', - 'src/angular-cache.js', - 'test/karma.start.js', - 'test/*.js' - ] - } - }, - '1.1.4': { - options: { - files: [ - 'bower_components/angular-1.1.4/angular.js', - // hopefully this works. 1.1.4 isn't available on bower - 'bower_components/angular-mocks-1.1.5/angular-mocks.js', - 'src/angular-cache.js', - 'test/karma.start.js', - 'test/*.js' - ] - } - }, - '1.1.5': { - options: { - files: [ - 'bower_components/angular-1.1.5/angular.js', - 'bower_components/angular-mocks-1.1.5/angular-mocks.js', - 'src/angular-cache.js', - 'test/karma.start.js', - 'test/*.js' - ] - } - }, - '1.2.0': { - options: { - files: [ - 'bower_components/angular-1.2.0/angular.js', - 'bower_components/angular-mocks-1.2.0/angular-mocks.js', - 'src/angular-cache.js', - 'test/karma.start.js', - 'test/*.js' - ] - } - }, - '1.2.1': { - options: { - files: [ - 'bower_components/angular-1.2.1/angular.js', - 'bower_components/angular-mocks-1.2.1/angular-mocks.js', - 'src/angular-cache.js', - 'test/karma.start.js', - 'test/*.js' - ] - } - }, - '1.2.2': { - options: { - files: [ - 'bower_components/angular-1.2.2/angular.js', - 'bower_components/angular-mocks-1.2.2/angular-mocks.js', - 'src/angular-cache.js', - 'test/karma.start.js', - 'test/*.js' - ] - } - }, - '1.2.3': { - options: { - files: [ - 'bower_components/angular-1.2.3/angular.js', - 'bower_components/angular-mocks-1.2.3/angular-mocks.js', - 'src/angular-cache.js', - 'test/karma.start.js', - 'test/*.js' - ] - } - }, - '1.2.4': { - options: { - files: [ - 'bower_components/angular-1.2.4/angular.js', - 'bower_components/angular-mocks-1.2.4/angular-mocks.js', - 'src/angular-cache.js', - 'test/karma.start.js', - 'test/*.js' - ] - } - }, - '1.2.5': { - options: { - files: [ - 'bower_components/angular-1.2.5/angular.js', - 'bower_components/angular-mocks-1.2.5/angular-mocks.js', - 'src/angular-cache.js', - 'test/karma.start.js', - 'test/*.js' - ] - } - }, - '1.2.6': { - options: { - files: [ - 'bower_components/angular-1.2.6/angular.js', - 'bower_components/angular-mocks-1.2.6/angular-mocks.js', - 'src/angular-cache.js', - 'test/karma.start.js', - 'test/*.js' - ] - } - }, - '1.2.7': { - options: { - files: [ - 'bower_components/angular-1.2.7/angular.js', - 'bower_components/angular-mocks-1.2.7/angular-mocks.js', - 'src/angular-cache.js', - 'test/karma.start.js', - 'test/*.js' - ] - } - } + // Project configuration. + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + // these folders will no longer be checked into development branches + clean: { + pre: ['dist/', 'coverage/'], + post: ['coverage/'] + }, + jshint: { + all: ['Gruntfile.js', 'src/**/*.js', 'test/*.js'], + jshintrc: '.jshintrc' + }, + copy: { + options: { + processContent: function (contents) { + contents = contents.replace(/<%= pkg.version %>/g, grunt.file.readJSON('package.json').version); + return contents; + } + }, + dist: { + src: ['src/angular-cache.js'], + dest: 'dist/angular-cache.js' + } + }, + uglify: { + main: { + options: { + banner: '/**\n' + + '* @author Jason Dobry \n' + + '* @file angular-cache.min.js\n' + + '* @version <%= pkg.version %> - Homepage \n' + + '* @copyright (c) 2013-2014 Jason Dobry \n' + + '* @license MIT \n' + + '*\n' + + '* @overview angular-cache is a very useful replacement for Angular\'s $cacheFactory.\n' + + '*/\n' }, - coveralls: { - options: { - coverage_dir: 'coverage' - } + files: { + 'dist/angular-cache.min.js': ['dist/angular-cache.js'] + } + } + }, + karma: { + options: { + configFile: './karma.conf.js' + }, + dev: { + browsers: ['Chrome'], + autoWatch: true, + singleRun: false + }, + '1.0.4': { + options: { + files: [ + 'bower_components/angular-1.0.4/angular.js', + 'bower_components/angular-mocks-1.0.4/angular-mocks.js', + 'src/angular-cache.js', + 'test/karma.start.js', + 'test/*.js' + ] + } + }, + '1.0.5': { + options: { + files: [ + 'bower_components/angular-1.0.5/angular.js', + 'bower_components/angular-mocks-1.0.5/angular-mocks.js', + 'src/angular-cache.js', + 'test/karma.start.js', + 'test/*.js' + ] + } + }, + '1.0.6': { + options: { + files: [ + 'bower_components/angular-1.0.6/angular.js', + 'bower_components/angular-mocks-1.0.6/angular-mocks.js', + 'src/angular-cache.js', + 'test/karma.start.js', + 'test/*.js' + ] + } + }, + '1.0.7': { + options: { + files: [ + 'bower_components/angular-1.0.7/angular.js', + 'bower_components/angular-mocks-1.0.7/angular-mocks.js', + 'src/angular-cache.js', + 'test/karma.start.js', + 'test/*.js' + ] + } + }, + '1.0.8': { + options: { + files: [ + 'bower_components/angular-1.0.8/angular.js', + 'bower_components/angular-mocks-1.0.8/angular-mocks.js', + 'src/angular-cache.js', + 'test/karma.start.js', + 'test/*.js' + ] + } + }, + '1.1.4': { + options: { + files: [ + 'bower_components/angular-1.1.4/angular.js', + // hopefully this works. 1.1.4 isn't available on bower + 'bower_components/angular-mocks-1.1.5/angular-mocks.js', + 'src/angular-cache.js', + 'test/karma.start.js', + 'test/*.js' + ] + } + }, + '1.1.5': { + options: { + files: [ + 'bower_components/angular-1.1.5/angular.js', + 'bower_components/angular-mocks-1.1.5/angular-mocks.js', + 'src/angular-cache.js', + 'test/karma.start.js', + 'test/*.js' + ] + } + }, + '1.2.0': { + options: { + files: [ + 'bower_components/angular-1.2.0/angular.js', + 'bower_components/angular-mocks-1.2.0/angular-mocks.js', + 'src/angular-cache.js', + 'test/karma.start.js', + 'test/*.js' + ] + } + }, + '1.2.1': { + options: { + files: [ + 'bower_components/angular-1.2.1/angular.js', + 'bower_components/angular-mocks-1.2.1/angular-mocks.js', + 'src/angular-cache.js', + 'test/karma.start.js', + 'test/*.js' + ] + } + }, + '1.2.2': { + options: { + files: [ + 'bower_components/angular-1.2.2/angular.js', + 'bower_components/angular-mocks-1.2.2/angular-mocks.js', + 'src/angular-cache.js', + 'test/karma.start.js', + 'test/*.js' + ] + } + }, + '1.2.3': { + options: { + files: [ + 'bower_components/angular-1.2.3/angular.js', + 'bower_components/angular-mocks-1.2.3/angular-mocks.js', + 'src/angular-cache.js', + 'test/karma.start.js', + 'test/*.js' + ] + } + }, + '1.2.4': { + options: { + files: [ + 'bower_components/angular-1.2.4/angular.js', + 'bower_components/angular-mocks-1.2.4/angular-mocks.js', + 'src/angular-cache.js', + 'test/karma.start.js', + 'test/*.js' + ] + } + }, + '1.2.5': { + options: { + files: [ + 'bower_components/angular-1.2.5/angular.js', + 'bower_components/angular-mocks-1.2.5/angular-mocks.js', + 'src/angular-cache.js', + 'test/karma.start.js', + 'test/*.js' + ] + } + }, + '1.2.6': { + options: { + files: [ + 'bower_components/angular-1.2.6/angular.js', + 'bower_components/angular-mocks-1.2.6/angular-mocks.js', + 'src/angular-cache.js', + 'test/karma.start.js', + 'test/*.js' + ] + } + }, + '1.2.7': { + options: { + files: [ + 'bower_components/angular-1.2.7/angular.js', + 'bower_components/angular-mocks-1.2.7/angular-mocks.js', + 'src/angular-cache.js', + 'test/karma.start.js', + 'test/*.js' + ] + } + }, + '1.2.16': { + options: { + files: [ + 'bower_components/angular-1.2.16/angular.js', + 'bower_components/angular-mocks-1.2.16/angular-mocks.js', + 'src/angular-cache.js', + 'test/karma.start.js', + 'test/*.js' + ] } - }); + } + }, + coveralls: { + options: { + coverage_dir: 'coverage' + } + } + }); - grunt.registerTask('test', ['karma:1.1.5']); - grunt.registerTask('release', ['clean:pre', 'jshint', 'copy', 'uglify', 'test', 'clean:post']); - grunt.registerTask('build', ['release', 'clean:pre']); - grunt.registerTask('default', ['build']); + grunt.registerTask('test', ['karma:1.2.16']); + grunt.registerTask('release', ['clean:pre', 'jshint', 'copy', 'uglify', 'test', 'clean:post']); + grunt.registerTask('build', ['release', 'clean:pre']); + grunt.registerTask('default', ['build']); - // Used by the CLI build servers - grunt.registerTask('test-cli', ['karma:1.0.4', 'karma:1.0.5', 'karma:1.0.6', 'karma:1.0.7', 'karma:1.0.8', 'karma:1.1.4', 'karma:1.1.5', 'karma:1.2.1', 'karma:1.2.2', 'karma:1.2.3', 'karma:1.2.4', 'karma:1.2.5', 'karma:1.2.6', 'karma:1.2.7']); - grunt.registerTask('cli', ['clean', 'jshint', 'copy', 'uglify', 'test-cli', 'coveralls']); + // Used by the CLI build servers + grunt.registerTask('test-cli', ['karma:1.0.4', 'karma:1.0.5', 'karma:1.0.6', 'karma:1.0.7', 'karma:1.0.8', 'karma:1.1.4', 'karma:1.1.5', 'karma:1.2.1', 'karma:1.2.2', 'karma:1.2.3', 'karma:1.2.4', 'karma:1.2.5', 'karma:1.2.6', 'karma:1.2.7', 'karma:1.2.16']); + grunt.registerTask('cli', ['clean', 'jshint', 'copy', 'uglify', 'test-cli']); }; diff --git a/README.md b/README.md index 993cb46..b364706 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ## angular-cache __A very useful replacement for Angular's $cacheFactory.__ -__Version:__ 2.3.1 +__Version:__ 2.4.1 All [Documentation](http://jmdobry.github.io/angular-cache) can be found at [jmdobry.github.io/angular-cache](http://jmdobry.github.io/angular-cache). diff --git a/bower.json b/bower.json index cfadea9..c756787 100644 --- a/bower.json +++ b/bower.json @@ -2,11 +2,11 @@ "author": "Jason Dobry", "name": "angular-cache", "description": "angular-cache is a very useful replacement for Angular's $cacheFactory.", - "version": "2.3.3", + "version": "2.4.1", "homepage": "http://jmdobry.github.io/angular-cache/", "repository": { "type": "git", - "url": "git://github.com/jmdobry/angular-cache.git" + "url": "https://github.com/jmdobry/angular-cache.git" }, "main": "./dist/angular-cache.min.js", "ignore": [ @@ -44,6 +44,7 @@ "angular-1.2.5": "angular#1.2.5", "angular-1.2.6": "angular#1.2.6", "angular-1.2.7": "angular#1.2.7", + "angular-1.2.16": "angular#1.2.16", "angular-mocks-1.0.4": "angular-mocks#1.0.4", "angular-mocks-1.0.5": "angular-mocks#1.0.5", "angular-mocks-1.0.6": "angular-mocks#1.0.6", @@ -57,7 +58,8 @@ "angular-mocks-1.2.4": "angular-mocks#1.2.4", "angular-mocks-1.2.5": "angular-mocks#1.2.5", "angular-mocks-1.2.6": "angular-mocks#1.2.6", - "angular-mocks-1.2.7": "angular-mocks#1.2.7" + "angular-mocks-1.2.7": "angular-mocks#1.2.7", + "angular-mocks-1.2.16": "angular-mocks#1.2.16" }, "resolutions": { "angular": "1.0.8" diff --git a/dist/angular-cache.js b/dist/angular-cache.js index 27b3937..6aaaed4 100644 --- a/dist/angular-cache.js +++ b/dist/angular-cache.js @@ -1,1194 +1,1267 @@ /** * @author Jason Dobry - * @file angular-cache-2.3.2.js - * @version 2.3.2 - Homepage - * @copyright (c) 2013 Jason Dobry + * @file angular-cache.js + * @version 2.4.1 - Homepage + * @copyright (c) 2013-2014 Jason Dobry * @license MIT * * @overview angular-cache is a very useful replacement for Angular's $cacheFactory. */ (function (window, angular, undefined) { - 'use strict'; - - angular.module('jmdobry.binary-heap', []); - - /** - * @desc Provider for the BinaryHeap. - */ - function BinaryHeapProvider() { - this.$get = function () { - /** - * @method bubbleUp - * @param {array} heap The heap. - * @param {function} weightFunc The weight function. - * @param {number} n The index of the element to bubble up. - */ - function bubbleUp(heap, weightFunc, n) { - var element = heap[n], - weight = weightFunc(element); - // When at 0, an element can not go up any further. - while (n > 0) { - // Compute the parent element's index, and fetch it. - var parentN = Math.floor((n + 1) / 2) - 1, - parent = heap[parentN]; - // If the parent has a lesser weight, things are in order and we - // are done. - if (weight >= weightFunc(parent)) { - break; - } else { - heap[parentN] = element; - heap[n] = parent; - n = parentN; - } - } - } - - /** - * @method bubbleDown - * @param {array} heap The heap. - * @param {function} weightFunc The weight function. - * @param {number} n The index of the element to sink down. - */ - function bubbleDown(heap, weightFunc, n) { - var length = heap.length, - node = heap[n], - nodeWeight = weightFunc(node); - - while (true) { - var child2N = (n + 1) * 2, - child1N = child2N - 1; - var swap = null; - if (child1N < length) { - var child1 = heap[child1N], - child1Weight = weightFunc(child1); - // If the score is less than our node's, we need to swap. - if (child1Weight < nodeWeight) { - swap = child1N; - } - } - // Do the same checks for the other child. - if (child2N < length) { - var child2 = heap[child2N], - child2Weight = weightFunc(child2); - if (child2Weight < (swap === null ? nodeWeight : weightFunc(heap[child1N]))) { - swap = child2N; - } - } - - if (swap === null) { - break; - } else { - heap[n] = heap[swap]; - heap[swap] = node; - n = swap; - } - } - } - - /** - * @class BinaryHeap - * @desc BinaryHeap implementation of a priority queue. - * @param {function} weightFunc Function that returns the value that should be used for node value comparison. - * @example - * angular.module('app').controller(function (BinaryHeap) { + 'use strict'; + + /** + * @desc Provider for the BinaryHeap. + */ + function BinaryHeapProvider() { + this.$get = function () { + /** + * @method bubbleUp + * @param {array} heap The heap. + * @param {function} weightFunc The weight function. + * @param {number} n The index of the element to bubble up. + */ + function bubbleUp(heap, weightFunc, n) { + var element = heap[n], + weight = weightFunc(element); + // When at 0, an element can not go up any further. + while (n > 0) { + // Compute the parent element's index, and fetch it. + var parentN = Math.floor((n + 1) / 2) - 1, + parent = heap[parentN]; + // If the parent has a lesser weight, things are in order and we + // are done. + if (weight >= weightFunc(parent)) { + break; + } else { + heap[parentN] = element; + heap[n] = parent; + n = parentN; + } + } + } + + /** + * @method bubbleDown + * @param {array} heap The heap. + * @param {function} weightFunc The weight function. + * @param {number} n The index of the element to sink down. + */ + function bubbleDown(heap, weightFunc, n) { + var length = heap.length, + node = heap[n], + nodeWeight = weightFunc(node); + + while (true) { + var child2N = (n + 1) * 2, + child1N = child2N - 1; + var swap = null; + if (child1N < length) { + var child1 = heap[child1N], + child1Weight = weightFunc(child1); + // If the score is less than our node's, we need to swap. + if (child1Weight < nodeWeight) { + swap = child1N; + } + } + // Do the same checks for the other child. + if (child2N < length) { + var child2 = heap[child2N], + child2Weight = weightFunc(child2); + if (child2Weight < (swap === null ? nodeWeight : weightFunc(heap[child1N]))) { + swap = child2N; + } + } + + if (swap === null) { + break; + } else { + heap[n] = heap[swap]; + heap[swap] = node; + n = swap; + } + } + } + + /** + * @class BinaryHeap + * @desc BinaryHeap implementation of a priority queue. + * @param {function} weightFunc Function that returns the value that should be used for node value comparison. + * @example + * angular.module('app').controller(function (BinaryHeap) { * var bHeap = new BinaryHeap(function (x) { * return x.value; * }); * ); */ - function BinaryHeap(weightFunc) { - if (weightFunc && !angular.isFunction(weightFunc)) { - throw new Error('BinaryHeap(weightFunc): weightFunc: must be a function!'); - } - weightFunc = weightFunc || function (x) { - return x; - }; - this.weightFunc = weightFunc; - this.heap = []; - } - - /** - * @method BinaryHeap.push - * @desc Push an element into the binary heap. - * @param {*} node The element to push into the binary heap. - */ - BinaryHeap.prototype.push = function (node) { - this.heap.push(node); - bubbleUp(this.heap, this.weightFunc, this.heap.length - 1); - }; - - /** - * @method BinaryHeap.peek - * @desc Return, but do not remove, the minimum element in the binary heap. - * @returns {*} - */ - BinaryHeap.prototype.peek = function () { - return this.heap[0]; - }; - - /** - * @method BinaryHeap.pop - * @desc Remove and return the minimum element in the binary heap. - * @returns {*} - */ - BinaryHeap.prototype.pop = function () { - var front = this.heap[0], - end = this.heap.pop(); - if (this.heap.length > 0) { - this.heap[0] = end; - bubbleDown(this.heap, this.weightFunc, 0); - } - return front; - }; - - /** - * @method BinaryHeap.remove - * @desc Remove the first node in the priority queue that satisfies angular.equals comparison with - * the given node. - * @param {*} node The node to remove. - * @returns {*} The removed node. - */ - BinaryHeap.prototype.remove = function (node) { - var length = this.heap.length; - for (var i = 0; i < length; i++) { - if (angular.equals(this.heap[i], node)) { - var removed = this.heap[i], - end = this.heap.pop(); - if (i !== length - 1) { - this.heap[i] = end; - bubbleUp(this.heap, this.weightFunc, i); - bubbleDown(this.heap, this.weightFunc, i); - } - return removed; - } - } - return null; - }; - - /** - * @method BinaryHeap.removeAll - * @desc Remove all nodes from this BinaryHeap. - */ - BinaryHeap.prototype.removeAll = function () { - this.heap = []; - }; - - /** - * @method BinaryHeap.size - * @desc Return the size of the priority queue. - * @returns {number} The size of the priority queue. - */ - BinaryHeap.prototype.size = function () { - return this.heap.length; - }; - - return BinaryHeap; - }; - } - - angular.module('jmdobry.binary-heap').provider('BinaryHeap', BinaryHeapProvider); - - /** - * @desc Provides an $AngularCacheFactoryProvider, which gives you the ability to use an - * $angularCacheFactory. The $angularCacheFactory produces AngularCache objects, which - * the same abilities as the cache objects that come with Angular, except with some added - * functionality. - */ - angular.module('jmdobry.angular-cache', ['ng', 'jmdobry.binary-heap']); - - /** - * @class $AngularCacheFactoryProvider - * @desc Provider for the $angularCacheFactory. - * @see {@link http://docs.angularjs.org/api/ng.$cacheFactory|ng.$cacheFactory} - */ - function $AngularCacheFactoryProvider() { - - var cacheDefaults, - DEFAULTS = function () { - return { - capacity: Number.MAX_VALUE, - maxAge: null, - deleteOnExpire: 'none', - onExpire: null, - cacheFlushInterval: null, - recycleFreq: 1000, - storageMode: 'none', - storageImpl: null, - verifyIntegrity: true, - disabled: false - }; - }; - - /** - * @method _validateNumberOption - * @desc Validates the given number option. - * @param {number} option The number option to check. - * @param {function} cb Callback function - */ - function _validateNumberOption(option, cb) { - if (!angular.isNumber(option)) { - cb('must be a number!'); - } else if (option < 0) { - cb('must be greater than zero!'); - } else { - cb(null); - } - } - - /** - * @method $AngularCacheFactoryProvider.setCacheDefaults - * @desc Set the default configuration for all caches created by $angularCacheFactory. - * @param {object} options - */ - this.setCacheDefaults = function (options) { - var errStr = '$angularCacheFactoryProvider.setCacheDefaults(options): '; - options = options || {}; - - if (!angular.isObject(options)) { - throw new Error(errStr + 'options: must be an object!'); - } - - if ('disabled' in options) { - options.disabled = options.disabled === true; - } - - if ('capacity' in options) { - _validateNumberOption(options.capacity, function (err) { - if (err) { - throw new Error(errStr + 'capacity: ' + err); - } - }); - } - - if ('deleteOnExpire' in options) { - if (!angular.isString(options.deleteOnExpire)) { - throw new Error(errStr + 'deleteOnExpire: must be a string!'); - } else if (options.deleteOnExpire !== 'none' && options.deleteOnExpire !== 'passive' && options.deleteOnExpire !== 'aggressive') { - throw new Error(errStr + 'deleteOnExpire: accepted values are "none", "passive" or "aggressive"!'); - } - } - - if ('maxAge' in options) { - _validateNumberOption(options.maxAge, function (err) { - if (err) { - throw new Error(errStr + 'maxAge: ' + err); - } - }); - } - - if ('recycleFreq' in options) { - _validateNumberOption(options.recycleFreq, function (err) { - if (err) { - throw new Error(errStr + 'recycleFreq: ' + err); - } - }); - } - - if ('cacheFlushInterval' in options) { - _validateNumberOption(options.cacheFlushInterval, function (err) { - if (err) { - throw new Error(errStr + 'cacheFlushInterval: ' + err); - } - }); - } - - if ('storageMode' in options) { - if (!angular.isString(options.storageMode)) { - throw new Error(errStr + 'storageMode: must be a string!'); - } else if (options.storageMode !== 'none' && options.storageMode !== 'localStorage' && options.storageMode !== 'sessionStorage') { - throw new Error(errStr + 'storageMode: accepted values are "none", "localStorage" or "sessionStorage"!'); - } - if ('storageImpl' in options) { - if (!angular.isObject(options.storageImpl)) { - throw new Error(errStr + 'storageImpl: must be an object!'); - } else if (!('setItem' in options.storageImpl) || typeof options.storageImpl.setItem !== 'function') { - throw new Error(errStr + 'storageImpl: must implement "setItem(key, value)"!'); - } else if (!('getItem' in options.storageImpl) || typeof options.storageImpl.getItem !== 'function') { - throw new Error(errStr + 'storageImpl: must implement "getItem(key)"!'); - } else if (!('removeItem' in options.storageImpl) || typeof options.storageImpl.removeItem !== 'function') { - throw new Error(errStr + 'storageImpl: must implement "removeItem(key)"!'); - } - } - } - - if ('onExpire' in options) { - if (typeof options.onExpire !== 'function') { - throw new Error(errStr + 'onExpire: must be a function!'); - } - } - - cacheDefaults = angular.extend({}, DEFAULTS(), options); - }; - - // Initialize cacheDefaults with the defaults - this.setCacheDefaults({}); - - /** - * @ignore - */ - this.$get = ['$window', 'BinaryHeap', function ($window, BinaryHeap) { - var caches = {}; - - /** - * Stringify a number. - * @param {number|*} number The number to be stringified. - * @returns {*} number or a string. - * @private - */ - function _stringifyNumber(number) { - if (number && angular.isNumber(number)) { - return number.toString(); - } - return number; - } - - /** - * @method _keySet - * @desc Returns an object of the keys of the given collection. - * @param {object} collection The collection from which to get the set of keys. - * @returns {object} A hash of the keys of the given collection. - */ - function _keySet(collection) { - var keySet = {}, key; - for (key in collection) { - if (collection.hasOwnProperty(key)) { - keySet[key] = key; - } - } - return keySet; - } - - /** - * @method _keys - * @desc Returns an array of the keys of the given collection. - * @param {object} collection The collection from which to get the keys. - * @returns {array} An array of the keys of the given collection. - */ - function _keys(collection) { - var keys = [], key; - for (key in collection) { - if (collection.hasOwnProperty(key)) { - keys.push(key); - } - } - return keys; - } - - /** - * @class AngularCache - * @desc Instantiated via $angularCacheFactory(cacheId[, options]) - * @param {string} cacheId The id of the new cache. - * @param {object} [options] See [Configuration Options]{@link https://github.com/jmdobry/angular-cache#configuration}. - */ - function AngularCache(cacheId, options) { - var config = angular.extend({}, { id: cacheId }), - data = {}, - expiresHeap = new BinaryHeap(function (x) { - return x.expires; - }), - lruHeap = new BinaryHeap(function (x) { - return x.accessed; - }), - prefix = 'angular-cache.caches.' + cacheId, - cacheDirty = false, - self = this, - storage = null; - - options = options || {}; - - /** - * @method _setCapacity - * @desc Set the capacity for this cache. - * @param {number} capacity The new capacity for this cache. - */ - function _setCapacity(capacity) { - _validateNumberOption(capacity, function (err) { - if (err) { - throw new Error('capacity: ' + err); - } else { - config.capacity = capacity; - while (lruHeap.size() > config.capacity) { - self.remove(lruHeap.peek().key, { verifyIntegrity: false }); - } - } - }); - } - - /** - * @method _setDeleteOnExpire - * @desc Set the deleteOnExpire setting for this cache. - * @param {string} deleteOnExpire The new deleteOnExpire for this cache. - */ - function _setDeleteOnExpire(deleteOnExpire) { - if (!angular.isString(deleteOnExpire)) { - throw new Error('deleteOnExpire: must be a string!'); - } else if (deleteOnExpire !== 'none' && deleteOnExpire !== 'passive' && deleteOnExpire !== 'aggressive') { - throw new Error('deleteOnExpire: accepted values are "none", "passive" or "aggressive"!'); - } else { - config.deleteOnExpire = deleteOnExpire; - } - } - - /** - * @method _setMaxAge - * @desc Set the maxAge for this cache. - * @param {number} maxAge The new maxAge for this cache. - */ - function _setMaxAge(maxAge) { - var keys = _keys(data); - if (maxAge === null) { - if (config.maxAge) { - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - if (!('maxAge' in data[key])) { - delete data[key].expires; - expiresHeap.remove(data[key]); - } - } - } - config.maxAge = maxAge; - } else { - _validateNumberOption(maxAge, function (err) { - if (err) { - throw new Error('maxAge: ' + err); - } else { - if (maxAge !== config.maxAge) { - config.maxAge = maxAge; - var now = new Date().getTime(); - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - if (!('maxAge' in data[key])) { - expiresHeap.remove(data[key]); - data[key].expires = data[key].created + config.maxAge; - expiresHeap.push(data[key]); - if (data[key].expires < now) { - self.remove(key, { verifyIntegrity: false }); - } - } - } - } - } - }); - } - } - - /** - * @method _flushExpired - * @desc Remove expired items from the expiresHeap. - * @private - */ - function _flushExpired() { - var now = new Date().getTime(), - item = expiresHeap.peek(); - - while (item && item.expires && item.expires < now) { - self.remove(item.key, { verifyIntegrity: false }); - if (config.onExpire) { - config.onExpire(item.key, item.value); - } - item = expiresHeap.peek(); - } - } - - /** - * @method _setRecycleFreq - * @desc Set the recycleFreq setting for this cache. - * @param {number} recycleFreq The new recycleFreq for this cache. - */ - function _setRecycleFreq(recycleFreq) { - if (recycleFreq === null) { - if (config.recycleFreqId) { - clearInterval(config.recycleFreqId); - delete config.recycleFreqId; - } - config.recycleFreq = cacheDefaults.recycleFreq; - config.recycleFreqId = setInterval(_flushExpired, config.recycleFreq); - } else { - _validateNumberOption(recycleFreq, function (err) { - if (err) { - throw new Error('recycleFreq: ' + err); - } else { - config.recycleFreq = recycleFreq; - if (config.recycleFreqId) { - clearInterval(config.recycleFreqId); - } - config.recycleFreqId = setInterval(_flushExpired, config.recycleFreq); - } - }); - } - } - - /** - * @method _setCacheFlushInterval - * @desc Set the cacheFlushInterval for this cache. - * @param {number} cacheFlushInterval The new cacheFlushInterval for this cache. - */ - function _setCacheFlushInterval(cacheFlushInterval) { - if (cacheFlushInterval === null) { - if (config.cacheFlushIntervalId) { - clearInterval(config.cacheFlushIntervalId); - delete config.cacheFlushIntervalId; - } - config.cacheFlushInterval = cacheFlushInterval; - } else { - _validateNumberOption(cacheFlushInterval, function (err) { - if (err) { - throw new Error('cacheFlushInterval: ' + err); - } else { - if (cacheFlushInterval !== config.cacheFlushInterval) { - if (config.cacheFlushIntervalId) { - clearInterval(config.cacheFlushIntervalId); - } - config.cacheFlushInterval = cacheFlushInterval; - config.cacheFlushIntervalId = setInterval(self.removeAll, config.cacheFlushInterval); - } - } - }); - } - } - - /** - * @method _setStorageMode - * @desc Configure the cache to use localStorage. - * @param {string} storageMode "localStorage"|"sessionStorage"|null - * @param {object} storageImpl The storage polyfill/replacement to use. - */ - function _setStorageMode(storageMode, storageImpl) { - var keys, i; - if (!angular.isString(storageMode)) { - throw new Error('storageMode: must be a string!'); - } else if (storageMode !== 'none' && storageMode !== 'localStorage' && storageMode !== 'sessionStorage') { - throw new Error('storageMode: accepted values are "none", "localStorage" or "sessionStorage"!'); - } - if ((config.storageMode === 'localStorage' || config.storageMode === 'sessionStorage') && - (storageMode !== config.storageMode)) { - keys = _keys(data); - for (i = 0; i < keys.length; i++) { - storage.removeItem(prefix + '.data.' + keys[i]); - } - storage.removeItem(prefix + '.keys'); - } - config.storageMode = storageMode; - if (storageImpl) { - if (!angular.isObject(storageImpl)) { - throw new Error('storageImpl: must be an object!'); - } else if (!('setItem' in storageImpl) || typeof storageImpl.setItem !== 'function') { - throw new Error('storageImpl: must implement "setItem(key, value)"!'); - } else if (!('getItem' in storageImpl) || typeof storageImpl.getItem !== 'function') { - throw new Error('storageImpl: must implement "getItem(key)"!'); - } else if (!('removeItem' in storageImpl) || typeof storageImpl.removeItem !== 'function') { - throw new Error('storageImpl: must implement "removeItem(key)"!'); - } - storage = storageImpl; - } else if (config.storageMode === 'localStorage') { - storage = $window.localStorage; - } else if (config.storageMode === 'sessionStorage') { - storage = $window.sessionStorage; - } - - if (config.storageMode !== 'none' && storage) { - if (!cacheDirty) { - _loadCacheConfig(); - } else { - keys = _keys(data); - for (i = 0; i < keys.length; i++) { - _syncToStorage(keys[i]); - } - } - } - } - - /** - * @method _setOptions - * @desc Configure this cache with the given options. - * @param {object} cacheOptions New configuration options for the cache. - * @param {boolean} [strict] If true then any existing configuration will be reset to default before - * applying the new options, otherwise only the options specified in the options hash will be altered. - * @param {object} [options] Configuration. - */ - function _setOptions(cacheOptions, strict, options) { - cacheOptions = cacheOptions || {}; - options = options || {}; - strict = !!strict; - if (!angular.isObject(cacheOptions)) { - throw new Error('AngularCache.setOptions(cacheOptions, strict, options): cacheOptions: must be an object!'); - } - - _verifyIntegrity(options.verifyIntegrity); - - if (strict) { - cacheOptions = angular.extend({}, cacheDefaults, cacheOptions); - } - - if ('disabled' in cacheOptions) { - config.disabled = cacheOptions.disabled === true; - } - if ('verifyIntegrity' in cacheOptions) { - config.verifyIntegrity = cacheOptions.verifyIntegrity === true; - } - if ('capacity' in cacheOptions) { - _setCapacity(cacheOptions.capacity); - } - - if ('deleteOnExpire' in cacheOptions) { - _setDeleteOnExpire(cacheOptions.deleteOnExpire); - } - - if ('maxAge' in cacheOptions) { - _setMaxAge(cacheOptions.maxAge); - } - - if ('recycleFreq' in cacheOptions) { - _setRecycleFreq(cacheOptions.recycleFreq); - } - - if ('cacheFlushInterval' in cacheOptions) { - _setCacheFlushInterval(cacheOptions.cacheFlushInterval); - } - - if ('storageMode' in cacheOptions) { - _setStorageMode(cacheOptions.storageMode, cacheOptions.storageImpl); - } - - if ('onExpire' in cacheOptions) { - if (cacheOptions.onExpire !== null && typeof cacheOptions.onExpire !== 'function') { - throw new Error('onExpire: must be a function!'); - } - config.onExpire = cacheOptions.onExpire; - } - - cacheDirty = true; - } - - /** - * @method _loadCacheConfig - * @desc If storageMode is set, attempt to load previous cache configuration from localStorage. - */ - function _loadCacheConfig() { - var keys = angular.fromJson(storage.getItem(prefix + '.keys')); - storage.removeItem(prefix + '.keys'); - if (keys && keys.length) { - for (var i = 0; i < keys.length; i++) { - var data = angular.fromJson(storage.getItem(prefix + '.data.' + keys[i])) || {}, - maxAge = data.maxAge || config.maxAge, - deleteOnExpire = data.deleteOnExpire || config.deleteOnExpire; - if (maxAge && ((new Date().getTime() - data.created) > maxAge) && deleteOnExpire === 'aggressive') { - storage.removeItem(prefix + '.data.' + keys[i]); - } else { - var options = { - created: data.created - }; - if (data.expires) { - options.expires = data.expires; - } - if (data.accessed) { - options.accessed = data.accessed; - } - if (data.maxAge) { - options.maxAge = data.maxAge; - } - if (data.deleteOnExpire) { - options.deleteOnExpire = data.deleteOnExpire; - } - self.put(keys[i], data.value, options); - } - } - _syncToStorage(null); - } - } - - /** - * @method _syncToStorage - * @desc If storageMode is set, sync to localStorage. - * @param {string} key The identifier of the item to sync. - */ - function _syncToStorage(key) { - if (config.storageMode !== 'none' && storage) { - storage.setItem(prefix + '.keys', angular.toJson(_keys(data))); - if (key) { - storage.setItem(prefix + '.data.' + key, angular.toJson(data[key])); - } - } - } - - function _verifyIntegrity(verifyIntegrity) { - if (verifyIntegrity || (verifyIntegrity !== false && config.verifyIntegrity)) { - if (config.storageMode !== 'none' && storage) { - var keys = _keys(data); - storage.setItem(prefix + '.keys', angular.toJson(keys)); - for (var i = 0; i < keys.length; i++) { - storage.setItem(prefix + '.data.' + keys[i], angular.toJson(data[keys[i]])); - } - } - } - } - - function _readItemFromStorage(key, verifyIntegrity) { - if (verifyIntegrity || (verifyIntegrity !== false && config.verifyIntegrity)) { - if (config.storageMode !== 'none' && storage) { - var itemJson = storage.getItem(prefix + '.data.' + key); - - if (!itemJson && key in data) { - self.remove(key); - } else if (itemJson) { - var item = angular.fromJson(itemJson), - value = item ? item.value : null; - - if (value) { - self.put(key, value); - } - } - } - } - } - - function _saveKeysToStorage(keys) { - if (config.storageMode !== 'none' && storage) { - var keysToSave = keys || _keys(data); - storage.setItem(prefix + '.keys', angular.toJson(keysToSave)); - } - } - - function _saveItemToStorage(key) { - if (config.storageMode !== 'none' && storage && key in data) { - storage.setItem(prefix + '.data.' + key, angular.toJson(data[key])); - } - } - - function _removeAllFromStorage() { - if (config.storageMode !== 'none' && storage) { - var keys = _keys(data); - for (var i = 0; i < keys.length; i++) { - storage.removeItem(prefix + '.data.' + keys[i]); - } - storage.setItem(prefix + '.keys', angular.toJson([])); - } - } - - /** - * @method AngularCache.put - * @desc Add a key-value pair to the cache. - * @param {string} key The identifier for the item to add to the cache. - * @param {*} value The value of the item to add to the cache. - * @param {object} [options] {{ maxAge: {number}, deleteOnExpire: {string} }} - * @returns {*} value The value of the item added to the cache. - */ - this.put = function (key, value, options) { - if (config.disabled) { - return; - } - options = options || {}; - - key = _stringifyNumber(key); - - if (!angular.isString(key)) { - throw new Error('AngularCache.put(key, value, options): key: must be a string!'); - } else if (options && !angular.isObject(options)) { - throw new Error('AngularCache.put(key, value, options): options: must be an object!'); - } else if (options.maxAge && options.maxAge !== null) { - _validateNumberOption(options.maxAge, function (err) { - if (err) { - throw new Error('AngularCache.put(key, value, options): maxAge: ' + err); - } - }); - } else if (options.deleteOnExpire && !angular.isString(options.deleteOnExpire)) { - throw new Error('AngularCache.put(key, value, options): deleteOnExpire: must be a string!'); - } else if (options.deleteOnExpire && options.deleteOnExpire !== 'none' && options.deleteOnExpire !== 'passive' && options.deleteOnExpire !== 'aggressive') { - throw new Error('AngularCache.put(key, value, options): deleteOnExpire: accepted values are "none", "passive" or "aggressive"!'); - } else if (angular.isUndefined(value)) { - return; - } - - var now = new Date().getTime(), - deleteOnExpire, item; - - _verifyIntegrity(options.verifyIntegrity); - - if (data[key]) { - expiresHeap.remove(data[key]); - lruHeap.remove(data[key]); - } else { - data[key] = { key: key }; - } - - item = data[key]; - item.value = value; - item.created = (parseInt(options.created, 10)) || item.created || now; - item.accessed = (parseInt(options.accessed, 10)) || now; - - if (options.deleteOnExpire) { - item.deleteOnExpire = options.deleteOnExpire; - } - if (options.maxAge) { - item.maxAge = options.maxAge; - } - - if (item.maxAge || config.maxAge) { - item.expires = item.created + (item.maxAge || config.maxAge); - } - - deleteOnExpire = item.deleteOnExpire || config.deleteOnExpire; - - if (item.expires && deleteOnExpire === 'aggressive') { - expiresHeap.push(item); - } - - // Sync with localStorage, etc. - _saveKeysToStorage(); - _saveItemToStorage(key); - - lruHeap.push(item); - - if (lruHeap.size() > config.capacity) { - this.remove(lruHeap.peek().key, { verifyIntegrity: false }); - } - - return value; - }; - - /** - * @method AngularCache.get - * @desc Retrieve the item from the cache with the specified key. - * @param {string|Array} key The key of the item to retrieve or an array of keys of items to retrieve. - * @param {object} [options] Configuration. - * @returns {*} The value of the item in the cache with the specified key. - */ - this.get = function (key, options) { - if (config.disabled) { - return; - } - if (angular.isArray(key)) { - var keys = key, - values = []; - - angular.forEach(keys, function (key) { - var value = self.get(key, options); - if (angular.isDefined(value)) { - values.push(value); - } - }); - - return values; - } else { - key = _stringifyNumber(key); - } - - options = options || {}; - if (!angular.isString(key)) { - throw new Error('AngularCache.get(key, options): key: must be a string!'); - } else if (options && !angular.isObject(options)) { - throw new Error('AngularCache.get(key, options): options: must be an object!'); - } else if (options.onExpire && !angular.isFunction(options.onExpire)) { - throw new Error('AngularCache.get(key, options): onExpire: must be a function!'); - } - - _readItemFromStorage(key, options.verifyIntegrity); - - if (!(key in data)) { - return; - } - - var item = data[key], - value = item.value, - now = new Date().getTime(), - deleteOnExpire = item.deleteOnExpire || config.deleteOnExpire; - - lruHeap.remove(item); - item.accessed = now; - lruHeap.push(item); - - if (deleteOnExpire === 'passive' && 'expires' in item && item.expires < now) { - this.remove(key, { verifyIntegrity: false }); - - if (config.onExpire) { - config.onExpire(key, item.value, (options.onExpire)); - } else if (options.onExpire) { - options.onExpire(key, item.value); - } - value = undefined; - } - - _saveItemToStorage(key); - - return value; - }; - - /** - * @method AngularCache.remove - * @desc Remove the item with the specified key from the cache. - * @param {string} key The key of the item to remove. - * @param {object} [options] Configuration. - */ - this.remove = function (key, options) { - options = options || {}; - _verifyIntegrity(options.verifyIntegrity); - lruHeap.remove(data[key]); - expiresHeap.remove(data[key]); - if (config.storageMode !== 'none' && storage) { - storage.removeItem(prefix + '.data.' + key); - } - delete data[key]; - _saveKeysToStorage(); - }; - - /** - * @method AngularCache.removeAll - * @desc Clear the cache. - */ - this.removeAll = function () { - _removeAllFromStorage(); - lruHeap.removeAll(); - expiresHeap.removeAll(); - data = {}; - }; - - /** - * @method AngularCache.removeExpired - * @desc Remove all expired items from the cache. - * @param {object} [options] Configuration. - * @returns {object|array} Object or array of removed items. - */ - this.removeExpired = function (options) { - options = options || {}; - var now = new Date().getTime(), - keys = _keys(data), - expired = {}; - - for (var i = 0; i < keys.length; i++) { - if (data[keys[i]] && data[keys[i]].expires && data[keys[i]].expires < now) { - expired[keys[i]] = data[keys[i]].value; - } - } - for (var key in expired) { - self.remove(key); - } - _verifyIntegrity(options.verifyIntegrity); - if (options.asArray) { - var expiredArray = []; - for (key in expired) { - expiredArray.push(expired[key]); - } - return expiredArray; - } else { - return expired; - } - }; - - /** - * @method AngularCache.destroy - * @desc Completely destroy the cache. - */ - this.destroy = function () { - if (config.cacheFlushIntervalId) { - clearInterval(config.cacheFlushIntervalId); - } - if (config.recycleFreqId) { - clearInterval(config.recycleFreqId); - } - this.removeAll(); - if (config.storageMode !== 'none' && storage) { - storage.removeItem(prefix + '.keys'); - storage.removeItem(prefix); - } - storage = null; - data = null; - lruHeap = null; - expiresHeap = null; - config = null; - prefix = null; - self = null; - var methodKeys = _keys(this); - - // Prevent this cache from being used after it has been destroyed - for (var i = 0; i < methodKeys.length; i++) { - if (this.hasOwnProperty(methodKeys[i])) { - delete this[methodKeys[i]]; - } - } - - caches[cacheId] = null; - delete caches[cacheId]; - }; - - /** - * @method AngularCache.info - * @desc Return an object containing information about the cache. - * @param {string} [key] The key of the item about which to retrieve information. - * @returns {object} stats Object containing information about this cache or the item with the - * specified key. - */ - this.info = function (key) { - if (key) { - if (data[key]) { - var info = { - created: data[key].created, - accessed: data[key].accessed, - expires: data[key].expires, - maxAge: data[key].maxAge || config.maxAge, - deleteOnExpire: data[key].deleteOnExpire || config.deleteOnExpire, - isExpired: false - }; - if (info.maxAge) { - info.isExpired = (new Date().getTime() - info.created) > info.maxAge; - } - return info; - } else { - return data[key]; - } - } else { - return angular.extend({}, config, { size: lruHeap && lruHeap.size() || 0 }); - } - }; - - /** - * @method AngularCache.keySet - * @desc Return the set of the keys of all items in the cache. - * @returns {object} The set of the keys of all items currently in this cache. - */ - this.keySet = function () { - return _keySet(data); - }; - - /** - * @method AngularCache.keys - * @desc Return an array of the keys of all items in the cache. - * @returns {array} An array of the keys of all items in the cache. - */ - this.keys = function () { - return _keys(data); - }; - - /** - * @method AngularCache.setOptions - * @desc Configure this cache with the given options. - * @param {object} cacheOptions - * @param {boolean} [strict] If true then any existing configuration will be reset to defaults before - * applying the new options, otherwise only the options specified in the hash will be altered. - * @param {object} [options] Configuration. - */ - this.setOptions = _setOptions; - - // Initialize this cache with the default and given options - _setOptions(options, true, { verifyIntegrity: false }); - } - - /** - * @class AngularCacheFactory - * @param {string} cacheId The id of the new cache. - * @param {object} [options] See [Configuration Options]{@link https://github.com/jmdobry/angular-cache#configuration}. - * @returns {AngularCache} - */ - function angularCacheFactory(cacheId, options) { - if (cacheId in caches) { - throw new Error('cacheId ' + cacheId + ' taken!'); - } else if (!angular.isString(cacheId)) { - throw new Error('cacheId must be a string!'); - } - - caches[cacheId] = new AngularCache(cacheId, options); - return caches[cacheId]; - } - - /** - * @method AngularCacheFactory.info - * @desc Return an object containing information about all caches in $angularCacheFactory. - * @returns {object} An object containing information about all caches of this factory. - */ - angularCacheFactory.info = function () { - var keys = _keys(caches); - var info = { - size: keys.length, - caches: {} - }; - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - info.caches[key] = caches[key].info(); - } - info.cacheDefaults = angular.extend({}, cacheDefaults); - return info; - }; - - /** - * @method AngularCacheFactory.get - * @desc Return the cache with the specified cacheId. - * @param {string} cacheId The id of the desired cache. - * @returns {AngularCache} The cache with the specified cachedId. - */ - angularCacheFactory.get = function (cacheId) { - if (!angular.isString(cacheId)) { - throw new Error('$angularCacheFactory.get(cacheId): cacheId: must be a string!'); - } - return caches[cacheId]; - }; - - /** - * @method AngularCacheFactory.keySet - * @desc Return the set of keys of all current caches owned by angularCacheFactory. - * @returns {object} The set of keys associated with all current caches owned by this - * angularCacheFactory. - */ - angularCacheFactory.keySet = function () { - return _keySet(caches); - }; - - /** - * @method AngularCacheFactory.keys - * @desc Return an array of the keys of all caches owned by angularCacheFactory. - * @returns {array} An array of the keys associated with all current caches owned by - * this angularCacheFactory. - */ - angularCacheFactory.keys = function () { - return _keys(caches); - }; - - /** - * @method AngularCacheFactory.removeAll - * @desc Destroy all caches in $angularCacheFactory. - */ - angularCacheFactory.removeAll = function () { - var keys = _keys(caches); - for (var i = 0; i < keys.length; i++) { - caches[keys[i]].destroy(); - } - }; - - /** - * @method AngularCacheFactory.clearAll - * @desc Clears the contents of every cache in $angularCacheFactory. - */ - angularCacheFactory.clearAll = function () { - var keys = _keys(caches); - for (var i = 0; i < keys.length; i++) { - caches[keys[i]].removeAll(); - } - }; - - /** - * @method AngularCacheFactory.enableAll - * @desc Enable any disabled caches. - */ - angularCacheFactory.enableAll = function () { - var keys = _keys(caches); - for (var i = 0; i < keys.length; i++) { - caches[keys[i]].setOptions({ disabled: false }); - } - }; - - /** - * @method AngularCacheFactory.disableAll - * @desc Disable all caches. - */ - angularCacheFactory.disableAll = function () { - var keys = _keys(caches); - for (var i = 0; i < keys.length; i++) { - caches[keys[i]].setOptions({ disabled: true }); - } - }; - - return angularCacheFactory; - }]; - } - - // Register the new provider with Angular. - angular.module('jmdobry.angular-cache').provider('$angularCacheFactory', $AngularCacheFactoryProvider); + function BinaryHeap(weightFunc) { + if (weightFunc && !angular.isFunction(weightFunc)) { + throw new Error('BinaryHeap(weightFunc): weightFunc: must be a function!'); + } + weightFunc = weightFunc || function (x) { + return x; + }; + this.weightFunc = weightFunc; + this.heap = []; + } + + /** + * @method BinaryHeap.push + * @desc Push an element into the binary heap. + * @param {*} node The element to push into the binary heap. + */ + BinaryHeap.prototype.push = function (node) { + this.heap.push(node); + bubbleUp(this.heap, this.weightFunc, this.heap.length - 1); + }; + + /** + * @method BinaryHeap.peek + * @desc Return, but do not remove, the minimum element in the binary heap. + * @returns {*} + */ + BinaryHeap.prototype.peek = function () { + return this.heap[0]; + }; + + /** + * @method BinaryHeap.pop + * @desc Remove and return the minimum element in the binary heap. + * @returns {*} + */ + BinaryHeap.prototype.pop = function () { + var front = this.heap[0], + end = this.heap.pop(); + if (this.heap.length > 0) { + this.heap[0] = end; + bubbleDown(this.heap, this.weightFunc, 0); + } + return front; + }; + + /** + * @method BinaryHeap.remove + * @desc Remove the first node in the priority queue that satisfies angular.equals comparison with + * the given node. + * @param {*} node The node to remove. + * @returns {*} The removed node. + */ + BinaryHeap.prototype.remove = function (node) { + var length = this.heap.length; + for (var i = 0; i < length; i++) { + if (angular.equals(this.heap[i], node)) { + var removed = this.heap[i], + end = this.heap.pop(); + if (i !== length - 1) { + this.heap[i] = end; + bubbleUp(this.heap, this.weightFunc, i); + bubbleDown(this.heap, this.weightFunc, i); + } + return removed; + } + } + return null; + }; + + /** + * @method BinaryHeap.removeAll + * @desc Remove all nodes from this BinaryHeap. + */ + BinaryHeap.prototype.removeAll = function () { + this.heap = []; + }; + + /** + * @method BinaryHeap.size + * @desc Return the size of the priority queue. + * @returns {number} The size of the priority queue. + */ + BinaryHeap.prototype.size = function () { + return this.heap.length; + }; + + return BinaryHeap; + }; + } + + angular.module('jmdobry.binary-heap', []). + provider('BinaryHeap', BinaryHeapProvider); + + /** + * @class $AngularCacheFactoryProvider + * @desc Provider for the $angularCacheFactory. + * @see {@link http://docs.angularjs.org/api/ng.$cacheFactory|ng.$cacheFactory} + */ + function $AngularCacheFactoryProvider() { + + var cacheDefaults, + DEFAULTS = function () { + return { + capacity: Number.MAX_VALUE, + maxAge: null, + deleteOnExpire: 'none', + onExpire: null, + cacheFlushInterval: null, + recycleFreq: 1000, + storageMode: 'none', + storageImpl: null, + verifyIntegrity: true, + disabled: false, + storePromises: false + }; + }; + + /** + * @method _validateNumberOption + * @desc Validates the given number option. + * @param {number} option The number option to check. + * @param {function} cb Callback function + */ + function _validateNumberOption(option, cb) { + if (!angular.isNumber(option)) { + cb('must be a number!'); + } else if (option < 0) { + cb('must be greater than zero!'); + } else { + cb(null); + } + } + + /** + * @method $AngularCacheFactoryProvider.setCacheDefaults + * @desc Set the default configuration for all caches created by $angularCacheFactory. + * @param {object} options + */ + this.setCacheDefaults = function (options) { + var errStr = '$angularCacheFactoryProvider.setCacheDefaults(options): '; + options = options || {}; + + try { + localStorage.setItem('angular-cache.test', '1'); + localStorage.removeItem('angular-cache.test'); + } catch (e) { + options.disabled = true; + } + + if (!angular.isObject(options)) { + throw new Error(errStr + 'options: must be an object!'); + } + + if ('disabled' in options) { + options.disabled = options.disabled === true; + } + + if ('storePromises' in options) { + options.storePromises = options.storePromises === true; + } + + if ('capacity' in options) { + _validateNumberOption(options.capacity, function (err) { + if (err) { + throw new Error(errStr + 'capacity: ' + err); + } + }); + } + + if ('deleteOnExpire' in options) { + if (!angular.isString(options.deleteOnExpire)) { + throw new Error(errStr + 'deleteOnExpire: must be a string!'); + } else if (options.deleteOnExpire !== 'none' && options.deleteOnExpire !== 'passive' && options.deleteOnExpire !== 'aggressive') { + throw new Error(errStr + 'deleteOnExpire: accepted values are "none", "passive" or "aggressive"!'); + } + } + + if ('maxAge' in options) { + _validateNumberOption(options.maxAge, function (err) { + if (err) { + throw new Error(errStr + 'maxAge: ' + err); + } + }); + } + + if ('recycleFreq' in options) { + _validateNumberOption(options.recycleFreq, function (err) { + if (err) { + throw new Error(errStr + 'recycleFreq: ' + err); + } + }); + } + + if ('cacheFlushInterval' in options) { + _validateNumberOption(options.cacheFlushInterval, function (err) { + if (err) { + throw new Error(errStr + 'cacheFlushInterval: ' + err); + } + }); + } + + if ('storageMode' in options) { + if (!angular.isString(options.storageMode)) { + throw new Error(errStr + 'storageMode: must be a string!'); + } else if (options.storageMode !== 'none' && options.storageMode !== 'localStorage' && options.storageMode !== 'sessionStorage') { + throw new Error(errStr + 'storageMode: accepted values are "none", "localStorage" or "sessionStorage"!'); + } + if ('storageImpl' in options) { + if (!angular.isObject(options.storageImpl)) { + throw new Error(errStr + 'storageImpl: must be an object!'); + } else if (!('setItem' in options.storageImpl) || typeof options.storageImpl.setItem !== 'function') { + throw new Error(errStr + 'storageImpl: must implement "setItem(key, value)"!'); + } else if (!('getItem' in options.storageImpl) || typeof options.storageImpl.getItem !== 'function') { + throw new Error(errStr + 'storageImpl: must implement "getItem(key)"!'); + } else if (!('removeItem' in options.storageImpl) || typeof options.storageImpl.removeItem !== 'function') { + throw new Error(errStr + 'storageImpl: must implement "removeItem(key)"!'); + } + } + } + + if ('onExpire' in options) { + if (typeof options.onExpire !== 'function') { + throw new Error(errStr + 'onExpire: must be a function!'); + } + } + + cacheDefaults = angular.extend({}, DEFAULTS(), options); + }; + + // Initialize cacheDefaults with the defaults + this.setCacheDefaults({}); + + /** + * @ignore + */ + this.$get = ['$window', 'BinaryHeap', function ($window, BinaryHeap) { + var caches = {}; + + /** + * Stringify a number. + * @param {number|*} number The number to be stringified. + * @returns {*} number or a string. + * @private + */ + function _stringifyNumber(number) { + if (number && angular.isNumber(number)) { + return number.toString(); + } + return number; + } + + /** + * @method _keySet + * @desc Returns an object of the keys of the given collection. + * @param {object} collection The collection from which to get the set of keys. + * @returns {object} A hash of the keys of the given collection. + */ + function _keySet(collection) { + var keySet = {}, key; + for (key in collection) { + if (collection.hasOwnProperty(key)) { + keySet[key] = key; + } + } + return keySet; + } + + /** + * @method _keys + * @desc Returns an array of the keys of the given collection. + * @param {object} collection The collection from which to get the keys. + * @returns {array} An array of the keys of the given collection. + */ + function _keys(collection) { + var keys = [], key; + for (key in collection) { + if (collection.hasOwnProperty(key)) { + keys.push(key); + } + } + return keys; + } + + /** + * @class AngularCache + * @desc Instantiated via $angularCacheFactory(cacheId[, options]) + * @param {string} cacheId The id of the new cache. + * @param {object} [options] See [Configuration Options]{@link https://github.com/jmdobry/angular-cache#configuration}. + */ + function AngularCache(cacheId, options) { + var config = angular.extend({}, { id: cacheId }), + data = {}, + expiresHeap = new BinaryHeap(function (x) { + return x.expires; + }), + lruHeap = new BinaryHeap(function (x) { + return x.accessed; + }), + prefix = 'angular-cache.caches.' + cacheId, + cacheDirty = false, + self = this, + storage = null, + promiseStorage = {}; + + options = options || {}; + + /** + * @method _setCapacity + * @desc Set the capacity for this cache. + * @param {number} capacity The new capacity for this cache. + */ + function _setCapacity(capacity) { + _validateNumberOption(capacity, function (err) { + if (err) { + throw new Error('capacity: ' + err); + } else { + config.capacity = capacity; + while (lruHeap.size() > config.capacity) { + self.remove(lruHeap.peek().key, { verifyIntegrity: false }); + } + } + }); + } + + /** + * @method _setDeleteOnExpire + * @desc Set the deleteOnExpire setting for this cache. + * @param {string} deleteOnExpire The new deleteOnExpire for this cache. + */ + function _setDeleteOnExpire(deleteOnExpire) { + if (!angular.isString(deleteOnExpire)) { + throw new Error('deleteOnExpire: must be a string!'); + } else if (deleteOnExpire !== 'none' && deleteOnExpire !== 'passive' && deleteOnExpire !== 'aggressive') { + throw new Error('deleteOnExpire: accepted values are "none", "passive" or "aggressive"!'); + } else { + config.deleteOnExpire = deleteOnExpire; + } + } + + /** + * @method _setMaxAge + * @desc Set the maxAge for this cache. + * @param {number} maxAge The new maxAge for this cache. + */ + function _setMaxAge(maxAge) { + var keys = _keys(data); + if (maxAge === null) { + if (config.maxAge) { + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + if (!('maxAge' in data[key])) { + delete data[key].expires; + expiresHeap.remove(data[key]); + } + } + } + config.maxAge = maxAge; + } else { + _validateNumberOption(maxAge, function (err) { + if (err) { + throw new Error('maxAge: ' + err); + } else { + if (maxAge !== config.maxAge) { + config.maxAge = maxAge; + var now = new Date().getTime(); + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + if (!('maxAge' in data[key])) { + expiresHeap.remove(data[key]); + data[key].expires = data[key].created + config.maxAge; + expiresHeap.push(data[key]); + if (data[key].expires < now) { + self.remove(key, { verifyIntegrity: false }); + } + } + } + } + } + }); + } + } + + /** + * @method _flushExpired + * @desc Remove expired items from the expiresHeap. + * @private + */ + function _flushExpired() { + var now = new Date().getTime(), + item = expiresHeap.peek(); + + while (item && item.expires && item.expires < now) { + self.remove(item.key, { verifyIntegrity: false }); + if (config.onExpire) { + config.onExpire(item.key, item.value); + } + item = expiresHeap.peek(); + } + } + + /** + * @method _setRecycleFreq + * @desc Set the recycleFreq setting for this cache. + * @param {number} recycleFreq The new recycleFreq for this cache. + */ + function _setRecycleFreq(recycleFreq) { + if (recycleFreq === null) { + if (config.recycleFreqId) { + clearInterval(config.recycleFreqId); + delete config.recycleFreqId; + } + config.recycleFreq = cacheDefaults.recycleFreq; + config.recycleFreqId = setInterval(_flushExpired, config.recycleFreq); + } else { + _validateNumberOption(recycleFreq, function (err) { + if (err) { + throw new Error('recycleFreq: ' + err); + } else { + config.recycleFreq = recycleFreq; + if (config.recycleFreqId) { + clearInterval(config.recycleFreqId); + } + config.recycleFreqId = setInterval(_flushExpired, config.recycleFreq); + } + }); + } + } + + /** + * @method _setCacheFlushInterval + * @desc Set the cacheFlushInterval for this cache. + * @param {number} cacheFlushInterval The new cacheFlushInterval for this cache. + */ + function _setCacheFlushInterval(cacheFlushInterval) { + if (cacheFlushInterval === null) { + if (config.cacheFlushIntervalId) { + clearInterval(config.cacheFlushIntervalId); + delete config.cacheFlushIntervalId; + } + config.cacheFlushInterval = cacheFlushInterval; + } else { + _validateNumberOption(cacheFlushInterval, function (err) { + if (err) { + throw new Error('cacheFlushInterval: ' + err); + } else { + if (cacheFlushInterval !== config.cacheFlushInterval) { + if (config.cacheFlushIntervalId) { + clearInterval(config.cacheFlushIntervalId); + } + config.cacheFlushInterval = cacheFlushInterval; + config.cacheFlushIntervalId = setInterval(self.removeAll, config.cacheFlushInterval); + } + } + }); + } + } + + /** + * @method _setStorageMode + * @desc Configure the cache to use localStorage. + * @param {string} storageMode "localStorage"|"sessionStorage"|null + * @param {object} storageImpl The storage polyfill/replacement to use. + */ + function _setStorageMode(storageMode, storageImpl) { + var keys, i; + if (!angular.isString(storageMode)) { + throw new Error('storageMode: must be a string!'); + } else if (storageMode !== 'none' && storageMode !== 'localStorage' && storageMode !== 'sessionStorage') { + throw new Error('storageMode: accepted values are "none", "localStorage" or "sessionStorage"!'); + } + if ((config.storageMode === 'localStorage' || config.storageMode === 'sessionStorage') && + (storageMode !== config.storageMode)) { + keys = _keys(data); + for (i = 0; i < keys.length; i++) { + storage.removeItem(prefix + '.data.' + keys[i]); + } + storage.removeItem(prefix + '.keys'); + } + config.storageMode = storageMode; + if (storageImpl) { + if (!angular.isObject(storageImpl)) { + throw new Error('storageImpl: must be an object!'); + } else if (!('setItem' in storageImpl) || typeof storageImpl.setItem !== 'function') { + throw new Error('storageImpl: must implement "setItem(key, value)"!'); + } else if (!('getItem' in storageImpl) || typeof storageImpl.getItem !== 'function') { + throw new Error('storageImpl: must implement "getItem(key)"!'); + } else if (!('removeItem' in storageImpl) || typeof storageImpl.removeItem !== 'function') { + throw new Error('storageImpl: must implement "removeItem(key)"!'); + } + storage = storageImpl; + } else if (config.storageMode === 'localStorage') { + storage = $window.localStorage; + } else if (config.storageMode === 'sessionStorage') { + storage = $window.sessionStorage; + } + + if (config.storageMode !== 'none' && storage) { + if (!cacheDirty) { + _loadCacheConfig(); + } else { + keys = _keys(data); + for (i = 0; i < keys.length; i++) { + _syncToStorage(keys[i]); + } + } + } + } + + /** + * @method _setOptions + * @desc Configure this cache with the given options. + * @param {object} cacheOptions New configuration options for the cache. + * @param {boolean} [strict] If true then any existing configuration will be reset to default before + * applying the new options, otherwise only the options specified in the options hash will be altered. + * @param {object} [options] Configuration. + */ + function _setOptions(cacheOptions, strict, options) { + cacheOptions = cacheOptions || {}; + options = options || {}; + strict = !!strict; + if (!angular.isObject(cacheOptions)) { + throw new Error('AngularCache.setOptions(cacheOptions, strict, options): cacheOptions: must be an object!'); + } + + _verifyIntegrity(options.verifyIntegrity); + + if (strict) { + cacheOptions = angular.extend({}, cacheDefaults, cacheOptions); + } + + if ('disabled' in cacheOptions) { + config.disabled = cacheOptions.disabled === true; + } + if ('verifyIntegrity' in cacheOptions) { + config.verifyIntegrity = cacheOptions.verifyIntegrity === true; + } + if ('storePromises' in cacheOptions) { + config.storePromises = cacheOptions.storePromises === true; + } + if ('capacity' in cacheOptions) { + _setCapacity(cacheOptions.capacity); + } + + if ('deleteOnExpire' in cacheOptions) { + _setDeleteOnExpire(cacheOptions.deleteOnExpire); + } + + if ('maxAge' in cacheOptions) { + _setMaxAge(cacheOptions.maxAge); + } + + if ('recycleFreq' in cacheOptions) { + _setRecycleFreq(cacheOptions.recycleFreq); + } + + if ('cacheFlushInterval' in cacheOptions) { + _setCacheFlushInterval(cacheOptions.cacheFlushInterval); + } + + if ('storageMode' in cacheOptions) { + _setStorageMode(cacheOptions.storageMode, cacheOptions.storageImpl); + } + + if ('onExpire' in cacheOptions) { + if (cacheOptions.onExpire !== null && typeof cacheOptions.onExpire !== 'function') { + throw new Error('onExpire: must be a function!'); + } + config.onExpire = cacheOptions.onExpire; + } + + cacheDirty = true; + } + + /** + * @method _loadCacheConfig + * @desc If storageMode is set, attempt to load previous cache configuration from localStorage. + */ + function _loadCacheConfig() { + var keys = angular.fromJson(storage.getItem(prefix + '.keys')); + storage.removeItem(prefix + '.keys'); + if (keys && keys.length) { + for (var i = 0; i < keys.length; i++) { + var data = angular.fromJson(storage.getItem(prefix + '.data.' + keys[i])) || {}, + maxAge = data.maxAge || config.maxAge, + deleteOnExpire = data.deleteOnExpire || config.deleteOnExpire; + if (maxAge && ((new Date().getTime() - data.created) > maxAge) && deleteOnExpire === 'aggressive') { + storage.removeItem(prefix + '.data.' + keys[i]); + } else { + var options = { + created: data.created + }; + if (data.expires) { + options.expires = data.expires; + } + if (data.accessed) { + options.accessed = data.accessed; + } + if (data.maxAge) { + options.maxAge = data.maxAge; + } + if (data.deleteOnExpire) { + options.deleteOnExpire = data.deleteOnExpire; + } + if (data.storePromises) { + options.storePromises = data.storePromises; + } + self.put(keys[i], data.value, options); + } + } + _syncToStorage(null); + } + } + + /** + * @method _syncToStorage + * @desc If storageMode is set, sync to localStorage. + * @param {string} key The identifier of the item to sync. + */ + function _syncToStorage(key) { + if (config.storageMode !== 'none' && storage) { + storage.setItem(prefix + '.keys', angular.toJson(_keys(data))); + if (key) { + storage.setItem(prefix + '.data.' + key, angular.toJson(data[key])); + } + } + } + + function _verifyIntegrity(verifyIntegrity) { + if (verifyIntegrity || (verifyIntegrity !== false && config.verifyIntegrity)) { + if (config.storageMode !== 'none' && storage) { + var keys = _keys(data); + storage.setItem(prefix + '.keys', angular.toJson(keys)); + for (var i = 0; i < keys.length; i++) { + storage.setItem(prefix + '.data.' + keys[i], angular.toJson(data[keys[i]])); + } + } + } + } + + function _readItemFromStorage(key, verifyIntegrity) { + if (verifyIntegrity || (verifyIntegrity !== false && config.verifyIntegrity)) { + if (config.storageMode !== 'none' && storage) { + var itemJson = storage.getItem(prefix + '.data.' + key); + + if (!itemJson && key in data) { + self.remove(key); + } else if (itemJson) { + var item = angular.fromJson(itemJson), + value = item ? item.value : null; + + var options = {}; + if (item && item.created) { + options.created = item.created; + } + if (item && item.expires) { + options.expires = item.expires; + } + if (item && item.accessed) { + options.accessed = item.accessed; + } + if (item && item.maxAge) { + options.maxAge = item.maxAge; + } + if (item && item.deleteOnExpire) { + options.deleteOnExpire = item.deleteOnExpire; + } + if (item && item.storePromises) { + options.storePromises = item.storePromises; + } + + if (value) { + self.put(key, value, options); + } + } + } + } + } + + function _saveKeysToStorage(keys) { + if (config.storageMode !== 'none' && storage) { + var keysToSave = keys || _keys(data); + storage.setItem(prefix + '.keys', angular.toJson(keysToSave)); + } + } + + function _saveItemToStorage(key) { + if (config.storageMode !== 'none' && storage && key in data) { + storage.setItem(prefix + '.data.' + key, angular.toJson(data[key])); + } + } + + function _removeAllFromStorage() { + if (config.storageMode !== 'none' && storage) { + var keys = _keys(data); + for (var i = 0; i < keys.length; i++) { + storage.removeItem(prefix + '.data.' + keys[i]); + } + storage.setItem(prefix + '.keys', angular.toJson([])); + } + } + + /** + * @method AngularCache.put + * @desc Add a key-value pair to the cache. + * @param {string} key The identifier for the item to add to the cache. + * @param {*} value The value of the item to add to the cache. + * @param {object} [options] {{ maxAge: {number}, deleteOnExpire: {string} }} + * @returns {*} value The value of the item added to the cache. + */ + this.put = function (key, value, options) { + if (config.disabled) { + return; + } + key = _stringifyNumber(key); + + if (value && value.then) { + if (!config.storePromises) { + value.then(function (v) { + if (angular.isObject(v) && 'status' in v && 'data' in v) { + self.put(key, [v.status, v.data, v.headers(), v.statusText]); + } else { + self.put(key, v, options); + } + }); + } else { + promiseStorage[key] = value; + } + return; + } + options = options || {}; + + if (!angular.isString(key)) { + throw new Error('AngularCache.put(key, value, options): key: must be a string!'); + } else if (options && !angular.isObject(options)) { + throw new Error('AngularCache.put(key, value, options): options: must be an object!'); + } else if (options.maxAge && options.maxAge !== null) { + _validateNumberOption(options.maxAge, function (err) { + if (err) { + throw new Error('AngularCache.put(key, value, options): maxAge: ' + err); + } + }); + } else if (options.deleteOnExpire && !angular.isString(options.deleteOnExpire)) { + throw new Error('AngularCache.put(key, value, options): deleteOnExpire: must be a string!'); + } else if (options.deleteOnExpire && options.deleteOnExpire !== 'none' && options.deleteOnExpire !== 'passive' && options.deleteOnExpire !== 'aggressive') { + throw new Error('AngularCache.put(key, value, options): deleteOnExpire: accepted values are "none", "passive" or "aggressive"!'); + } else if (angular.isUndefined(value)) { + return; + } + + var now = new Date().getTime(), + deleteOnExpire, item; + + _verifyIntegrity(options.verifyIntegrity); + + if (data[key]) { + expiresHeap.remove(data[key]); + lruHeap.remove(data[key]); + } else { + data[key] = { key: key }; + } + + item = data[key]; + item.value = value; + item.created = (parseInt(options.created, 10)) || item.created || now; + item.accessed = (parseInt(options.accessed, 10)) || now; + + if (options.deleteOnExpire) { + item.deleteOnExpire = options.deleteOnExpire; + } + if (options.storePromises) { + item.storePromises = options.storePromises; + } + if (options.maxAge) { + item.maxAge = options.maxAge; + } + + if (item.maxAge || config.maxAge) { + if ('expires' in options) { + item.expires = parseInt(options.expires, 10); + } else if (!angular.isNumber(item.expires)) { + item.expires = item.created + (item.maxAge || config.maxAge); + } + } + + deleteOnExpire = item.deleteOnExpire || config.deleteOnExpire; + + if (item.expires && deleteOnExpire === 'aggressive') { + expiresHeap.push(item); + } + + // Sync with localStorage, etc. + _saveKeysToStorage(); + _saveItemToStorage(key); + + lruHeap.push(item); + + if (lruHeap.size() > config.capacity) { + this.remove(lruHeap.peek().key, { verifyIntegrity: false }); + } + + return value; + }; + + /** + * @method AngularCache.get + * @desc Retrieve the item from the cache with the specified key. + * @param {string|Array} key The key of the item to retrieve or an array of keys of items to retrieve. + * @param {object} [options] Configuration. + * @returns {*} The value of the item in the cache with the specified key. + */ + this.get = function (key, options) { + if (config.disabled) { + return; + } + if (angular.isArray(key)) { + var keys = key, + values = []; + + angular.forEach(keys, function (key) { + var value = self.get(key, options); + if (angular.isDefined(value)) { + values.push(value); + } + }); + + return values; + } else { + key = _stringifyNumber(key); + } + + if (config.storePromises) { + return promiseStorage[key]; + } + + options = options || {}; + if (!angular.isString(key)) { + throw new Error('AngularCache.get(key, options): key: must be a string!'); + } else if (options && !angular.isObject(options)) { + throw new Error('AngularCache.get(key, options): options: must be an object!'); + } else if (options.onExpire && !angular.isFunction(options.onExpire)) { + throw new Error('AngularCache.get(key, options): onExpire: must be a function!'); + } + + _readItemFromStorage(key, options.verifyIntegrity); + + if (!(key in data)) { + return; + } + + var item = data[key], + value = item.value, + now = new Date().getTime(), + deleteOnExpire = item.deleteOnExpire || config.deleteOnExpire; + + lruHeap.remove(item); + item.accessed = now; + lruHeap.push(item); + + if (deleteOnExpire === 'passive' && 'expires' in item && item.expires < now) { + this.remove(key, { verifyIntegrity: false }); + + if (config.onExpire) { + config.onExpire(key, item.value, (options.onExpire)); + } else if (options.onExpire) { + options.onExpire(key, item.value); + } + value = undefined; + } + + _saveItemToStorage(key); + + return value; + }; + + /** + * @method AngularCache.remove + * @desc Remove the item with the specified key from the cache. + * @param {string} key The key of the item to remove. + * @param {object} [options] Configuration. + */ + this.remove = function (key, options) { + if (config.disabled) { + return; + } + options = options || {}; + if (config.storePromises) { + delete promiseStorage[key]; + } + _verifyIntegrity(options.verifyIntegrity); + lruHeap.remove(data[key]); + expiresHeap.remove(data[key]); + if (config.storageMode !== 'none' && storage) { + storage.removeItem(prefix + '.data.' + key); + } + delete data[key]; + _saveKeysToStorage(); + }; + + /** + * @method AngularCache.removeAll + * @desc Clear the cache. + */ + this.removeAll = function () { + if (config.disabled) { + return; + } + _removeAllFromStorage(); + lruHeap.removeAll(); + expiresHeap.removeAll(); + data = {}; + }; + + /** + * @method AngularCache.removeExpired + * @desc Remove all expired items from the cache. + * @param {object} [options] Configuration. + * @returns {object|array} Object or array of removed items. + */ + this.removeExpired = function (options) { + options = options || {}; + var now = new Date().getTime(), + keys = _keys(data), + expired = {}; + + for (var i = 0; i < keys.length; i++) { + if (data[keys[i]] && data[keys[i]].expires && data[keys[i]].expires < now) { + expired[keys[i]] = data[keys[i]].value; + } + } + for (var key in expired) { + self.remove(key); + } + _verifyIntegrity(options.verifyIntegrity); + if (options.asArray) { + var expiredArray = []; + for (key in expired) { + expiredArray.push(expired[key]); + } + return expiredArray; + } else { + return expired; + } + }; + + /** + * @method AngularCache.destroy + * @desc Completely destroy the cache. + */ + this.destroy = function () { + if (config.cacheFlushIntervalId) { + clearInterval(config.cacheFlushIntervalId); + } + if (config.recycleFreqId) { + clearInterval(config.recycleFreqId); + } + this.removeAll(); + if (config.storageMode !== 'none' && storage) { + storage.removeItem(prefix + '.keys'); + storage.removeItem(prefix); + } + storage = null; + promiseStorage = null; + data = null; + lruHeap = null; + expiresHeap = null; + config = null; + prefix = null; + self = null; + var methodKeys = _keys(this); + + // Prevent this cache from being used after it has been destroyed + for (var i = 0; i < methodKeys.length; i++) { + if (this.hasOwnProperty(methodKeys[i])) { + delete this[methodKeys[i]]; + } + } + + caches[cacheId] = null; + delete caches[cacheId]; + }; + + /** + * @method AngularCache.info + * @desc Return an object containing information about the cache. + * @param {string} [key] The key of the item about which to retrieve information. + * @returns {object} stats Object containing information about this cache or the item with the + * specified key. + */ + this.info = function (key) { + if (key) { + if (data[key]) { + var info = { + created: data[key].created, + accessed: data[key].accessed, + expires: data[key].expires, + maxAge: data[key].maxAge || config.maxAge, + deleteOnExpire: data[key].deleteOnExpire || config.deleteOnExpire, + storePromises: data[key].storePromises || config.storePromises, + isExpired: false + }; + if (info.maxAge) { + info.isExpired = (new Date().getTime() - info.created) > info.maxAge; + } + return info; + } else { + return data[key]; + } + } else { + return angular.extend({}, config, { size: lruHeap && lruHeap.size() || 0 }); + } + }; + + /** + * @method AngularCache.keySet + * @desc Return the set of the keys of all items in the cache. + * @returns {object} The set of the keys of all items currently in this cache. + */ + this.keySet = function () { + return _keySet(data); + }; + + /** + * @method AngularCache.keys + * @desc Return an array of the keys of all items in the cache. + * @returns {array} An array of the keys of all items in the cache. + */ + this.keys = function () { + return _keys(data); + }; + + /** + * @method AngularCache.setOptions + * @desc Configure this cache with the given options. + * @param {object} cacheOptions + * @param {boolean} [strict] If true then any existing configuration will be reset to defaults before + * applying the new options, otherwise only the options specified in the hash will be altered. + * @param {object} [options] Configuration. + */ + this.setOptions = _setOptions; + + // Initialize this cache with the default and given options + _setOptions(options, true, { verifyIntegrity: false }); + } + + /** + * @class AngularCacheFactory + * @param {string} cacheId The id of the new cache. + * @param {object} [options] See [Configuration Options]{@link https://github.com/jmdobry/angular-cache#configuration}. + * @returns {AngularCache} + */ + function angularCacheFactory(cacheId, options) { + if (cacheId in caches) { + throw new Error('cacheId ' + cacheId + ' taken!'); + } else if (!angular.isString(cacheId)) { + throw new Error('cacheId must be a string!'); + } + + caches[cacheId] = new AngularCache(cacheId, options); + return caches[cacheId]; + } + + /** + * @method AngularCacheFactory.info + * @desc Return an object containing information about all caches in $angularCacheFactory. + * @returns {object} An object containing information about all caches of this factory. + */ + angularCacheFactory.info = function () { + var keys = _keys(caches); + var info = { + size: keys.length, + caches: {} + }; + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + info.caches[key] = caches[key].info(); + } + info.cacheDefaults = angular.extend({}, cacheDefaults); + return info; + }; + + /** + * @method AngularCacheFactory.get + * @desc Return the cache with the specified cacheId. + * @param {string} cacheId The id of the desired cache. + * @returns {AngularCache} The cache with the specified cachedId. + */ + angularCacheFactory.get = function (cacheId) { + if (!angular.isString(cacheId)) { + throw new Error('$angularCacheFactory.get(cacheId): cacheId: must be a string!'); + } + return caches[cacheId]; + }; + + /** + * @method AngularCacheFactory.keySet + * @desc Return the set of keys of all current caches owned by angularCacheFactory. + * @returns {object} The set of keys associated with all current caches owned by this + * angularCacheFactory. + */ + angularCacheFactory.keySet = function () { + return _keySet(caches); + }; + + /** + * @method AngularCacheFactory.keys + * @desc Return an array of the keys of all caches owned by angularCacheFactory. + * @returns {array} An array of the keys associated with all current caches owned by + * this angularCacheFactory. + */ + angularCacheFactory.keys = function () { + return _keys(caches); + }; + + /** + * @method AngularCacheFactory.removeAll + * @desc Destroy all caches in $angularCacheFactory. + */ + angularCacheFactory.removeAll = function () { + var keys = _keys(caches); + for (var i = 0; i < keys.length; i++) { + caches[keys[i]].destroy(); + } + }; + + /** + * @method AngularCacheFactory.clearAll + * @desc Clears the contents of every cache in $angularCacheFactory. + */ + angularCacheFactory.clearAll = function () { + var keys = _keys(caches); + for (var i = 0; i < keys.length; i++) { + caches[keys[i]].removeAll(); + } + }; + + /** + * @method AngularCacheFactory.enableAll + * @desc Enable any disabled caches. + */ + angularCacheFactory.enableAll = function () { + var keys = _keys(caches); + for (var i = 0; i < keys.length; i++) { + caches[keys[i]].setOptions({ disabled: false }); + } + }; + + /** + * @method AngularCacheFactory.disableAll + * @desc Disable all caches. + */ + angularCacheFactory.disableAll = function () { + var keys = _keys(caches); + for (var i = 0; i < keys.length; i++) { + caches[keys[i]].setOptions({ disabled: true }); + } + }; + + return angularCacheFactory; + }]; + } + + /** + * @desc Provides an $AngularCacheFactoryProvider, which gives you the ability to use an + * $angularCacheFactory. The $angularCacheFactory produces AngularCache objects, which + * the same abilities as the cache objects that come with Angular, except with some added + * functionality. + */ + angular.module('jmdobry.angular-cache', ['ng', 'jmdobry.binary-heap']). + provider('$angularCacheFactory', $AngularCacheFactoryProvider); + })(window, window.angular); diff --git a/dist/angular-cache.min.js b/dist/angular-cache.min.js index 8dfb4e6..5980911 100644 --- a/dist/angular-cache.min.js +++ b/dist/angular-cache.min.js @@ -1,10 +1,10 @@ /** * @author Jason Dobry * @file angular-cache.min.js -* @version 2.3.3 - Homepage -* @copyright (c) 2013 Jason Dobry +* @version 2.4.1 - Homepage +* @copyright (c) 2013-2014 Jason Dobry * @license MIT * * @overview angular-cache is a very useful replacement for Angular's $cacheFactory. */ -!function(a,b,c){"use strict";function d(){this.$get=function(){function a(a,b,c){for(var d=a[c],e=b(d);c>0;){var f=Math.floor((c+1)/2)-1,g=a[f];if(e>=b(g))break;a[f]=d,a[c]=g,c=f}}function c(a,b,c){for(var d=a.length,e=a[c],f=b(e);;){var g=2*(c+1),h=g-1,i=null;if(d>h){var j=a[h],k=b(j);f>k&&(i=h)}if(d>g){var l=a[g],m=b(l);m<(null===i?f:b(a[h]))&&(i=g)}if(null===i)break;a[c]=a[i],a[i]=e,c=i}}function d(a){if(a&&!b.isFunction(a))throw new Error("BinaryHeap(weightFunc): weightFunc: must be a function!");a=a||function(a){return a},this.weightFunc=a,this.heap=[]}return d.prototype.push=function(b){this.heap.push(b),a(this.heap,this.weightFunc,this.heap.length-1)},d.prototype.peek=function(){return this.heap[0]},d.prototype.pop=function(){var a=this.heap[0],b=this.heap.pop();return this.heap.length>0&&(this.heap[0]=b,c(this.heap,this.weightFunc,0)),a},d.prototype.remove=function(d){for(var e=this.heap.length,f=0;e>f;f++)if(b.equals(this.heap[f],d)){var g=this.heap[f],h=this.heap.pop();return f!==e-1&&(this.heap[f]=h,a(this.heap,this.weightFunc,f),c(this.heap,this.weightFunc,f)),g}return null},d.prototype.removeAll=function(){this.heap=[]},d.prototype.size=function(){return this.heap.length},d}}function e(){function a(a,c){c(b.isNumber(a)?0>a?"must be greater than zero!":null:"must be a number!")}var d,e=function(){return{capacity:Number.MAX_VALUE,maxAge:null,deleteOnExpire:"none",onExpire:null,cacheFlushInterval:null,recycleFreq:1e3,storageMode:"none",storageImpl:null,verifyIntegrity:!0,disabled:!1}};this.setCacheDefaults=function(c){var f="$angularCacheFactoryProvider.setCacheDefaults(options): ";if(c=c||{},!b.isObject(c))throw new Error(f+"options: must be an object!");if("disabled"in c&&(c.disabled=c.disabled===!0),"capacity"in c&&a(c.capacity,function(a){if(a)throw new Error(f+"capacity: "+a)}),"deleteOnExpire"in c){if(!b.isString(c.deleteOnExpire))throw new Error(f+"deleteOnExpire: must be a string!");if("none"!==c.deleteOnExpire&&"passive"!==c.deleteOnExpire&&"aggressive"!==c.deleteOnExpire)throw new Error(f+'deleteOnExpire: accepted values are "none", "passive" or "aggressive"!')}if("maxAge"in c&&a(c.maxAge,function(a){if(a)throw new Error(f+"maxAge: "+a)}),"recycleFreq"in c&&a(c.recycleFreq,function(a){if(a)throw new Error(f+"recycleFreq: "+a)}),"cacheFlushInterval"in c&&a(c.cacheFlushInterval,function(a){if(a)throw new Error(f+"cacheFlushInterval: "+a)}),"storageMode"in c){if(!b.isString(c.storageMode))throw new Error(f+"storageMode: must be a string!");if("none"!==c.storageMode&&"localStorage"!==c.storageMode&&"sessionStorage"!==c.storageMode)throw new Error(f+'storageMode: accepted values are "none", "localStorage" or "sessionStorage"!');if("storageImpl"in c){if(!b.isObject(c.storageImpl))throw new Error(f+"storageImpl: must be an object!");if(!("setItem"in c.storageImpl&&"function"==typeof c.storageImpl.setItem))throw new Error(f+'storageImpl: must implement "setItem(key, value)"!');if(!("getItem"in c.storageImpl&&"function"==typeof c.storageImpl.getItem))throw new Error(f+'storageImpl: must implement "getItem(key)"!');if(!("removeItem"in c.storageImpl)||"function"!=typeof c.storageImpl.removeItem)throw new Error(f+'storageImpl: must implement "removeItem(key)"!')}}if("onExpire"in c&&"function"!=typeof c.onExpire)throw new Error(f+"onExpire: must be a function!");d=b.extend({},e(),c)},this.setCacheDefaults({}),this.$get=["$window","BinaryHeap",function(e,f){function g(a){return a&&b.isNumber(a)?a.toString():a}function h(a){var b,c={};for(b in a)a.hasOwnProperty(b)&&(c[b]=b);return c}function i(a){var b,c=[];for(b in a)a.hasOwnProperty(b)&&c.push(b);return c}function j(j,k){function m(b){a(b,function(a){if(a)throw new Error("capacity: "+a);for(B.capacity=b;E.size()>B.capacity;)H.remove(E.peek().key,{verifyIntegrity:!1})})}function n(a){if(!b.isString(a))throw new Error("deleteOnExpire: must be a string!");if("none"!==a&&"passive"!==a&&"aggressive"!==a)throw new Error('deleteOnExpire: accepted values are "none", "passive" or "aggressive"!');B.deleteOnExpire=a}function o(b){var c=i(C);if(null===b){if(B.maxAge)for(var d=0;de&&"aggressive"===f)I.removeItem(F+".data."+a[c]);else{var g={created:d.created};d.expires&&(g.expires=d.expires),d.accessed&&(g.accessed=d.accessed),d.maxAge&&(g.maxAge=d.maxAge),d.deleteOnExpire&&(g.deleteOnExpire=d.deleteOnExpire),H.put(a[c],d.value,g)}}v(null)}}function v(a){"none"!==B.storageMode&&I&&(I.setItem(F+".keys",b.toJson(i(C))),a&&I.setItem(F+".data."+a,b.toJson(C[a])))}function w(a){if((a||a!==!1&&B.verifyIntegrity)&&"none"!==B.storageMode&&I){var c=i(C);I.setItem(F+".keys",b.toJson(c));for(var d=0;dB.capacity&&this.remove(E.peek().key,{verifyIntegrity:!1}),d}},this.get=function(a,d){if(!B.disabled){if(b.isArray(a)){var e=a,f=[];return b.forEach(e,function(a){var c=H.get(a,d);b.isDefined(c)&&f.push(c)}),f}if(a=g(a),d=d||{},!b.isString(a))throw new Error("AngularCache.get(key, options): key: must be a string!");if(d&&!b.isObject(d))throw new Error("AngularCache.get(key, options): options: must be an object!");if(d.onExpire&&!b.isFunction(d.onExpire))throw new Error("AngularCache.get(key, options): onExpire: must be a function!");if(x(a,d.verifyIntegrity),a in C){var h=C[a],i=h.value,j=(new Date).getTime(),k=h.deleteOnExpire||B.deleteOnExpire;return E.remove(h),h.accessed=j,E.push(h),"passive"===k&&"expires"in h&&h.expiresc.maxAge),c}return C[a]}return b.extend({},B,{size:E&&E.size()||0})},this.keySet=function(){return h(C)},this.keys=function(){return i(C)},this.setOptions=t,t(k,!0,{verifyIntegrity:!1})}function k(a,c){if(a in l)throw new Error("cacheId "+a+" taken!");if(!b.isString(a))throw new Error("cacheId must be a string!");return l[a]=new j(a,c),l[a]}var l={};return k.info=function(){for(var a=i(l),c={size:a.length,caches:{}},e=0;e0;){var f=Math.floor((c+1)/2)-1,g=a[f];if(e>=b(g))break;a[f]=d,a[c]=g,c=f}}function c(a,b,c){for(var d=a.length,e=a[c],f=b(e);;){var g=2*(c+1),h=g-1,i=null;if(d>h){var j=a[h],k=b(j);f>k&&(i=h)}if(d>g){var l=a[g],m=b(l);m<(null===i?f:b(a[h]))&&(i=g)}if(null===i)break;a[c]=a[i],a[i]=e,c=i}}function d(a){if(a&&!b.isFunction(a))throw new Error("BinaryHeap(weightFunc): weightFunc: must be a function!");a=a||function(a){return a},this.weightFunc=a,this.heap=[]}return d.prototype.push=function(b){this.heap.push(b),a(this.heap,this.weightFunc,this.heap.length-1)},d.prototype.peek=function(){return this.heap[0]},d.prototype.pop=function(){var a=this.heap[0],b=this.heap.pop();return this.heap.length>0&&(this.heap[0]=b,c(this.heap,this.weightFunc,0)),a},d.prototype.remove=function(d){for(var e=this.heap.length,f=0;e>f;f++)if(b.equals(this.heap[f],d)){var g=this.heap[f],h=this.heap.pop();return f!==e-1&&(this.heap[f]=h,a(this.heap,this.weightFunc,f),c(this.heap,this.weightFunc,f)),g}return null},d.prototype.removeAll=function(){this.heap=[]},d.prototype.size=function(){return this.heap.length},d}}function e(){function a(a,c){c(b.isNumber(a)?0>a?"must be greater than zero!":null:"must be a number!")}var d,e=function(){return{capacity:Number.MAX_VALUE,maxAge:null,deleteOnExpire:"none",onExpire:null,cacheFlushInterval:null,recycleFreq:1e3,storageMode:"none",storageImpl:null,verifyIntegrity:!0,disabled:!1,storePromises:!1}};this.setCacheDefaults=function(c){var f="$angularCacheFactoryProvider.setCacheDefaults(options): ";c=c||{};try{localStorage.setItem("angular-cache.test","1"),localStorage.removeItem("angular-cache.test")}catch(g){c.disabled=!0}if(!b.isObject(c))throw new Error(f+"options: must be an object!");if("disabled"in c&&(c.disabled=c.disabled===!0),"storePromises"in c&&(c.storePromises=c.storePromises===!0),"capacity"in c&&a(c.capacity,function(a){if(a)throw new Error(f+"capacity: "+a)}),"deleteOnExpire"in c){if(!b.isString(c.deleteOnExpire))throw new Error(f+"deleteOnExpire: must be a string!");if("none"!==c.deleteOnExpire&&"passive"!==c.deleteOnExpire&&"aggressive"!==c.deleteOnExpire)throw new Error(f+'deleteOnExpire: accepted values are "none", "passive" or "aggressive"!')}if("maxAge"in c&&a(c.maxAge,function(a){if(a)throw new Error(f+"maxAge: "+a)}),"recycleFreq"in c&&a(c.recycleFreq,function(a){if(a)throw new Error(f+"recycleFreq: "+a)}),"cacheFlushInterval"in c&&a(c.cacheFlushInterval,function(a){if(a)throw new Error(f+"cacheFlushInterval: "+a)}),"storageMode"in c){if(!b.isString(c.storageMode))throw new Error(f+"storageMode: must be a string!");if("none"!==c.storageMode&&"localStorage"!==c.storageMode&&"sessionStorage"!==c.storageMode)throw new Error(f+'storageMode: accepted values are "none", "localStorage" or "sessionStorage"!');if("storageImpl"in c){if(!b.isObject(c.storageImpl))throw new Error(f+"storageImpl: must be an object!");if(!("setItem"in c.storageImpl&&"function"==typeof c.storageImpl.setItem))throw new Error(f+'storageImpl: must implement "setItem(key, value)"!');if(!("getItem"in c.storageImpl&&"function"==typeof c.storageImpl.getItem))throw new Error(f+'storageImpl: must implement "getItem(key)"!');if(!("removeItem"in c.storageImpl)||"function"!=typeof c.storageImpl.removeItem)throw new Error(f+'storageImpl: must implement "removeItem(key)"!')}}if("onExpire"in c&&"function"!=typeof c.onExpire)throw new Error(f+"onExpire: must be a function!");d=b.extend({},e(),c)},this.setCacheDefaults({}),this.$get=["$window","BinaryHeap",function(e,f){function g(a){return a&&b.isNumber(a)?a.toString():a}function h(a){var b,c={};for(b in a)a.hasOwnProperty(b)&&(c[b]=b);return c}function i(a){var b,c=[];for(b in a)a.hasOwnProperty(b)&&c.push(b);return c}function j(j,k){function m(b){a(b,function(a){if(a)throw new Error("capacity: "+a);for(B.capacity=b;E.size()>B.capacity;)H.remove(E.peek().key,{verifyIntegrity:!1})})}function n(a){if(!b.isString(a))throw new Error("deleteOnExpire: must be a string!");if("none"!==a&&"passive"!==a&&"aggressive"!==a)throw new Error('deleteOnExpire: accepted values are "none", "passive" or "aggressive"!');B.deleteOnExpire=a}function o(b){var c=i(C);if(null===b){if(B.maxAge)for(var d=0;de&&"aggressive"===f)I.removeItem(F+".data."+a[c]);else{var g={created:d.created};d.expires&&(g.expires=d.expires),d.accessed&&(g.accessed=d.accessed),d.maxAge&&(g.maxAge=d.maxAge),d.deleteOnExpire&&(g.deleteOnExpire=d.deleteOnExpire),d.storePromises&&(g.storePromises=d.storePromises),H.put(a[c],d.value,g)}}v(null)}}function v(a){"none"!==B.storageMode&&I&&(I.setItem(F+".keys",b.toJson(i(C))),a&&I.setItem(F+".data."+a,b.toJson(C[a])))}function w(a){if((a||a!==!1&&B.verifyIntegrity)&&"none"!==B.storageMode&&I){var c=i(C);I.setItem(F+".keys",b.toJson(c));for(var d=0;dB.capacity&&this.remove(E.peek().key,{verifyIntegrity:!1}),d}},this.get=function(a,d){if(!B.disabled){if(b.isArray(a)){var e=a,f=[];return b.forEach(e,function(a){var c=H.get(a,d);b.isDefined(c)&&f.push(c)}),f}if(a=g(a),B.storePromises)return J[a];if(d=d||{},!b.isString(a))throw new Error("AngularCache.get(key, options): key: must be a string!");if(d&&!b.isObject(d))throw new Error("AngularCache.get(key, options): options: must be an object!");if(d.onExpire&&!b.isFunction(d.onExpire))throw new Error("AngularCache.get(key, options): onExpire: must be a function!");if(x(a,d.verifyIntegrity),a in C){var h=C[a],i=h.value,j=(new Date).getTime(),k=h.deleteOnExpire||B.deleteOnExpire;return E.remove(h),h.accessed=j,E.push(h),"passive"===k&&"expires"in h&&h.expiresc.maxAge),c}return C[a]}return b.extend({},B,{size:E&&E.size()||0})},this.keySet=function(){return h(C)},this.keys=function(){return i(C)},this.setOptions=t,t(k,!0,{verifyIntegrity:!1})}function k(a,c){if(a in l)throw new Error("cacheId "+a+" taken!");if(!b.isString(a))throw new Error("cacheId must be a string!");return l[a]=new j(a,c),l[a]}var l={};return k.info=function(){for(var a=i(l),c={size:a.length,caches:{}},e=0;e - * @file angular-cache-2.3.2.js - * @version 2.3.2 - Homepage - * @copyright (c) 2013 Jason Dobry + * @file angular-cache.js + * @version 2.4.1 - Homepage + * @copyright (c) 2013-2014 Jason Dobry * @license MIT * * @overview angular-cache is a very useful replacement for Angular's $cacheFactory. */ (function (window, angular, undefined) { - 'use strict'; - - angular.module('jmdobry.binary-heap', []); - - /** - * @desc Provider for the BinaryHeap. - */ - function BinaryHeapProvider() { - this.$get = function () { - /** - * @method bubbleUp - * @param {array} heap The heap. - * @param {function} weightFunc The weight function. - * @param {number} n The index of the element to bubble up. - */ - function bubbleUp(heap, weightFunc, n) { - var element = heap[n], - weight = weightFunc(element); - // When at 0, an element can not go up any further. - while (n > 0) { - // Compute the parent element's index, and fetch it. - var parentN = Math.floor((n + 1) / 2) - 1, - parent = heap[parentN]; - // If the parent has a lesser weight, things are in order and we - // are done. - if (weight >= weightFunc(parent)) { - break; - } else { - heap[parentN] = element; - heap[n] = parent; - n = parentN; - } - } - } - - /** - * @method bubbleDown - * @param {array} heap The heap. - * @param {function} weightFunc The weight function. - * @param {number} n The index of the element to sink down. - */ - function bubbleDown(heap, weightFunc, n) { - var length = heap.length, - node = heap[n], - nodeWeight = weightFunc(node); - - while (true) { - var child2N = (n + 1) * 2, - child1N = child2N - 1; - var swap = null; - if (child1N < length) { - var child1 = heap[child1N], - child1Weight = weightFunc(child1); - // If the score is less than our node's, we need to swap. - if (child1Weight < nodeWeight) { - swap = child1N; - } - } - // Do the same checks for the other child. - if (child2N < length) { - var child2 = heap[child2N], - child2Weight = weightFunc(child2); - if (child2Weight < (swap === null ? nodeWeight : weightFunc(heap[child1N]))) { - swap = child2N; - } - } - - if (swap === null) { - break; - } else { - heap[n] = heap[swap]; - heap[swap] = node; - n = swap; - } - } - } - - /** - * @class BinaryHeap - * @desc BinaryHeap implementation of a priority queue. - * @param {function} weightFunc Function that returns the value that should be used for node value comparison. - * @example - * angular.module('app').controller(function (BinaryHeap) { + 'use strict'; + + /** + * @desc Provider for the BinaryHeap. + */ + function BinaryHeapProvider() { + this.$get = function () { + /** + * @method bubbleUp + * @param {array} heap The heap. + * @param {function} weightFunc The weight function. + * @param {number} n The index of the element to bubble up. + */ + function bubbleUp(heap, weightFunc, n) { + var element = heap[n], + weight = weightFunc(element); + // When at 0, an element can not go up any further. + while (n > 0) { + // Compute the parent element's index, and fetch it. + var parentN = Math.floor((n + 1) / 2) - 1, + parent = heap[parentN]; + // If the parent has a lesser weight, things are in order and we + // are done. + if (weight >= weightFunc(parent)) { + break; + } else { + heap[parentN] = element; + heap[n] = parent; + n = parentN; + } + } + } + + /** + * @method bubbleDown + * @param {array} heap The heap. + * @param {function} weightFunc The weight function. + * @param {number} n The index of the element to sink down. + */ + function bubbleDown(heap, weightFunc, n) { + var length = heap.length, + node = heap[n], + nodeWeight = weightFunc(node); + + while (true) { + var child2N = (n + 1) * 2, + child1N = child2N - 1; + var swap = null; + if (child1N < length) { + var child1 = heap[child1N], + child1Weight = weightFunc(child1); + // If the score is less than our node's, we need to swap. + if (child1Weight < nodeWeight) { + swap = child1N; + } + } + // Do the same checks for the other child. + if (child2N < length) { + var child2 = heap[child2N], + child2Weight = weightFunc(child2); + if (child2Weight < (swap === null ? nodeWeight : weightFunc(heap[child1N]))) { + swap = child2N; + } + } + + if (swap === null) { + break; + } else { + heap[n] = heap[swap]; + heap[swap] = node; + n = swap; + } + } + } + + /** + * @class BinaryHeap + * @desc BinaryHeap implementation of a priority queue. + * @param {function} weightFunc Function that returns the value that should be used for node value comparison. + * @example + * angular.module('app').controller(function (BinaryHeap) { * var bHeap = new BinaryHeap(function (x) { * return x.value; * }); * ); */ - function BinaryHeap(weightFunc) { - if (weightFunc && !angular.isFunction(weightFunc)) { - throw new Error('BinaryHeap(weightFunc): weightFunc: must be a function!'); - } - weightFunc = weightFunc || function (x) { - return x; - }; - this.weightFunc = weightFunc; - this.heap = []; - } - - /** - * @method BinaryHeap.push - * @desc Push an element into the binary heap. - * @param {*} node The element to push into the binary heap. - */ - BinaryHeap.prototype.push = function (node) { - this.heap.push(node); - bubbleUp(this.heap, this.weightFunc, this.heap.length - 1); - }; - - /** - * @method BinaryHeap.peek - * @desc Return, but do not remove, the minimum element in the binary heap. - * @returns {*} - */ - BinaryHeap.prototype.peek = function () { - return this.heap[0]; - }; - - /** - * @method BinaryHeap.pop - * @desc Remove and return the minimum element in the binary heap. - * @returns {*} - */ - BinaryHeap.prototype.pop = function () { - var front = this.heap[0], - end = this.heap.pop(); - if (this.heap.length > 0) { - this.heap[0] = end; - bubbleDown(this.heap, this.weightFunc, 0); - } - return front; - }; - - /** - * @method BinaryHeap.remove - * @desc Remove the first node in the priority queue that satisfies angular.equals comparison with - * the given node. - * @param {*} node The node to remove. - * @returns {*} The removed node. - */ - BinaryHeap.prototype.remove = function (node) { - var length = this.heap.length; - for (var i = 0; i < length; i++) { - if (angular.equals(this.heap[i], node)) { - var removed = this.heap[i], - end = this.heap.pop(); - if (i !== length - 1) { - this.heap[i] = end; - bubbleUp(this.heap, this.weightFunc, i); - bubbleDown(this.heap, this.weightFunc, i); - } - return removed; - } - } - return null; - }; - - /** - * @method BinaryHeap.removeAll - * @desc Remove all nodes from this BinaryHeap. - */ - BinaryHeap.prototype.removeAll = function () { - this.heap = []; - }; - - /** - * @method BinaryHeap.size - * @desc Return the size of the priority queue. - * @returns {number} The size of the priority queue. - */ - BinaryHeap.prototype.size = function () { - return this.heap.length; - }; - - return BinaryHeap; - }; - } - - angular.module('jmdobry.binary-heap').provider('BinaryHeap', BinaryHeapProvider); - - /** - * @desc Provides an $AngularCacheFactoryProvider, which gives you the ability to use an - * $angularCacheFactory. The $angularCacheFactory produces AngularCache objects, which - * the same abilities as the cache objects that come with Angular, except with some added - * functionality. - */ - angular.module('jmdobry.angular-cache', ['ng', 'jmdobry.binary-heap']); - - /** - * @class $AngularCacheFactoryProvider - * @desc Provider for the $angularCacheFactory. - * @see {@link http://docs.angularjs.org/api/ng.$cacheFactory|ng.$cacheFactory} - */ - function $AngularCacheFactoryProvider() { - - var cacheDefaults, - DEFAULTS = function () { - return { - capacity: Number.MAX_VALUE, - maxAge: null, - deleteOnExpire: 'none', - onExpire: null, - cacheFlushInterval: null, - recycleFreq: 1000, - storageMode: 'none', - storageImpl: null, - verifyIntegrity: true, - disabled: false - }; - }; - - /** - * @method _validateNumberOption - * @desc Validates the given number option. - * @param {number} option The number option to check. - * @param {function} cb Callback function - */ - function _validateNumberOption(option, cb) { - if (!angular.isNumber(option)) { - cb('must be a number!'); - } else if (option < 0) { - cb('must be greater than zero!'); - } else { - cb(null); - } - } - - /** - * @method $AngularCacheFactoryProvider.setCacheDefaults - * @desc Set the default configuration for all caches created by $angularCacheFactory. - * @param {object} options - */ - this.setCacheDefaults = function (options) { - var errStr = '$angularCacheFactoryProvider.setCacheDefaults(options): '; - options = options || {}; - - if (!angular.isObject(options)) { - throw new Error(errStr + 'options: must be an object!'); - } - - if ('disabled' in options) { - options.disabled = options.disabled === true; - } - - if ('capacity' in options) { - _validateNumberOption(options.capacity, function (err) { - if (err) { - throw new Error(errStr + 'capacity: ' + err); - } - }); - } - - if ('deleteOnExpire' in options) { - if (!angular.isString(options.deleteOnExpire)) { - throw new Error(errStr + 'deleteOnExpire: must be a string!'); - } else if (options.deleteOnExpire !== 'none' && options.deleteOnExpire !== 'passive' && options.deleteOnExpire !== 'aggressive') { - throw new Error(errStr + 'deleteOnExpire: accepted values are "none", "passive" or "aggressive"!'); - } - } - - if ('maxAge' in options) { - _validateNumberOption(options.maxAge, function (err) { - if (err) { - throw new Error(errStr + 'maxAge: ' + err); - } - }); - } - - if ('recycleFreq' in options) { - _validateNumberOption(options.recycleFreq, function (err) { - if (err) { - throw new Error(errStr + 'recycleFreq: ' + err); - } - }); - } - - if ('cacheFlushInterval' in options) { - _validateNumberOption(options.cacheFlushInterval, function (err) { - if (err) { - throw new Error(errStr + 'cacheFlushInterval: ' + err); - } - }); - } - - if ('storageMode' in options) { - if (!angular.isString(options.storageMode)) { - throw new Error(errStr + 'storageMode: must be a string!'); - } else if (options.storageMode !== 'none' && options.storageMode !== 'localStorage' && options.storageMode !== 'sessionStorage') { - throw new Error(errStr + 'storageMode: accepted values are "none", "localStorage" or "sessionStorage"!'); - } - if ('storageImpl' in options) { - if (!angular.isObject(options.storageImpl)) { - throw new Error(errStr + 'storageImpl: must be an object!'); - } else if (!('setItem' in options.storageImpl) || typeof options.storageImpl.setItem !== 'function') { - throw new Error(errStr + 'storageImpl: must implement "setItem(key, value)"!'); - } else if (!('getItem' in options.storageImpl) || typeof options.storageImpl.getItem !== 'function') { - throw new Error(errStr + 'storageImpl: must implement "getItem(key)"!'); - } else if (!('removeItem' in options.storageImpl) || typeof options.storageImpl.removeItem !== 'function') { - throw new Error(errStr + 'storageImpl: must implement "removeItem(key)"!'); - } - } - } - - if ('onExpire' in options) { - if (typeof options.onExpire !== 'function') { - throw new Error(errStr + 'onExpire: must be a function!'); - } - } - - cacheDefaults = angular.extend({}, DEFAULTS(), options); - }; - - // Initialize cacheDefaults with the defaults - this.setCacheDefaults({}); - - /** - * @ignore - */ - this.$get = ['$window', 'BinaryHeap', function ($window, BinaryHeap) { - var caches = {}; - - /** - * Stringify a number. - * @param {number|*} number The number to be stringified. - * @returns {*} number or a string. - * @private - */ - function _stringifyNumber(number) { - if (number && angular.isNumber(number)) { - return number.toString(); - } - return number; - } - - /** - * @method _keySet - * @desc Returns an object of the keys of the given collection. - * @param {object} collection The collection from which to get the set of keys. - * @returns {object} A hash of the keys of the given collection. - */ - function _keySet(collection) { - var keySet = {}, key; - for (key in collection) { - if (collection.hasOwnProperty(key)) { - keySet[key] = key; - } - } - return keySet; - } - - /** - * @method _keys - * @desc Returns an array of the keys of the given collection. - * @param {object} collection The collection from which to get the keys. - * @returns {array} An array of the keys of the given collection. - */ - function _keys(collection) { - var keys = [], key; - for (key in collection) { - if (collection.hasOwnProperty(key)) { - keys.push(key); - } - } - return keys; - } - - /** - * @class AngularCache - * @desc Instantiated via $angularCacheFactory(cacheId[, options]) - * @param {string} cacheId The id of the new cache. - * @param {object} [options] See [Configuration Options]{@link https://github.com/jmdobry/angular-cache#configuration}. - */ - function AngularCache(cacheId, options) { - var config = angular.extend({}, { id: cacheId }), - data = {}, - expiresHeap = new BinaryHeap(function (x) { - return x.expires; - }), - lruHeap = new BinaryHeap(function (x) { - return x.accessed; - }), - prefix = 'angular-cache.caches.' + cacheId, - cacheDirty = false, - self = this, - storage = null; - - options = options || {}; - - /** - * @method _setCapacity - * @desc Set the capacity for this cache. - * @param {number} capacity The new capacity for this cache. - */ - function _setCapacity(capacity) { - _validateNumberOption(capacity, function (err) { - if (err) { - throw new Error('capacity: ' + err); - } else { - config.capacity = capacity; - while (lruHeap.size() > config.capacity) { - self.remove(lruHeap.peek().key, { verifyIntegrity: false }); - } - } - }); - } - - /** - * @method _setDeleteOnExpire - * @desc Set the deleteOnExpire setting for this cache. - * @param {string} deleteOnExpire The new deleteOnExpire for this cache. - */ - function _setDeleteOnExpire(deleteOnExpire) { - if (!angular.isString(deleteOnExpire)) { - throw new Error('deleteOnExpire: must be a string!'); - } else if (deleteOnExpire !== 'none' && deleteOnExpire !== 'passive' && deleteOnExpire !== 'aggressive') { - throw new Error('deleteOnExpire: accepted values are "none", "passive" or "aggressive"!'); - } else { - config.deleteOnExpire = deleteOnExpire; - } - } - - /** - * @method _setMaxAge - * @desc Set the maxAge for this cache. - * @param {number} maxAge The new maxAge for this cache. - */ - function _setMaxAge(maxAge) { - var keys = _keys(data); - if (maxAge === null) { - if (config.maxAge) { - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - if (!('maxAge' in data[key])) { - delete data[key].expires; - expiresHeap.remove(data[key]); - } - } - } - config.maxAge = maxAge; - } else { - _validateNumberOption(maxAge, function (err) { - if (err) { - throw new Error('maxAge: ' + err); - } else { - if (maxAge !== config.maxAge) { - config.maxAge = maxAge; - var now = new Date().getTime(); - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - if (!('maxAge' in data[key])) { - expiresHeap.remove(data[key]); - data[key].expires = data[key].created + config.maxAge; - expiresHeap.push(data[key]); - if (data[key].expires < now) { - self.remove(key, { verifyIntegrity: false }); - } - } - } - } - } - }); - } - } - - /** - * @method _flushExpired - * @desc Remove expired items from the expiresHeap. - * @private - */ - function _flushExpired() { - var now = new Date().getTime(), - item = expiresHeap.peek(); - - while (item && item.expires && item.expires < now) { - self.remove(item.key, { verifyIntegrity: false }); - if (config.onExpire) { - config.onExpire(item.key, item.value); - } - item = expiresHeap.peek(); - } - } - - /** - * @method _setRecycleFreq - * @desc Set the recycleFreq setting for this cache. - * @param {number} recycleFreq The new recycleFreq for this cache. - */ - function _setRecycleFreq(recycleFreq) { - if (recycleFreq === null) { - if (config.recycleFreqId) { - clearInterval(config.recycleFreqId); - delete config.recycleFreqId; - } - config.recycleFreq = cacheDefaults.recycleFreq; - config.recycleFreqId = setInterval(_flushExpired, config.recycleFreq); - } else { - _validateNumberOption(recycleFreq, function (err) { - if (err) { - throw new Error('recycleFreq: ' + err); - } else { - config.recycleFreq = recycleFreq; - if (config.recycleFreqId) { - clearInterval(config.recycleFreqId); - } - config.recycleFreqId = setInterval(_flushExpired, config.recycleFreq); - } - }); - } - } - - /** - * @method _setCacheFlushInterval - * @desc Set the cacheFlushInterval for this cache. - * @param {number} cacheFlushInterval The new cacheFlushInterval for this cache. - */ - function _setCacheFlushInterval(cacheFlushInterval) { - if (cacheFlushInterval === null) { - if (config.cacheFlushIntervalId) { - clearInterval(config.cacheFlushIntervalId); - delete config.cacheFlushIntervalId; - } - config.cacheFlushInterval = cacheFlushInterval; - } else { - _validateNumberOption(cacheFlushInterval, function (err) { - if (err) { - throw new Error('cacheFlushInterval: ' + err); - } else { - if (cacheFlushInterval !== config.cacheFlushInterval) { - if (config.cacheFlushIntervalId) { - clearInterval(config.cacheFlushIntervalId); - } - config.cacheFlushInterval = cacheFlushInterval; - config.cacheFlushIntervalId = setInterval(self.removeAll, config.cacheFlushInterval); - } - } - }); - } - } - - /** - * @method _setStorageMode - * @desc Configure the cache to use localStorage. - * @param {string} storageMode "localStorage"|"sessionStorage"|null - * @param {object} storageImpl The storage polyfill/replacement to use. - */ - function _setStorageMode(storageMode, storageImpl) { - var keys, i; - if (!angular.isString(storageMode)) { - throw new Error('storageMode: must be a string!'); - } else if (storageMode !== 'none' && storageMode !== 'localStorage' && storageMode !== 'sessionStorage') { - throw new Error('storageMode: accepted values are "none", "localStorage" or "sessionStorage"!'); - } - if ((config.storageMode === 'localStorage' || config.storageMode === 'sessionStorage') && - (storageMode !== config.storageMode)) { - keys = _keys(data); - for (i = 0; i < keys.length; i++) { - storage.removeItem(prefix + '.data.' + keys[i]); - } - storage.removeItem(prefix + '.keys'); - } - config.storageMode = storageMode; - if (storageImpl) { - if (!angular.isObject(storageImpl)) { - throw new Error('storageImpl: must be an object!'); - } else if (!('setItem' in storageImpl) || typeof storageImpl.setItem !== 'function') { - throw new Error('storageImpl: must implement "setItem(key, value)"!'); - } else if (!('getItem' in storageImpl) || typeof storageImpl.getItem !== 'function') { - throw new Error('storageImpl: must implement "getItem(key)"!'); - } else if (!('removeItem' in storageImpl) || typeof storageImpl.removeItem !== 'function') { - throw new Error('storageImpl: must implement "removeItem(key)"!'); - } - storage = storageImpl; - } else if (config.storageMode === 'localStorage') { - storage = $window.localStorage; - } else if (config.storageMode === 'sessionStorage') { - storage = $window.sessionStorage; - } - - if (config.storageMode !== 'none' && storage) { - if (!cacheDirty) { - _loadCacheConfig(); - } else { - keys = _keys(data); - for (i = 0; i < keys.length; i++) { - _syncToStorage(keys[i]); - } - } - } - } - - /** - * @method _setOptions - * @desc Configure this cache with the given options. - * @param {object} cacheOptions New configuration options for the cache. - * @param {boolean} [strict] If true then any existing configuration will be reset to default before - * applying the new options, otherwise only the options specified in the options hash will be altered. - * @param {object} [options] Configuration. - */ - function _setOptions(cacheOptions, strict, options) { - cacheOptions = cacheOptions || {}; - options = options || {}; - strict = !!strict; - if (!angular.isObject(cacheOptions)) { - throw new Error('AngularCache.setOptions(cacheOptions, strict, options): cacheOptions: must be an object!'); - } - - _verifyIntegrity(options.verifyIntegrity); - - if (strict) { - cacheOptions = angular.extend({}, cacheDefaults, cacheOptions); - } - - if ('disabled' in cacheOptions) { - config.disabled = cacheOptions.disabled === true; - } - if ('verifyIntegrity' in cacheOptions) { - config.verifyIntegrity = cacheOptions.verifyIntegrity === true; - } - if ('capacity' in cacheOptions) { - _setCapacity(cacheOptions.capacity); - } - - if ('deleteOnExpire' in cacheOptions) { - _setDeleteOnExpire(cacheOptions.deleteOnExpire); - } - - if ('maxAge' in cacheOptions) { - _setMaxAge(cacheOptions.maxAge); - } - - if ('recycleFreq' in cacheOptions) { - _setRecycleFreq(cacheOptions.recycleFreq); - } - - if ('cacheFlushInterval' in cacheOptions) { - _setCacheFlushInterval(cacheOptions.cacheFlushInterval); - } - - if ('storageMode' in cacheOptions) { - _setStorageMode(cacheOptions.storageMode, cacheOptions.storageImpl); - } - - if ('onExpire' in cacheOptions) { - if (cacheOptions.onExpire !== null && typeof cacheOptions.onExpire !== 'function') { - throw new Error('onExpire: must be a function!'); - } - config.onExpire = cacheOptions.onExpire; - } - - cacheDirty = true; - } - - /** - * @method _loadCacheConfig - * @desc If storageMode is set, attempt to load previous cache configuration from localStorage. - */ - function _loadCacheConfig() { - var keys = angular.fromJson(storage.getItem(prefix + '.keys')); - storage.removeItem(prefix + '.keys'); - if (keys && keys.length) { - for (var i = 0; i < keys.length; i++) { - var data = angular.fromJson(storage.getItem(prefix + '.data.' + keys[i])) || {}, - maxAge = data.maxAge || config.maxAge, - deleteOnExpire = data.deleteOnExpire || config.deleteOnExpire; - if (maxAge && ((new Date().getTime() - data.created) > maxAge) && deleteOnExpire === 'aggressive') { - storage.removeItem(prefix + '.data.' + keys[i]); - } else { - var options = { - created: data.created - }; - if (data.expires) { - options.expires = data.expires; - } - if (data.accessed) { - options.accessed = data.accessed; - } - if (data.maxAge) { - options.maxAge = data.maxAge; - } - if (data.deleteOnExpire) { - options.deleteOnExpire = data.deleteOnExpire; - } - self.put(keys[i], data.value, options); - } - } - _syncToStorage(null); - } - } - - /** - * @method _syncToStorage - * @desc If storageMode is set, sync to localStorage. - * @param {string} key The identifier of the item to sync. - */ - function _syncToStorage(key) { - if (config.storageMode !== 'none' && storage) { - storage.setItem(prefix + '.keys', angular.toJson(_keys(data))); - if (key) { - storage.setItem(prefix + '.data.' + key, angular.toJson(data[key])); - } - } - } - - function _verifyIntegrity(verifyIntegrity) { - if (verifyIntegrity || (verifyIntegrity !== false && config.verifyIntegrity)) { - if (config.storageMode !== 'none' && storage) { - var keys = _keys(data); - storage.setItem(prefix + '.keys', angular.toJson(keys)); - for (var i = 0; i < keys.length; i++) { - storage.setItem(prefix + '.data.' + keys[i], angular.toJson(data[keys[i]])); - } - } - } - } - - function _readItemFromStorage(key, verifyIntegrity) { - if (verifyIntegrity || (verifyIntegrity !== false && config.verifyIntegrity)) { - if (config.storageMode !== 'none' && storage) { - var itemJson = storage.getItem(prefix + '.data.' + key); - - if (!itemJson && key in data) { - self.remove(key); - } else if (itemJson) { - var item = angular.fromJson(itemJson), - value = item ? item.value : null; - - if (value) { - self.put(key, value); - } - } - } - } - } - - function _saveKeysToStorage(keys) { - if (config.storageMode !== 'none' && storage) { - var keysToSave = keys || _keys(data); - storage.setItem(prefix + '.keys', angular.toJson(keysToSave)); - } - } - - function _saveItemToStorage(key) { - if (config.storageMode !== 'none' && storage && key in data) { - storage.setItem(prefix + '.data.' + key, angular.toJson(data[key])); - } - } - - function _removeAllFromStorage() { - if (config.storageMode !== 'none' && storage) { - var keys = _keys(data); - for (var i = 0; i < keys.length; i++) { - storage.removeItem(prefix + '.data.' + keys[i]); - } - storage.setItem(prefix + '.keys', angular.toJson([])); - } - } - - /** - * @method AngularCache.put - * @desc Add a key-value pair to the cache. - * @param {string} key The identifier for the item to add to the cache. - * @param {*} value The value of the item to add to the cache. - * @param {object} [options] {{ maxAge: {number}, deleteOnExpire: {string} }} - * @returns {*} value The value of the item added to the cache. - */ - this.put = function (key, value, options) { - if (config.disabled) { - return; - } - options = options || {}; - - key = _stringifyNumber(key); - - if (!angular.isString(key)) { - throw new Error('AngularCache.put(key, value, options): key: must be a string!'); - } else if (options && !angular.isObject(options)) { - throw new Error('AngularCache.put(key, value, options): options: must be an object!'); - } else if (options.maxAge && options.maxAge !== null) { - _validateNumberOption(options.maxAge, function (err) { - if (err) { - throw new Error('AngularCache.put(key, value, options): maxAge: ' + err); - } - }); - } else if (options.deleteOnExpire && !angular.isString(options.deleteOnExpire)) { - throw new Error('AngularCache.put(key, value, options): deleteOnExpire: must be a string!'); - } else if (options.deleteOnExpire && options.deleteOnExpire !== 'none' && options.deleteOnExpire !== 'passive' && options.deleteOnExpire !== 'aggressive') { - throw new Error('AngularCache.put(key, value, options): deleteOnExpire: accepted values are "none", "passive" or "aggressive"!'); - } else if (angular.isUndefined(value)) { - return; - } - - var now = new Date().getTime(), - deleteOnExpire, item; - - _verifyIntegrity(options.verifyIntegrity); - - if (data[key]) { - expiresHeap.remove(data[key]); - lruHeap.remove(data[key]); - } else { - data[key] = { key: key }; - } - - item = data[key]; - item.value = value; - item.created = (parseInt(options.created, 10)) || item.created || now; - item.accessed = (parseInt(options.accessed, 10)) || now; - - if (options.deleteOnExpire) { - item.deleteOnExpire = options.deleteOnExpire; - } - if (options.maxAge) { - item.maxAge = options.maxAge; - } - - if (item.maxAge || config.maxAge) { - item.expires = item.created + (item.maxAge || config.maxAge); - } - - deleteOnExpire = item.deleteOnExpire || config.deleteOnExpire; - - if (item.expires && deleteOnExpire === 'aggressive') { - expiresHeap.push(item); - } - - // Sync with localStorage, etc. - _saveKeysToStorage(); - _saveItemToStorage(key); - - lruHeap.push(item); - - if (lruHeap.size() > config.capacity) { - this.remove(lruHeap.peek().key, { verifyIntegrity: false }); - } - - return value; - }; - - /** - * @method AngularCache.get - * @desc Retrieve the item from the cache with the specified key. - * @param {string|Array} key The key of the item to retrieve or an array of keys of items to retrieve. - * @param {object} [options] Configuration. - * @returns {*} The value of the item in the cache with the specified key. - */ - this.get = function (key, options) { - if (config.disabled) { - return; - } - if (angular.isArray(key)) { - var keys = key, - values = []; - - angular.forEach(keys, function (key) { - var value = self.get(key, options); - if (angular.isDefined(value)) { - values.push(value); - } - }); - - return values; - } else { - key = _stringifyNumber(key); - } - - options = options || {}; - if (!angular.isString(key)) { - throw new Error('AngularCache.get(key, options): key: must be a string!'); - } else if (options && !angular.isObject(options)) { - throw new Error('AngularCache.get(key, options): options: must be an object!'); - } else if (options.onExpire && !angular.isFunction(options.onExpire)) { - throw new Error('AngularCache.get(key, options): onExpire: must be a function!'); - } - - _readItemFromStorage(key, options.verifyIntegrity); - - if (!(key in data)) { - return; - } - - var item = data[key], - value = item.value, - now = new Date().getTime(), - deleteOnExpire = item.deleteOnExpire || config.deleteOnExpire; - - lruHeap.remove(item); - item.accessed = now; - lruHeap.push(item); - - if (deleteOnExpire === 'passive' && 'expires' in item && item.expires < now) { - this.remove(key, { verifyIntegrity: false }); - - if (config.onExpire) { - config.onExpire(key, item.value, (options.onExpire)); - } else if (options.onExpire) { - options.onExpire(key, item.value); - } - value = undefined; - } - - _saveItemToStorage(key); - - return value; - }; - - /** - * @method AngularCache.remove - * @desc Remove the item with the specified key from the cache. - * @param {string} key The key of the item to remove. - * @param {object} [options] Configuration. - */ - this.remove = function (key, options) { - options = options || {}; - _verifyIntegrity(options.verifyIntegrity); - lruHeap.remove(data[key]); - expiresHeap.remove(data[key]); - if (config.storageMode !== 'none' && storage) { - storage.removeItem(prefix + '.data.' + key); - } - delete data[key]; - _saveKeysToStorage(); - }; - - /** - * @method AngularCache.removeAll - * @desc Clear the cache. - */ - this.removeAll = function () { - _removeAllFromStorage(); - lruHeap.removeAll(); - expiresHeap.removeAll(); - data = {}; - }; - - /** - * @method AngularCache.removeExpired - * @desc Remove all expired items from the cache. - * @param {object} [options] Configuration. - * @returns {object|array} Object or array of removed items. - */ - this.removeExpired = function (options) { - options = options || {}; - var now = new Date().getTime(), - keys = _keys(data), - expired = {}; - - for (var i = 0; i < keys.length; i++) { - if (data[keys[i]] && data[keys[i]].expires && data[keys[i]].expires < now) { - expired[keys[i]] = data[keys[i]].value; - } - } - for (var key in expired) { - self.remove(key); - } - _verifyIntegrity(options.verifyIntegrity); - if (options.asArray) { - var expiredArray = []; - for (key in expired) { - expiredArray.push(expired[key]); - } - return expiredArray; - } else { - return expired; - } - }; - - /** - * @method AngularCache.destroy - * @desc Completely destroy the cache. - */ - this.destroy = function () { - if (config.cacheFlushIntervalId) { - clearInterval(config.cacheFlushIntervalId); - } - if (config.recycleFreqId) { - clearInterval(config.recycleFreqId); - } - this.removeAll(); - if (config.storageMode !== 'none' && storage) { - storage.removeItem(prefix + '.keys'); - storage.removeItem(prefix); - } - storage = null; - data = null; - lruHeap = null; - expiresHeap = null; - config = null; - prefix = null; - self = null; - var methodKeys = _keys(this); - - // Prevent this cache from being used after it has been destroyed - for (var i = 0; i < methodKeys.length; i++) { - if (this.hasOwnProperty(methodKeys[i])) { - delete this[methodKeys[i]]; - } - } - - caches[cacheId] = null; - delete caches[cacheId]; - }; - - /** - * @method AngularCache.info - * @desc Return an object containing information about the cache. - * @param {string} [key] The key of the item about which to retrieve information. - * @returns {object} stats Object containing information about this cache or the item with the - * specified key. - */ - this.info = function (key) { - if (key) { - if (data[key]) { - var info = { - created: data[key].created, - accessed: data[key].accessed, - expires: data[key].expires, - maxAge: data[key].maxAge || config.maxAge, - deleteOnExpire: data[key].deleteOnExpire || config.deleteOnExpire, - isExpired: false - }; - if (info.maxAge) { - info.isExpired = (new Date().getTime() - info.created) > info.maxAge; - } - return info; - } else { - return data[key]; - } - } else { - return angular.extend({}, config, { size: lruHeap && lruHeap.size() || 0 }); - } - }; - - /** - * @method AngularCache.keySet - * @desc Return the set of the keys of all items in the cache. - * @returns {object} The set of the keys of all items currently in this cache. - */ - this.keySet = function () { - return _keySet(data); - }; - - /** - * @method AngularCache.keys - * @desc Return an array of the keys of all items in the cache. - * @returns {array} An array of the keys of all items in the cache. - */ - this.keys = function () { - return _keys(data); - }; - - /** - * @method AngularCache.setOptions - * @desc Configure this cache with the given options. - * @param {object} cacheOptions - * @param {boolean} [strict] If true then any existing configuration will be reset to defaults before - * applying the new options, otherwise only the options specified in the hash will be altered. - * @param {object} [options] Configuration. - */ - this.setOptions = _setOptions; - - // Initialize this cache with the default and given options - _setOptions(options, true, { verifyIntegrity: false }); - } - - /** - * @class AngularCacheFactory - * @param {string} cacheId The id of the new cache. - * @param {object} [options] See [Configuration Options]{@link https://github.com/jmdobry/angular-cache#configuration}. - * @returns {AngularCache} - */ - function angularCacheFactory(cacheId, options) { - if (cacheId in caches) { - throw new Error('cacheId ' + cacheId + ' taken!'); - } else if (!angular.isString(cacheId)) { - throw new Error('cacheId must be a string!'); - } - - caches[cacheId] = new AngularCache(cacheId, options); - return caches[cacheId]; - } - - /** - * @method AngularCacheFactory.info - * @desc Return an object containing information about all caches in $angularCacheFactory. - * @returns {object} An object containing information about all caches of this factory. - */ - angularCacheFactory.info = function () { - var keys = _keys(caches); - var info = { - size: keys.length, - caches: {} - }; - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - info.caches[key] = caches[key].info(); - } - info.cacheDefaults = angular.extend({}, cacheDefaults); - return info; - }; - - /** - * @method AngularCacheFactory.get - * @desc Return the cache with the specified cacheId. - * @param {string} cacheId The id of the desired cache. - * @returns {AngularCache} The cache with the specified cachedId. - */ - angularCacheFactory.get = function (cacheId) { - if (!angular.isString(cacheId)) { - throw new Error('$angularCacheFactory.get(cacheId): cacheId: must be a string!'); - } - return caches[cacheId]; - }; - - /** - * @method AngularCacheFactory.keySet - * @desc Return the set of keys of all current caches owned by angularCacheFactory. - * @returns {object} The set of keys associated with all current caches owned by this - * angularCacheFactory. - */ - angularCacheFactory.keySet = function () { - return _keySet(caches); - }; - - /** - * @method AngularCacheFactory.keys - * @desc Return an array of the keys of all caches owned by angularCacheFactory. - * @returns {array} An array of the keys associated with all current caches owned by - * this angularCacheFactory. - */ - angularCacheFactory.keys = function () { - return _keys(caches); - }; - - /** - * @method AngularCacheFactory.removeAll - * @desc Destroy all caches in $angularCacheFactory. - */ - angularCacheFactory.removeAll = function () { - var keys = _keys(caches); - for (var i = 0; i < keys.length; i++) { - caches[keys[i]].destroy(); - } - }; - - /** - * @method AngularCacheFactory.clearAll - * @desc Clears the contents of every cache in $angularCacheFactory. - */ - angularCacheFactory.clearAll = function () { - var keys = _keys(caches); - for (var i = 0; i < keys.length; i++) { - caches[keys[i]].removeAll(); - } - }; - - /** - * @method AngularCacheFactory.enableAll - * @desc Enable any disabled caches. - */ - angularCacheFactory.enableAll = function () { - var keys = _keys(caches); - for (var i = 0; i < keys.length; i++) { - caches[keys[i]].setOptions({ disabled: false }); - } - }; - - /** - * @method AngularCacheFactory.disableAll - * @desc Disable all caches. - */ - angularCacheFactory.disableAll = function () { - var keys = _keys(caches); - for (var i = 0; i < keys.length; i++) { - caches[keys[i]].setOptions({ disabled: true }); - } - }; - - return angularCacheFactory; - }]; - } - - // Register the new provider with Angular. - angular.module('jmdobry.angular-cache').provider('$angularCacheFactory', $AngularCacheFactoryProvider); + function BinaryHeap(weightFunc) { + if (weightFunc && !angular.isFunction(weightFunc)) { + throw new Error('BinaryHeap(weightFunc): weightFunc: must be a function!'); + } + weightFunc = weightFunc || function (x) { + return x; + }; + this.weightFunc = weightFunc; + this.heap = []; + } + + /** + * @method BinaryHeap.push + * @desc Push an element into the binary heap. + * @param {*} node The element to push into the binary heap. + */ + BinaryHeap.prototype.push = function (node) { + this.heap.push(node); + bubbleUp(this.heap, this.weightFunc, this.heap.length - 1); + }; + + /** + * @method BinaryHeap.peek + * @desc Return, but do not remove, the minimum element in the binary heap. + * @returns {*} + */ + BinaryHeap.prototype.peek = function () { + return this.heap[0]; + }; + + /** + * @method BinaryHeap.pop + * @desc Remove and return the minimum element in the binary heap. + * @returns {*} + */ + BinaryHeap.prototype.pop = function () { + var front = this.heap[0], + end = this.heap.pop(); + if (this.heap.length > 0) { + this.heap[0] = end; + bubbleDown(this.heap, this.weightFunc, 0); + } + return front; + }; + + /** + * @method BinaryHeap.remove + * @desc Remove the first node in the priority queue that satisfies angular.equals comparison with + * the given node. + * @param {*} node The node to remove. + * @returns {*} The removed node. + */ + BinaryHeap.prototype.remove = function (node) { + var length = this.heap.length; + for (var i = 0; i < length; i++) { + if (angular.equals(this.heap[i], node)) { + var removed = this.heap[i], + end = this.heap.pop(); + if (i !== length - 1) { + this.heap[i] = end; + bubbleUp(this.heap, this.weightFunc, i); + bubbleDown(this.heap, this.weightFunc, i); + } + return removed; + } + } + return null; + }; + + /** + * @method BinaryHeap.removeAll + * @desc Remove all nodes from this BinaryHeap. + */ + BinaryHeap.prototype.removeAll = function () { + this.heap = []; + }; + + /** + * @method BinaryHeap.size + * @desc Return the size of the priority queue. + * @returns {number} The size of the priority queue. + */ + BinaryHeap.prototype.size = function () { + return this.heap.length; + }; + + return BinaryHeap; + }; + } + + angular.module('jmdobry.binary-heap', []). + provider('BinaryHeap', BinaryHeapProvider); + + /** + * @class $AngularCacheFactoryProvider + * @desc Provider for the $angularCacheFactory. + * @see {@link http://docs.angularjs.org/api/ng.$cacheFactory|ng.$cacheFactory} + */ + function $AngularCacheFactoryProvider() { + + var cacheDefaults, + DEFAULTS = function () { + return { + capacity: Number.MAX_VALUE, + maxAge: null, + deleteOnExpire: 'none', + onExpire: null, + cacheFlushInterval: null, + recycleFreq: 1000, + storageMode: 'none', + storageImpl: null, + verifyIntegrity: true, + disabled: false, + storePromises: false + }; + }; + + /** + * @method _validateNumberOption + * @desc Validates the given number option. + * @param {number} option The number option to check. + * @param {function} cb Callback function + */ + function _validateNumberOption(option, cb) { + if (!angular.isNumber(option)) { + cb('must be a number!'); + } else if (option < 0) { + cb('must be greater than zero!'); + } else { + cb(null); + } + } + + /** + * @method $AngularCacheFactoryProvider.setCacheDefaults + * @desc Set the default configuration for all caches created by $angularCacheFactory. + * @param {object} options + */ + this.setCacheDefaults = function (options) { + var errStr = '$angularCacheFactoryProvider.setCacheDefaults(options): '; + options = options || {}; + + try { + localStorage.setItem('angular-cache.test', '1'); + localStorage.removeItem('angular-cache.test'); + } catch (e) { + options.disabled = true; + } + + if (!angular.isObject(options)) { + throw new Error(errStr + 'options: must be an object!'); + } + + if ('disabled' in options) { + options.disabled = options.disabled === true; + } + + if ('storePromises' in options) { + options.storePromises = options.storePromises === true; + } + + if ('capacity' in options) { + _validateNumberOption(options.capacity, function (err) { + if (err) { + throw new Error(errStr + 'capacity: ' + err); + } + }); + } + + if ('deleteOnExpire' in options) { + if (!angular.isString(options.deleteOnExpire)) { + throw new Error(errStr + 'deleteOnExpire: must be a string!'); + } else if (options.deleteOnExpire !== 'none' && options.deleteOnExpire !== 'passive' && options.deleteOnExpire !== 'aggressive') { + throw new Error(errStr + 'deleteOnExpire: accepted values are "none", "passive" or "aggressive"!'); + } + } + + if ('maxAge' in options) { + _validateNumberOption(options.maxAge, function (err) { + if (err) { + throw new Error(errStr + 'maxAge: ' + err); + } + }); + } + + if ('recycleFreq' in options) { + _validateNumberOption(options.recycleFreq, function (err) { + if (err) { + throw new Error(errStr + 'recycleFreq: ' + err); + } + }); + } + + if ('cacheFlushInterval' in options) { + _validateNumberOption(options.cacheFlushInterval, function (err) { + if (err) { + throw new Error(errStr + 'cacheFlushInterval: ' + err); + } + }); + } + + if ('storageMode' in options) { + if (!angular.isString(options.storageMode)) { + throw new Error(errStr + 'storageMode: must be a string!'); + } else if (options.storageMode !== 'none' && options.storageMode !== 'localStorage' && options.storageMode !== 'sessionStorage') { + throw new Error(errStr + 'storageMode: accepted values are "none", "localStorage" or "sessionStorage"!'); + } + if ('storageImpl' in options) { + if (!angular.isObject(options.storageImpl)) { + throw new Error(errStr + 'storageImpl: must be an object!'); + } else if (!('setItem' in options.storageImpl) || typeof options.storageImpl.setItem !== 'function') { + throw new Error(errStr + 'storageImpl: must implement "setItem(key, value)"!'); + } else if (!('getItem' in options.storageImpl) || typeof options.storageImpl.getItem !== 'function') { + throw new Error(errStr + 'storageImpl: must implement "getItem(key)"!'); + } else if (!('removeItem' in options.storageImpl) || typeof options.storageImpl.removeItem !== 'function') { + throw new Error(errStr + 'storageImpl: must implement "removeItem(key)"!'); + } + } + } + + if ('onExpire' in options) { + if (typeof options.onExpire !== 'function') { + throw new Error(errStr + 'onExpire: must be a function!'); + } + } + + cacheDefaults = angular.extend({}, DEFAULTS(), options); + }; + + // Initialize cacheDefaults with the defaults + this.setCacheDefaults({}); + + /** + * @ignore + */ + this.$get = ['$window', 'BinaryHeap', function ($window, BinaryHeap) { + var caches = {}; + + /** + * Stringify a number. + * @param {number|*} number The number to be stringified. + * @returns {*} number or a string. + * @private + */ + function _stringifyNumber(number) { + if (number && angular.isNumber(number)) { + return number.toString(); + } + return number; + } + + /** + * @method _keySet + * @desc Returns an object of the keys of the given collection. + * @param {object} collection The collection from which to get the set of keys. + * @returns {object} A hash of the keys of the given collection. + */ + function _keySet(collection) { + var keySet = {}, key; + for (key in collection) { + if (collection.hasOwnProperty(key)) { + keySet[key] = key; + } + } + return keySet; + } + + /** + * @method _keys + * @desc Returns an array of the keys of the given collection. + * @param {object} collection The collection from which to get the keys. + * @returns {array} An array of the keys of the given collection. + */ + function _keys(collection) { + var keys = [], key; + for (key in collection) { + if (collection.hasOwnProperty(key)) { + keys.push(key); + } + } + return keys; + } + + /** + * @class AngularCache + * @desc Instantiated via $angularCacheFactory(cacheId[, options]) + * @param {string} cacheId The id of the new cache. + * @param {object} [options] See [Configuration Options]{@link https://github.com/jmdobry/angular-cache#configuration}. + */ + function AngularCache(cacheId, options) { + var config = angular.extend({}, { id: cacheId }), + data = {}, + expiresHeap = new BinaryHeap(function (x) { + return x.expires; + }), + lruHeap = new BinaryHeap(function (x) { + return x.accessed; + }), + prefix = 'angular-cache.caches.' + cacheId, + cacheDirty = false, + self = this, + storage = null, + promiseStorage = {}; + + options = options || {}; + + /** + * @method _setCapacity + * @desc Set the capacity for this cache. + * @param {number} capacity The new capacity for this cache. + */ + function _setCapacity(capacity) { + _validateNumberOption(capacity, function (err) { + if (err) { + throw new Error('capacity: ' + err); + } else { + config.capacity = capacity; + while (lruHeap.size() > config.capacity) { + self.remove(lruHeap.peek().key, { verifyIntegrity: false }); + } + } + }); + } + + /** + * @method _setDeleteOnExpire + * @desc Set the deleteOnExpire setting for this cache. + * @param {string} deleteOnExpire The new deleteOnExpire for this cache. + */ + function _setDeleteOnExpire(deleteOnExpire) { + if (!angular.isString(deleteOnExpire)) { + throw new Error('deleteOnExpire: must be a string!'); + } else if (deleteOnExpire !== 'none' && deleteOnExpire !== 'passive' && deleteOnExpire !== 'aggressive') { + throw new Error('deleteOnExpire: accepted values are "none", "passive" or "aggressive"!'); + } else { + config.deleteOnExpire = deleteOnExpire; + } + } + + /** + * @method _setMaxAge + * @desc Set the maxAge for this cache. + * @param {number} maxAge The new maxAge for this cache. + */ + function _setMaxAge(maxAge) { + var keys = _keys(data); + if (maxAge === null) { + if (config.maxAge) { + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + if (!('maxAge' in data[key])) { + delete data[key].expires; + expiresHeap.remove(data[key]); + } + } + } + config.maxAge = maxAge; + } else { + _validateNumberOption(maxAge, function (err) { + if (err) { + throw new Error('maxAge: ' + err); + } else { + if (maxAge !== config.maxAge) { + config.maxAge = maxAge; + var now = new Date().getTime(); + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + if (!('maxAge' in data[key])) { + expiresHeap.remove(data[key]); + data[key].expires = data[key].created + config.maxAge; + expiresHeap.push(data[key]); + if (data[key].expires < now) { + self.remove(key, { verifyIntegrity: false }); + } + } + } + } + } + }); + } + } + + /** + * @method _flushExpired + * @desc Remove expired items from the expiresHeap. + * @private + */ + function _flushExpired() { + var now = new Date().getTime(), + item = expiresHeap.peek(); + + while (item && item.expires && item.expires < now) { + self.remove(item.key, { verifyIntegrity: false }); + if (config.onExpire) { + config.onExpire(item.key, item.value); + } + item = expiresHeap.peek(); + } + } + + /** + * @method _setRecycleFreq + * @desc Set the recycleFreq setting for this cache. + * @param {number} recycleFreq The new recycleFreq for this cache. + */ + function _setRecycleFreq(recycleFreq) { + if (recycleFreq === null) { + if (config.recycleFreqId) { + clearInterval(config.recycleFreqId); + delete config.recycleFreqId; + } + config.recycleFreq = cacheDefaults.recycleFreq; + config.recycleFreqId = setInterval(_flushExpired, config.recycleFreq); + } else { + _validateNumberOption(recycleFreq, function (err) { + if (err) { + throw new Error('recycleFreq: ' + err); + } else { + config.recycleFreq = recycleFreq; + if (config.recycleFreqId) { + clearInterval(config.recycleFreqId); + } + config.recycleFreqId = setInterval(_flushExpired, config.recycleFreq); + } + }); + } + } + + /** + * @method _setCacheFlushInterval + * @desc Set the cacheFlushInterval for this cache. + * @param {number} cacheFlushInterval The new cacheFlushInterval for this cache. + */ + function _setCacheFlushInterval(cacheFlushInterval) { + if (cacheFlushInterval === null) { + if (config.cacheFlushIntervalId) { + clearInterval(config.cacheFlushIntervalId); + delete config.cacheFlushIntervalId; + } + config.cacheFlushInterval = cacheFlushInterval; + } else { + _validateNumberOption(cacheFlushInterval, function (err) { + if (err) { + throw new Error('cacheFlushInterval: ' + err); + } else { + if (cacheFlushInterval !== config.cacheFlushInterval) { + if (config.cacheFlushIntervalId) { + clearInterval(config.cacheFlushIntervalId); + } + config.cacheFlushInterval = cacheFlushInterval; + config.cacheFlushIntervalId = setInterval(self.removeAll, config.cacheFlushInterval); + } + } + }); + } + } + + /** + * @method _setStorageMode + * @desc Configure the cache to use localStorage. + * @param {string} storageMode "localStorage"|"sessionStorage"|null + * @param {object} storageImpl The storage polyfill/replacement to use. + */ + function _setStorageMode(storageMode, storageImpl) { + var keys, i; + if (!angular.isString(storageMode)) { + throw new Error('storageMode: must be a string!'); + } else if (storageMode !== 'none' && storageMode !== 'localStorage' && storageMode !== 'sessionStorage') { + throw new Error('storageMode: accepted values are "none", "localStorage" or "sessionStorage"!'); + } + if ((config.storageMode === 'localStorage' || config.storageMode === 'sessionStorage') && + (storageMode !== config.storageMode)) { + keys = _keys(data); + for (i = 0; i < keys.length; i++) { + storage.removeItem(prefix + '.data.' + keys[i]); + } + storage.removeItem(prefix + '.keys'); + } + config.storageMode = storageMode; + if (storageImpl) { + if (!angular.isObject(storageImpl)) { + throw new Error('storageImpl: must be an object!'); + } else if (!('setItem' in storageImpl) || typeof storageImpl.setItem !== 'function') { + throw new Error('storageImpl: must implement "setItem(key, value)"!'); + } else if (!('getItem' in storageImpl) || typeof storageImpl.getItem !== 'function') { + throw new Error('storageImpl: must implement "getItem(key)"!'); + } else if (!('removeItem' in storageImpl) || typeof storageImpl.removeItem !== 'function') { + throw new Error('storageImpl: must implement "removeItem(key)"!'); + } + storage = storageImpl; + } else if (config.storageMode === 'localStorage') { + storage = $window.localStorage; + } else if (config.storageMode === 'sessionStorage') { + storage = $window.sessionStorage; + } + + if (config.storageMode !== 'none' && storage) { + if (!cacheDirty) { + _loadCacheConfig(); + } else { + keys = _keys(data); + for (i = 0; i < keys.length; i++) { + _syncToStorage(keys[i]); + } + } + } + } + + /** + * @method _setOptions + * @desc Configure this cache with the given options. + * @param {object} cacheOptions New configuration options for the cache. + * @param {boolean} [strict] If true then any existing configuration will be reset to default before + * applying the new options, otherwise only the options specified in the options hash will be altered. + * @param {object} [options] Configuration. + */ + function _setOptions(cacheOptions, strict, options) { + cacheOptions = cacheOptions || {}; + options = options || {}; + strict = !!strict; + if (!angular.isObject(cacheOptions)) { + throw new Error('AngularCache.setOptions(cacheOptions, strict, options): cacheOptions: must be an object!'); + } + + _verifyIntegrity(options.verifyIntegrity); + + if (strict) { + cacheOptions = angular.extend({}, cacheDefaults, cacheOptions); + } + + if ('disabled' in cacheOptions) { + config.disabled = cacheOptions.disabled === true; + } + if ('verifyIntegrity' in cacheOptions) { + config.verifyIntegrity = cacheOptions.verifyIntegrity === true; + } + if ('storePromises' in cacheOptions) { + config.storePromises = cacheOptions.storePromises === true; + } + if ('capacity' in cacheOptions) { + _setCapacity(cacheOptions.capacity); + } + + if ('deleteOnExpire' in cacheOptions) { + _setDeleteOnExpire(cacheOptions.deleteOnExpire); + } + + if ('maxAge' in cacheOptions) { + _setMaxAge(cacheOptions.maxAge); + } + + if ('recycleFreq' in cacheOptions) { + _setRecycleFreq(cacheOptions.recycleFreq); + } + + if ('cacheFlushInterval' in cacheOptions) { + _setCacheFlushInterval(cacheOptions.cacheFlushInterval); + } + + if ('storageMode' in cacheOptions) { + _setStorageMode(cacheOptions.storageMode, cacheOptions.storageImpl); + } + + if ('onExpire' in cacheOptions) { + if (cacheOptions.onExpire !== null && typeof cacheOptions.onExpire !== 'function') { + throw new Error('onExpire: must be a function!'); + } + config.onExpire = cacheOptions.onExpire; + } + + cacheDirty = true; + } + + /** + * @method _loadCacheConfig + * @desc If storageMode is set, attempt to load previous cache configuration from localStorage. + */ + function _loadCacheConfig() { + var keys = angular.fromJson(storage.getItem(prefix + '.keys')); + storage.removeItem(prefix + '.keys'); + if (keys && keys.length) { + for (var i = 0; i < keys.length; i++) { + var data = angular.fromJson(storage.getItem(prefix + '.data.' + keys[i])) || {}, + maxAge = data.maxAge || config.maxAge, + deleteOnExpire = data.deleteOnExpire || config.deleteOnExpire; + if (maxAge && ((new Date().getTime() - data.created) > maxAge) && deleteOnExpire === 'aggressive') { + storage.removeItem(prefix + '.data.' + keys[i]); + } else { + var options = { + created: data.created + }; + if (data.expires) { + options.expires = data.expires; + } + if (data.accessed) { + options.accessed = data.accessed; + } + if (data.maxAge) { + options.maxAge = data.maxAge; + } + if (data.deleteOnExpire) { + options.deleteOnExpire = data.deleteOnExpire; + } + if (data.storePromises) { + options.storePromises = data.storePromises; + } + self.put(keys[i], data.value, options); + } + } + _syncToStorage(null); + } + } + + /** + * @method _syncToStorage + * @desc If storageMode is set, sync to localStorage. + * @param {string} key The identifier of the item to sync. + */ + function _syncToStorage(key) { + if (config.storageMode !== 'none' && storage) { + storage.setItem(prefix + '.keys', angular.toJson(_keys(data))); + if (key) { + storage.setItem(prefix + '.data.' + key, angular.toJson(data[key])); + } + } + } + + function _verifyIntegrity(verifyIntegrity) { + if (verifyIntegrity || (verifyIntegrity !== false && config.verifyIntegrity)) { + if (config.storageMode !== 'none' && storage) { + var keys = _keys(data); + storage.setItem(prefix + '.keys', angular.toJson(keys)); + for (var i = 0; i < keys.length; i++) { + storage.setItem(prefix + '.data.' + keys[i], angular.toJson(data[keys[i]])); + } + } + } + } + + function _readItemFromStorage(key, verifyIntegrity) { + if (verifyIntegrity || (verifyIntegrity !== false && config.verifyIntegrity)) { + if (config.storageMode !== 'none' && storage) { + var itemJson = storage.getItem(prefix + '.data.' + key); + + if (!itemJson && key in data) { + self.remove(key); + } else if (itemJson) { + var item = angular.fromJson(itemJson), + value = item ? item.value : null; + + var options = {}; + if (item && item.created) { + options.created = item.created; + } + if (item && item.expires) { + options.expires = item.expires; + } + if (item && item.accessed) { + options.accessed = item.accessed; + } + if (item && item.maxAge) { + options.maxAge = item.maxAge; + } + if (item && item.deleteOnExpire) { + options.deleteOnExpire = item.deleteOnExpire; + } + if (item && item.storePromises) { + options.storePromises = item.storePromises; + } + + if (value) { + self.put(key, value, options); + } + } + } + } + } + + function _saveKeysToStorage(keys) { + if (config.storageMode !== 'none' && storage) { + var keysToSave = keys || _keys(data); + storage.setItem(prefix + '.keys', angular.toJson(keysToSave)); + } + } + + function _saveItemToStorage(key) { + if (config.storageMode !== 'none' && storage && key in data) { + storage.setItem(prefix + '.data.' + key, angular.toJson(data[key])); + } + } + + function _removeAllFromStorage() { + if (config.storageMode !== 'none' && storage) { + var keys = _keys(data); + for (var i = 0; i < keys.length; i++) { + storage.removeItem(prefix + '.data.' + keys[i]); + } + storage.setItem(prefix + '.keys', angular.toJson([])); + } + } + + /** + * @method AngularCache.put + * @desc Add a key-value pair to the cache. + * @param {string} key The identifier for the item to add to the cache. + * @param {*} value The value of the item to add to the cache. + * @param {object} [options] {{ maxAge: {number}, deleteOnExpire: {string} }} + * @returns {*} value The value of the item added to the cache. + */ + this.put = function (key, value, options) { + if (config.disabled) { + return; + } + key = _stringifyNumber(key); + + if (value && value.then) { + if (!config.storePromises) { + value.then(function (v) { + if (angular.isObject(v) && 'status' in v && 'data' in v) { + self.put(key, [v.status, v.data, v.headers(), v.statusText]); + } else { + self.put(key, v, options); + } + }); + } else { + promiseStorage[key] = value; + } + return; + } + options = options || {}; + + if (!angular.isString(key)) { + throw new Error('AngularCache.put(key, value, options): key: must be a string!'); + } else if (options && !angular.isObject(options)) { + throw new Error('AngularCache.put(key, value, options): options: must be an object!'); + } else if (options.maxAge && options.maxAge !== null) { + _validateNumberOption(options.maxAge, function (err) { + if (err) { + throw new Error('AngularCache.put(key, value, options): maxAge: ' + err); + } + }); + } else if (options.deleteOnExpire && !angular.isString(options.deleteOnExpire)) { + throw new Error('AngularCache.put(key, value, options): deleteOnExpire: must be a string!'); + } else if (options.deleteOnExpire && options.deleteOnExpire !== 'none' && options.deleteOnExpire !== 'passive' && options.deleteOnExpire !== 'aggressive') { + throw new Error('AngularCache.put(key, value, options): deleteOnExpire: accepted values are "none", "passive" or "aggressive"!'); + } else if (angular.isUndefined(value)) { + return; + } + + var now = new Date().getTime(), + deleteOnExpire, item; + + _verifyIntegrity(options.verifyIntegrity); + + if (data[key]) { + expiresHeap.remove(data[key]); + lruHeap.remove(data[key]); + } else { + data[key] = { key: key }; + } + + item = data[key]; + item.value = value; + item.created = (parseInt(options.created, 10)) || item.created || now; + item.accessed = (parseInt(options.accessed, 10)) || now; + + if (options.deleteOnExpire) { + item.deleteOnExpire = options.deleteOnExpire; + } + if (options.storePromises) { + item.storePromises = options.storePromises; + } + if (options.maxAge) { + item.maxAge = options.maxAge; + } + + if (item.maxAge || config.maxAge) { + if ('expires' in options) { + item.expires = parseInt(options.expires, 10); + } else if (!angular.isNumber(item.expires)) { + item.expires = item.created + (item.maxAge || config.maxAge); + } + } + + deleteOnExpire = item.deleteOnExpire || config.deleteOnExpire; + + if (item.expires && deleteOnExpire === 'aggressive') { + expiresHeap.push(item); + } + + // Sync with localStorage, etc. + _saveKeysToStorage(); + _saveItemToStorage(key); + + lruHeap.push(item); + + if (lruHeap.size() > config.capacity) { + this.remove(lruHeap.peek().key, { verifyIntegrity: false }); + } + + return value; + }; + + /** + * @method AngularCache.get + * @desc Retrieve the item from the cache with the specified key. + * @param {string|Array} key The key of the item to retrieve or an array of keys of items to retrieve. + * @param {object} [options] Configuration. + * @returns {*} The value of the item in the cache with the specified key. + */ + this.get = function (key, options) { + if (config.disabled) { + return; + } + if (angular.isArray(key)) { + var keys = key, + values = []; + + angular.forEach(keys, function (key) { + var value = self.get(key, options); + if (angular.isDefined(value)) { + values.push(value); + } + }); + + return values; + } else { + key = _stringifyNumber(key); + } + + if (config.storePromises) { + return promiseStorage[key]; + } + + options = options || {}; + if (!angular.isString(key)) { + throw new Error('AngularCache.get(key, options): key: must be a string!'); + } else if (options && !angular.isObject(options)) { + throw new Error('AngularCache.get(key, options): options: must be an object!'); + } else if (options.onExpire && !angular.isFunction(options.onExpire)) { + throw new Error('AngularCache.get(key, options): onExpire: must be a function!'); + } + + _readItemFromStorage(key, options.verifyIntegrity); + + if (!(key in data)) { + return; + } + + var item = data[key], + value = item.value, + now = new Date().getTime(), + deleteOnExpire = item.deleteOnExpire || config.deleteOnExpire; + + lruHeap.remove(item); + item.accessed = now; + lruHeap.push(item); + + if (deleteOnExpire === 'passive' && 'expires' in item && item.expires < now) { + this.remove(key, { verifyIntegrity: false }); + + if (config.onExpire) { + config.onExpire(key, item.value, (options.onExpire)); + } else if (options.onExpire) { + options.onExpire(key, item.value); + } + value = undefined; + } + + _saveItemToStorage(key); + + return value; + }; + + /** + * @method AngularCache.remove + * @desc Remove the item with the specified key from the cache. + * @param {string} key The key of the item to remove. + * @param {object} [options] Configuration. + */ + this.remove = function (key, options) { + if (config.disabled) { + return; + } + options = options || {}; + if (config.storePromises) { + delete promiseStorage[key]; + } + _verifyIntegrity(options.verifyIntegrity); + lruHeap.remove(data[key]); + expiresHeap.remove(data[key]); + if (config.storageMode !== 'none' && storage) { + storage.removeItem(prefix + '.data.' + key); + } + delete data[key]; + _saveKeysToStorage(); + }; + + /** + * @method AngularCache.removeAll + * @desc Clear the cache. + */ + this.removeAll = function () { + if (config.disabled) { + return; + } + _removeAllFromStorage(); + lruHeap.removeAll(); + expiresHeap.removeAll(); + data = {}; + }; + + /** + * @method AngularCache.removeExpired + * @desc Remove all expired items from the cache. + * @param {object} [options] Configuration. + * @returns {object|array} Object or array of removed items. + */ + this.removeExpired = function (options) { + options = options || {}; + var now = new Date().getTime(), + keys = _keys(data), + expired = {}; + + for (var i = 0; i < keys.length; i++) { + if (data[keys[i]] && data[keys[i]].expires && data[keys[i]].expires < now) { + expired[keys[i]] = data[keys[i]].value; + } + } + for (var key in expired) { + self.remove(key); + } + _verifyIntegrity(options.verifyIntegrity); + if (options.asArray) { + var expiredArray = []; + for (key in expired) { + expiredArray.push(expired[key]); + } + return expiredArray; + } else { + return expired; + } + }; + + /** + * @method AngularCache.destroy + * @desc Completely destroy the cache. + */ + this.destroy = function () { + if (config.cacheFlushIntervalId) { + clearInterval(config.cacheFlushIntervalId); + } + if (config.recycleFreqId) { + clearInterval(config.recycleFreqId); + } + this.removeAll(); + if (config.storageMode !== 'none' && storage) { + storage.removeItem(prefix + '.keys'); + storage.removeItem(prefix); + } + storage = null; + promiseStorage = null; + data = null; + lruHeap = null; + expiresHeap = null; + config = null; + prefix = null; + self = null; + var methodKeys = _keys(this); + + // Prevent this cache from being used after it has been destroyed + for (var i = 0; i < methodKeys.length; i++) { + if (this.hasOwnProperty(methodKeys[i])) { + delete this[methodKeys[i]]; + } + } + + caches[cacheId] = null; + delete caches[cacheId]; + }; + + /** + * @method AngularCache.info + * @desc Return an object containing information about the cache. + * @param {string} [key] The key of the item about which to retrieve information. + * @returns {object} stats Object containing information about this cache or the item with the + * specified key. + */ + this.info = function (key) { + if (key) { + if (data[key]) { + var info = { + created: data[key].created, + accessed: data[key].accessed, + expires: data[key].expires, + maxAge: data[key].maxAge || config.maxAge, + deleteOnExpire: data[key].deleteOnExpire || config.deleteOnExpire, + storePromises: data[key].storePromises || config.storePromises, + isExpired: false + }; + if (info.maxAge) { + info.isExpired = (new Date().getTime() - info.created) > info.maxAge; + } + return info; + } else { + return data[key]; + } + } else { + return angular.extend({}, config, { size: lruHeap && lruHeap.size() || 0 }); + } + }; + + /** + * @method AngularCache.keySet + * @desc Return the set of the keys of all items in the cache. + * @returns {object} The set of the keys of all items currently in this cache. + */ + this.keySet = function () { + return _keySet(data); + }; + + /** + * @method AngularCache.keys + * @desc Return an array of the keys of all items in the cache. + * @returns {array} An array of the keys of all items in the cache. + */ + this.keys = function () { + return _keys(data); + }; + + /** + * @method AngularCache.setOptions + * @desc Configure this cache with the given options. + * @param {object} cacheOptions + * @param {boolean} [strict] If true then any existing configuration will be reset to defaults before + * applying the new options, otherwise only the options specified in the hash will be altered. + * @param {object} [options] Configuration. + */ + this.setOptions = _setOptions; + + // Initialize this cache with the default and given options + _setOptions(options, true, { verifyIntegrity: false }); + } + + /** + * @class AngularCacheFactory + * @param {string} cacheId The id of the new cache. + * @param {object} [options] See [Configuration Options]{@link https://github.com/jmdobry/angular-cache#configuration}. + * @returns {AngularCache} + */ + function angularCacheFactory(cacheId, options) { + if (cacheId in caches) { + throw new Error('cacheId ' + cacheId + ' taken!'); + } else if (!angular.isString(cacheId)) { + throw new Error('cacheId must be a string!'); + } + + caches[cacheId] = new AngularCache(cacheId, options); + return caches[cacheId]; + } + + /** + * @method AngularCacheFactory.info + * @desc Return an object containing information about all caches in $angularCacheFactory. + * @returns {object} An object containing information about all caches of this factory. + */ + angularCacheFactory.info = function () { + var keys = _keys(caches); + var info = { + size: keys.length, + caches: {} + }; + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + info.caches[key] = caches[key].info(); + } + info.cacheDefaults = angular.extend({}, cacheDefaults); + return info; + }; + + /** + * @method AngularCacheFactory.get + * @desc Return the cache with the specified cacheId. + * @param {string} cacheId The id of the desired cache. + * @returns {AngularCache} The cache with the specified cachedId. + */ + angularCacheFactory.get = function (cacheId) { + if (!angular.isString(cacheId)) { + throw new Error('$angularCacheFactory.get(cacheId): cacheId: must be a string!'); + } + return caches[cacheId]; + }; + + /** + * @method AngularCacheFactory.keySet + * @desc Return the set of keys of all current caches owned by angularCacheFactory. + * @returns {object} The set of keys associated with all current caches owned by this + * angularCacheFactory. + */ + angularCacheFactory.keySet = function () { + return _keySet(caches); + }; + + /** + * @method AngularCacheFactory.keys + * @desc Return an array of the keys of all caches owned by angularCacheFactory. + * @returns {array} An array of the keys associated with all current caches owned by + * this angularCacheFactory. + */ + angularCacheFactory.keys = function () { + return _keys(caches); + }; + + /** + * @method AngularCacheFactory.removeAll + * @desc Destroy all caches in $angularCacheFactory. + */ + angularCacheFactory.removeAll = function () { + var keys = _keys(caches); + for (var i = 0; i < keys.length; i++) { + caches[keys[i]].destroy(); + } + }; + + /** + * @method AngularCacheFactory.clearAll + * @desc Clears the contents of every cache in $angularCacheFactory. + */ + angularCacheFactory.clearAll = function () { + var keys = _keys(caches); + for (var i = 0; i < keys.length; i++) { + caches[keys[i]].removeAll(); + } + }; + + /** + * @method AngularCacheFactory.enableAll + * @desc Enable any disabled caches. + */ + angularCacheFactory.enableAll = function () { + var keys = _keys(caches); + for (var i = 0; i < keys.length; i++) { + caches[keys[i]].setOptions({ disabled: false }); + } + }; + + /** + * @method AngularCacheFactory.disableAll + * @desc Disable all caches. + */ + angularCacheFactory.disableAll = function () { + var keys = _keys(caches); + for (var i = 0; i < keys.length; i++) { + caches[keys[i]].setOptions({ disabled: true }); + } + }; + + return angularCacheFactory; + }]; + } + + /** + * @desc Provides an $AngularCacheFactoryProvider, which gives you the ability to use an + * $angularCacheFactory. The $angularCacheFactory produces AngularCache objects, which + * the same abilities as the cache objects that come with Angular, except with some added + * functionality. + */ + angular.module('jmdobry.angular-cache', ['ng', 'jmdobry.binary-heap']). + provider('$angularCacheFactory', $AngularCacheFactoryProvider); + })(window, window.angular); diff --git a/test/angularCache.destroy-test.js b/test/angularCache.destroy-test.js index fa65b6d..e97f588 100644 --- a/test/angularCache.destroy-test.js +++ b/test/angularCache.destroy-test.js @@ -1,47 +1,47 @@ describe('AngularCache.destroy()', function () { - it('should destroy the cache and remove all traces of its existence.', function () { - var cache = $angularCacheFactory('cache'); - cache.destroy(); - try { - expect(cache.info()).toEqual({ size: 0 }); - fail('should not be able to use a cache after destroying it'); - } catch (err) { + it('should destroy the cache and remove all traces of its existence.', function () { + var cache = $angularCacheFactory('cache'); + cache.destroy(); + try { + expect(cache.info()).toEqual({ size: 0 }); + fail('should not be able to use a cache after destroying it'); + } catch (err) { - } - expect($angularCacheFactory.get('cache')).toEqual(undefined); - }); - it('should remove items from localStorage when storageMode is used.', function () { - var localStorageCache = $angularCacheFactory('localStorageCache', { storageMode: 'localStorage' }), - sessionStorageCache = $angularCacheFactory('sessionStorageCache', { storageMode: 'sessionStorage' }); + } + expect($angularCacheFactory.get('cache')).toEqual(undefined); + }); + it('should remove items from localStorage when storageMode is used.', function () { + var localStorageCache = $angularCacheFactory('localStorageCache', { storageMode: 'localStorage' }), + sessionStorageCache = $angularCacheFactory('sessionStorageCache', { storageMode: 'sessionStorage' }); - localStorageCache.put('item1', 'value1'); - sessionStorageCache.put('item1', 'value1'); - localStorageCache.put('item2', 'value2'); - sessionStorageCache.put('item2', 'value2'); + localStorageCache.put('item1', 'value1'); + sessionStorageCache.put('item1', 'value1'); + localStorageCache.put('item2', 'value2'); + sessionStorageCache.put('item2', 'value2'); - if (localStorage) { - expect(angular.fromJson(localStorage.getItem('angular-cache.caches.localStorageCache.data.item1')).value).toEqual('value1'); - expect(angular.fromJson(localStorage.getItem('angular-cache.caches.localStorageCache.data.item2')).value).toEqual('value2'); - expect(localStorage.getItem('angular-cache.caches.localStorageCache.keys')).toEqual('["item1","item2"]'); - } - if (sessionStorage) { - expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.data.item1')).value).toEqual('value1'); - expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.data.item2')).value).toEqual('value2'); - expect(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.keys')).toEqual('["item1","item2"]'); - } + if (localStorage) { + expect(angular.fromJson(localStorage.getItem('angular-cache.caches.localStorageCache.data.item1')).value).toEqual('value1'); + expect(angular.fromJson(localStorage.getItem('angular-cache.caches.localStorageCache.data.item2')).value).toEqual('value2'); + expect(localStorage.getItem('angular-cache.caches.localStorageCache.keys')).toEqual('["item1","item2"]'); + } + if (sessionStorage) { + expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.data.item1')).value).toEqual('value1'); + expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.data.item2')).value).toEqual('value2'); + expect(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.keys')).toEqual('["item1","item2"]'); + } - localStorageCache.destroy(); - sessionStorageCache.destroy(); + localStorageCache.destroy(); + sessionStorageCache.destroy(); - if (localStorage) { - expect(angular.fromJson(localStorage.getItem('angular-cache.caches.localStorageCache.data.item1'))).toEqual(null); - expect(angular.fromJson(localStorage.getItem('angular-cache.caches.localStorageCache.data.item2'))).toEqual(null); - expect(localStorage.getItem('angular-cache.caches.localStorageCache.keys')).toEqual(null); - } - if (sessionStorage) { - expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.data.item1'))).toEqual(null); - expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.data.item2'))).toEqual(null); - expect(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.keys')).toEqual(null); - } - }); -}); \ No newline at end of file + if (localStorage) { + expect(angular.fromJson(localStorage.getItem('angular-cache.caches.localStorageCache.data.item1'))).toEqual(null); + expect(angular.fromJson(localStorage.getItem('angular-cache.caches.localStorageCache.data.item2'))).toEqual(null); + expect(localStorage.getItem('angular-cache.caches.localStorageCache.keys')).toEqual(null); + } + if (sessionStorage) { + expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.data.item1'))).toEqual(null); + expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.data.item2'))).toEqual(null); + expect(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.keys')).toEqual(null); + } + }); +}); diff --git a/test/angularCache.get-test.js b/test/angularCache.get-test.js index 7b1ba92..3a69975 100644 --- a/test/angularCache.get-test.js +++ b/test/angularCache.get-test.js @@ -1,147 +1,161 @@ describe('AngularCache.get(key)', function () { - it('should do nothing if the cache is disabled.', function () { - var cache = $angularCacheFactory('cache'); + it('should do nothing if the cache is disabled.', function () { + var cache = $angularCacheFactory('cache'); - expect(cache.info().size).toEqual(0); - cache.put('1', 'item'); - expect(cache.get('1')).toEqual('item'); - expect(cache.info().size).toEqual(1); - cache.setOptions({ disabled: true }); - expect(cache.info().size).toEqual(1); - expect(cache.get('1')).toBeUndefined(); - }); - it('should throw an error if "key" is not a string or array.', function () { - var cache = $angularCacheFactory('cache'); - for (var i = 0; i < TYPES_EXCEPT_STRING_OR_ARRAY_OR_NUMBER.length; i++) { - try { - cache.get(TYPES_EXCEPT_STRING_OR_ARRAY_OR_NUMBER[i]); - fail(TYPES_EXCEPT_STRING_OR_ARRAY_OR_NUMBER[i]); - } catch (err) { - expect(err.message).toEqual('AngularCache.get(key, options): key: must be a string!'); - continue; - } - fail(TYPES_EXCEPT_STRING_OR_ARRAY_OR_NUMBER[i]); - } - }); - it('should throw an error if "options" is not an object.', function () { - var cache = $angularCacheFactory('cache'); - for (var i = 0; i < TYPES_EXCEPT_OBJECT.length; i++) { - try { - cache.get('item', TYPES_EXCEPT_OBJECT[i]); - if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { - fail(TYPES_EXCEPT_OBJECT[i]); - } - } catch (err) { - expect(err.message).toEqual('AngularCache.get(key, options): options: must be an object!'); - continue; - } - if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { - fail(TYPES_EXCEPT_OBJECT[i]); - } - } - }); - it('should throw an error if "onExpire" is not a function.', function () { - var cache = $angularCacheFactory('cache'); - for (var i = 0; i < TYPES_EXCEPT_FUNCTION.length; i++) { - try { - cache.get('item', { onExpire: TYPES_EXCEPT_FUNCTION[i] }); - if (TYPES_EXCEPT_FUNCTION[i] !== null && TYPES_EXCEPT_FUNCTION[i] !== undefined && TYPES_EXCEPT_FUNCTION[i] !== false) { - fail(TYPES_EXCEPT_FUNCTION[i]); - } - } catch (err) { - expect(err.message).toEqual('AngularCache.get(key, options): onExpire: must be a function!'); - continue; - } - if (TYPES_EXCEPT_FUNCTION[i] !== null && TYPES_EXCEPT_FUNCTION[i] !== undefined && TYPES_EXCEPT_FUNCTION[i] !== false) { - fail(TYPES_EXCEPT_FUNCTION[i]); - } - } - }); - it('should return the correct value for the specified key.', function () { - var cache = $angularCacheFactory('cache'); - var value1 = 'value1', - value2 = 2, - value3 = { - value3: 'stuff' - }; - cache.put('item1', value1); - cache.put('item2', value2); - cache.put('item3', value3); - expect(cache.get('item1')).toEqual(value1); - expect(cache.get('item2')).toEqual(value2); - expect(cache.get('item3')).toEqual(value3); - }); - it('should return undefined if the key isn\'t in the cache.', function () { - var cache = $angularCacheFactory('cache'); - expect(cache.get('item')).toEqual(undefined); - }); - it('should execute globally configured "onExpire" callback if the item is expired in passive mode and global "onExpire" callback is configured.', function () { - var cache = $angularCacheFactory('cache', { - maxAge: 10, - recycleFreq: 20, - deleteOnExpire: 'passive', - onExpire: function (key, value, done) { - done(key, value, 'executed global callback'); - } - }); - cache.put('item', 'value'); - waits(100); - runs(function () { - cache.get('item', { onExpire: function (key, value, test) { - expect(key).toEqual('item'); - expect(value).toEqual('value'); - expect(test).toEqual('executed global callback'); - }}); - }); - }); - it('should execute globally configured "onExpire" callback when an item is aggressively deleted and global "onExpire" callback is configured.', function () { - var onExpire = jasmine.createSpy(); - var cache = $angularCacheFactory('cache', { - maxAge: 10, - recycleFreq: 20, - deleteOnExpire: 'aggressive', - onExpire: onExpire - }); - cache.put('item', 'value'); - waits(100); - runs(function () { - expect(onExpire).toHaveBeenCalled(); - expect(onExpire).toHaveBeenCalledWith('item', 'value'); - }); - }); - it('should execute local "onExpire" callback if the item is expired in passive mode and global "onExpire" callback is NOT configured.', function () { - var cache = $angularCacheFactory('cache', { - maxAge: 10, - deleteOnExpire: 'passive', - recycleFreq: 20 - }); - cache.put('item', 'value'); - waits(100); - runs(function () { - cache.get('item', { onExpire: function (key, value) { - expect(key).toEqual('item'); - expect(value).toEqual('value'); - }}); - }); - }); - it('should return the correct values for multiple keys.', function () { - var cache = $angularCacheFactory('cache'); - var value1 = 'value1', - value2 = 2, - value3 = { - value3: 'stuff' - }; - cache.put('item1', value1); - cache.put('item2', value2); - cache.put('item3', value3); - expect(cache.get(['item1', 'item2', 'item3'])).toEqual([value1, value2, value3]); - }); - it('should not return undefined values for multiple keys.', function () { - var cache = $angularCacheFactory('cache'); - var value1 = 'value1', - value2 = 2; - cache.put('item1', value1); - cache.put('item2', value2); - expect(cache.get(['item1', 'item2', 'item3'])).toEqual([value1, value2]); - }); + expect(cache.info().size).toEqual(0); + cache.put('1', 'item'); + expect(cache.get('1')).toEqual('item'); + expect(cache.info().size).toEqual(1); + cache.setOptions({ disabled: true }); + expect(cache.info().size).toEqual(1); + expect(cache.get('1')).toBeUndefined(); + }); + it('should throw an error if "key" is not a string or array.', function () { + var cache = $angularCacheFactory('cache'); + for (var i = 0; i < TYPES_EXCEPT_STRING_OR_ARRAY_OR_NUMBER.length; i++) { + try { + cache.get(TYPES_EXCEPT_STRING_OR_ARRAY_OR_NUMBER[i]); + fail(TYPES_EXCEPT_STRING_OR_ARRAY_OR_NUMBER[i]); + } catch (err) { + expect(err.message).toEqual('AngularCache.get(key, options): key: must be a string!'); + continue; + } + fail(TYPES_EXCEPT_STRING_OR_ARRAY_OR_NUMBER[i]); + } + }); + it('should throw an error if "options" is not an object.', function () { + var cache = $angularCacheFactory('cache'); + for (var i = 0; i < TYPES_EXCEPT_OBJECT.length; i++) { + try { + cache.get('item', TYPES_EXCEPT_OBJECT[i]); + if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { + fail(TYPES_EXCEPT_OBJECT[i]); + } + } catch (err) { + expect(err.message).toEqual('AngularCache.get(key, options): options: must be an object!'); + continue; + } + if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { + fail(TYPES_EXCEPT_OBJECT[i]); + } + } + }); + it('should throw an error if "onExpire" is not a function.', function () { + var cache = $angularCacheFactory('cache'); + for (var i = 0; i < TYPES_EXCEPT_FUNCTION.length; i++) { + try { + cache.get('item', { onExpire: TYPES_EXCEPT_FUNCTION[i] }); + if (TYPES_EXCEPT_FUNCTION[i] !== null && TYPES_EXCEPT_FUNCTION[i] !== undefined && TYPES_EXCEPT_FUNCTION[i] !== false) { + fail(TYPES_EXCEPT_FUNCTION[i]); + } + } catch (err) { + expect(err.message).toEqual('AngularCache.get(key, options): onExpire: must be a function!'); + continue; + } + if (TYPES_EXCEPT_FUNCTION[i] !== null && TYPES_EXCEPT_FUNCTION[i] !== undefined && TYPES_EXCEPT_FUNCTION[i] !== false) { + fail(TYPES_EXCEPT_FUNCTION[i]); + } + } + }); + it('should return the correct value for the specified key.', function () { + var cache = $angularCacheFactory('cache'); + var value1 = 'value1', + value2 = 2, + value3 = { + value3: 'stuff' + }; + cache.put('item1', value1); + cache.put('item2', value2); + cache.put('item3', value3); + expect(cache.get('item1')).toEqual(value1); + expect(cache.get('item2')).toEqual(value2); + expect(cache.get('item3')).toEqual(value3); + }); + it('should return undefined if the key isn\'t in the cache.', function () { + var cache = $angularCacheFactory('cache'); + expect(cache.get('item')).toEqual(undefined); + }); + it('should execute globally configured "onExpire" callback if the item is expired in passive mode and global "onExpire" callback is configured.', function () { + var cache = $angularCacheFactory('cache', { + maxAge: 10, + recycleFreq: 20, + deleteOnExpire: 'passive', + onExpire: function (key, value, done) { + done(key, value, 'executed global callback'); + } + }); + cache.put('item', 'value'); + waits(100); + runs(function () { + cache.get('item', { onExpire: function (key, value, test) { + expect(key).toEqual('item'); + expect(value).toEqual('value'); + expect(test).toEqual('executed global callback'); + }}); + }); + }); + it('should execute globally configured "onExpire" callback when an item is aggressively deleted and global "onExpire" callback is configured.', function () { + var onExpire = jasmine.createSpy(); + var cache = $angularCacheFactory('cache', { + maxAge: 10, + recycleFreq: 20, + deleteOnExpire: 'aggressive', + onExpire: onExpire + }); + cache.put('item', 'value'); + waits(100); + runs(function () { + expect(onExpire).toHaveBeenCalled(); + expect(onExpire).toHaveBeenCalledWith('item', 'value'); + }); + }); + it('should execute local "onExpire" callback if the item is expired in passive mode and global "onExpire" callback is NOT configured.', function () { + var cache = $angularCacheFactory('cache', { + maxAge: 10, + deleteOnExpire: 'passive', + recycleFreq: 20 + }); + cache.put('item', 'value'); + waits(100); + runs(function () { + cache.get('item', { onExpire: function (key, value) { + expect(key).toEqual('item'); + expect(value).toEqual('value'); + }}); + }); + }); + it('should return the correct values for multiple keys.', function () { + var cache = $angularCacheFactory('cache'); + var value1 = 'value1', + value2 = 2, + value3 = { + value3: 'stuff' + }; + cache.put('item1', value1); + cache.put('item2', value2); + cache.put('item3', value3); + expect(cache.get(['item1', 'item2', 'item3'])).toEqual([value1, value2, value3]); + }); + it('should not return undefined values for multiple keys.', function () { + var cache = $angularCacheFactory('cache'); + var value1 = 'value1', + value2 = 2; + cache.put('item1', value1); + cache.put('item2', value2); + expect(cache.get(['item1', 'item2', 'item3'])).toEqual([value1, value2]); + }); + it('should return a promise if storePromises is true.', function () { + var cache = $angularCacheFactory('cache', {storePromises : true}); + var deferred = $q.defer(); + var promise = deferred.promise; + cache.put('mypromise', promise); + expect(cache.get('mypromise')).toBe(promise); + }); + it('should not return a promise if storePromises is false.', function () { + var cache = $angularCacheFactory('cache', {storePromises : false}); + var deferred = $q.defer(); + var promise = deferred.promise; + cache.put('mypromise', promise); + expect(cache.get('mypromise')).toBeUndefined(); + }); }); diff --git a/test/angularCache.info-test.js b/test/angularCache.info-test.js index bb4a38f..d12356f 100644 --- a/test/angularCache.info-test.js +++ b/test/angularCache.info-test.js @@ -1,81 +1,81 @@ describe('AngularCache.info()', function () { - it('should return the correct values.', function () { - var onExpire = function () { - }; - var cache = $angularCacheFactory('cache'), - cache2 = $angularCacheFactory('cache2', { maxAge: 1000 }), - cache3 = $angularCacheFactory('cache3', { cacheFlushInterval: 1000 }), - cache4 = $angularCacheFactory('cache4', { capacity: 1000 }), - cache5 = $angularCacheFactory('cache5', { storageMode: 'localStorage' }), - cache6 = $angularCacheFactory('cache6', { storageMode: 'sessionStorage' }); - cache7 = $angularCacheFactory('cache7', { maxAge: 100, onExpire: onExpire }); - var cacheInfo = cache.info(); - expect(cacheInfo.id).toEqual('cache'); - expect(cacheInfo.capacity).toEqual(Number.MAX_VALUE); - expect(cacheInfo.size).toEqual(0); - expect(cacheInfo.recycleFreq).toEqual(1000); - expect(cacheInfo.maxAge).toEqual(null); - expect(cacheInfo.cacheFlushInterval).toEqual(null); - expect(typeof cacheInfo.recycleFreqId).toEqual('number'); - expect(cacheInfo.deleteOnExpire).toEqual('none'); - expect(cacheInfo.storageMode).toEqual('none'); - expect(cacheInfo.onExpire).toEqual(null); - cache.put('item', 'value'); - cache.put('item2', 'value2', { maxAge: 200, deleteOnExpire: 'aggressive' }); + it('should return the correct values.', function () { + var onExpire = function () { + }; + var cache = $angularCacheFactory('cache'), + cache2 = $angularCacheFactory('cache2', { maxAge: 1000 }), + cache3 = $angularCacheFactory('cache3', { cacheFlushInterval: 1000 }), + cache4 = $angularCacheFactory('cache4', { capacity: 1000 }), + cache5 = $angularCacheFactory('cache5', { storageMode: 'localStorage' }), + cache6 = $angularCacheFactory('cache6', { storageMode: 'sessionStorage' }); + cache7 = $angularCacheFactory('cache7', { maxAge: 100, onExpire: onExpire }); + var cacheInfo = cache.info(); + expect(cacheInfo.id).toEqual('cache'); + expect(cacheInfo.capacity).toEqual(Number.MAX_VALUE); + expect(cacheInfo.size).toEqual(0); + expect(cacheInfo.recycleFreq).toEqual(1000); + expect(cacheInfo.maxAge).toEqual(null); + expect(cacheInfo.cacheFlushInterval).toEqual(null); + expect(typeof cacheInfo.recycleFreqId).toEqual('number'); + expect(cacheInfo.deleteOnExpire).toEqual('none'); + expect(cacheInfo.storageMode).toEqual('none'); + expect(cacheInfo.onExpire).toEqual(null); + cache.put('item', 'value'); + cache.put('item2', 'value2', { maxAge: 200, deleteOnExpire: 'aggressive' }); - // AngularCache#info(key) - expect(cache.info('non-existent item')).not.toBeDefined(); - expect(typeof cache.info('item').created).toEqual('number'); - expect(typeof cache.info('item').expires).toEqual('undefined'); - expect(typeof cache.info('item').accessed).toEqual('number'); - expect(cache.info('item').maxAge).toEqual(null); - expect(cache.info('item').deleteOnExpire).toEqual('none'); - expect(typeof cache.info('item2').created).toEqual('number'); - expect(typeof cache.info('item2').expires).toEqual('number'); - expect(typeof cache.info('item2').accessed).toEqual('number'); - expect(cache.info('item2').maxAge).toEqual(200); - expect(cache.info('item2').deleteOnExpire).toEqual('aggressive'); + // AngularCache#info(key) + expect(cache.info('non-existent item')).not.toBeDefined(); + expect(typeof cache.info('item').created).toEqual('number'); + expect(typeof cache.info('item').expires).toEqual('undefined'); + expect(typeof cache.info('item').accessed).toEqual('number'); + expect(cache.info('item').maxAge).toEqual(null); + expect(cache.info('item').deleteOnExpire).toEqual('none'); + expect(typeof cache.info('item2').created).toEqual('number'); + expect(typeof cache.info('item2').expires).toEqual('number'); + expect(typeof cache.info('item2').accessed).toEqual('number'); + expect(cache.info('item2').maxAge).toEqual(200); + expect(cache.info('item2').deleteOnExpire).toEqual('aggressive'); - expect(cache.info().size).toEqual(2); + expect(cache.info().size).toEqual(2); - var cacheInfo2 = cache2.info(); - expect(cacheInfo2.id).toEqual('cache2'); - expect(cacheInfo2.capacity).toEqual(Number.MAX_VALUE); - expect(cacheInfo2.size).toEqual(0); - expect(cacheInfo2.recycleFreq).toEqual(1000); - expect(cacheInfo2.maxAge).toEqual(1000); - expect(cacheInfo2.cacheFlushInterval).toEqual(null); - expect(typeof cacheInfo2.recycleFreqId).toEqual('number'); - expect(cacheInfo2.deleteOnExpire).toEqual('none'); - expect(cacheInfo2.storageMode).toEqual('none'); - expect(cacheInfo2.onExpire).toEqual(null); + var cacheInfo2 = cache2.info(); + expect(cacheInfo2.id).toEqual('cache2'); + expect(cacheInfo2.capacity).toEqual(Number.MAX_VALUE); + expect(cacheInfo2.size).toEqual(0); + expect(cacheInfo2.recycleFreq).toEqual(1000); + expect(cacheInfo2.maxAge).toEqual(1000); + expect(cacheInfo2.cacheFlushInterval).toEqual(null); + expect(typeof cacheInfo2.recycleFreqId).toEqual('number'); + expect(cacheInfo2.deleteOnExpire).toEqual('none'); + expect(cacheInfo2.storageMode).toEqual('none'); + expect(cacheInfo2.onExpire).toEqual(null); - expect(cache3.info().id).toEqual('cache3'); - expect(cache3.info().capacity).toEqual(Number.MAX_VALUE); - expect(cache3.info().cacheFlushInterval).toEqual(1000); - expect(cache3.info().size).toEqual(0); + expect(cache3.info().id).toEqual('cache3'); + expect(cache3.info().capacity).toEqual(Number.MAX_VALUE); + expect(cache3.info().cacheFlushInterval).toEqual(1000); + expect(cache3.info().size).toEqual(0); - var cacheInfo4 = cache4.info(); - expect(cacheInfo4.id).toEqual('cache4'); - expect(cacheInfo4.capacity).toEqual(1000); - expect(cacheInfo4.size).toEqual(0); - expect(cacheInfo4.recycleFreq).toEqual(1000); - expect(cacheInfo4.maxAge).toEqual(null); - expect(cacheInfo4.cacheFlushInterval).toEqual(null); - expect(typeof cacheInfo4.recycleFreqId).toEqual('number'); - expect(cacheInfo4.deleteOnExpire).toEqual('none'); - expect(cacheInfo4.storageMode).toEqual('none'); - expect(cacheInfo4.onExpire).toEqual(null); - if (localStorage) { - expect(cache5.info().storageMode).toEqual('localStorage'); - } else { - expect(cache5.info().storageMode).toEqual(null); - } - if (sessionStorage) { - expect(cache6.info().storageMode).toEqual('sessionStorage'); - } else { - expect(cache6.info().storageMode).toEqual(null); - } - expect(cache7.info().onExpire).toEqual(onExpire); - }); -}); \ No newline at end of file + var cacheInfo4 = cache4.info(); + expect(cacheInfo4.id).toEqual('cache4'); + expect(cacheInfo4.capacity).toEqual(1000); + expect(cacheInfo4.size).toEqual(0); + expect(cacheInfo4.recycleFreq).toEqual(1000); + expect(cacheInfo4.maxAge).toEqual(null); + expect(cacheInfo4.cacheFlushInterval).toEqual(null); + expect(typeof cacheInfo4.recycleFreqId).toEqual('number'); + expect(cacheInfo4.deleteOnExpire).toEqual('none'); + expect(cacheInfo4.storageMode).toEqual('none'); + expect(cacheInfo4.onExpire).toEqual(null); + if (localStorage) { + expect(cache5.info().storageMode).toEqual('localStorage'); + } else { + expect(cache5.info().storageMode).toEqual(null); + } + if (sessionStorage) { + expect(cache6.info().storageMode).toEqual('sessionStorage'); + } else { + expect(cache6.info().storageMode).toEqual(null); + } + expect(cache7.info().onExpire).toEqual(onExpire); + }); +}); diff --git a/test/angularCache.keySet-test.js b/test/angularCache.keySet-test.js index b9f90b8..6cdb43f 100644 --- a/test/angularCache.keySet-test.js +++ b/test/angularCache.keySet-test.js @@ -1,31 +1,31 @@ describe('AngularCache.keySet()', function () { - it('should return the set of keys of all items in the cache.', function () { - var itemKeys = ['item1', 'item2', 'item3']; + it('should return the set of keys of all items in the cache.', function () { + var itemKeys = ['item1', 'item2', 'item3']; - var cache = $angularCacheFactory('cache'); + var cache = $angularCacheFactory('cache'); - cache.put(itemKeys[0], itemKeys[0]); - cache.put(itemKeys[1], itemKeys[1]); - cache.put(itemKeys[2], itemKeys[2]); + cache.put(itemKeys[0], itemKeys[0]); + cache.put(itemKeys[1], itemKeys[1]); + cache.put(itemKeys[2], itemKeys[2]); - var keySet = cache.keySet(); + var keySet = cache.keySet(); - expect(keySet.hasOwnProperty(itemKeys[0])).toEqual(true); - expect(keySet.hasOwnProperty(itemKeys[1])).toEqual(true); - expect(keySet.hasOwnProperty(itemKeys[2])).toEqual(true); + expect(keySet.hasOwnProperty(itemKeys[0])).toEqual(true); + expect(keySet.hasOwnProperty(itemKeys[1])).toEqual(true); + expect(keySet.hasOwnProperty(itemKeys[2])).toEqual(true); - expect(keySet[itemKeys[0]]).toEqual(itemKeys[0]); - expect(keySet[itemKeys[1]]).toEqual(itemKeys[1]); - expect(keySet[itemKeys[2]]).toEqual(itemKeys[2]); + expect(keySet[itemKeys[0]]).toEqual(itemKeys[0]); + expect(keySet[itemKeys[1]]).toEqual(itemKeys[1]); + expect(keySet[itemKeys[2]]).toEqual(itemKeys[2]); - cache.remove(itemKeys[0]); - cache.remove(itemKeys[1]); - cache.remove(itemKeys[2]); + cache.remove(itemKeys[0]); + cache.remove(itemKeys[1]); + cache.remove(itemKeys[2]); - keySet = cache.keySet(); + keySet = cache.keySet(); - expect(keySet.hasOwnProperty(itemKeys[0])).toEqual(false); - expect(keySet.hasOwnProperty(itemKeys[1])).toEqual(false); - expect(keySet.hasOwnProperty(itemKeys[2])).toEqual(false); - }); -}); \ No newline at end of file + expect(keySet.hasOwnProperty(itemKeys[0])).toEqual(false); + expect(keySet.hasOwnProperty(itemKeys[1])).toEqual(false); + expect(keySet.hasOwnProperty(itemKeys[2])).toEqual(false); + }); +}); diff --git a/test/angularCache.keys-test.js b/test/angularCache.keys-test.js index a03403b..63e80d2 100644 --- a/test/angularCache.keys-test.js +++ b/test/angularCache.keys-test.js @@ -1,25 +1,25 @@ describe('AngularCache.keys()', function () { - it('should return the array of keys of all items in the cache.', function () { - var itemKeys = ['item1', 'item2', 'item3']; + it('should return the array of keys of all items in the cache.', function () { + var itemKeys = ['item1', 'item2', 'item3']; - var cache = $angularCacheFactory('cache'); + var cache = $angularCacheFactory('cache'); - cache.put(itemKeys[0], itemKeys[0]); - cache.put(itemKeys[1], itemKeys[1]); - cache.put(itemKeys[2], itemKeys[2]); + cache.put(itemKeys[0], itemKeys[0]); + cache.put(itemKeys[1], itemKeys[1]); + cache.put(itemKeys[2], itemKeys[2]); - var keys = cache.keys(); + var keys = cache.keys(); - expect(keys[0]).toEqual(itemKeys[0]); - expect(keys[1]).toEqual(itemKeys[1]); - expect(keys[2]).toEqual(itemKeys[2]); + expect(keys[0]).toEqual(itemKeys[0]); + expect(keys[1]).toEqual(itemKeys[1]); + expect(keys[2]).toEqual(itemKeys[2]); - cache.remove(itemKeys[0]); - cache.remove(itemKeys[1]); - cache.remove(itemKeys[2]); + cache.remove(itemKeys[0]); + cache.remove(itemKeys[1]); + cache.remove(itemKeys[2]); - keys = cache.keys(); + keys = cache.keys(); - expect(keys.length).toEqual(0); - }); -}); \ No newline at end of file + expect(keys.length).toEqual(0); + }); +}); diff --git a/test/angularCache.put-test.js b/test/angularCache.put-test.js index d1b9f85..356e7ac 100644 --- a/test/angularCache.put-test.js +++ b/test/angularCache.put-test.js @@ -1,228 +1,277 @@ describe('AngularCache.put(key, value, options)', function () { - it('should do nothing if the cache is disabled.', function () { - var cache = $angularCacheFactory('cache', { disabled: true }); + it('should do nothing if the cache is disabled.', function () { + var cache = $angularCacheFactory('cache', { disabled: true }); - expect(cache.info().size).toEqual(0); - expect(cache.put('1', 'item')).toBeUndefined(); - expect(cache.info().size).toEqual(0); - }); - it('should throw an error if "key" is not a string.', function () { - var cache = $angularCacheFactory('cache'); - for (var i = 0; i < TYPES_EXCEPT_STRING_OR_NUMBER.length; i++) { - try { - cache.put(TYPES_EXCEPT_STRING_OR_NUMBER[i], 'value'); - fail(TYPES_EXCEPT_STRING_OR_NUMBER[i]); - } catch (err) { - expect(err.message).toEqual('AngularCache.put(key, value, options): key: must be a string!'); - continue; - } - fail(TYPES_EXCEPT_STRING_OR_NUMBER[i]); + expect(cache.info().size).toEqual(0); + expect(cache.put('1', 'item')).toBeUndefined(); + expect(cache.info().size).toEqual(0); + }); + it('should throw an error if "key" is not a string.', function () { + var cache = $angularCacheFactory('cache'); + for (var i = 0; i < TYPES_EXCEPT_STRING_OR_NUMBER.length; i++) { + try { + cache.put(TYPES_EXCEPT_STRING_OR_NUMBER[i], 'value'); + fail(TYPES_EXCEPT_STRING_OR_NUMBER[i]); + } catch (err) { + expect(err.message).toEqual('AngularCache.put(key, value, options): key: must be a string!'); + continue; + } + fail(TYPES_EXCEPT_STRING_OR_NUMBER[i]); + } + }); + it('should throw an error if "options" is not an object.', function () { + var cache = $angularCacheFactory('cache'); + for (var i = 0; i < TYPES_EXCEPT_OBJECT.length; i++) { + try { + cache.put('item', 'value', TYPES_EXCEPT_OBJECT[i]); + if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { + fail(TYPES_EXCEPT_OBJECT[i]); } - }); - it('should throw an error if "options" is not an object.', function () { - var cache = $angularCacheFactory('cache'); - for (var i = 0; i < TYPES_EXCEPT_OBJECT.length; i++) { - try { - cache.put('item', 'value', TYPES_EXCEPT_OBJECT[i]); - if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { - fail(TYPES_EXCEPT_OBJECT[i]); - } - } catch (err) { - expect(err.message).toEqual('AngularCache.put(key, value, options): options: must be an object!'); - continue; - } - if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { - fail(TYPES_EXCEPT_OBJECT[i]); - } - } - }); - it('should throw an error if "maxAge" is not valid.', function () { - var cache = $angularCacheFactory('cache'); - var maxAge = Math.floor((Math.random() * 100000) + 1) * -1; - try { - cache.put('item', 'value', { maxAge: maxAge }); - fail(); - } catch (err) { - var msg = err.message; - } - expect(msg).toEqual('AngularCache.put(key, value, options): maxAge: must be greater than zero!'); - for (var i = 0; i < TYPES_EXCEPT_NUMBER.length; i++) { - try { - cache.put('item', 'value', { maxAge: TYPES_EXCEPT_NUMBER[i] }); - if (TYPES_EXCEPT_NUMBER[i] !== null && TYPES_EXCEPT_NUMBER[i] !== undefined && TYPES_EXCEPT_NUMBER[i] !== false) { - fail(TYPES_EXCEPT_NUMBER[i]); - } - } catch (err) { - expect(err.message).toEqual('AngularCache.put(key, value, options): maxAge: must be a number!'); - continue; - } - if (TYPES_EXCEPT_NUMBER[i] !== null && TYPES_EXCEPT_NUMBER[i] !== undefined && TYPES_EXCEPT_NUMBER[i] !== false) { - fail(TYPES_EXCEPT_NUMBER[i]); - } - } - }); - it('should throw an error if "deleteOnExpire" is not valid.', function () { - var cache = $angularCacheFactory('cache'); - var deleteOnExpire = 'fail'; - try { - cache.put('item', 'value', { deleteOnExpire: deleteOnExpire }); - fail('should not reach this!'); - } catch (err) { - var msg = err.message; + } catch (err) { + expect(err.message).toEqual('AngularCache.put(key, value, options): options: must be an object!'); + continue; + } + if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { + fail(TYPES_EXCEPT_OBJECT[i]); + } + } + }); + it('should throw an error if "maxAge" is not valid.', function () { + var cache = $angularCacheFactory('cache'); + var maxAge = Math.floor((Math.random() * 100000) + 1) * -1; + try { + cache.put('item', 'value', { maxAge: maxAge }); + fail(); + } catch (err) { + var msg = err.message; + } + expect(msg).toEqual('AngularCache.put(key, value, options): maxAge: must be greater than zero!'); + for (var i = 0; i < TYPES_EXCEPT_NUMBER.length; i++) { + try { + cache.put('item', 'value', { maxAge: TYPES_EXCEPT_NUMBER[i] }); + if (TYPES_EXCEPT_NUMBER[i] !== null && TYPES_EXCEPT_NUMBER[i] !== undefined && TYPES_EXCEPT_NUMBER[i] !== false) { + fail(TYPES_EXCEPT_NUMBER[i]); } - expect(msg).toEqual('AngularCache.put(key, value, options): deleteOnExpire: accepted values are "none", "passive" or "aggressive"!'); - for (var i = 0; i < TYPES_EXCEPT_STRING.length; i++) { - try { - cache.put('item', 'value', { deleteOnExpire: TYPES_EXCEPT_STRING[i] }); - if (TYPES_EXCEPT_STRING[i] !== null && TYPES_EXCEPT_STRING[i] !== undefined && TYPES_EXCEPT_STRING[i] !== false) { - fail(TYPES_EXCEPT_STRING[i]); - } - } catch (err) { - expect(err.message).toEqual('AngularCache.put(key, value, options): deleteOnExpire: must be a string!'); - continue; - } - if (TYPES_EXCEPT_STRING[i] !== null && TYPES_EXCEPT_STRING[i] !== undefined && TYPES_EXCEPT_STRING[i] !== false) { - fail(TYPES_EXCEPT_STRING[i]); - } + } catch (err) { + expect(err.message).toEqual('AngularCache.put(key, value, options): maxAge: must be a number!'); + continue; + } + if (TYPES_EXCEPT_NUMBER[i] !== null && TYPES_EXCEPT_NUMBER[i] !== undefined && TYPES_EXCEPT_NUMBER[i] !== false) { + fail(TYPES_EXCEPT_NUMBER[i]); + } + } + }); + it('should throw an error if "deleteOnExpire" is not valid.', function () { + var cache = $angularCacheFactory('cache'); + var deleteOnExpire = 'fail'; + try { + cache.put('item', 'value', { deleteOnExpire: deleteOnExpire }); + fail('should not reach this!'); + } catch (err) { + var msg = err.message; + } + expect(msg).toEqual('AngularCache.put(key, value, options): deleteOnExpire: accepted values are "none", "passive" or "aggressive"!'); + for (var i = 0; i < TYPES_EXCEPT_STRING.length; i++) { + try { + cache.put('item', 'value', { deleteOnExpire: TYPES_EXCEPT_STRING[i] }); + if (TYPES_EXCEPT_STRING[i] !== null && TYPES_EXCEPT_STRING[i] !== undefined && TYPES_EXCEPT_STRING[i] !== false) { + fail(TYPES_EXCEPT_STRING[i]); } + } catch (err) { + expect(err.message).toEqual('AngularCache.put(key, value, options): deleteOnExpire: must be a string!'); + continue; + } + if (TYPES_EXCEPT_STRING[i] !== null && TYPES_EXCEPT_STRING[i] !== undefined && TYPES_EXCEPT_STRING[i] !== false) { + fail(TYPES_EXCEPT_STRING[i]); + } + } + }); + it('should not add values that are not defined.', function () { + var cache = $angularCacheFactory('cache'); + cache.put('item', null); + expect(cache.get('item')).toEqual(undefined); + cache.put('item', undefined); + expect(cache.get('item')).toEqual(undefined); + }); + it('should increase the size of the cache by one.', function () { + var cache = $angularCacheFactory('cache'); + expect(cache.info().size).toEqual(0); + cache.put('item', 'value1'); + expect(cache.info().size).toEqual(1); + cache.put('item2', 'value2'); + expect(cache.info().size).toEqual(2); + }); + it('should overwrite an item if it is re-added to the cache.', function () { + var cache = $angularCacheFactory('cache'); + expect(cache.info().size).toEqual(0); + cache.put('item', 'value1'); + expect(cache.info().size).toEqual(1); + cache.put('item', 'value2'); + expect(cache.info().size).toEqual(1); + expect(cache.get('item')).toEqual('value2'); + }); + it('should remove the least recently used item if the capacity has been reached.', function () { + var cache = $angularCacheFactory('cache', { capacity: 2 }); + expect(cache.info().size).toEqual(0); + cache.put('item1', 'value1'); + expect(cache.info().size).toEqual(1); + cache.put('item2', 'value2'); + expect(cache.info().size).toEqual(2); + cache.put('item3', 'value3'); + expect(cache.info().size).toEqual(2); + expect(cache.get('item1')).toEqual(undefined); + expect(cache.get('item2')).toEqual('value2'); + expect(cache.get('item3')).toEqual('value3'); + cache.get('item2'); + cache.put('item1', 'value1'); + expect(cache.get('item3')).toEqual(undefined); + expect(cache.get('item1')).toEqual('value1'); + expect(cache.get('item2')).toEqual('value2'); + }); + it('should not delete items if maxAge is specified and deleteOnExpire is set to "none".', function () { + var cache = $angularCacheFactory('cache', { maxAge: 10, deleteOnExpire: 'none', recycleFreq: 20 }); + cache.put('item1', 'value1'); + expect(cache.get('item1')).toEqual('value1'); + waits(100); + runs(function () { + expect(cache.get('item1')).toEqual('value1'); + expect(cache.info('item1').isExpired).toEqual(true); }); - it('should not add values that are not defined.', function () { - var cache = $angularCacheFactory('cache'); - cache.put('item', null); - expect(cache.get('item')).toEqual(undefined); - cache.put('item', undefined); - expect(cache.get('item')).toEqual(undefined); - }); - it('should increase the size of the cache by one.', function () { - var cache = $angularCacheFactory('cache'); - expect(cache.info().size).toEqual(0); - cache.put('item', 'value1'); - expect(cache.info().size).toEqual(1); - cache.put('item2', 'value2'); - expect(cache.info().size).toEqual(2); + }); + it('should remove items if maxAge is specified and deleteOnExpire is set to "aggressive".', function () { + var cache = $angularCacheFactory('cache', { maxAge: 10, deleteOnExpire: 'aggressive', recycleFreq: 20 }); + cache.put('item1', 'value1'); + expect(cache.get('item1')).toEqual('value1'); + waits(100); + runs(function () { + expect(cache.info('item1')).not.toBeDefined(); + expect(cache.get('item1')).not.toBeDefined(); }); - it('should overwrite an item if it is re-added to the cache.', function () { - var cache = $angularCacheFactory('cache'); - expect(cache.info().size).toEqual(0); - cache.put('item', 'value1'); - expect(cache.info().size).toEqual(1); - cache.put('item', 'value2'); - expect(cache.info().size).toEqual(1); - expect(cache.get('item')).toEqual('value2'); + }); + it('should should lazy delete an item when maxAge is specified and deleteOnExpire is set to "passive".', function () { + var cache = $angularCacheFactory('cache', { maxAge: 10, deleteOnExpire: 'passive' }); + cache.put('item1', 'value1'); + expect(cache.get('item1')).toEqual('value1'); + waits(100); + runs(function () { + expect(cache.info('item1').isExpired).toEqual(true); + expect(cache.get('item1')).not.toBeDefined(); }); - it('should remove the least recently used item if the capacity has been reached.', function () { - var cache = $angularCacheFactory('cache', { capacity: 2 }); - expect(cache.info().size).toEqual(0); - cache.put('item1', 'value1'); - expect(cache.info().size).toEqual(1); - cache.put('item2', 'value2'); - expect(cache.info().size).toEqual(2); - cache.put('item3', 'value3'); - expect(cache.info().size).toEqual(2); - expect(cache.get('item1')).toEqual(undefined); - expect(cache.get('item2')).toEqual('value2'); - expect(cache.get('item3')).toEqual('value3'); - cache.get('item2'); - cache.put('item1', 'value1'); - expect(cache.get('item3')).toEqual(undefined); - expect(cache.get('item1')).toEqual('value1'); - expect(cache.get('item2')).toEqual('value2'); - }); - it('should not delete items if maxAge is specified and deleteOnExpire is set to "none".', function () { - var cache = $angularCacheFactory('cache', { maxAge: 10, deleteOnExpire: 'none', recycleFreq: 20 }); - cache.put('item1', 'value1'); - expect(cache.get('item1')).toEqual('value1'); - waits(100); - runs(function () { - expect(cache.get('item1')).toEqual('value1'); - expect(cache.info('item1').isExpired).toEqual(true); - }); + }); + it('should not delete items if maxAge is specified and deleteOnExpire is set to "none" for an item.', function () { + var cache = $angularCacheFactory('cache'); + cache.put('item1', 'value1', { maxAge: 10, deleteOnExpire: 'none' }); + expect(cache.get('item1')).toEqual('value1'); + waits(100); + runs(function () { + expect(cache.get('item1')).toEqual('value1'); + expect(cache.info('item1').isExpired).toEqual(true); }); - it('should remove items if maxAge is specified and deleteOnExpire is set to "aggressive".', function () { - var cache = $angularCacheFactory('cache', { maxAge: 10, deleteOnExpire: 'aggressive', recycleFreq: 20 }); - cache.put('item1', 'value1'); - expect(cache.get('item1')).toEqual('value1'); - waits(100); - runs(function () { - expect(cache.info('item1')).not.toBeDefined(); - expect(cache.get('item1')).not.toBeDefined(); - }); + }); + it('should remove an item if maxAge for item is specified and deleteOnExpire is set to "aggressive".', function () { + var cache = $angularCacheFactory('cache', { recycleFreq: 10 }); + cache.put('item1', 'value1', { maxAge: 10, deleteOnExpire: 'aggressive' }); + expect(cache.get('item1')).toEqual('value1'); + waits(100); + runs(function () { + expect(cache.info('item1')).not.toBeDefined(); + expect(cache.get('item1')).not.toBeDefined(); }); - it('should should lazy delete an item when maxAge is specified and deleteOnExpire is set to "passive".', function () { - var cache = $angularCacheFactory('cache', { maxAge: 10, deleteOnExpire: 'passive' }); - cache.put('item1', 'value1'); - expect(cache.get('item1')).toEqual('value1'); - waits(100); - runs(function () { - expect(cache.info('item1').isExpired).toEqual(true); - expect(cache.get('item1')).not.toBeDefined(); - }); + }); + it('should passively expire an item if maxAge for the item is specified and deleteOnExpire is set to "passive".', function () { + var cache = $angularCacheFactory('cache'); + cache.put('item1', 'value1', { maxAge: 10, deleteOnExpire: 'passive' }); + expect(cache.get('item1')).toEqual('value1'); + waits(100); + runs(function () { + expect(cache.info('item1').isExpired).toEqual(true); + expect(cache.get('item1')).toEqual(undefined); }); - it('should not delete items if maxAge is specified and deleteOnExpire is set to "none" for an item.', function () { - var cache = $angularCacheFactory('cache'); - cache.put('item1', 'value1', { maxAge: 10, deleteOnExpire: 'none' }); - expect(cache.get('item1')).toEqual('value1'); - waits(100); - runs(function () { - expect(cache.get('item1')).toEqual('value1'); - expect(cache.info('item1').isExpired).toEqual(true); - }); + }); + it('maxAge for a specific item should override maxAge for the cache.', function () { + var cache = $angularCacheFactory('cache', { maxAge: 1000, deleteOnExpire: 'aggressive', recycleFreq: 20 }); + cache.put('item1', 'value1', { maxAge: 5 }); + expect(cache.info('item1').maxAge).toEqual(5); + expect(cache.get('item1')).toEqual('value1'); + waits(100); + runs(function () { + expect(cache.get('item1')).toEqual(undefined); }); - it('should remove an item if maxAge for item is specified and deleteOnExpire is set to "aggressive".', function () { - var cache = $angularCacheFactory('cache', { recycleFreq: 10 }); - cache.put('item1', 'value1', { maxAge: 10, deleteOnExpire: 'aggressive' }); - expect(cache.get('item1')).toEqual('value1'); - waits(100); - runs(function () { - expect(cache.info('item1')).not.toBeDefined(); - expect(cache.get('item1')).not.toBeDefined(); - }); + }); + it('deleteOnExpire set to "passive" for a specific item should override deleteOnExpire set to "aggressive" for the cache.', function () { + var cache = $angularCacheFactory('cache', { maxAge: 10, deleteOnExpire: 'aggressive' }); + cache.put('item1', 'value1', { maxAge: 10, deleteOnExpire: 'passive' }); + expect(cache.get('item1')).toEqual('value1'); + expect(cache.info('item1').deleteOnExpire).toEqual("passive"); + waits(100); + runs(function () { + expect(cache.info('item1').isExpired).toEqual(true); + expect(cache.get('item1')).toEqual(undefined); }); - it('should passively expire an item if maxAge for the item is specified and deleteOnExpire is set to "passive".', function () { - var cache = $angularCacheFactory('cache'); - cache.put('item1', 'value1', { maxAge: 10, deleteOnExpire: 'passive' }); - expect(cache.get('item1')).toEqual('value1'); - waits(100); - runs(function () { - expect(cache.info('item1').isExpired).toEqual(true); - expect(cache.get('item1')).toEqual(undefined); - }); + }); + it('should work with normal promises.', function () { + var cache = $angularCacheFactory('cache', { + maxAge: 10, + deleteOnExpire: 'aggressive', + recycleFreq: 20 }); - it('maxAge for a specific item should override maxAge for the cache.', function () { - var cache = $angularCacheFactory('cache', { maxAge: 1000, deleteOnExpire: 'aggressive', recycleFreq: 20 }); - cache.put('item1', 'value1', { maxAge: 5 }); - expect(cache.info('item1').maxAge).toEqual(5); + var deferred = $q.defer(); + cache.put('item1', deferred.promise); + waits(100); + runs(function () { + $rootScope.$apply(function () { + deferred.resolve('value1'); + }); + $rootScope.$apply(function () { expect(cache.get('item1')).toEqual('value1'); waits(100); runs(function () { - expect(cache.get('item1')).toEqual(undefined); + expect(cache.get('item1')).toBeUndefined(); }); + }); }); - it('deleteOnExpire set to "passive" for a specific item should override deleteOnExpire set to "aggressive" for the cache.', function () { - var cache = $angularCacheFactory('cache', { maxAge: 10, deleteOnExpire: 'aggressive' }); - cache.put('item1', 'value1', { maxAge: 10, deleteOnExpire: 'passive' }); - expect(cache.get('item1')).toEqual('value1'); - expect(cache.info('item1').deleteOnExpire).toEqual("passive"); - waits(100); - runs(function () { - expect(cache.info('item1').isExpired).toEqual(true); - expect(cache.get('item1')).toEqual(undefined); - }); + }); + it('should work with $http promises.', function () { + $httpBackend.expectGET('test.com').respond({ name: 'John' }); + var cache = $angularCacheFactory('cache', {}); + $http.get('test.com', { + cache: cache + }).success(function (data) { + expect(data).toEqual({ name: 'John' }); + $http.get('test.com', { + cache: cache + }).success(function (data) { + expect(data).toEqual({ name: 'John' }); + }); + $rootScope.$safeApply(); }); - it('should save data to localStorage when storageMode is used.', function () { - var localStorageCache = $angularCacheFactory('localStorageCache', { storageMode: 'localStorage' }), - sessionStorageCache = $angularCacheFactory('sessionStorageCache', { storageMode: 'sessionStorage' }); + $httpBackend.flush(); + }); + it('should save data to localStorage when storageMode is used.', function () { + var localStorageCache = $angularCacheFactory('localStorageCache', { storageMode: 'localStorage' }), + sessionStorageCache = $angularCacheFactory('sessionStorageCache', { storageMode: 'sessionStorage' }); - localStorageCache.put('item1', 'value1'); - sessionStorageCache.put('item1', 'value1'); + localStorageCache.put('item1', 'value1'); + sessionStorageCache.put('item1', 'value1'); - if (localStorage) { - expect(angular.fromJson(localStorage.getItem('angular-cache.caches.localStorageCache.data.item1')).value).toEqual('value1'); - expect(localStorage.getItem('angular-cache.caches.localStorageCache.keys')).toEqual('["item1"]'); - } - if (sessionStorage) { - expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.data.item1')).value).toEqual('value1'); - expect(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.keys')).toEqual('["item1"]'); - } + if (localStorage) { + expect(angular.fromJson(localStorage.getItem('angular-cache.caches.localStorageCache.data.item1')).value).toEqual('value1'); + expect(localStorage.getItem('angular-cache.caches.localStorageCache.keys')).toEqual('["item1"]'); + } + if (sessionStorage) { + expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.data.item1')).value).toEqual('value1'); + expect(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.keys')).toEqual('["item1"]'); + } + }); + it('should store promises when storePromises is true.', function () { + $httpBackend.expectGET('test.com').respond({ name: 'John' }); + var cache = $angularCacheFactory('cache', {storePromises : true}); + $http.get('test.com', { + cache: cache + }); + $http.get('test.com', { + cache: cache }); + $httpBackend.flush(); + }); }); diff --git a/test/angularCache.remove-test.js b/test/angularCache.remove-test.js index f8470ab..3b0f57d 100644 --- a/test/angularCache.remove-test.js +++ b/test/angularCache.remove-test.js @@ -1,62 +1,71 @@ describe('AngularCache.remove(key)', function () { - it('should remove the item with the specified key.', function () { - var cache = $angularCacheFactory('cache'); - var value1 = 'value1', - value2 = 2, - value3 = { - value3: 'stuff' - }; - cache.put('item1', value1); - cache.put('item2', value2); - cache.put('item3', value3); - cache.remove('item1'); - expect(cache.get('item1')).toEqual(undefined); - cache.remove('item2'); - expect(cache.get('item2')).toEqual(undefined); - cache.remove('item3'); - expect(cache.get('item3')).toEqual(undefined); - }); - it('should reduce the size of the cache by one if the size is greater than zero.', function () { - var cache = $angularCacheFactory('cache'); - cache.put('item1', 'value1'); - expect(cache.info().size).toEqual(1); - cache.put('item2', 'value2'); - expect(cache.info().size).toEqual(2); - cache.remove('item1'); - expect(cache.info().size).toEqual(1); - cache.remove('item2'); - expect(cache.info().size).toEqual(0); - cache.remove('item1'); - expect(cache.info().size).toEqual(0); - cache.remove('item2'); - expect(cache.info().size).toEqual(0); - }); - it('should remove items from localStorage when storageMode is used.', function () { - var localStorageCache = $angularCacheFactory('localStorageCache', { storageMode: 'localStorage' }), - sessionStorageCache = $angularCacheFactory('sessionStorageCache', { storageMode: 'sessionStorage' }); + it('should remove the item with the specified key.', function () { + var cache = $angularCacheFactory('cache'); + var value1 = 'value1', + value2 = 2, + value3 = { + value3: 'stuff' + }; + cache.put('item1', value1); + cache.put('item2', value2); + cache.put('item3', value3); + cache.remove('item1'); + expect(cache.get('item1')).toEqual(undefined); + cache.remove('item2'); + expect(cache.get('item2')).toEqual(undefined); + cache.remove('item3'); + expect(cache.get('item3')).toEqual(undefined); + }); + it('should reduce the size of the cache by one if the size is greater than zero.', function () { + var cache = $angularCacheFactory('cache'); + cache.put('item1', 'value1'); + expect(cache.info().size).toEqual(1); + cache.put('item2', 'value2'); + expect(cache.info().size).toEqual(2); + cache.remove('item1'); + expect(cache.info().size).toEqual(1); + cache.remove('item2'); + expect(cache.info().size).toEqual(0); + cache.remove('item1'); + expect(cache.info().size).toEqual(0); + cache.remove('item2'); + expect(cache.info().size).toEqual(0); + }); + it('should remove items from localStorage when storageMode is used.', function () { + var localStorageCache = $angularCacheFactory('localStorageCache', { storageMode: 'localStorage' }), + sessionStorageCache = $angularCacheFactory('sessionStorageCache', { storageMode: 'sessionStorage' }); - localStorageCache.put('item1', 'value1'); - sessionStorageCache.put('item1', 'value1'); + localStorageCache.put('item1', 'value1'); + sessionStorageCache.put('item1', 'value1'); - if (localStorage) { - expect(angular.fromJson(localStorage.getItem('angular-cache.caches.localStorageCache.data.item1')).value).toEqual('value1'); - expect(localStorage.getItem('angular-cache.caches.localStorageCache.keys')).toEqual('["item1"]'); - } - if (sessionStorage) { - expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.data.item1')).value).toEqual('value1'); - expect(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.keys')).toEqual('["item1"]'); - } + if (localStorage) { + expect(angular.fromJson(localStorage.getItem('angular-cache.caches.localStorageCache.data.item1')).value).toEqual('value1'); + expect(localStorage.getItem('angular-cache.caches.localStorageCache.keys')).toEqual('["item1"]'); + } + if (sessionStorage) { + expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.data.item1')).value).toEqual('value1'); + expect(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.keys')).toEqual('["item1"]'); + } - localStorageCache.remove('item1'); - sessionStorageCache.remove('item1'); + localStorageCache.remove('item1'); + sessionStorageCache.remove('item1'); - if (localStorage) { - expect(angular.fromJson(localStorage.getItem('angular-cache.caches.localStorageCache.data.item1'))).toEqual(null); - expect(localStorage.getItem('angular-cache.caches.localStorageCache.keys')).toEqual('[]'); - } - if (sessionStorage) { - expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.data.item1'))).toEqual(null); - expect(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.keys')).toEqual('[]'); - } - }); -}); \ No newline at end of file + if (localStorage) { + expect(angular.fromJson(localStorage.getItem('angular-cache.caches.localStorageCache.data.item1'))).toEqual(null); + expect(localStorage.getItem('angular-cache.caches.localStorageCache.keys')).toEqual('[]'); + } + if (sessionStorage) { + expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.data.item1'))).toEqual(null); + expect(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.keys')).toEqual('[]'); + } + }); + it('should allow to remove a promise if storePromises is true.', function () { + var cache = $angularCacheFactory('cache', {storePromises : true}); + var deferred = $q.defer(); + var promise = deferred.promise; + cache.put('mypromise', promise); + cache.remove('mypromise'); + //we can't check on cache info size as promises are not counted + expect(cache.get('mypromise')).toBeUndefined(); + }); +}); diff --git a/test/angularCache.removeAll-test.js b/test/angularCache.removeAll-test.js index fd3b872..bff26be 100644 --- a/test/angularCache.removeAll-test.js +++ b/test/angularCache.removeAll-test.js @@ -1,51 +1,51 @@ describe('AngularCache.removeAll()', function () { - it('should remove all items in the cache.', function () { - var cache = $angularCacheFactory('cache'); - var value1 = 'value1', - value2 = 2, - value3 = { - value3: 'stuff' - }; - cache.put('item1', value1); - cache.put('item2', value2); - cache.put('item3', value3); - cache.removeAll(); - expect(cache.get('item1')).toEqual(undefined); - expect(cache.get('item2')).toEqual(undefined); - expect(cache.get('item3')).toEqual(undefined); - }); - it('should remove items from localStorage when storageMode is used.', function () { - var localStorageCache = $angularCacheFactory('localStorageCache', { storageMode: 'localStorage' }), - sessionStorageCache = $angularCacheFactory('sessionStorageCache', { storageMode: 'sessionStorage' }); + it('should remove all items in the cache.', function () { + var cache = $angularCacheFactory('cache'); + var value1 = 'value1', + value2 = 2, + value3 = { + value3: 'stuff' + }; + cache.put('item1', value1); + cache.put('item2', value2); + cache.put('item3', value3); + cache.removeAll(); + expect(cache.get('item1')).toEqual(undefined); + expect(cache.get('item2')).toEqual(undefined); + expect(cache.get('item3')).toEqual(undefined); + }); + it('should remove items from localStorage when storageMode is used.', function () { + var localStorageCache = $angularCacheFactory('localStorageCache', { storageMode: 'localStorage' }), + sessionStorageCache = $angularCacheFactory('sessionStorageCache', { storageMode: 'sessionStorage' }); - localStorageCache.put('item1', 'value1'); - sessionStorageCache.put('item1', 'value1'); - localStorageCache.put('item2', 'value2'); - sessionStorageCache.put('item2', 'value2'); + localStorageCache.put('item1', 'value1'); + sessionStorageCache.put('item1', 'value1'); + localStorageCache.put('item2', 'value2'); + sessionStorageCache.put('item2', 'value2'); - if (localStorage) { - expect(angular.fromJson(localStorage.getItem('angular-cache.caches.localStorageCache.data.item1')).value).toEqual('value1'); - expect(angular.fromJson(localStorage.getItem('angular-cache.caches.localStorageCache.data.item2')).value).toEqual('value2'); - expect(localStorage.getItem('angular-cache.caches.localStorageCache.keys')).toEqual('["item1","item2"]'); - } - if (sessionStorage) { - expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.data.item1')).value).toEqual('value1'); - expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.data.item2')).value).toEqual('value2'); - expect(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.keys')).toEqual('["item1","item2"]'); - } + if (localStorage) { + expect(angular.fromJson(localStorage.getItem('angular-cache.caches.localStorageCache.data.item1')).value).toEqual('value1'); + expect(angular.fromJson(localStorage.getItem('angular-cache.caches.localStorageCache.data.item2')).value).toEqual('value2'); + expect(localStorage.getItem('angular-cache.caches.localStorageCache.keys')).toEqual('["item1","item2"]'); + } + if (sessionStorage) { + expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.data.item1')).value).toEqual('value1'); + expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.data.item2')).value).toEqual('value2'); + expect(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.keys')).toEqual('["item1","item2"]'); + } - localStorageCache.removeAll(); - sessionStorageCache.removeAll(); + localStorageCache.removeAll(); + sessionStorageCache.removeAll(); - if (localStorage) { - expect(angular.fromJson(localStorage.getItem('angular-cache.caches.localStorageCache.data.item1'))).toEqual(null); - expect(angular.fromJson(localStorage.getItem('angular-cache.caches.localStorageCache.data.item2'))).toEqual(null); - expect(localStorage.getItem('angular-cache.caches.localStorageCache.keys')).toEqual('[]'); - } - if (sessionStorage) { - expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.data.item1'))).toEqual(null); - expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.data.item2'))).toEqual(null); - expect(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.keys')).toEqual('[]'); - } - }); -}); \ No newline at end of file + if (localStorage) { + expect(angular.fromJson(localStorage.getItem('angular-cache.caches.localStorageCache.data.item1'))).toEqual(null); + expect(angular.fromJson(localStorage.getItem('angular-cache.caches.localStorageCache.data.item2'))).toEqual(null); + expect(localStorage.getItem('angular-cache.caches.localStorageCache.keys')).toEqual('[]'); + } + if (sessionStorage) { + expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.data.item1'))).toEqual(null); + expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.data.item2'))).toEqual(null); + expect(sessionStorage.getItem('angular-cache.caches.sessionStorageCache.keys')).toEqual('[]'); + } + }); +}); diff --git a/test/angularCache.removeExpired-test.js b/test/angularCache.removeExpired-test.js index 28ab1d8..332e1ff 100644 --- a/test/angularCache.removeExpired-test.js +++ b/test/angularCache.removeExpired-test.js @@ -1,28 +1,28 @@ describe('AngularCache.removeExpired()', function () { - it('should remove all expired items.', function () { - var cache = $angularCacheFactory('cache', { - deleteOnExpire: 'none', - maxAge: 10, - recycleFreq: 20 - }); - var value1 = 'value1', - value2 = 2, - value3 = { - value3: 'stuff' - }; - cache.put('item1', value1, { - deleteOnExpire: 'passive' - }); - cache.put('item2', value2, { - deleteOnExpire: 'aggressive' - }); - cache.put('item3', value3); - waits(100); - runs(function () { - cache.removeExpired(); - expect(cache.info().size).toEqual(0); - cache.put('item3', value3); - expect(cache.info().size).toEqual(1); - }); - }); + it('should remove all expired items.', function () { + var cache = $angularCacheFactory('cache', { + deleteOnExpire: 'none', + maxAge: 10, + recycleFreq: 20 + }); + var value1 = 'value1', + value2 = 2, + value3 = { + value3: 'stuff' + }; + cache.put('item1', value1, { + deleteOnExpire: 'passive' + }); + cache.put('item2', value2, { + deleteOnExpire: 'aggressive' + }); + cache.put('item3', value3); + waits(100); + runs(function () { + cache.removeExpired(); + expect(cache.info().size).toEqual(0); + cache.put('item3', value3); + expect(cache.info().size).toEqual(1); + }); + }); }); diff --git a/test/angularCache.setOptions-test.js b/test/angularCache.setOptions-test.js index 2d5e586..03706a1 100644 --- a/test/angularCache.setOptions-test.js +++ b/test/angularCache.setOptions-test.js @@ -1,242 +1,242 @@ describe('AngularCache.setOptions()', function () { - it('should throw an error if "options" is not an object.', function () { - var cache = $angularCacheFactory('cache'); - for (var i = 0; i < TYPES_EXCEPT_OBJECT.length; i++) { - try { - cache.setOptions(TYPES_EXCEPT_OBJECT[i]); - if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { - fail(TYPES_EXCEPT_OBJECT[i]); - } - } catch (err) { - expect(err.message).toEqual('AngularCache.setOptions(cacheOptions, strict, options): cacheOptions: must be an object!'); - continue; - } - if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { - fail(TYPES_EXCEPT_OBJECT[i]); - } - } - }); - it('should correctly reset to defaults if strict mode is true', function () { - var onExpire = function () { - }; - var cache = $angularCacheFactory('cache', { - maxAge: 100, - cacheFlushInterval: 200, - onExpire: onExpire, - storageMode: 'localStorage', - disabled: true - }); - expect(cache.info().maxAge).toEqual(100); - expect(cache.info().cacheFlushInterval).toEqual(200); - expect(cache.info().onExpire).toEqual(onExpire); - expect(cache.info().storageMode).toEqual('localStorage'); - expect(cache.info().disabled).toEqual(true); - cache.setOptions({ }, true); - expect(cache.info().maxAge).toEqual(null); - expect(cache.info().cacheFlushInterval).toEqual(null); - expect(cache.info().onExpire).toEqual(null); - expect(cache.info().storageMode).toEqual('none'); - expect(cache.info().disabled).toEqual(false); - }); - it('should correctly modify the capacity of a cache', function () { - var cache = $angularCacheFactory('cache'); - expect(cache.info().capacity).toEqual(Number.MAX_VALUE); - cache.setOptions({ capacity: 5 }, false); - expect(cache.info().capacity).toEqual(5); - cache.put('item1', 1); - cache.put('item2', 2); - cache.put('item3', 3); - cache.put('item4', 4); - cache.put('item5', 5); - cache.put('item6', 6); - expect(cache.get('item1')).not.toBeDefined(); - waits(50); - runs(function () { - cache.get('item2'); - cache.get('item3'); - cache.get('item6'); - cache.setOptions({ capacity: 3 }, false); - // Least-recently used items over the new capacity should have been removed. - expect(cache.get('item4')).not.toBeDefined(); - expect(cache.get('item5')).not.toBeDefined(); - expect(cache.info().size).toEqual(3); - }); - }); - it('should correctly modify the maxAge of a cache', function () { - var cache = $angularCacheFactory('cache'); - expect(cache.info().maxAge).toEqual(null); - cache.setOptions({ maxAge: 10, deleteOnExpire: 'aggressive', recycleFreq: 20 }, false); - expect(cache.info().maxAge).toEqual(10); - cache.put('item1', 1); - cache.put('item2', 2); - waits(100); - runs(function () { - expect(cache.get('item1')).not.toBeDefined(); - expect(cache.get('item2')).not.toBeDefined(); - cache.setOptions({ maxAge: 10, deleteOnExpire: 'aggressive', recycleFreq: 20 }, false); - expect(cache.info().maxAge).toEqual(10); - cache.put('item1', 1); - cache.put('item2', 2); - waits(100); - // The new items should be removed after 500 ms (the new maxAge) - runs(function () { - expect(cache.get('item1')).not.toBeDefined(); - expect(cache.get('item2')).not.toBeDefined(); - cache.put('item1', 1); - cache.put('item2', 2, { maxAge: 25, deleteOnExpire: 'aggressive' }); - cache.setOptions({ maxAge: null, deleteOnExpire: 'none', recycleFreq: 20 }, false); - expect(cache.info().maxAge).toEqual(null); - waits(100); - // The new items should be removed after 500 ms (the new maxAge) - runs(function () { - expect(cache.get('item1')).toEqual(1); - expect(cache.info('item1').expires).not.toBeDefined(); - expect(cache.get('item2')).not.toBeDefined(); - expect(cache.info('item2')).not.toBeDefined(); + it('should throw an error if "options" is not an object.', function () { + var cache = $angularCacheFactory('cache'); + for (var i = 0; i < TYPES_EXCEPT_OBJECT.length; i++) { + try { + cache.setOptions(TYPES_EXCEPT_OBJECT[i]); + if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { + fail(TYPES_EXCEPT_OBJECT[i]); + } + } catch (err) { + expect(err.message).toEqual('AngularCache.setOptions(cacheOptions, strict, options): cacheOptions: must be an object!'); + continue; + } + if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { + fail(TYPES_EXCEPT_OBJECT[i]); + } + } + }); + it('should correctly reset to defaults if strict mode is true', function () { + var onExpire = function () { + }; + var cache = $angularCacheFactory('cache', { + maxAge: 100, + cacheFlushInterval: 200, + onExpire: onExpire, + storageMode: 'localStorage', + disabled: true + }); + expect(cache.info().maxAge).toEqual(100); + expect(cache.info().cacheFlushInterval).toEqual(200); + expect(cache.info().onExpire).toEqual(onExpire); + expect(cache.info().storageMode).toEqual('localStorage'); + expect(cache.info().disabled).toEqual(true); + cache.setOptions({ }, true); + expect(cache.info().maxAge).toEqual(null); + expect(cache.info().cacheFlushInterval).toEqual(null); + expect(cache.info().onExpire).toEqual(null); + expect(cache.info().storageMode).toEqual('none'); + expect(cache.info().disabled).toEqual(false); + }); + it('should correctly modify the capacity of a cache', function () { + var cache = $angularCacheFactory('cache'); + expect(cache.info().capacity).toEqual(Number.MAX_VALUE); + cache.setOptions({ capacity: 5 }, false); + expect(cache.info().capacity).toEqual(5); + cache.put('item1', 1); + cache.put('item2', 2); + cache.put('item3', 3); + cache.put('item4', 4); + cache.put('item5', 5); + cache.put('item6', 6); + expect(cache.get('item1')).not.toBeDefined(); + waits(50); + runs(function () { + cache.get('item2'); + cache.get('item3'); + cache.get('item6'); + cache.setOptions({ capacity: 3 }, false); + // Least-recently used items over the new capacity should have been removed. + expect(cache.get('item4')).not.toBeDefined(); + expect(cache.get('item5')).not.toBeDefined(); + expect(cache.info().size).toEqual(3); + }); + }); + it('should correctly modify the maxAge of a cache', function () { + var cache = $angularCacheFactory('cache'); + expect(cache.info().maxAge).toEqual(null); + cache.setOptions({ maxAge: 10, deleteOnExpire: 'aggressive', recycleFreq: 20 }, false); + expect(cache.info().maxAge).toEqual(10); + cache.put('item1', 1); + cache.put('item2', 2); + waits(100); + runs(function () { + expect(cache.get('item1')).not.toBeDefined(); + expect(cache.get('item2')).not.toBeDefined(); + cache.setOptions({ maxAge: 10, deleteOnExpire: 'aggressive', recycleFreq: 20 }, false); + expect(cache.info().maxAge).toEqual(10); + cache.put('item1', 1); + cache.put('item2', 2); + waits(100); + // The new items should be removed after 500 ms (the new maxAge) + runs(function () { + expect(cache.get('item1')).not.toBeDefined(); + expect(cache.get('item2')).not.toBeDefined(); + cache.put('item1', 1); + cache.put('item2', 2, { maxAge: 25, deleteOnExpire: 'aggressive' }); + cache.setOptions({ maxAge: null, deleteOnExpire: 'none', recycleFreq: 20 }, false); + expect(cache.info().maxAge).toEqual(null); + waits(100); + // The new items should be removed after 500 ms (the new maxAge) + runs(function () { + expect(cache.get('item1')).toEqual(1); + expect(cache.info('item1').expires).not.toBeDefined(); + expect(cache.get('item2')).not.toBeDefined(); + expect(cache.info('item2')).not.toBeDefined(); - cache.setOptions({ maxAge: 1000, deleteOnExpire: 'aggressive', recycleFreq: 20 }, false); - cache.put('item1', 1); - cache.put('item2', 2); - waits(100); - // The new items should be removed after 500 ms (the new maxAge) - runs(function () { - cache.setOptions({ maxAge: 10, deleteOnExpire: 'aggressive' }, false); - expect(cache.get('item1')).not.toBeDefined(); - expect(cache.get('item2')).not.toBeDefined(); - }); - }); - }); - }); - }); - it('should correctly modify the cacheFlushInterval of a cache', function () { - var cache = $angularCacheFactory('cache'); - expect(cache.info().cacheFlushInterval).toEqual(null); - cache.setOptions({ cacheFlushInterval: 10 }, false); - expect(cache.info().cacheFlushInterval).toEqual(10); - cache.put('item1', 1); - cache.put('item2', 2); - waits(100); - // The first items should be removed after 2000 ms - runs(function () { - expect(cache.get('item1')).not.toBeDefined(); - expect(cache.get('item2')).not.toBeDefined(); - cache.setOptions({ cacheFlushInterval: 20 }, false); - expect(cache.info().cacheFlushInterval).toEqual(20); - cache.put('item1', 1); - cache.put('item2', 2); - waits(100); - // The new items should be removed after 500 ms (the new maxAge) - runs(function () { - expect(cache.get('item1')).not.toBeDefined(); - expect(cache.get('item2')).not.toBeDefined(); - cache.setOptions({ cacheFlushInterval: 20 }, false); - expect(cache.info().cacheFlushInterval).toEqual(20); - cache.put('item1', 1); - cache.put('item2', 2); - waits(100); - // The new items should be removed after 500 ms (the new maxAge) - runs(function () { - expect(cache.get('item1')).not.toBeDefined(); - expect(cache.get('item2')).not.toBeDefined(); - }); - }); - }); - }); - it('should correctly modify the deleteOnExpire of a cache', function () { - var cache = $angularCacheFactory('cache', { maxAge: 10 }); - expect(cache.info().deleteOnExpire).toEqual('none'); - cache.setOptions({ deleteOnExpire: 'passive' }, false); - expect(cache.info().deleteOnExpire).toEqual('passive'); - cache.put('item1', 1); - cache.put('item2', 2); - waits(100); - // The first items should be removed after 2000 ms - runs(function () { - expect(cache.get('item1')).not.toBeDefined(); - expect(cache.get('item2')).not.toBeDefined(); - cache.setOptions({ maxAge: 10, deleteOnExpire: 'aggressive', recycleFreq: 20 }, false); - expect(cache.info().deleteOnExpire).toEqual('aggressive'); - cache.put('item1', 1); - cache.put('item2', 2); - waits(100); - // The new items should be removed after 500 ms (the new maxAge) - runs(function () { - expect(cache.get('item1')).not.toBeDefined(); - expect(cache.get('item2')).not.toBeDefined(); - }); - }); - }); - it('should correctly set configuration to default when "strict" is true', function () { - var cache = $angularCacheFactory('cache', { - capacity: 10, - maxAge: 1000, - cacheFlushInterval: 1000, - deleteOnExpire: 'aggressive', - storageMode: 'none' - }); - cache.setOptions({}, true); - var cacheInfo = cache.info(); - expect(cacheInfo.id).toEqual('cache'); - expect(cacheInfo.capacity).toEqual(Number.MAX_VALUE); - expect(cacheInfo.size).toEqual(0); - expect(cacheInfo.recycleFreq).toEqual(1000); - expect(cacheInfo.maxAge).toEqual(null); - expect(cacheInfo.cacheFlushInterval).toEqual(null); - expect(typeof cacheInfo.recycleFreqId).toEqual('number'); - expect(cacheInfo.deleteOnExpire).toEqual('none'); - expect(cacheInfo.storageMode).toEqual('none'); - expect(cacheInfo.onExpire).toEqual(null); + cache.setOptions({ maxAge: 1000, deleteOnExpire: 'aggressive', recycleFreq: 20 }, false); + cache.put('item1', 1); + cache.put('item2', 2); + waits(100); + // The new items should be removed after 500 ms (the new maxAge) + runs(function () { + cache.setOptions({ maxAge: 10, deleteOnExpire: 'aggressive' }, false); + expect(cache.get('item1')).not.toBeDefined(); + expect(cache.get('item2')).not.toBeDefined(); + }); + }); + }); + }); + }); + it('should correctly modify the cacheFlushInterval of a cache', function () { + var cache = $angularCacheFactory('cache'); + expect(cache.info().cacheFlushInterval).toEqual(null); + cache.setOptions({ cacheFlushInterval: 10 }, false); + expect(cache.info().cacheFlushInterval).toEqual(10); + cache.put('item1', 1); + cache.put('item2', 2); + waits(100); + // The first items should be removed after 2000 ms + runs(function () { + expect(cache.get('item1')).not.toBeDefined(); + expect(cache.get('item2')).not.toBeDefined(); + cache.setOptions({ cacheFlushInterval: 20 }, false); + expect(cache.info().cacheFlushInterval).toEqual(20); + cache.put('item1', 1); + cache.put('item2', 2); + waits(100); + // The new items should be removed after 500 ms (the new maxAge) + runs(function () { + expect(cache.get('item1')).not.toBeDefined(); + expect(cache.get('item2')).not.toBeDefined(); + cache.setOptions({ cacheFlushInterval: 20 }, false); + expect(cache.info().cacheFlushInterval).toEqual(20); + cache.put('item1', 1); + cache.put('item2', 2); + waits(100); + // The new items should be removed after 500 ms (the new maxAge) + runs(function () { + expect(cache.get('item1')).not.toBeDefined(); + expect(cache.get('item2')).not.toBeDefined(); + }); + }); + }); + }); + it('should correctly modify the deleteOnExpire of a cache', function () { + var cache = $angularCacheFactory('cache', { maxAge: 10 }); + expect(cache.info().deleteOnExpire).toEqual('none'); + cache.setOptions({ deleteOnExpire: 'passive' }, false); + expect(cache.info().deleteOnExpire).toEqual('passive'); + cache.put('item1', 1); + cache.put('item2', 2); + waits(100); + // The first items should be removed after 2000 ms + runs(function () { + expect(cache.get('item1')).not.toBeDefined(); + expect(cache.get('item2')).not.toBeDefined(); + cache.setOptions({ maxAge: 10, deleteOnExpire: 'aggressive', recycleFreq: 20 }, false); + expect(cache.info().deleteOnExpire).toEqual('aggressive'); + cache.put('item1', 1); + cache.put('item2', 2); + waits(100); + // The new items should be removed after 500 ms (the new maxAge) + runs(function () { + expect(cache.get('item1')).not.toBeDefined(); + expect(cache.get('item2')).not.toBeDefined(); + }); + }); + }); + it('should correctly set configuration to default when "strict" is true', function () { + var cache = $angularCacheFactory('cache', { + capacity: 10, + maxAge: 1000, + cacheFlushInterval: 1000, + deleteOnExpire: 'aggressive', + storageMode: 'none' + }); + cache.setOptions({}, true); + var cacheInfo = cache.info(); + expect(cacheInfo.id).toEqual('cache'); + expect(cacheInfo.capacity).toEqual(Number.MAX_VALUE); + expect(cacheInfo.size).toEqual(0); + expect(cacheInfo.recycleFreq).toEqual(1000); + expect(cacheInfo.maxAge).toEqual(null); + expect(cacheInfo.cacheFlushInterval).toEqual(null); + expect(typeof cacheInfo.recycleFreqId).toEqual('number'); + expect(cacheInfo.deleteOnExpire).toEqual('none'); + expect(cacheInfo.storageMode).toEqual('none'); + expect(cacheInfo.onExpire).toEqual(null); - cache.setOptions({ recycleFreq: null }); - expect(cache.info().recycleFreq).toEqual(1000); - }); - it('should correctly switch to using local/session storage when storageMode is activated', function () { - var cache = $angularCacheFactory('cache'), - cache2 = $angularCacheFactory('cache2'); - cache.put('item', 'value'); - cache2.put('item', 'value'); - cache.setOptions({ maxAge: 10, deleteOnExpire: 'aggressive', storageMode: 'localStorage', recycleFreq: 20 }); - cache2.setOptions({ maxAge: 10, deleteOnExpire: 'aggressive', storageMode: 'sessionStorage', recycleFreq: 20 }); + cache.setOptions({ recycleFreq: null }); + expect(cache.info().recycleFreq).toEqual(1000); + }); + it('should correctly switch to using local/session storage when storageMode is activated', function () { + var cache = $angularCacheFactory('cache'), + cache2 = $angularCacheFactory('cache2'); + cache.put('item', 'value'); + cache2.put('item', 'value'); + cache.setOptions({ maxAge: 10, deleteOnExpire: 'aggressive', storageMode: 'localStorage', recycleFreq: 20 }); + cache2.setOptions({ maxAge: 10, deleteOnExpire: 'aggressive', storageMode: 'sessionStorage', recycleFreq: 20 }); - if (localStorage) { - expect(angular.fromJson(localStorage.getItem('angular-cache.caches.cache.data.item')).value).toEqual('value'); - } - if (sessionStorage) { - expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.cache2.data.item')).value).toEqual('value'); - } - waits(100); - runs(function () { - expect(cache.get('item')).not.toBeDefined(); - expect(cache2.get('item')).not.toBeDefined(); - if (localStorage) { - expect(localStorage.getItem('angular-cache.caches.cache.data.item')).toEqual(null); - } - if (sessionStorage) { - expect(sessionStorage.getItem('angular-cache.caches.cache2.data.item')).toEqual(null); - } - }); - }); - it('should correctly stop using local/session storage when storageMode is deactivated', function () { - var cache = $angularCacheFactory('cache', { storageMode: 'localStorage' }), - cache2 = $angularCacheFactory('cache2', { storageMode: 'sessionStorage' }); - cache.put('item', 'value'); - cache2.put('item', 'value'); + if (localStorage) { + expect(angular.fromJson(localStorage.getItem('angular-cache.caches.cache.data.item')).value).toEqual('value'); + } + if (sessionStorage) { + expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.cache2.data.item')).value).toEqual('value'); + } + waits(100); + runs(function () { + expect(cache.get('item')).not.toBeDefined(); + expect(cache2.get('item')).not.toBeDefined(); + if (localStorage) { + expect(localStorage.getItem('angular-cache.caches.cache.data.item')).toEqual(null); + } + if (sessionStorage) { + expect(sessionStorage.getItem('angular-cache.caches.cache2.data.item')).toEqual(null); + } + }); + }); + it('should correctly stop using local/session storage when storageMode is deactivated', function () { + var cache = $angularCacheFactory('cache', { storageMode: 'localStorage' }), + cache2 = $angularCacheFactory('cache2', { storageMode: 'sessionStorage' }); + cache.put('item', 'value'); + cache2.put('item', 'value'); - if (localStorage) { - expect(angular.fromJson(localStorage.getItem('angular-cache.caches.cache.data.item')).value).toEqual('value'); - } - if (sessionStorage) { - expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.cache2.data.item')).value).toEqual('value'); - } + if (localStorage) { + expect(angular.fromJson(localStorage.getItem('angular-cache.caches.cache.data.item')).value).toEqual('value'); + } + if (sessionStorage) { + expect(angular.fromJson(sessionStorage.getItem('angular-cache.caches.cache2.data.item')).value).toEqual('value'); + } - cache.setOptions({ storageMode: 'none' }, true); - cache2.setOptions({ storageMode: 'none' }, true); + cache.setOptions({ storageMode: 'none' }, true); + cache2.setOptions({ storageMode: 'none' }, true); - if (localStorage) { - expect(localStorage.getItem('angular-cache.caches.cache.data.item')).toEqual(null); - } - if (sessionStorage) { - expect(sessionStorage.getItem('angular-cache.caches.cache2.data.item')).toEqual(null); - } - }); + if (localStorage) { + expect(localStorage.getItem('angular-cache.caches.cache.data.item')).toEqual(null); + } + if (sessionStorage) { + expect(sessionStorage.getItem('angular-cache.caches.cache2.data.item')).toEqual(null); + } + }); }); diff --git a/test/angularCacheFactory-test.js b/test/angularCacheFactory-test.js index e29868a..283f605 100644 --- a/test/angularCacheFactory-test.js +++ b/test/angularCacheFactory-test.js @@ -1,338 +1,338 @@ describe('$angularCacheFactory(cacheId, options)', function () { - it('should be able to create a default cache.', function () { - var cache = $angularCacheFactory('cache'); - expect(cache).toBeDefined(); - expect(cache.info().id).toEqual('cache'); - expect(cache.info().capacity).toEqual(CACHE_DEFAULTS.capacity); - expect(cache.info().maxAge).toEqual(CACHE_DEFAULTS.maxAge); - expect(cache.info().cacheFlushInterval).toEqual(CACHE_DEFAULTS.cacheFlushInterval); - expect(cache.info().deleteOnExpire).toEqual(CACHE_DEFAULTS.deleteOnExpire); - expect(cache.info().onExpire).toEqual(CACHE_DEFAULTS.onExpire); - expect(cache.info().recycleFreq).toEqual(CACHE_DEFAULTS.recycleFreq); - expect(cache.info().storageMode).toEqual(CACHE_DEFAULTS.storageMode); - expect(cache.info().storageImpl).toEqual(CACHE_DEFAULTS.storageImpl); - expect(cache.info().verifyIntegrity).toEqual(CACHE_DEFAULTS.verifyIntegrity); - cache.destroy(); - }); - it('should be able to create a cache with options.', function () { - var options = { - capacity: Math.floor((Math.random() * 100000) + 1), - maxAge: Math.floor((Math.random() * 100000) + 1), - cacheFlushInterval: Math.floor((Math.random() * 100000) + 1), - deleteOnExpire: 'aggressive', - storageMode: 'localStorage', - localStorageImpl: { - setItem: function () { - }, - getItem: function () { - }, - removeItem: function () { - } - }, - verifyIntegrity: false, - recycleFreq: 2000, - onExpire: function () { - }, - readOnGet: false - }; - var cache = $angularCacheFactory('cache', options); - expect(cache).toBeDefined(); - expect(cache.info().id).toEqual('cache'); - expect(cache.info().capacity).toEqual(options.capacity); - expect(cache.info().maxAge).toEqual(options.maxAge); - expect(cache.info().cacheFlushInterval).toEqual(options.cacheFlushInterval); - expect(cache.info().deleteOnExpire).toEqual(options.deleteOnExpire); - expect(cache.info().storageMode).toEqual(options.storageMode); - expect(cache.info().localStorageImpl).not.toBeDefined(); // We don't expose this to the user - expect(cache.info().onExpire).toEqual(options.onExpire); - cache.destroy(); - expect($angularCacheFactory.get('cache')).not.toBeDefined(); - }); - it('should throw an exception if "capacity" is not a number or is less than zero.', function () { - try { - $angularCacheFactory('capacityCache99', { capacity: Math.floor((Math.random() * 100000) + 1) * -1 }); - fail(); - } catch (err) { - var msg = err.message; + it('should be able to create a default cache.', function () { + var cache = $angularCacheFactory('cache'); + expect(cache).toBeDefined(); + expect(cache.info().id).toEqual('cache'); + expect(cache.info().capacity).toEqual(CACHE_DEFAULTS.capacity); + expect(cache.info().maxAge).toEqual(CACHE_DEFAULTS.maxAge); + expect(cache.info().cacheFlushInterval).toEqual(CACHE_DEFAULTS.cacheFlushInterval); + expect(cache.info().deleteOnExpire).toEqual(CACHE_DEFAULTS.deleteOnExpire); + expect(cache.info().onExpire).toEqual(CACHE_DEFAULTS.onExpire); + expect(cache.info().recycleFreq).toEqual(CACHE_DEFAULTS.recycleFreq); + expect(cache.info().storageMode).toEqual(CACHE_DEFAULTS.storageMode); + expect(cache.info().storageImpl).toEqual(CACHE_DEFAULTS.storageImpl); + expect(cache.info().verifyIntegrity).toEqual(CACHE_DEFAULTS.verifyIntegrity); + cache.destroy(); + }); + it('should be able to create a cache with options.', function () { + var options = { + capacity: Math.floor((Math.random() * 100000) + 1), + maxAge: Math.floor((Math.random() * 100000) + 1), + cacheFlushInterval: Math.floor((Math.random() * 100000) + 1), + deleteOnExpire: 'aggressive', + storageMode: 'localStorage', + localStorageImpl: { + setItem: function () { + }, + getItem: function () { + }, + removeItem: function () { } - expect(msg).toEqual('capacity: must be greater than zero!'); - for (var i = 0; i < TYPES_EXCEPT_NUMBER.length; i++) { - try { - $angularCacheFactory('capacityCache' + i, { capacity: TYPES_EXCEPT_NUMBER[i] }); - fail(); - } catch (err) { - expect(err.message).toEqual('capacity: must be a number!'); - continue; - } - fail(); + }, + verifyIntegrity: false, + recycleFreq: 2000, + onExpire: function () { + }, + readOnGet: false + }; + var cache = $angularCacheFactory('cache', options); + expect(cache).toBeDefined(); + expect(cache.info().id).toEqual('cache'); + expect(cache.info().capacity).toEqual(options.capacity); + expect(cache.info().maxAge).toEqual(options.maxAge); + expect(cache.info().cacheFlushInterval).toEqual(options.cacheFlushInterval); + expect(cache.info().deleteOnExpire).toEqual(options.deleteOnExpire); + expect(cache.info().storageMode).toEqual(options.storageMode); + expect(cache.info().localStorageImpl).not.toBeDefined(); // We don't expose this to the user + expect(cache.info().onExpire).toEqual(options.onExpire); + cache.destroy(); + expect($angularCacheFactory.get('cache')).not.toBeDefined(); + }); + it('should throw an exception if "capacity" is not a number or is less than zero.', function () { + try { + $angularCacheFactory('capacityCache99', { capacity: Math.floor((Math.random() * 100000) + 1) * -1 }); + fail(); + } catch (err) { + var msg = err.message; + } + expect(msg).toEqual('capacity: must be greater than zero!'); + for (var i = 0; i < TYPES_EXCEPT_NUMBER.length; i++) { + try { + $angularCacheFactory('capacityCache' + i, { capacity: TYPES_EXCEPT_NUMBER[i] }); + fail(); + } catch (err) { + expect(err.message).toEqual('capacity: must be a number!'); + continue; + } + fail(); + } + }); + it('should validate maxAge.', function () { + var maxAge = Math.floor((Math.random() * 100000) + 1) * -1; + try { + $angularCacheFactory('maxAgeCache99', { maxAge: maxAge }); + fail(); + } catch (err) { + var msg = err.message; + } + expect(msg).toEqual('maxAge: must be greater than zero!'); + for (var i = 0; i < TYPES_EXCEPT_NUMBER.length; i++) { + try { + $angularCacheFactory('maxAgeCache' + i, { maxAge: TYPES_EXCEPT_NUMBER[i] }); + if (TYPES_EXCEPT_NUMBER[i] !== null) { + fail(TYPES_EXCEPT_NUMBER[i]); } - }); - it('should validate maxAge.', function () { - var maxAge = Math.floor((Math.random() * 100000) + 1) * -1; - try { - $angularCacheFactory('maxAgeCache99', { maxAge: maxAge }); - fail(); - } catch (err) { - var msg = err.message; + } catch (err) { + expect(err.message).toEqual('maxAge: must be a number!'); + continue; + } + if (TYPES_EXCEPT_NUMBER[i] !== null) { + fail(TYPES_EXCEPT_NUMBER[i]); + } + } + }); + it('should validate cacheFlushInterval.', function () { + var cacheFlushInterval = Math.floor((Math.random() * 100000) + 1) * -1; + try { + $angularCacheFactory('cacheFlushIntervalCache99', { cacheFlushInterval: cacheFlushInterval }); + fail(); + } catch (err) { + var msg = err.message; + } + expect(msg).toEqual('cacheFlushInterval: must be greater than zero!'); + for (var i = 0; i < TYPES_EXCEPT_NUMBER.length; i++) { + try { + $angularCacheFactory('cacheFlushIntervalCache' + i, { cacheFlushInterval: TYPES_EXCEPT_NUMBER[i] }); + if (TYPES_EXCEPT_NUMBER[i] !== null) { + fail(); } - expect(msg).toEqual('maxAge: must be greater than zero!'); - for (var i = 0; i < TYPES_EXCEPT_NUMBER.length; i++) { - try { - $angularCacheFactory('maxAgeCache' + i, { maxAge: TYPES_EXCEPT_NUMBER[i] }); - if (TYPES_EXCEPT_NUMBER[i] !== null) { - fail(TYPES_EXCEPT_NUMBER[i]); - } - } catch (err) { - expect(err.message).toEqual('maxAge: must be a number!'); - continue; - } - if (TYPES_EXCEPT_NUMBER[i] !== null) { - fail(TYPES_EXCEPT_NUMBER[i]); - } + } catch (err) { + expect(err.message).toEqual('cacheFlushInterval: must be a number!'); + continue; + } + if (TYPES_EXCEPT_NUMBER[i] !== null) { + fail(); + } + } + }); + it('should validate recycleFreq.', function () { + var recycleFreq = Math.floor((Math.random() * 100000) + 1) * -1; + try { + $angularCacheFactory('recycleFreqCache99', { recycleFreq: recycleFreq }); + fail(); + } catch (err) { + var msg = err.message; + } + expect(msg).toEqual('recycleFreq: must be greater than zero!'); + for (var i = 0; i < TYPES_EXCEPT_NUMBER.length; i++) { + try { + $angularCacheFactory('recycleFreqCache' + i, { recycleFreq: TYPES_EXCEPT_NUMBER[i] }); + if (TYPES_EXCEPT_NUMBER[i] !== null) { + fail(); } - }); - it('should validate cacheFlushInterval.', function () { - var cacheFlushInterval = Math.floor((Math.random() * 100000) + 1) * -1; - try { - $angularCacheFactory('cacheFlushIntervalCache99', { cacheFlushInterval: cacheFlushInterval }); - fail(); - } catch (err) { - var msg = err.message; + } catch (err) { + expect(err.message).toEqual('recycleFreq: must be a number!'); + continue; + } + if (TYPES_EXCEPT_NUMBER[i] !== null) { + fail(); + } + } + }); + it('should validate onExpire.', function () { + var onExpire = 234; + try { + $angularCacheFactory('onExpireCache99', { onExpire: onExpire }); + expect('should not reach this!').toEqual(false); + } catch (err) { + var msg = err.message; + } + expect(msg).toEqual('onExpire: must be a function!'); + for (var i = 0; i < TYPES_EXCEPT_FUNCTION.length; i++) { + try { + $angularCacheFactory('onExpireCache' + i, { onExpire: TYPES_EXCEPT_FUNCTION[i] }); + if (TYPES_EXCEPT_FUNCTION[i] !== null) { + fail(); } - expect(msg).toEqual('cacheFlushInterval: must be greater than zero!'); - for (var i = 0; i < TYPES_EXCEPT_NUMBER.length; i++) { - try { - $angularCacheFactory('cacheFlushIntervalCache' + i, { cacheFlushInterval: TYPES_EXCEPT_NUMBER[i] }); - if (TYPES_EXCEPT_NUMBER[i] !== null) { - fail(); - } - } catch (err) { - expect(err.message).toEqual('cacheFlushInterval: must be a number!'); - continue; - } - if (TYPES_EXCEPT_NUMBER[i] !== null) { - fail(); - } + } catch (err) { + expect(err.message).toEqual('onExpire: must be a function!'); + continue; + } + if (TYPES_EXCEPT_FUNCTION[i] !== null) { + fail(); + } + } + }); + it('should validate deleteOnExpire.', function () { + var deleteOnExpire = 'fail'; + try { + $angularCacheFactory('cache', { deleteOnExpire: deleteOnExpire }); + fail('should not reach this!'); + } catch (err) { + var msg = err.message; + } + expect(msg).toEqual('deleteOnExpire: accepted values are "none", "passive" or "aggressive"!'); + for (var i = 0; i < TYPES_EXCEPT_STRING.length; i++) { + try { + $angularCacheFactory('deleteOnExpireCache' + i, { deleteOnExpire: TYPES_EXCEPT_STRING[i] }); + fail(TYPES_EXCEPT_STRING[i]); + } catch (err) { + expect(err.message).toEqual('deleteOnExpire: must be a string!'); + continue; + } + fail(TYPES_EXCEPT_STRING[i]); + } + }); + it('should validate storageMode.', function () { + var storageMode = 'fail'; + try { + $angularCacheFactory('cache', { storageMode: storageMode }); + expect('should not reach this!').toEqual(false); + } catch (err) { + var msg = err.message; + } + expect(msg).toEqual('storageMode: accepted values are "none", "localStorage" or "sessionStorage"!'); + for (var i = 0; i < TYPES_EXCEPT_STRING.length; i++) { + try { + $angularCacheFactory('storageModeCache' + i, { storageMode: TYPES_EXCEPT_STRING[i] }); + fail(TYPES_EXCEPT_STRING[i]); + } catch (err) { + expect(err.message).toEqual('storageMode: must be a string!'); + continue; + } + fail(TYPES_EXCEPT_STRING[i]); + } + }); + it('should validate storageImpl.', function () { + var storageImpl = 'fail'; + try { + $angularCacheFactory('cache', { storageMode: 'localStorage', storageImpl: storageImpl }); + expect('should not reach this!').toEqual(false); + } catch (err) { + var msg = err.message; + } + expect(msg).toEqual('storageImpl: must be an object!'); + for (var i = 0; i < TYPES_EXCEPT_OBJECT.length; i++) { + try { + $angularCacheFactory('storageImplCache' + i, { storageMode: 'localStorage', storageImpl: TYPES_EXCEPT_OBJECT[i] }); + if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { + fail(TYPES_EXCEPT_OBJECT[i]); } - }); - it('should validate recycleFreq.', function () { - var recycleFreq = Math.floor((Math.random() * 100000) + 1) * -1; - try { - $angularCacheFactory('recycleFreqCache99', { recycleFreq: recycleFreq }); - fail(); - } catch (err) { - var msg = err.message; + } catch (err) { + expect(err.message.length).not.toEqual(0); + continue; + } + if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { + fail(TYPES_EXCEPT_OBJECT[i]); + } + } + try { + $angularCacheFactory('storageImplCache-noSetItem', { + storageMode: 'localStorage', + storageImpl: { + getItem: function () { + }, + removeItem: function () { + } } - expect(msg).toEqual('recycleFreq: must be greater than zero!'); - for (var i = 0; i < TYPES_EXCEPT_NUMBER.length; i++) { - try { - $angularCacheFactory('recycleFreqCache' + i, { recycleFreq: TYPES_EXCEPT_NUMBER[i] }); - if (TYPES_EXCEPT_NUMBER[i] !== null) { - fail(); - } - } catch (err) { - expect(err.message).toEqual('recycleFreq: must be a number!'); - continue; - } - if (TYPES_EXCEPT_NUMBER[i] !== null) { - fail(); - } + }); + fail(); + } catch (err) { + expect(err.message.length).not.toEqual(0); + } + try { + $angularCacheFactory('storageImplCache-noGetItem', { + storageMode: 'localStorage', + storageImpl: { + setItem: function () { + }, + removeItem: function () { + } } - }); - it('should validate onExpire.', function () { - var onExpire = 234; - try { - $angularCacheFactory('onExpireCache99', { onExpire: onExpire }); - expect('should not reach this!').toEqual(false); - } catch (err) { - var msg = err.message; + }); + fail(); + } catch (err) { + expect(err.message.length).not.toEqual(0); + } + try { + $angularCacheFactory('storageImplCache-noRemoveItem', { + storageMode: 'localStorage', + storageImpl: { + getItem: function () { + }, + setItem: function () { + } } - expect(msg).toEqual('onExpire: must be a function!'); - for (var i = 0; i < TYPES_EXCEPT_FUNCTION.length; i++) { - try { - $angularCacheFactory('onExpireCache' + i, { onExpire: TYPES_EXCEPT_FUNCTION[i] }); - if (TYPES_EXCEPT_FUNCTION[i] !== null) { - fail(); - } - } catch (err) { - expect(err.message).toEqual('onExpire: must be a function!'); - continue; - } - if (TYPES_EXCEPT_FUNCTION[i] !== null) { - fail(); - } + }); + fail(); + } catch (err) { + expect(err.message.length).not.toEqual(0); + } + try { + $angularCacheFactory('storageImplCache-stringGetItem', { + storageMode: 'localStorage', + storageImpl: { + getItem: 'should not be a string', + setItem: function () { + }, + removeItem: function () { + } } - }); - it('should validate deleteOnExpire.', function () { - var deleteOnExpire = 'fail'; - try { - $angularCacheFactory('cache', { deleteOnExpire: deleteOnExpire }); - fail('should not reach this!'); - } catch (err) { - var msg = err.message; + }); + fail(); + } catch (err) { + expect(err.message.length).not.toEqual(0); + } + try { + $angularCacheFactory('storageImplCache-stringSetItem', { + storageMode: 'localStorage', + storageImpl: { + getItem: function () { + }, + setItem: 'should not be a string', + removeItem: function () { + } } - expect(msg).toEqual('deleteOnExpire: accepted values are "none", "passive" or "aggressive"!'); - for (var i = 0; i < TYPES_EXCEPT_STRING.length; i++) { - try { - $angularCacheFactory('deleteOnExpireCache' + i, { deleteOnExpire: TYPES_EXCEPT_STRING[i] }); - fail(TYPES_EXCEPT_STRING[i]); - } catch (err) { - expect(err.message).toEqual('deleteOnExpire: must be a string!'); - continue; - } - fail(TYPES_EXCEPT_STRING[i]); + }); + fail(); + } catch (err) { + expect(err.message.length).not.toEqual(0); + } + try { + $angularCacheFactory('storageImplCache-stringRemoveItem', { + storageMode: 'localStorage', + storageImpl: { + setItem: function () { + }, + getItem: function () { + }, + removeItem: 'should not be a string' } - }); - it('should validate storageMode.', function () { - var storageMode = 'fail'; - try { - $angularCacheFactory('cache', { storageMode: storageMode }); - expect('should not reach this!').toEqual(false); - } catch (err) { - var msg = err.message; - } - expect(msg).toEqual('storageMode: accepted values are "none", "localStorage" or "sessionStorage"!'); - for (var i = 0; i < TYPES_EXCEPT_STRING.length; i++) { - try { - $angularCacheFactory('storageModeCache' + i, { storageMode: TYPES_EXCEPT_STRING[i] }); - fail(TYPES_EXCEPT_STRING[i]); - } catch (err) { - expect(err.message).toEqual('storageMode: must be a string!'); - continue; - } - fail(TYPES_EXCEPT_STRING[i]); - } - }); - it('should validate storageImpl.', function () { - var storageImpl = 'fail'; - try { - $angularCacheFactory('cache', { storageMode: 'localStorage', storageImpl: storageImpl }); - expect('should not reach this!').toEqual(false); - } catch (err) { - var msg = err.message; - } - expect(msg).toEqual('storageImpl: must be an object!'); - for (var i = 0; i < TYPES_EXCEPT_OBJECT.length; i++) { - try { - $angularCacheFactory('storageImplCache' + i, { storageMode: 'localStorage', storageImpl: TYPES_EXCEPT_OBJECT[i] }); - if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { - fail(TYPES_EXCEPT_OBJECT[i]); - } - } catch (err) { - expect(err.message.length).not.toEqual(0); - continue; - } - if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { - fail(TYPES_EXCEPT_OBJECT[i]); - } - } - try { - $angularCacheFactory('storageImplCache-noSetItem', { - storageMode: 'localStorage', - storageImpl: { - getItem: function () { - }, - removeItem: function () { - } - } - }); - fail(); - } catch (err) { - expect(err.message.length).not.toEqual(0); - } - try { - $angularCacheFactory('storageImplCache-noGetItem', { - storageMode: 'localStorage', - storageImpl: { - setItem: function () { - }, - removeItem: function () { - } - } - }); - fail(); - } catch (err) { - expect(err.message.length).not.toEqual(0); - } - try { - $angularCacheFactory('storageImplCache-noRemoveItem', { - storageMode: 'localStorage', - storageImpl: { - getItem: function () { - }, - setItem: function () { - } - } - }); - fail(); - } catch (err) { - expect(err.message.length).not.toEqual(0); - } - try { - $angularCacheFactory('storageImplCache-stringGetItem', { - storageMode: 'localStorage', - storageImpl: { - getItem: 'should not be a string', - setItem: function () { - }, - removeItem: function () { - } - } - }); - fail(); - } catch (err) { - expect(err.message.length).not.toEqual(0); - } - try { - $angularCacheFactory('storageImplCache-stringSetItem', { - storageMode: 'localStorage', - storageImpl: { - getItem: function () { - }, - setItem: 'should not be a string', - removeItem: function () { - } - } - }); - fail(); - } catch (err) { - expect(err.message.length).not.toEqual(0); - } - try { - $angularCacheFactory('storageImplCache-stringRemoveItem', { - storageMode: 'localStorage', - storageImpl: { - setItem: function () { - }, - getItem: function () { - }, - removeItem: 'should not be a string' - } - }); - fail(); - } catch (err) { - expect(err.message.length).not.toEqual(0); - } - }); - it('should prevent a cache from being duplicated.', function () { - try { - $angularCacheFactory('cache'); - $angularCacheFactory('cache'); - } catch (err) { - var msg = err.message; - } - expect(msg).toEqual('cacheId cache taken!'); - }); - it('should require cacheId to be a string.', function () { - for (var i = 0; i < TYPES_EXCEPT_STRING.length; i++) { - try { - $angularCacheFactory(TYPES_EXCEPT_STRING[i]); - fail(TYPES_EXCEPT_STRING[i]); - } catch (err) { - expect(err.message.length).not.toEqual(0); - continue; - } - fail(TYPES_EXCEPT_STRING[i]); - } - }); -}); \ No newline at end of file + }); + fail(); + } catch (err) { + expect(err.message.length).not.toEqual(0); + } + }); + it('should prevent a cache from being duplicated.', function () { + try { + $angularCacheFactory('cache'); + $angularCacheFactory('cache'); + } catch (err) { + var msg = err.message; + } + expect(msg).toEqual('cacheId cache taken!'); + }); + it('should require cacheId to be a string.', function () { + for (var i = 0; i < TYPES_EXCEPT_STRING.length; i++) { + try { + $angularCacheFactory(TYPES_EXCEPT_STRING[i]); + fail(TYPES_EXCEPT_STRING[i]); + } catch (err) { + expect(err.message.length).not.toEqual(0); + continue; + } + fail(TYPES_EXCEPT_STRING[i]); + } + }); +}); diff --git a/test/angularCacheFactory.clearAll-test.js b/test/angularCacheFactory.clearAll-test.js index ef333fd..e47c5cb 100644 --- a/test/angularCacheFactory.clearAll-test.js +++ b/test/angularCacheFactory.clearAll-test.js @@ -1,52 +1,52 @@ describe('$angularCacheFactory.clearAll()', function () { - it('should call "removeAll()" on all caches in $angularCacheFactory.', function () { - var cacheKeys = ['cache', 'cache1', 'cache2'], - caches = []; + it('should call "removeAll()" on all caches in $angularCacheFactory.', function () { + var cacheKeys = ['cache', 'cache1', 'cache2'], + caches = []; - caches.push($angularCacheFactory(cacheKeys[0])); - caches[0].put('item', 'value'); - caches[0].put('item2', 'value2'); - caches.push($angularCacheFactory(cacheKeys[1])); - caches[1].put('item', 'value'); - caches[1].put('item2', 'value2'); - caches.push($angularCacheFactory(cacheKeys[2])); - caches[2].put('item', 'value'); - caches[2].put('item2', 'value2'); + caches.push($angularCacheFactory(cacheKeys[0])); + caches[0].put('item', 'value'); + caches[0].put('item2', 'value2'); + caches.push($angularCacheFactory(cacheKeys[1])); + caches[1].put('item', 'value'); + caches[1].put('item2', 'value2'); + caches.push($angularCacheFactory(cacheKeys[2])); + caches[2].put('item', 'value'); + caches[2].put('item2', 'value2'); - spyOn(caches[0], 'removeAll'); - spyOn(caches[1], 'removeAll'); - spyOn(caches[2], 'removeAll'); + spyOn(caches[0], 'removeAll'); + spyOn(caches[1], 'removeAll'); + spyOn(caches[2], 'removeAll'); - $angularCacheFactory.clearAll(); + $angularCacheFactory.clearAll(); - expect(caches[0].removeAll.callCount).toEqual(1); - expect(caches[1].removeAll.callCount).toEqual(1); - expect(caches[2].removeAll.callCount).toEqual(1); - }); - it('should result in each cache being cleared.', function () { - var cacheKeys = ['cache', 'cache1', 'cache2'], - caches = []; + expect(caches[0].removeAll.callCount).toEqual(1); + expect(caches[1].removeAll.callCount).toEqual(1); + expect(caches[2].removeAll.callCount).toEqual(1); + }); + it('should result in each cache being cleared.', function () { + var cacheKeys = ['cache', 'cache1', 'cache2'], + caches = []; - caches.push($angularCacheFactory(cacheKeys[0])); - caches[0].put('item', 'value'); - caches[0].put('item2', 'value2'); - caches.push($angularCacheFactory(cacheKeys[1])); - caches[1].put('item', 'value'); - caches[1].put('item2', 'value2'); - caches.push($angularCacheFactory(cacheKeys[2])); - caches[2].put('item', 'value'); - caches[2].put('item2', 'value2'); + caches.push($angularCacheFactory(cacheKeys[0])); + caches[0].put('item', 'value'); + caches[0].put('item2', 'value2'); + caches.push($angularCacheFactory(cacheKeys[1])); + caches[1].put('item', 'value'); + caches[1].put('item2', 'value2'); + caches.push($angularCacheFactory(cacheKeys[2])); + caches[2].put('item', 'value'); + caches[2].put('item2', 'value2'); - $angularCacheFactory.clearAll(); + $angularCacheFactory.clearAll(); - expect(caches[0].get('item')).not.toBeDefined(); - expect(caches[1].get('item')).not.toBeDefined(); - expect(caches[2].get('item')).not.toBeDefined(); - expect(caches[0].get('item2')).not.toBeDefined(); - expect(caches[1].get('item2')).not.toBeDefined(); - expect(caches[2].get('item2')).not.toBeDefined(); - expect(caches[0].info().size).toEqual(0); - expect(caches[1].info().size).toEqual(0); - expect(caches[2].info().size).toEqual(0); - }); + expect(caches[0].get('item')).not.toBeDefined(); + expect(caches[1].get('item')).not.toBeDefined(); + expect(caches[2].get('item')).not.toBeDefined(); + expect(caches[0].get('item2')).not.toBeDefined(); + expect(caches[1].get('item2')).not.toBeDefined(); + expect(caches[2].get('item2')).not.toBeDefined(); + expect(caches[0].info().size).toEqual(0); + expect(caches[1].info().size).toEqual(0); + expect(caches[2].info().size).toEqual(0); + }); }); diff --git a/test/angularCacheFactory.disableAll-test.js b/test/angularCacheFactory.disableAll-test.js index da7d899..198b25d 100644 --- a/test/angularCacheFactory.disableAll-test.js +++ b/test/angularCacheFactory.disableAll-test.js @@ -1,19 +1,19 @@ describe('$angularCacheFactory.disableAll()', function () { - it('should disable all caches in $angularCacheFactory.', function () { - var cacheKeys = ['cache', 'cache1', 'cache2']; + it('should disable all caches in $angularCacheFactory.', function () { + var cacheKeys = ['cache', 'cache1', 'cache2']; - $angularCacheFactory(cacheKeys[0]); - $angularCacheFactory(cacheKeys[1], { disabled: true }); - $angularCacheFactory(cacheKeys[2]); + $angularCacheFactory(cacheKeys[0]); + $angularCacheFactory(cacheKeys[1], { disabled: true }); + $angularCacheFactory(cacheKeys[2]); - expect($angularCacheFactory.get(cacheKeys[0]).info().disabled).toEqual(false); - expect($angularCacheFactory.get(cacheKeys[1]).info().disabled).toEqual(true); - expect($angularCacheFactory.get(cacheKeys[2]).info().disabled).toEqual(false); + expect($angularCacheFactory.get(cacheKeys[0]).info().disabled).toEqual(false); + expect($angularCacheFactory.get(cacheKeys[1]).info().disabled).toEqual(true); + expect($angularCacheFactory.get(cacheKeys[2]).info().disabled).toEqual(false); - $angularCacheFactory.disableAll(); + $angularCacheFactory.disableAll(); - expect($angularCacheFactory.get(cacheKeys[0]).info().disabled).toEqual(true); - expect($angularCacheFactory.get(cacheKeys[1]).info().disabled).toEqual(true); - expect($angularCacheFactory.get(cacheKeys[2]).info().disabled).toEqual(true); - }); + expect($angularCacheFactory.get(cacheKeys[0]).info().disabled).toEqual(true); + expect($angularCacheFactory.get(cacheKeys[1]).info().disabled).toEqual(true); + expect($angularCacheFactory.get(cacheKeys[2]).info().disabled).toEqual(true); + }); }); diff --git a/test/angularCacheFactory.enableAll-test.js b/test/angularCacheFactory.enableAll-test.js index 833e3c0..d43533a 100644 --- a/test/angularCacheFactory.enableAll-test.js +++ b/test/angularCacheFactory.enableAll-test.js @@ -1,19 +1,19 @@ describe('$angularCacheFactory.enableAll()', function () { - it('should enable all caches in $angularCacheFactory.', function () { - var cacheKeys = ['cache', 'cache1', 'cache2']; + it('should enable all caches in $angularCacheFactory.', function () { + var cacheKeys = ['cache', 'cache1', 'cache2']; - $angularCacheFactory(cacheKeys[0], { disabled: true }); - $angularCacheFactory(cacheKeys[1]); - $angularCacheFactory(cacheKeys[2], { disabled: true }); + $angularCacheFactory(cacheKeys[0], { disabled: true }); + $angularCacheFactory(cacheKeys[1]); + $angularCacheFactory(cacheKeys[2], { disabled: true }); - expect($angularCacheFactory.get(cacheKeys[0]).info().disabled).toEqual(true); - expect($angularCacheFactory.get(cacheKeys[1]).info().disabled).toEqual(false); - expect($angularCacheFactory.get(cacheKeys[2]).info().disabled).toEqual(true); + expect($angularCacheFactory.get(cacheKeys[0]).info().disabled).toEqual(true); + expect($angularCacheFactory.get(cacheKeys[1]).info().disabled).toEqual(false); + expect($angularCacheFactory.get(cacheKeys[2]).info().disabled).toEqual(true); - $angularCacheFactory.enableAll(); + $angularCacheFactory.enableAll(); - expect($angularCacheFactory.get(cacheKeys[0]).info().disabled).toEqual(false); - expect($angularCacheFactory.get(cacheKeys[1]).info().disabled).toEqual(false); - expect($angularCacheFactory.get(cacheKeys[2]).info().disabled).toEqual(false); - }); + expect($angularCacheFactory.get(cacheKeys[0]).info().disabled).toEqual(false); + expect($angularCacheFactory.get(cacheKeys[1]).info().disabled).toEqual(false); + expect($angularCacheFactory.get(cacheKeys[2]).info().disabled).toEqual(false); + }); }); diff --git a/test/angularCacheFactory.get-test.js b/test/angularCacheFactory.get-test.js index b89d0ad..b5d21da 100644 --- a/test/angularCacheFactory.get-test.js +++ b/test/angularCacheFactory.get-test.js @@ -1,24 +1,24 @@ describe('$angularCacheFactory.get(cacheId)', function () { - it('should throw an exception if "cacheId" is not a string.', function () { - for (var i = 0; i < TYPES_EXCEPT_STRING.length; i++) { - try { - $angularCacheFactory.get(TYPES_EXCEPT_STRING[i]); - fail(); - } catch (err) { - expect(err.message).toEqual('$angularCacheFactory.get(cacheId): cacheId: must be a string!'); - continue; - } - fail(); - } - }); - it('should return "undefined" if the cache does not exist.', function () { - expect($angularCacheFactory.get('someNonExistentCache')).toEqual(undefined); - }); - it('should return the correct cache with the specified cacheId.', function () { - var cache = $angularCacheFactory('cache'), - cache2 = $angularCacheFactory('cache2'); - expect($angularCacheFactory.get('cache')).toEqual(cache); - expect($angularCacheFactory.get('cache2')).toEqual(cache2); - expect(cache).not.toEqual(cache2); - }); -}); \ No newline at end of file + it('should throw an exception if "cacheId" is not a string.', function () { + for (var i = 0; i < TYPES_EXCEPT_STRING.length; i++) { + try { + $angularCacheFactory.get(TYPES_EXCEPT_STRING[i]); + fail(); + } catch (err) { + expect(err.message).toEqual('$angularCacheFactory.get(cacheId): cacheId: must be a string!'); + continue; + } + fail(); + } + }); + it('should return "undefined" if the cache does not exist.', function () { + expect($angularCacheFactory.get('someNonExistentCache')).toEqual(undefined); + }); + it('should return the correct cache with the specified cacheId.', function () { + var cache = $angularCacheFactory('cache'), + cache2 = $angularCacheFactory('cache2'); + expect($angularCacheFactory.get('cache')).toEqual(cache); + expect($angularCacheFactory.get('cache2')).toEqual(cache2); + expect(cache).not.toEqual(cache2); + }); +}); diff --git a/test/angularCacheFactory.info-test.js b/test/angularCacheFactory.info-test.js index 9042f64..10eb9ea 100644 --- a/test/angularCacheFactory.info-test.js +++ b/test/angularCacheFactory.info-test.js @@ -1,45 +1,45 @@ describe('$angularCacheFactory.info()', function () { - it('should return the correct info for $angularCacheFactory.', function () { - var options = { - capacity: Math.floor((Math.random() * 100000) + 1), - maxAge: Math.floor((Math.random() * 100000) + 1), - cacheFlushInterval: Math.floor((Math.random() * 100000) + 1) - }, - caches = []; + it('should return the correct info for $angularCacheFactory.', function () { + var options = { + capacity: Math.floor((Math.random() * 100000) + 1), + maxAge: Math.floor((Math.random() * 100000) + 1), + cacheFlushInterval: Math.floor((Math.random() * 100000) + 1) + }, + caches = []; - caches.push($angularCacheFactory('cache')); - caches.push($angularCacheFactory('cache2', { - maxAge: options.maxAge - })); - caches.push($angularCacheFactory('cache3', { - capacity: options.capacity, - cacheFlushInterval: options.cacheFlushInterval - })); - var info = $angularCacheFactory.info(); - expect(info.size).toEqual(3); + caches.push($angularCacheFactory('cache')); + caches.push($angularCacheFactory('cache2', { + maxAge: options.maxAge + })); + caches.push($angularCacheFactory('cache3', { + capacity: options.capacity, + cacheFlushInterval: options.cacheFlushInterval + })); + var info = $angularCacheFactory.info(); + expect(info.size).toEqual(3); - expect(info.cacheDefaults.capacity).toEqual(CACHE_DEFAULTS.capacity); - expect(info.cacheDefaults.maxAge).toEqual(CACHE_DEFAULTS.maxAge); - expect(info.cacheDefaults.cacheFlushInterval).toEqual(CACHE_DEFAULTS.cacheFlushInterval); - expect(info.cacheDefaults.deleteOnExpire).toEqual(CACHE_DEFAULTS.deleteOnExpire); - expect(info.cacheDefaults.onExpire).toEqual(CACHE_DEFAULTS.onExpire); - expect(info.cacheDefaults.recycleFreq).toEqual(CACHE_DEFAULTS.recycleFreq); - expect(info.cacheDefaults.verifyIntegrity).toEqual(CACHE_DEFAULTS.verifyIntegrity); - expect(info.cacheDefaults.storageMode).toEqual(CACHE_DEFAULTS.storageMode); - expect(info.cacheDefaults.storageImpl).toEqual(CACHE_DEFAULTS.storageImpl); + expect(info.cacheDefaults.capacity).toEqual(CACHE_DEFAULTS.capacity); + expect(info.cacheDefaults.maxAge).toEqual(CACHE_DEFAULTS.maxAge); + expect(info.cacheDefaults.cacheFlushInterval).toEqual(CACHE_DEFAULTS.cacheFlushInterval); + expect(info.cacheDefaults.deleteOnExpire).toEqual(CACHE_DEFAULTS.deleteOnExpire); + expect(info.cacheDefaults.onExpire).toEqual(CACHE_DEFAULTS.onExpire); + expect(info.cacheDefaults.recycleFreq).toEqual(CACHE_DEFAULTS.recycleFreq); + expect(info.cacheDefaults.verifyIntegrity).toEqual(CACHE_DEFAULTS.verifyIntegrity); + expect(info.cacheDefaults.storageMode).toEqual(CACHE_DEFAULTS.storageMode); + expect(info.cacheDefaults.storageImpl).toEqual(CACHE_DEFAULTS.storageImpl); - expect(info.caches.cache.id).toEqual(caches[0].info().id); - expect(info.caches.cache.capacity).toEqual(caches[0].info().capacity); - expect(info.caches.cache.size).toEqual(caches[0].info().size); + expect(info.caches.cache.id).toEqual(caches[0].info().id); + expect(info.caches.cache.capacity).toEqual(caches[0].info().capacity); + expect(info.caches.cache.size).toEqual(caches[0].info().size); - expect(info.caches.cache2.id).toEqual(caches[1].info().id); - expect(info.caches.cache2.capacity).toEqual(caches[1].info().capacity); - expect(info.caches.cache2.size).toEqual(caches[1].info().size); - expect(info.caches.cache2.maxAge).toEqual(caches[1].info().maxAge); + expect(info.caches.cache2.id).toEqual(caches[1].info().id); + expect(info.caches.cache2.capacity).toEqual(caches[1].info().capacity); + expect(info.caches.cache2.size).toEqual(caches[1].info().size); + expect(info.caches.cache2.maxAge).toEqual(caches[1].info().maxAge); - expect(info.caches.cache3.id).toEqual(caches[2].info().id); - expect(info.caches.cache3.capacity).toEqual(caches[2].info().capacity); - expect(info.caches.cache3.size).toEqual(caches[2].info().size); - expect(info.caches.cache3.cacheFlushInterval).toEqual(caches[2].info().cacheFlushInterval); - }); + expect(info.caches.cache3.id).toEqual(caches[2].info().id); + expect(info.caches.cache3.capacity).toEqual(caches[2].info().capacity); + expect(info.caches.cache3.size).toEqual(caches[2].info().size); + expect(info.caches.cache3.cacheFlushInterval).toEqual(caches[2].info().cacheFlushInterval); + }); }); diff --git a/test/angularCacheFactory.keySet-test.js b/test/angularCacheFactory.keySet-test.js index 5317f31..9b09420 100644 --- a/test/angularCacheFactory.keySet-test.js +++ b/test/angularCacheFactory.keySet-test.js @@ -1,48 +1,48 @@ describe('$angularCacheFactory.keySet()', function () { - it('should return the set of keys of all caches in $angularCacheFactory.', function () { - var cacheKeys = ['cache', 'cache1', 'cache2']; - - $angularCacheFactory(cacheKeys[0]); - $angularCacheFactory(cacheKeys[1]); - $angularCacheFactory(cacheKeys[2]); - - var keySet = $angularCacheFactory.keySet(); - - expect(keySet.hasOwnProperty(cacheKeys[0])).toEqual(true); - expect(keySet.hasOwnProperty(cacheKeys[1])).toEqual(true); - expect(keySet.hasOwnProperty(cacheKeys[2])).toEqual(true); - - expect(keySet[cacheKeys[0]]).toEqual(cacheKeys[0]); - expect(keySet[cacheKeys[1]]).toEqual(cacheKeys[1]); - expect(keySet[cacheKeys[2]]).toEqual(cacheKeys[2]); - - $angularCacheFactory.get(cacheKeys[0]).destroy(); - keySet = $angularCacheFactory.keySet(); - expect(keySet.hasOwnProperty(cacheKeys[0])).toEqual(false); - expect(keySet.hasOwnProperty(cacheKeys[1])).toEqual(true); - expect(keySet.hasOwnProperty(cacheKeys[2])).toEqual(true); - expect(keySet[cacheKeys[0]]).not.toBeDefined(); - expect(keySet[cacheKeys[1]]).toEqual(cacheKeys[1]); - expect(keySet[cacheKeys[2]]).toEqual(cacheKeys[2]); - - $angularCacheFactory.get(cacheKeys[1]).destroy(); - keySet = $angularCacheFactory.keySet(); - expect(keySet.hasOwnProperty(cacheKeys[0])).toEqual(false); - expect(keySet.hasOwnProperty(cacheKeys[1])).toEqual(false); - expect(keySet.hasOwnProperty(cacheKeys[2])).toEqual(true); - expect(keySet[cacheKeys[0]]).not.toBeDefined(); - expect(keySet[cacheKeys[1]]).not.toBeDefined(); - expect(keySet[cacheKeys[2]]).toEqual(cacheKeys[2]); - - $angularCacheFactory.get(cacheKeys[2]).destroy(); - - keySet = $angularCacheFactory.keySet(); - - expect(keySet.hasOwnProperty(cacheKeys[0])).toEqual(false); - expect(keySet.hasOwnProperty(cacheKeys[1])).toEqual(false); - expect(keySet.hasOwnProperty(cacheKeys[2])).toEqual(false); - expect(keySet[cacheKeys[0]]).not.toBeDefined(); - expect(keySet[cacheKeys[1]]).not.toBeDefined(); - expect(keySet[cacheKeys[2]]).not.toBeDefined(); - }); + it('should return the set of keys of all caches in $angularCacheFactory.', function () { + var cacheKeys = ['cache', 'cache1', 'cache2']; + + $angularCacheFactory(cacheKeys[0]); + $angularCacheFactory(cacheKeys[1]); + $angularCacheFactory(cacheKeys[2]); + + var keySet = $angularCacheFactory.keySet(); + + expect(keySet.hasOwnProperty(cacheKeys[0])).toEqual(true); + expect(keySet.hasOwnProperty(cacheKeys[1])).toEqual(true); + expect(keySet.hasOwnProperty(cacheKeys[2])).toEqual(true); + + expect(keySet[cacheKeys[0]]).toEqual(cacheKeys[0]); + expect(keySet[cacheKeys[1]]).toEqual(cacheKeys[1]); + expect(keySet[cacheKeys[2]]).toEqual(cacheKeys[2]); + + $angularCacheFactory.get(cacheKeys[0]).destroy(); + keySet = $angularCacheFactory.keySet(); + expect(keySet.hasOwnProperty(cacheKeys[0])).toEqual(false); + expect(keySet.hasOwnProperty(cacheKeys[1])).toEqual(true); + expect(keySet.hasOwnProperty(cacheKeys[2])).toEqual(true); + expect(keySet[cacheKeys[0]]).not.toBeDefined(); + expect(keySet[cacheKeys[1]]).toEqual(cacheKeys[1]); + expect(keySet[cacheKeys[2]]).toEqual(cacheKeys[2]); + + $angularCacheFactory.get(cacheKeys[1]).destroy(); + keySet = $angularCacheFactory.keySet(); + expect(keySet.hasOwnProperty(cacheKeys[0])).toEqual(false); + expect(keySet.hasOwnProperty(cacheKeys[1])).toEqual(false); + expect(keySet.hasOwnProperty(cacheKeys[2])).toEqual(true); + expect(keySet[cacheKeys[0]]).not.toBeDefined(); + expect(keySet[cacheKeys[1]]).not.toBeDefined(); + expect(keySet[cacheKeys[2]]).toEqual(cacheKeys[2]); + + $angularCacheFactory.get(cacheKeys[2]).destroy(); + + keySet = $angularCacheFactory.keySet(); + + expect(keySet.hasOwnProperty(cacheKeys[0])).toEqual(false); + expect(keySet.hasOwnProperty(cacheKeys[1])).toEqual(false); + expect(keySet.hasOwnProperty(cacheKeys[2])).toEqual(false); + expect(keySet[cacheKeys[0]]).not.toBeDefined(); + expect(keySet[cacheKeys[1]]).not.toBeDefined(); + expect(keySet[cacheKeys[2]]).not.toBeDefined(); + }); }); diff --git a/test/angularCacheFactory.keys-test.js b/test/angularCacheFactory.keys-test.js index 007b3a9..61df360 100644 --- a/test/angularCacheFactory.keys-test.js +++ b/test/angularCacheFactory.keys-test.js @@ -1,32 +1,32 @@ describe('$angularCacheFactory.keys()', function () { - it('should return the array of keys of all caches in $angularCacheFactory.', function () { - var cacheKeys = ['cache', 'cache1', 'cache2']; + it('should return the array of keys of all caches in $angularCacheFactory.', function () { + var cacheKeys = ['cache', 'cache1', 'cache2']; - $angularCacheFactory(cacheKeys[0]); - $angularCacheFactory(cacheKeys[1]); - $angularCacheFactory(cacheKeys[2]); + $angularCacheFactory(cacheKeys[0]); + $angularCacheFactory(cacheKeys[1]); + $angularCacheFactory(cacheKeys[2]); - var keys = $angularCacheFactory.keys(); - expect(keys.length).toEqual(3); - expect(keys[0]).toEqual(cacheKeys[0]); - expect(keys[1]).toEqual(cacheKeys[1]); - expect(keys[2]).toEqual(cacheKeys[2]); + var keys = $angularCacheFactory.keys(); + expect(keys.length).toEqual(3); + expect(keys[0]).toEqual(cacheKeys[0]); + expect(keys[1]).toEqual(cacheKeys[1]); + expect(keys[2]).toEqual(cacheKeys[2]); - $angularCacheFactory.get(cacheKeys[0]).destroy(); - keys = $angularCacheFactory.keys(); - expect(keys.length).toEqual(2); - expect(keys.indexOf(cacheKeys[1])).not.toEqual(-1); - expect(keys.indexOf(cacheKeys[2])).not.toEqual(-1); + $angularCacheFactory.get(cacheKeys[0]).destroy(); + keys = $angularCacheFactory.keys(); + expect(keys.length).toEqual(2); + expect(keys.indexOf(cacheKeys[1])).not.toEqual(-1); + expect(keys.indexOf(cacheKeys[2])).not.toEqual(-1); - $angularCacheFactory.get(cacheKeys[1]).destroy(); - keys = $angularCacheFactory.keys(); - expect(keys.length).toEqual(1); - expect(keys.indexOf(cacheKeys[2])).not.toEqual(-1); + $angularCacheFactory.get(cacheKeys[1]).destroy(); + keys = $angularCacheFactory.keys(); + expect(keys.length).toEqual(1); + expect(keys.indexOf(cacheKeys[2])).not.toEqual(-1); - $angularCacheFactory.get(cacheKeys[2]).destroy(); + $angularCacheFactory.get(cacheKeys[2]).destroy(); - keys = $angularCacheFactory.keys(); + keys = $angularCacheFactory.keys(); - expect(keys.length).toEqual(0); - }); + expect(keys.length).toEqual(0); + }); }); diff --git a/test/angularCacheFactory.removeAll-test.js b/test/angularCacheFactory.removeAll-test.js index 8e7e211..1c5e59c 100644 --- a/test/angularCacheFactory.removeAll-test.js +++ b/test/angularCacheFactory.removeAll-test.js @@ -1,33 +1,33 @@ describe('$angularCacheFactory.removeAll()', function () { - it('should call "destroy()" on all caches currently owned by the factory.', function () { - var cacheKeys = ['cache', 'cache1', 'cache2'], - caches = []; + it('should call "destroy()" on all caches currently owned by the factory.', function () { + var cacheKeys = ['cache', 'cache1', 'cache2'], + caches = []; - caches.push($angularCacheFactory(cacheKeys[0])); - caches.push($angularCacheFactory(cacheKeys[1])); - caches.push($angularCacheFactory(cacheKeys[2])); + caches.push($angularCacheFactory(cacheKeys[0])); + caches.push($angularCacheFactory(cacheKeys[1])); + caches.push($angularCacheFactory(cacheKeys[2])); - spyOn(caches[0], 'destroy'); - spyOn(caches[1], 'destroy'); - spyOn(caches[2], 'destroy'); - $angularCacheFactory.removeAll(); + spyOn(caches[0], 'destroy'); + spyOn(caches[1], 'destroy'); + spyOn(caches[2], 'destroy'); + $angularCacheFactory.removeAll(); - expect(caches[0].destroy.callCount).toEqual(1); - expect(caches[1].destroy.callCount).toEqual(1); - expect(caches[2].destroy.callCount).toEqual(1); - }); - it('should result in all caches being removed from $angularCacheFactory.', function () { - var cacheKeys = ['cache', 'cache1', 'cache2'], - caches = []; + expect(caches[0].destroy.callCount).toEqual(1); + expect(caches[1].destroy.callCount).toEqual(1); + expect(caches[2].destroy.callCount).toEqual(1); + }); + it('should result in all caches being removed from $angularCacheFactory.', function () { + var cacheKeys = ['cache', 'cache1', 'cache2'], + caches = []; - caches.push($angularCacheFactory(cacheKeys[0])); - caches.push($angularCacheFactory(cacheKeys[1])); - caches.push($angularCacheFactory(cacheKeys[2])); + caches.push($angularCacheFactory(cacheKeys[0])); + caches.push($angularCacheFactory(cacheKeys[1])); + caches.push($angularCacheFactory(cacheKeys[2])); - $angularCacheFactory.removeAll(); + $angularCacheFactory.removeAll(); - expect($angularCacheFactory.get(cacheKeys[0])).not.toBeDefined(); - expect($angularCacheFactory.get(cacheKeys[1])).not.toBeDefined(); - expect($angularCacheFactory.get(cacheKeys[2])).not.toBeDefined(); - }); + expect($angularCacheFactory.get(cacheKeys[0])).not.toBeDefined(); + expect($angularCacheFactory.get(cacheKeys[1])).not.toBeDefined(); + expect($angularCacheFactory.get(cacheKeys[2])).not.toBeDefined(); + }); }); diff --git a/test/angularCacheFactoryProvider.setCacheDefaults-test.js b/test/angularCacheFactoryProvider.setCacheDefaults-test.js index c9f5f6a..d4a1845 100644 --- a/test/angularCacheFactoryProvider.setCacheDefaults-test.js +++ b/test/angularCacheFactoryProvider.setCacheDefaults-test.js @@ -1,350 +1,351 @@ describe('$angularCacheFactoryProvider.setCacheDefaults(options)', function () { - it('should have the correct defaults.', function () { - expect($angularCacheFactory.info().cacheDefaults).toEqual({ - capacity: CACHE_DEFAULTS.capacity, - maxAge: CACHE_DEFAULTS.maxAge, - cacheFlushInterval: CACHE_DEFAULTS.cacheFlushInterval, - deleteOnExpire: CACHE_DEFAULTS.deleteOnExpire, - onExpire: CACHE_DEFAULTS.onExpire, - recycleFreq: CACHE_DEFAULTS.recycleFreq, - storageMode: CACHE_DEFAULTS.storageMode, - storageImpl: CACHE_DEFAULTS.storageImpl, - verifyIntegrity: CACHE_DEFAULTS.verifyIntegrity, - disabled: CACHE_DEFAULTS.disabled, - readOnGet: CACHE_DEFAULTS.readOnGet - }); - var cache = $angularCacheFactory('cache'); - expect(cache).toBeDefined(); - expect(cache.info().id).toEqual('cache'); - expect(cache.info().capacity).toEqual(CACHE_DEFAULTS.capacity); - expect(cache.info().maxAge).toEqual(CACHE_DEFAULTS.maxAge); - expect(cache.info().cacheFlushInterval).toEqual(CACHE_DEFAULTS.cacheFlushInterval); - expect(cache.info().deleteOnExpire).toEqual(CACHE_DEFAULTS.deleteOnExpire); - expect(cache.info().onExpire).toEqual(CACHE_DEFAULTS.onExpire); - expect(cache.info().recycleFreq).toEqual(CACHE_DEFAULTS.recycleFreq); - expect(cache.info().storageMode).toEqual(CACHE_DEFAULTS.storageMode); - expect(cache.info().storageImpl).toEqual(CACHE_DEFAULTS.storageImpl); - expect(cache.info().verifyIntegrity).toEqual(CACHE_DEFAULTS.verifyIntegrity); - expect(cache.info().readOnGet).toEqual(CACHE_DEFAULTS.readOnGet); - expect(cache.info().disabled).toEqual(CACHE_DEFAULTS.disabled); - cache.destroy(); - }); - it('should set the default options.', function () { - var options = { - capacity: Math.floor((Math.random() * 100000) + 1), - maxAge: Math.floor((Math.random() * 100000) + 1), - cacheFlushInterval: Math.floor((Math.random() * 100000) + 1), - deleteOnExpire: 'aggressive', - storageMode: 'localStorage', - localStorageImpl: { - setItem: function () { - }, - getItem: function () { - }, - removeItem: function () { - } - }, - verifyIntegrity: false, - recycleFreq: 2000, - disabled: true, - onExpire: function () { - } - }; - $angularCacheFactoryProvider.setCacheDefaults(options); - var cache = $angularCacheFactory('cache'); - expect(cache).toBeDefined(); - expect(cache.info().id).toEqual('cache'); - expect(cache.info().capacity).toEqual(options.capacity); - expect(cache.info().maxAge).toEqual(options.maxAge); - expect(cache.info().cacheFlushInterval).toEqual(options.cacheFlushInterval); - expect(cache.info().deleteOnExpire).toEqual(options.deleteOnExpire); - expect(cache.info().storageMode).toEqual(options.storageMode); - expect(cache.info().localStorageImpl).not.toBeDefined(); // We don't expose this to the user - expect(cache.info().onExpire).toEqual(options.onExpire); - expect(cache.info().disabled).toEqual(options.disabled); - cache.destroy(); - expect($angularCacheFactory.get('cache')).not.toBeDefined(); - }); - it('should throw an exception if "capacity" is not a number or is less than zero.', function () { - try { - $angularCacheFactoryProvider.setCacheDefaults({ capacity: Math.floor((Math.random() * 100000) + 1) * -1 }); - fail(); - } catch (err) { - var msg = err.message; - } - expect(msg).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): capacity: must be greater than zero!'); - for (var i = 0; i < TYPES_EXCEPT_NUMBER.length; i++) { - try { - $angularCacheFactoryProvider.setCacheDefaults({ capacity: TYPES_EXCEPT_NUMBER[i] }); - fail(); - } catch (err) { - expect(err.message).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): capacity: must be a number!'); - continue; - } - fail(); - } - }); - it('should validate maxAge.', function () { - var maxAge = Math.floor((Math.random() * 100000) + 1) * -1; - try { - $angularCacheFactoryProvider.setCacheDefaults({ maxAge: maxAge }); - fail(); - } catch (err) { - var msg = err.message; - } - expect(msg).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): maxAge: must be greater than zero!'); - for (var i = 0; i < TYPES_EXCEPT_NUMBER.length; i++) { - try { - $angularCacheFactoryProvider.setCacheDefaults({ maxAge: TYPES_EXCEPT_NUMBER[i] }); - if (TYPES_EXCEPT_NUMBER[i] !== null) { - fail(); - } - } catch (err) { - expect(err.message).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): maxAge: must be a number!'); - continue; - } - if (TYPES_EXCEPT_NUMBER[i] !== null) { - fail(); - } - } - }); - it('should validate cacheFlushInterval.', function () { - var cacheFlushInterval = Math.floor((Math.random() * 100000) + 1) * -1; - try { - $angularCacheFactoryProvider.setCacheDefaults({ cacheFlushInterval: cacheFlushInterval }); - fail(); - } catch (err) { - var msg = err.message; - } - expect(msg).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): cacheFlushInterval: must be greater than zero!'); - for (var i = 0; i < TYPES_EXCEPT_NUMBER.length; i++) { - try { - $angularCacheFactoryProvider.setCacheDefaults({ cacheFlushInterval: TYPES_EXCEPT_NUMBER[i] }); - if (TYPES_EXCEPT_NUMBER[i] !== null) { - fail(); - } - } catch (err) { - expect(err.message).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): cacheFlushInterval: must be a number!'); - continue; - } - if (TYPES_EXCEPT_NUMBER[i] !== null) { - fail(); - } - } - }); - it('should validate recycleFreq.', function () { - var recycleFreq = Math.floor((Math.random() * 100000) + 1) * -1; - try { - $angularCacheFactoryProvider.setCacheDefaults({ recycleFreq: recycleFreq }); - fail(); - } catch (err) { - var msg = err.message; - } - expect(msg).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): recycleFreq: must be greater than zero!'); - for (var i = 0; i < TYPES_EXCEPT_NUMBER.length; i++) { - try { - $angularCacheFactoryProvider.setCacheDefaults({ recycleFreq: TYPES_EXCEPT_NUMBER[i] }); - if (TYPES_EXCEPT_NUMBER[i] !== null) { - fail(); - } - } catch (err) { - expect(err.message).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): recycleFreq: must be a number!'); - continue; - } - if (TYPES_EXCEPT_NUMBER[i] !== null) { - fail(); - } - } - }); - it('should validate onExpire.', function () { - var onExpire = 234; - try { - $angularCacheFactoryProvider.setCacheDefaults({ onExpire: onExpire }); - expect('should not reach this!').toEqual(false); - } catch (err) { - var msg = err.message; - } - expect(msg).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): onExpire: must be a function!'); - for (var i = 0; i < TYPES_EXCEPT_FUNCTION.length; i++) { - try { - $angularCacheFactoryProvider.setCacheDefaults({ onExpire: TYPES_EXCEPT_FUNCTION[i] }); - if (TYPES_EXCEPT_FUNCTION[i] !== null) { - fail(); - } - } catch (err) { - expect(err.message).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): onExpire: must be a function!'); - continue; - } - if (TYPES_EXCEPT_FUNCTION[i] !== null) { - fail(); - } - } - }); - it('should validate deleteOnExpire.', function () { - var deleteOnExpire = 'fail'; - try { - $angularCacheFactoryProvider.setCacheDefaults({ deleteOnExpire: deleteOnExpire }); - expect('should not reach this!').toEqual(false); - } catch (err) { - var msg = err.message; - } - expect(msg).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): deleteOnExpire: accepted values are "none", "passive" or "aggressive"!'); - for (var i = 0; i < TYPES_EXCEPT_STRING.length; i++) { - try { - $angularCacheFactoryProvider.setCacheDefaults({ deleteOnExpire: TYPES_EXCEPT_STRING[i] }); - fail(); - } catch (err) { - expect(err.message).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): deleteOnExpire: must be a string!'); - continue; - } - fail(); - } - }); - it('should validate storageMode.', function () { - var storageMode = 'fail'; - try { - $angularCacheFactoryProvider.setCacheDefaults({ storageMode: storageMode }); - expect('should not reach this!').toEqual(false); - } catch (err) { - var msg = err.message; - } - expect(msg).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): storageMode: accepted values are "none", "localStorage" or "sessionStorage"!'); - for (var i = 0; i < TYPES_EXCEPT_STRING.length; i++) { - try { - $angularCacheFactoryProvider.setCacheDefaults({ storageMode: TYPES_EXCEPT_STRING[i] }); - fail(); - } catch (err) { - expect(err.message).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): storageMode: must be a string!'); - continue; - } - fail(); - } - }); - it('should validate storageImpl.', function () { - var storageImpl = 'fail'; - try { - $angularCacheFactoryProvider.setCacheDefaults({ storageMode: 'localStorage', storageImpl: storageImpl }); - expect('should not reach this!').toEqual(false); - } catch (err) { - var msg = err.message; - } - expect(msg).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): storageImpl: must be an object!'); - for (var i = 0; i < TYPES_EXCEPT_OBJECT.length; i++) { - try { - $angularCacheFactoryProvider.setCacheDefaults({ storageMode: 'localStorage', storageImpl: TYPES_EXCEPT_OBJECT[i] }); - if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { - fail(TYPES_EXCEPT_OBJECT[i]); - } - } catch (err) { - expect(err.message.length).not.toEqual(0); - continue; - } - if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { - fail(TYPES_EXCEPT_OBJECT[i]); - } - } - try { - $angularCacheFactoryProvider.setCacheDefaults({ - storageMode: 'localStorage', - storageImpl: { - getItem: function () { - }, - removeItem: function () { - } - } - }); - fail(); - } catch (err) { - expect(err.message.length).not.toEqual(0); - } - try { - $angularCacheFactoryProvider.setCacheDefaults({ - storageMode: 'localStorage', - storageImpl: { - setItem: function () { - }, - removeItem: function () { - } - } - }); - fail(); - } catch (err) { - expect(err.message.length).not.toEqual(0); - } - try { - $angularCacheFactoryProvider.setCacheDefaults({ - storageMode: 'localStorage', - storageImpl: { - getItem: function () { - }, - setItem: function () { - } - } - }); - fail(); - } catch (err) { - expect(err.message.length).not.toEqual(0); - } - try { - $angularCacheFactoryProvider.setCacheDefaults({ - storageMode: 'localStorage', - storageImpl: { - getItem: 'should not be a string', - setItem: function () { - }, - removeItem: function () { - } - } - }); - fail(); - } catch (err) { - expect(err.message.length).not.toEqual(0); - } - try { - $angularCacheFactoryProvider.setCacheDefaults({ - storageMode: 'localStorage', - storageImpl: { - getItem: function () { - }, - setItem: 'should not be a string', - removeItem: function () { - } - } - }); - fail(); - } catch (err) { - expect(err.message.length).not.toEqual(0); - } - try { - $angularCacheFactoryProvider.setCacheDefaults({ - storageMode: 'localStorage', - storageImpl: { - setItem: function () { - }, - getItem: function () { - }, - removeItem: 'should not be a string' - } - }); - fail(); - } catch (err) { - expect(err.message.length).not.toEqual(0); - } - }); - it('should require options to be an object.', function () { - for (var i = 0; i < TYPES_EXCEPT_OBJECT.length; i++) { - try { - $angularCacheFactoryProvider.setCacheDefaults(TYPES_EXCEPT_OBJECT[i]); - if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { - fail(TYPES_EXCEPT_OBJECT[i]); - } - } catch (err) { - expect(err.message.length).not.toEqual(0); - continue; - } - if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { - fail(TYPES_EXCEPT_OBJECT[i]); - } - } - }); + it('should have the correct defaults.', function () { + expect($angularCacheFactory.info().cacheDefaults).toEqual({ + capacity: CACHE_DEFAULTS.capacity, + maxAge: CACHE_DEFAULTS.maxAge, + cacheFlushInterval: CACHE_DEFAULTS.cacheFlushInterval, + deleteOnExpire: CACHE_DEFAULTS.deleteOnExpire, + onExpire: CACHE_DEFAULTS.onExpire, + recycleFreq: CACHE_DEFAULTS.recycleFreq, + storageMode: CACHE_DEFAULTS.storageMode, + storageImpl: CACHE_DEFAULTS.storageImpl, + verifyIntegrity: CACHE_DEFAULTS.verifyIntegrity, + disabled: CACHE_DEFAULTS.disabled, + readOnGet: CACHE_DEFAULTS.readOnGet, + storePromises : CACHE_DEFAULTS.storePromises + }); + var cache = $angularCacheFactory('cache'); + expect(cache).toBeDefined(); + expect(cache.info().id).toEqual('cache'); + expect(cache.info().capacity).toEqual(CACHE_DEFAULTS.capacity); + expect(cache.info().maxAge).toEqual(CACHE_DEFAULTS.maxAge); + expect(cache.info().cacheFlushInterval).toEqual(CACHE_DEFAULTS.cacheFlushInterval); + expect(cache.info().deleteOnExpire).toEqual(CACHE_DEFAULTS.deleteOnExpire); + expect(cache.info().onExpire).toEqual(CACHE_DEFAULTS.onExpire); + expect(cache.info().recycleFreq).toEqual(CACHE_DEFAULTS.recycleFreq); + expect(cache.info().storageMode).toEqual(CACHE_DEFAULTS.storageMode); + expect(cache.info().storageImpl).toEqual(CACHE_DEFAULTS.storageImpl); + expect(cache.info().verifyIntegrity).toEqual(CACHE_DEFAULTS.verifyIntegrity); + expect(cache.info().readOnGet).toEqual(CACHE_DEFAULTS.readOnGet); + expect(cache.info().disabled).toEqual(CACHE_DEFAULTS.disabled); + cache.destroy(); + }); + it('should set the default options.', function () { + var options = { + capacity: Math.floor((Math.random() * 100000) + 1), + maxAge: Math.floor((Math.random() * 100000) + 1), + cacheFlushInterval: Math.floor((Math.random() * 100000) + 1), + deleteOnExpire: 'aggressive', + storageMode: 'localStorage', + localStorageImpl: { + setItem: function () { + }, + getItem: function () { + }, + removeItem: function () { + } + }, + verifyIntegrity: false, + recycleFreq: 2000, + disabled: true, + onExpire: function () { + } + }; + $angularCacheFactoryProvider.setCacheDefaults(options); + var cache = $angularCacheFactory('cache'); + expect(cache).toBeDefined(); + expect(cache.info().id).toEqual('cache'); + expect(cache.info().capacity).toEqual(options.capacity); + expect(cache.info().maxAge).toEqual(options.maxAge); + expect(cache.info().cacheFlushInterval).toEqual(options.cacheFlushInterval); + expect(cache.info().deleteOnExpire).toEqual(options.deleteOnExpire); + expect(cache.info().storageMode).toEqual(options.storageMode); + expect(cache.info().localStorageImpl).not.toBeDefined(); // We don't expose this to the user + expect(cache.info().onExpire).toEqual(options.onExpire); + expect(cache.info().disabled).toEqual(options.disabled); + cache.destroy(); + expect($angularCacheFactory.get('cache')).not.toBeDefined(); + }); + it('should throw an exception if "capacity" is not a number or is less than zero.', function () { + try { + $angularCacheFactoryProvider.setCacheDefaults({ capacity: Math.floor((Math.random() * 100000) + 1) * -1 }); + fail(); + } catch (err) { + var msg = err.message; + } + expect(msg).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): capacity: must be greater than zero!'); + for (var i = 0; i < TYPES_EXCEPT_NUMBER.length; i++) { + try { + $angularCacheFactoryProvider.setCacheDefaults({ capacity: TYPES_EXCEPT_NUMBER[i] }); + fail(); + } catch (err) { + expect(err.message).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): capacity: must be a number!'); + continue; + } + fail(); + } + }); + it('should validate maxAge.', function () { + var maxAge = Math.floor((Math.random() * 100000) + 1) * -1; + try { + $angularCacheFactoryProvider.setCacheDefaults({ maxAge: maxAge }); + fail(); + } catch (err) { + var msg = err.message; + } + expect(msg).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): maxAge: must be greater than zero!'); + for (var i = 0; i < TYPES_EXCEPT_NUMBER.length; i++) { + try { + $angularCacheFactoryProvider.setCacheDefaults({ maxAge: TYPES_EXCEPT_NUMBER[i] }); + if (TYPES_EXCEPT_NUMBER[i] !== null) { + fail(); + } + } catch (err) { + expect(err.message).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): maxAge: must be a number!'); + continue; + } + if (TYPES_EXCEPT_NUMBER[i] !== null) { + fail(); + } + } + }); + it('should validate cacheFlushInterval.', function () { + var cacheFlushInterval = Math.floor((Math.random() * 100000) + 1) * -1; + try { + $angularCacheFactoryProvider.setCacheDefaults({ cacheFlushInterval: cacheFlushInterval }); + fail(); + } catch (err) { + var msg = err.message; + } + expect(msg).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): cacheFlushInterval: must be greater than zero!'); + for (var i = 0; i < TYPES_EXCEPT_NUMBER.length; i++) { + try { + $angularCacheFactoryProvider.setCacheDefaults({ cacheFlushInterval: TYPES_EXCEPT_NUMBER[i] }); + if (TYPES_EXCEPT_NUMBER[i] !== null) { + fail(); + } + } catch (err) { + expect(err.message).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): cacheFlushInterval: must be a number!'); + continue; + } + if (TYPES_EXCEPT_NUMBER[i] !== null) { + fail(); + } + } + }); + it('should validate recycleFreq.', function () { + var recycleFreq = Math.floor((Math.random() * 100000) + 1) * -1; + try { + $angularCacheFactoryProvider.setCacheDefaults({ recycleFreq: recycleFreq }); + fail(); + } catch (err) { + var msg = err.message; + } + expect(msg).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): recycleFreq: must be greater than zero!'); + for (var i = 0; i < TYPES_EXCEPT_NUMBER.length; i++) { + try { + $angularCacheFactoryProvider.setCacheDefaults({ recycleFreq: TYPES_EXCEPT_NUMBER[i] }); + if (TYPES_EXCEPT_NUMBER[i] !== null) { + fail(); + } + } catch (err) { + expect(err.message).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): recycleFreq: must be a number!'); + continue; + } + if (TYPES_EXCEPT_NUMBER[i] !== null) { + fail(); + } + } + }); + it('should validate onExpire.', function () { + var onExpire = 234; + try { + $angularCacheFactoryProvider.setCacheDefaults({ onExpire: onExpire }); + expect('should not reach this!').toEqual(false); + } catch (err) { + var msg = err.message; + } + expect(msg).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): onExpire: must be a function!'); + for (var i = 0; i < TYPES_EXCEPT_FUNCTION.length; i++) { + try { + $angularCacheFactoryProvider.setCacheDefaults({ onExpire: TYPES_EXCEPT_FUNCTION[i] }); + if (TYPES_EXCEPT_FUNCTION[i] !== null) { + fail(); + } + } catch (err) { + expect(err.message).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): onExpire: must be a function!'); + continue; + } + if (TYPES_EXCEPT_FUNCTION[i] !== null) { + fail(); + } + } + }); + it('should validate deleteOnExpire.', function () { + var deleteOnExpire = 'fail'; + try { + $angularCacheFactoryProvider.setCacheDefaults({ deleteOnExpire: deleteOnExpire }); + expect('should not reach this!').toEqual(false); + } catch (err) { + var msg = err.message; + } + expect(msg).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): deleteOnExpire: accepted values are "none", "passive" or "aggressive"!'); + for (var i = 0; i < TYPES_EXCEPT_STRING.length; i++) { + try { + $angularCacheFactoryProvider.setCacheDefaults({ deleteOnExpire: TYPES_EXCEPT_STRING[i] }); + fail(); + } catch (err) { + expect(err.message).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): deleteOnExpire: must be a string!'); + continue; + } + fail(); + } + }); + it('should validate storageMode.', function () { + var storageMode = 'fail'; + try { + $angularCacheFactoryProvider.setCacheDefaults({ storageMode: storageMode }); + expect('should not reach this!').toEqual(false); + } catch (err) { + var msg = err.message; + } + expect(msg).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): storageMode: accepted values are "none", "localStorage" or "sessionStorage"!'); + for (var i = 0; i < TYPES_EXCEPT_STRING.length; i++) { + try { + $angularCacheFactoryProvider.setCacheDefaults({ storageMode: TYPES_EXCEPT_STRING[i] }); + fail(); + } catch (err) { + expect(err.message).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): storageMode: must be a string!'); + continue; + } + fail(); + } + }); + it('should validate storageImpl.', function () { + var storageImpl = 'fail'; + try { + $angularCacheFactoryProvider.setCacheDefaults({ storageMode: 'localStorage', storageImpl: storageImpl }); + expect('should not reach this!').toEqual(false); + } catch (err) { + var msg = err.message; + } + expect(msg).toEqual('$angularCacheFactoryProvider.setCacheDefaults(options): storageImpl: must be an object!'); + for (var i = 0; i < TYPES_EXCEPT_OBJECT.length; i++) { + try { + $angularCacheFactoryProvider.setCacheDefaults({ storageMode: 'localStorage', storageImpl: TYPES_EXCEPT_OBJECT[i] }); + if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { + fail(TYPES_EXCEPT_OBJECT[i]); + } + } catch (err) { + expect(err.message.length).not.toEqual(0); + continue; + } + if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { + fail(TYPES_EXCEPT_OBJECT[i]); + } + } + try { + $angularCacheFactoryProvider.setCacheDefaults({ + storageMode: 'localStorage', + storageImpl: { + getItem: function () { + }, + removeItem: function () { + } + } + }); + fail(); + } catch (err) { + expect(err.message.length).not.toEqual(0); + } + try { + $angularCacheFactoryProvider.setCacheDefaults({ + storageMode: 'localStorage', + storageImpl: { + setItem: function () { + }, + removeItem: function () { + } + } + }); + fail(); + } catch (err) { + expect(err.message.length).not.toEqual(0); + } + try { + $angularCacheFactoryProvider.setCacheDefaults({ + storageMode: 'localStorage', + storageImpl: { + getItem: function () { + }, + setItem: function () { + } + } + }); + fail(); + } catch (err) { + expect(err.message.length).not.toEqual(0); + } + try { + $angularCacheFactoryProvider.setCacheDefaults({ + storageMode: 'localStorage', + storageImpl: { + getItem: 'should not be a string', + setItem: function () { + }, + removeItem: function () { + } + } + }); + fail(); + } catch (err) { + expect(err.message.length).not.toEqual(0); + } + try { + $angularCacheFactoryProvider.setCacheDefaults({ + storageMode: 'localStorage', + storageImpl: { + getItem: function () { + }, + setItem: 'should not be a string', + removeItem: function () { + } + } + }); + fail(); + } catch (err) { + expect(err.message.length).not.toEqual(0); + } + try { + $angularCacheFactoryProvider.setCacheDefaults({ + storageMode: 'localStorage', + storageImpl: { + setItem: function () { + }, + getItem: function () { + }, + removeItem: 'should not be a string' + } + }); + fail(); + } catch (err) { + expect(err.message.length).not.toEqual(0); + } + }); + it('should require options to be an object.', function () { + for (var i = 0; i < TYPES_EXCEPT_OBJECT.length; i++) { + try { + $angularCacheFactoryProvider.setCacheDefaults(TYPES_EXCEPT_OBJECT[i]); + if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { + fail(TYPES_EXCEPT_OBJECT[i]); + } + } catch (err) { + expect(err.message.length).not.toEqual(0); + continue; + } + if (TYPES_EXCEPT_OBJECT[i] !== null && TYPES_EXCEPT_OBJECT[i] !== undefined && TYPES_EXCEPT_OBJECT[i] !== false) { + fail(TYPES_EXCEPT_OBJECT[i]); + } + } + }); }); diff --git a/test/binaryHeap-test.js b/test/binaryHeap-test.js index cce9cf7..3e6ca30 100644 --- a/test/binaryHeap-test.js +++ b/test/binaryHeap-test.js @@ -1,23 +1,23 @@ describe('BinaryHeap(weightFunc)', function () { - it('should create an empty heap with size 0.', function () { - var heap = new BinaryHeap(); - expect(heap.size()).toEqual(0); - }); - it('should throw an error if "weightFunc" is not a function.', function () { - var heap; - for (var i = 0; i < TYPES_EXCEPT_FUNCTION.length; i++) { - try { - heap = new BinaryHeap(TYPES_EXCEPT_FUNCTION[i]); - if (TYPES_EXCEPT_FUNCTION[i]) { - fail(); - } - } catch (err) { - expect(err.message).toEqual('BinaryHeap(weightFunc): weightFunc: must be a function!'); - continue; - } - if (TYPES_EXCEPT_FUNCTION[i]) { - fail(); - } + it('should create an empty heap with size 0.', function () { + var heap = new BinaryHeap(); + expect(heap.size()).toEqual(0); + }); + it('should throw an error if "weightFunc" is not a function.', function () { + var heap; + for (var i = 0; i < TYPES_EXCEPT_FUNCTION.length; i++) { + try { + heap = new BinaryHeap(TYPES_EXCEPT_FUNCTION[i]); + if (TYPES_EXCEPT_FUNCTION[i]) { + fail(); } - }); -}); \ No newline at end of file + } catch (err) { + expect(err.message).toEqual('BinaryHeap(weightFunc): weightFunc: must be a function!'); + continue; + } + if (TYPES_EXCEPT_FUNCTION[i]) { + fail(); + } + } + }); +}); diff --git a/test/binaryHeap.peek-test.js b/test/binaryHeap.peek-test.js index cd6bac3..2977e60 100644 --- a/test/binaryHeap.peek-test.js +++ b/test/binaryHeap.peek-test.js @@ -1,138 +1,138 @@ describe('BinaryHeap.peek()', function () { - it('should show the item at the front of the BinaryHeap.', function () { - var heap = new BinaryHeap(); - var objHeap = new BinaryHeap(function (x) { - return x.value; - }); - var items = [20, 4, 33, 1, 0, 34, 22, 31, 32, 5, 6, 7], - objItems = []; - for (var i = 0; i < items.length; i++) { - objItems.push({ - value: items[i] - }); - } - - expect(heap.peek()).not.toBeDefined(); - expect(objHeap.peek()).not.toBeDefined(); - - heap.push(items[0]); - objHeap.push(objItems[0]); - expect(heap.peek()).toEqual(items[0]); - expect(objHeap.peek()).toEqual(objItems[0]); - - heap.push(items[1]); - objHeap.push(objItems[1]); - expect(heap.peek()).toEqual(items[1]); - expect(objHeap.peek()).toEqual(objItems[1]); - - heap.push(items[2]); - objHeap.push(objItems[2]); - expect(heap.peek()).toEqual(items[1]); - expect(objHeap.peek()).toEqual(objItems[1]); - - heap.push(items[3]); - objHeap.push(objItems[3]); - expect(heap.peek()).toEqual(items[3]); - expect(objHeap.peek()).toEqual(objItems[3]); - - heap.push(items[4]); - objHeap.push(objItems[4]); - expect(heap.peek()).toEqual(items[4]); - expect(objHeap.peek()).toEqual(objItems[4]); - - heap.push(items[5]); - objHeap.push(objItems[5]); - expect(heap.peek()).toEqual(items[4]); - expect(objHeap.peek()).toEqual(objItems[4]); - - heap.push(items[6]); - objHeap.push(objItems[6]); - expect(heap.peek()).toEqual(items[4]); - expect(objHeap.peek()).toEqual(objItems[4]); - - heap.push(items[7]); - objHeap.push(objItems[7]); - expect(heap.peek()).toEqual(items[4]); - expect(objHeap.peek()).toEqual(objItems[4]); - - heap.push(items[8]); - objHeap.push(objItems[8]); - expect(heap.peek()).toEqual(items[4]); - expect(objHeap.peek()).toEqual(objItems[4]); - - heap.push(items[9]); - objHeap.push(objItems[9]); - expect(heap.peek()).toEqual(items[4]); - expect(objHeap.peek()).toEqual(objItems[4]); - - heap.push(items[10]); - objHeap.push(objItems[10]); - expect(heap.peek()).toEqual(items[4]); - expect(objHeap.peek()).toEqual(objItems[4]); - - heap.push(items[11]); - objHeap.push(objItems[11]); - expect(heap.peek()).toEqual(items[4]); - expect(objHeap.peek()).toEqual(objItems[4]); - - expect(heap.pop()).toEqual(0); - expect(objHeap.pop()).toEqual({ value: 0 }); - expect(heap.peek()).toEqual(1); - expect(objHeap.peek()).toEqual({ value: 1 }); - - expect(heap.pop()).toEqual(1); - expect(objHeap.pop()).toEqual({ value: 1 }); - expect(heap.peek()).toEqual(4); - expect(objHeap.peek()).toEqual({ value: 4 }); - - expect(heap.pop()).toEqual(4); - expect(objHeap.pop()).toEqual({ value: 4 }); - expect(heap.peek()).toEqual(5); - expect(objHeap.peek()).toEqual({ value: 5 }); - - expect(heap.pop()).toEqual(5); - expect(objHeap.pop()).toEqual({ value: 5 }); - expect(heap.peek()).toEqual(6); - expect(objHeap.peek()).toEqual({ value: 6 }); - - expect(heap.pop()).toEqual(6); - expect(objHeap.pop()).toEqual({ value: 6 }); - expect(heap.peek()).toEqual(7); - expect(objHeap.peek()).toEqual({ value: 7 }); - - expect(heap.pop()).toEqual(7); - expect(objHeap.pop()).toEqual({ value: 7 }); - expect(heap.peek()).toEqual(20); - expect(objHeap.peek()).toEqual({ value: 20 }); - - expect(heap.pop()).toEqual(20); - expect(objHeap.pop()).toEqual({ value: 20 }); - expect(heap.peek()).toEqual(22); - expect(objHeap.peek()).toEqual({ value: 22 }); - - expect(heap.pop()).toEqual(22); - expect(objHeap.pop()).toEqual({ value: 22 }); - expect(heap.peek()).toEqual(31); - expect(objHeap.peek()).toEqual({ value: 31 }); - - expect(heap.pop()).toEqual(31); - expect(objHeap.pop()).toEqual({ value: 31 }); - expect(heap.peek()).toEqual(32); - expect(objHeap.peek()).toEqual({ value: 32 }); - - expect(heap.pop()).toEqual(32); - expect(objHeap.pop()).toEqual({ value: 32 }); - expect(heap.peek()).toEqual(33); - expect(objHeap.peek()).toEqual({ value: 33 }); - - expect(heap.pop()).toEqual(33); - expect(objHeap.pop()).toEqual({ value: 33 }); - expect(heap.peek()).toEqual(34); - expect(objHeap.peek()).toEqual({ value: 34 }); - - expect(heap.pop()).toEqual(34); - expect(objHeap.pop()).toEqual({ value: 34 }); - expect(heap.peek()).not.toBeDefined(); - expect(objHeap.peek()).not.toBeDefined(); + it('should show the item at the front of the BinaryHeap.', function () { + var heap = new BinaryHeap(); + var objHeap = new BinaryHeap(function (x) { + return x.value; }); -}); \ No newline at end of file + var items = [20, 4, 33, 1, 0, 34, 22, 31, 32, 5, 6, 7], + objItems = []; + for (var i = 0; i < items.length; i++) { + objItems.push({ + value: items[i] + }); + } + + expect(heap.peek()).not.toBeDefined(); + expect(objHeap.peek()).not.toBeDefined(); + + heap.push(items[0]); + objHeap.push(objItems[0]); + expect(heap.peek()).toEqual(items[0]); + expect(objHeap.peek()).toEqual(objItems[0]); + + heap.push(items[1]); + objHeap.push(objItems[1]); + expect(heap.peek()).toEqual(items[1]); + expect(objHeap.peek()).toEqual(objItems[1]); + + heap.push(items[2]); + objHeap.push(objItems[2]); + expect(heap.peek()).toEqual(items[1]); + expect(objHeap.peek()).toEqual(objItems[1]); + + heap.push(items[3]); + objHeap.push(objItems[3]); + expect(heap.peek()).toEqual(items[3]); + expect(objHeap.peek()).toEqual(objItems[3]); + + heap.push(items[4]); + objHeap.push(objItems[4]); + expect(heap.peek()).toEqual(items[4]); + expect(objHeap.peek()).toEqual(objItems[4]); + + heap.push(items[5]); + objHeap.push(objItems[5]); + expect(heap.peek()).toEqual(items[4]); + expect(objHeap.peek()).toEqual(objItems[4]); + + heap.push(items[6]); + objHeap.push(objItems[6]); + expect(heap.peek()).toEqual(items[4]); + expect(objHeap.peek()).toEqual(objItems[4]); + + heap.push(items[7]); + objHeap.push(objItems[7]); + expect(heap.peek()).toEqual(items[4]); + expect(objHeap.peek()).toEqual(objItems[4]); + + heap.push(items[8]); + objHeap.push(objItems[8]); + expect(heap.peek()).toEqual(items[4]); + expect(objHeap.peek()).toEqual(objItems[4]); + + heap.push(items[9]); + objHeap.push(objItems[9]); + expect(heap.peek()).toEqual(items[4]); + expect(objHeap.peek()).toEqual(objItems[4]); + + heap.push(items[10]); + objHeap.push(objItems[10]); + expect(heap.peek()).toEqual(items[4]); + expect(objHeap.peek()).toEqual(objItems[4]); + + heap.push(items[11]); + objHeap.push(objItems[11]); + expect(heap.peek()).toEqual(items[4]); + expect(objHeap.peek()).toEqual(objItems[4]); + + expect(heap.pop()).toEqual(0); + expect(objHeap.pop()).toEqual({ value: 0 }); + expect(heap.peek()).toEqual(1); + expect(objHeap.peek()).toEqual({ value: 1 }); + + expect(heap.pop()).toEqual(1); + expect(objHeap.pop()).toEqual({ value: 1 }); + expect(heap.peek()).toEqual(4); + expect(objHeap.peek()).toEqual({ value: 4 }); + + expect(heap.pop()).toEqual(4); + expect(objHeap.pop()).toEqual({ value: 4 }); + expect(heap.peek()).toEqual(5); + expect(objHeap.peek()).toEqual({ value: 5 }); + + expect(heap.pop()).toEqual(5); + expect(objHeap.pop()).toEqual({ value: 5 }); + expect(heap.peek()).toEqual(6); + expect(objHeap.peek()).toEqual({ value: 6 }); + + expect(heap.pop()).toEqual(6); + expect(objHeap.pop()).toEqual({ value: 6 }); + expect(heap.peek()).toEqual(7); + expect(objHeap.peek()).toEqual({ value: 7 }); + + expect(heap.pop()).toEqual(7); + expect(objHeap.pop()).toEqual({ value: 7 }); + expect(heap.peek()).toEqual(20); + expect(objHeap.peek()).toEqual({ value: 20 }); + + expect(heap.pop()).toEqual(20); + expect(objHeap.pop()).toEqual({ value: 20 }); + expect(heap.peek()).toEqual(22); + expect(objHeap.peek()).toEqual({ value: 22 }); + + expect(heap.pop()).toEqual(22); + expect(objHeap.pop()).toEqual({ value: 22 }); + expect(heap.peek()).toEqual(31); + expect(objHeap.peek()).toEqual({ value: 31 }); + + expect(heap.pop()).toEqual(31); + expect(objHeap.pop()).toEqual({ value: 31 }); + expect(heap.peek()).toEqual(32); + expect(objHeap.peek()).toEqual({ value: 32 }); + + expect(heap.pop()).toEqual(32); + expect(objHeap.pop()).toEqual({ value: 32 }); + expect(heap.peek()).toEqual(33); + expect(objHeap.peek()).toEqual({ value: 33 }); + + expect(heap.pop()).toEqual(33); + expect(objHeap.pop()).toEqual({ value: 33 }); + expect(heap.peek()).toEqual(34); + expect(objHeap.peek()).toEqual({ value: 34 }); + + expect(heap.pop()).toEqual(34); + expect(objHeap.pop()).toEqual({ value: 34 }); + expect(heap.peek()).not.toBeDefined(); + expect(objHeap.peek()).not.toBeDefined(); + }); +}); diff --git a/test/binaryHeap.pop-test.js b/test/binaryHeap.pop-test.js index 93342cd..e56d2f9 100644 --- a/test/binaryHeap.pop-test.js +++ b/test/binaryHeap.pop-test.js @@ -1,78 +1,78 @@ describe('BinaryHeap.pop()', function () { - it('should pop the item off of the front of the BinaryHeap.', function () { - var heap = new BinaryHeap(); - var objHeap = new BinaryHeap(function (x) { - return x.value; - }); - var items = [20, 4, 33, 1, 0, 34, 22, 31, 32, 5, 6, 7]; - for (var i = 0; i < items.length; i++) { - heap.push(items[i]); - objHeap.push({ - value: items[i] - }); - } + it('should pop the item off of the front of the BinaryHeap.', function () { + var heap = new BinaryHeap(); + var objHeap = new BinaryHeap(function (x) { + return x.value; + }); + var items = [20, 4, 33, 1, 0, 34, 22, 31, 32, 5, 6, 7]; + for (var i = 0; i < items.length; i++) { + heap.push(items[i]); + objHeap.push({ + value: items[i] + }); + } - expect(heap.size()).toEqual(12); - expect(objHeap.size()).toEqual(12); + expect(heap.size()).toEqual(12); + expect(objHeap.size()).toEqual(12); - expect(heap.pop()).toEqual(0); - expect(objHeap.pop()).toEqual({ value: 0 }); - expect(heap.size()).toEqual(11); - expect(objHeap.size()).toEqual(11); + expect(heap.pop()).toEqual(0); + expect(objHeap.pop()).toEqual({ value: 0 }); + expect(heap.size()).toEqual(11); + expect(objHeap.size()).toEqual(11); - expect(heap.pop()).toEqual(1); - expect(objHeap.pop()).toEqual({ value: 1 }); - expect(heap.size()).toEqual(10); - expect(objHeap.size()).toEqual(10); + expect(heap.pop()).toEqual(1); + expect(objHeap.pop()).toEqual({ value: 1 }); + expect(heap.size()).toEqual(10); + expect(objHeap.size()).toEqual(10); - expect(heap.pop()).toEqual(4); - expect(objHeap.pop()).toEqual({ value: 4 }); - expect(heap.size()).toEqual(9); - expect(objHeap.size()).toEqual(9); + expect(heap.pop()).toEqual(4); + expect(objHeap.pop()).toEqual({ value: 4 }); + expect(heap.size()).toEqual(9); + expect(objHeap.size()).toEqual(9); - expect(heap.pop()).toEqual(5); - expect(objHeap.pop()).toEqual({ value: 5 }); - expect(heap.size()).toEqual(8); - expect(objHeap.size()).toEqual(8); + expect(heap.pop()).toEqual(5); + expect(objHeap.pop()).toEqual({ value: 5 }); + expect(heap.size()).toEqual(8); + expect(objHeap.size()).toEqual(8); - expect(heap.pop()).toEqual(6); - expect(objHeap.pop()).toEqual({ value: 6 }); - expect(heap.size()).toEqual(7); - expect(objHeap.size()).toEqual(7); + expect(heap.pop()).toEqual(6); + expect(objHeap.pop()).toEqual({ value: 6 }); + expect(heap.size()).toEqual(7); + expect(objHeap.size()).toEqual(7); - expect(heap.pop()).toEqual(7); - expect(objHeap.pop()).toEqual({ value: 7 }); - expect(heap.size()).toEqual(6); - expect(objHeap.size()).toEqual(6); + expect(heap.pop()).toEqual(7); + expect(objHeap.pop()).toEqual({ value: 7 }); + expect(heap.size()).toEqual(6); + expect(objHeap.size()).toEqual(6); - expect(heap.pop()).toEqual(20); - expect(objHeap.pop()).toEqual({ value: 20 }); - expect(heap.size()).toEqual(5); - expect(objHeap.size()).toEqual(5); + expect(heap.pop()).toEqual(20); + expect(objHeap.pop()).toEqual({ value: 20 }); + expect(heap.size()).toEqual(5); + expect(objHeap.size()).toEqual(5); - expect(heap.pop()).toEqual(22); - expect(objHeap.pop()).toEqual({ value: 22 }); - expect(heap.size()).toEqual(4); - expect(objHeap.size()).toEqual(4); + expect(heap.pop()).toEqual(22); + expect(objHeap.pop()).toEqual({ value: 22 }); + expect(heap.size()).toEqual(4); + expect(objHeap.size()).toEqual(4); - expect(heap.pop()).toEqual(31); - expect(objHeap.pop()).toEqual({ value: 31 }); - expect(heap.size()).toEqual(3); - expect(objHeap.size()).toEqual(3); + expect(heap.pop()).toEqual(31); + expect(objHeap.pop()).toEqual({ value: 31 }); + expect(heap.size()).toEqual(3); + expect(objHeap.size()).toEqual(3); - expect(heap.pop()).toEqual(32); - expect(objHeap.pop()).toEqual({ value: 32 }); - expect(heap.size()).toEqual(2); - expect(objHeap.size()).toEqual(2); + expect(heap.pop()).toEqual(32); + expect(objHeap.pop()).toEqual({ value: 32 }); + expect(heap.size()).toEqual(2); + expect(objHeap.size()).toEqual(2); - expect(heap.pop()).toEqual(33); - expect(objHeap.pop()).toEqual({ value: 33 }); - expect(heap.size()).toEqual(1); - expect(objHeap.size()).toEqual(1); + expect(heap.pop()).toEqual(33); + expect(objHeap.pop()).toEqual({ value: 33 }); + expect(heap.size()).toEqual(1); + expect(objHeap.size()).toEqual(1); - expect(heap.pop()).toEqual(34); - expect(objHeap.pop()).toEqual({ value: 34 }); - expect(heap.size()).toEqual(0); - expect(objHeap.size()).toEqual(0); - }); -}); \ No newline at end of file + expect(heap.pop()).toEqual(34); + expect(objHeap.pop()).toEqual({ value: 34 }); + expect(heap.size()).toEqual(0); + expect(objHeap.size()).toEqual(0); + }); +}); diff --git a/test/binaryHeap.push-test.js b/test/binaryHeap.push-test.js index 6fbe2da..7ac8bf9 100644 --- a/test/binaryHeap.push-test.js +++ b/test/binaryHeap.push-test.js @@ -1,99 +1,99 @@ describe('BinaryHeap.push(node)', function () { - it('should push items to the front of the BinaryHeap.', function () { - var heap = new BinaryHeap(); - var objHeap = new BinaryHeap(function (x) { - return x.value; - }); - var items = [20, 4, 33, 1, 0, 34, 22, 31, 32, 5, 6, 7], - objItems = []; - for (var i = 0; i < items.length; i++) { - objItems.push({ - value: items[i] - }); - } + it('should push items to the front of the BinaryHeap.', function () { + var heap = new BinaryHeap(); + var objHeap = new BinaryHeap(function (x) { + return x.value; + }); + var items = [20, 4, 33, 1, 0, 34, 22, 31, 32, 5, 6, 7], + objItems = []; + for (var i = 0; i < items.length; i++) { + objItems.push({ + value: items[i] + }); + } - heap.push(items[0]); - objHeap.push(objItems[0]); - expect(heap.size()).toEqual(1); - expect(objHeap.size()).toEqual(1); - expect(heap.peek()).toEqual(items[0]); - expect(objHeap.peek()).toEqual(objItems[0]); + heap.push(items[0]); + objHeap.push(objItems[0]); + expect(heap.size()).toEqual(1); + expect(objHeap.size()).toEqual(1); + expect(heap.peek()).toEqual(items[0]); + expect(objHeap.peek()).toEqual(objItems[0]); - heap.push(items[1]); - objHeap.push(objItems[1]); - expect(heap.size()).toEqual(2); - expect(objHeap.size()).toEqual(2); - expect(heap.peek()).toEqual(items[1]); - expect(objHeap.peek()).toEqual(objItems[1]); + heap.push(items[1]); + objHeap.push(objItems[1]); + expect(heap.size()).toEqual(2); + expect(objHeap.size()).toEqual(2); + expect(heap.peek()).toEqual(items[1]); + expect(objHeap.peek()).toEqual(objItems[1]); - heap.push(items[2]); - objHeap.push(objItems[2]); - expect(heap.size()).toEqual(3); - expect(objHeap.size()).toEqual(3); - expect(heap.peek()).toEqual(items[1]); - expect(objHeap.peek()).toEqual(objItems[1]); + heap.push(items[2]); + objHeap.push(objItems[2]); + expect(heap.size()).toEqual(3); + expect(objHeap.size()).toEqual(3); + expect(heap.peek()).toEqual(items[1]); + expect(objHeap.peek()).toEqual(objItems[1]); - heap.push(items[3]); - objHeap.push(objItems[3]); - expect(heap.size()).toEqual(4); - expect(objHeap.size()).toEqual(4); - expect(heap.peek()).toEqual(items[3]); - expect(objHeap.peek()).toEqual(objItems[3]); + heap.push(items[3]); + objHeap.push(objItems[3]); + expect(heap.size()).toEqual(4); + expect(objHeap.size()).toEqual(4); + expect(heap.peek()).toEqual(items[3]); + expect(objHeap.peek()).toEqual(objItems[3]); - heap.push(items[4]); - objHeap.push(objItems[4]); - expect(heap.size()).toEqual(5); - expect(objHeap.size()).toEqual(5); - expect(heap.peek()).toEqual(items[4]); - expect(objHeap.peek()).toEqual(objItems[4]); + heap.push(items[4]); + objHeap.push(objItems[4]); + expect(heap.size()).toEqual(5); + expect(objHeap.size()).toEqual(5); + expect(heap.peek()).toEqual(items[4]); + expect(objHeap.peek()).toEqual(objItems[4]); - heap.push(items[5]); - objHeap.push(objItems[5]); - expect(heap.size()).toEqual(6); - expect(objHeap.size()).toEqual(6); - expect(heap.peek()).toEqual(items[4]); - expect(objHeap.peek()).toEqual(objItems[4]); + heap.push(items[5]); + objHeap.push(objItems[5]); + expect(heap.size()).toEqual(6); + expect(objHeap.size()).toEqual(6); + expect(heap.peek()).toEqual(items[4]); + expect(objHeap.peek()).toEqual(objItems[4]); - heap.push(items[6]); - objHeap.push(objItems[6]); - expect(heap.size()).toEqual(7); - expect(objHeap.size()).toEqual(7); - expect(heap.peek()).toEqual(items[4]); - expect(objHeap.peek()).toEqual(objItems[4]); + heap.push(items[6]); + objHeap.push(objItems[6]); + expect(heap.size()).toEqual(7); + expect(objHeap.size()).toEqual(7); + expect(heap.peek()).toEqual(items[4]); + expect(objHeap.peek()).toEqual(objItems[4]); - heap.push(items[7]); - objHeap.push(objItems[7]); - expect(heap.size()).toEqual(8); - expect(objHeap.size()).toEqual(8); - expect(heap.peek()).toEqual(items[4]); - expect(objHeap.peek()).toEqual(objItems[4]); + heap.push(items[7]); + objHeap.push(objItems[7]); + expect(heap.size()).toEqual(8); + expect(objHeap.size()).toEqual(8); + expect(heap.peek()).toEqual(items[4]); + expect(objHeap.peek()).toEqual(objItems[4]); - heap.push(items[8]); - objHeap.push(objItems[8]); - expect(heap.size()).toEqual(9); - expect(objHeap.size()).toEqual(9); - expect(heap.peek()).toEqual(items[4]); - expect(objHeap.peek()).toEqual(objItems[4]); + heap.push(items[8]); + objHeap.push(objItems[8]); + expect(heap.size()).toEqual(9); + expect(objHeap.size()).toEqual(9); + expect(heap.peek()).toEqual(items[4]); + expect(objHeap.peek()).toEqual(objItems[4]); - heap.push(items[9]); - objHeap.push(objItems[9]); - expect(heap.size()).toEqual(10); - expect(objHeap.size()).toEqual(10); - expect(heap.peek()).toEqual(items[4]); - expect(objHeap.peek()).toEqual(objItems[4]); + heap.push(items[9]); + objHeap.push(objItems[9]); + expect(heap.size()).toEqual(10); + expect(objHeap.size()).toEqual(10); + expect(heap.peek()).toEqual(items[4]); + expect(objHeap.peek()).toEqual(objItems[4]); - heap.push(items[10]); - objHeap.push(objItems[10]); - expect(heap.size()).toEqual(11); - expect(objHeap.size()).toEqual(11); - expect(heap.peek()).toEqual(items[4]); - expect(objHeap.peek()).toEqual(objItems[4]); + heap.push(items[10]); + objHeap.push(objItems[10]); + expect(heap.size()).toEqual(11); + expect(objHeap.size()).toEqual(11); + expect(heap.peek()).toEqual(items[4]); + expect(objHeap.peek()).toEqual(objItems[4]); - heap.push(items[11]); - objHeap.push(objItems[11]); - expect(heap.size()).toEqual(12); - expect(objHeap.size()).toEqual(12); - expect(heap.peek()).toEqual(items[4]); - expect(objHeap.peek()).toEqual(objItems[4]); - }); -}); \ No newline at end of file + heap.push(items[11]); + objHeap.push(objItems[11]); + expect(heap.size()).toEqual(12); + expect(objHeap.size()).toEqual(12); + expect(heap.peek()).toEqual(items[4]); + expect(objHeap.peek()).toEqual(objItems[4]); + }); +}); diff --git a/test/binaryHeap.remove-test.js b/test/binaryHeap.remove-test.js index b4a2dd7..f9086e5 100644 --- a/test/binaryHeap.remove-test.js +++ b/test/binaryHeap.remove-test.js @@ -1,75 +1,75 @@ describe('BinaryHeap.remove(node)', function () { - it('should remove the item from the heap.', function () { - var heap = new BinaryHeap(); - var objHeap = new BinaryHeap(function (x) { - return x.value; - }); - var items = [20, 4, 33, 1, 0, 34, 22, 31, 32, 5, 6, 7]; - for (var i = 0; i < items.length; i++) { - heap.push(items[i]); - objHeap.push({ - value: items[i] - }); - } + it('should remove the item from the heap.', function () { + var heap = new BinaryHeap(); + var objHeap = new BinaryHeap(function (x) { + return x.value; + }); + var items = [20, 4, 33, 1, 0, 34, 22, 31, 32, 5, 6, 7]; + for (var i = 0; i < items.length; i++) { + heap.push(items[i]); + objHeap.push({ + value: items[i] + }); + } - expect(heap.remove(0)).toEqual(0); - expect(objHeap.remove({ value: 0 })).toEqual({ value: 0 }); - expect(heap.peek()).toEqual(1); - expect(objHeap.peek()).toEqual({ value: 1 }); + expect(heap.remove(0)).toEqual(0); + expect(objHeap.remove({ value: 0 })).toEqual({ value: 0 }); + expect(heap.peek()).toEqual(1); + expect(objHeap.peek()).toEqual({ value: 1 }); - expect(heap.remove(1)).toEqual(1); - expect(objHeap.remove({ value: 1 })).toEqual({ value: 1 }); - expect(heap.peek()).toEqual(4); - expect(objHeap.peek()).toEqual({ value: 4 }); + expect(heap.remove(1)).toEqual(1); + expect(objHeap.remove({ value: 1 })).toEqual({ value: 1 }); + expect(heap.peek()).toEqual(4); + expect(objHeap.peek()).toEqual({ value: 4 }); - expect(heap.remove(4)).toEqual(4); - expect(objHeap.remove({ value: 4 })).toEqual({ value: 4 }); - expect(heap.peek()).toEqual(5); - expect(objHeap.peek()).toEqual({ value: 5 }); + expect(heap.remove(4)).toEqual(4); + expect(objHeap.remove({ value: 4 })).toEqual({ value: 4 }); + expect(heap.peek()).toEqual(5); + expect(objHeap.peek()).toEqual({ value: 5 }); - expect(heap.remove(5)).toEqual(5); - expect(objHeap.remove({ value: 5 })).toEqual({ value: 5 }); - expect(heap.peek()).toEqual(6); - expect(objHeap.peek()).toEqual({ value: 6 }); + expect(heap.remove(5)).toEqual(5); + expect(objHeap.remove({ value: 5 })).toEqual({ value: 5 }); + expect(heap.peek()).toEqual(6); + expect(objHeap.peek()).toEqual({ value: 6 }); - expect(heap.remove(6)).toEqual(6); - expect(objHeap.remove({ value: 6 })).toEqual({ value: 6 }); - expect(heap.peek()).toEqual(7); - expect(objHeap.peek()).toEqual({ value: 7 }); + expect(heap.remove(6)).toEqual(6); + expect(objHeap.remove({ value: 6 })).toEqual({ value: 6 }); + expect(heap.peek()).toEqual(7); + expect(objHeap.peek()).toEqual({ value: 7 }); - expect(heap.remove(7)).toEqual(7); - expect(objHeap.remove({ value: 7 })).toEqual({ value: 7 }); - expect(heap.peek()).toEqual(20); - expect(objHeap.peek()).toEqual({ value: 20 }); + expect(heap.remove(7)).toEqual(7); + expect(objHeap.remove({ value: 7 })).toEqual({ value: 7 }); + expect(heap.peek()).toEqual(20); + expect(objHeap.peek()).toEqual({ value: 20 }); - expect(heap.remove(20)).toEqual(20); - expect(objHeap.remove({ value: 20 })).toEqual({ value: 20 }); - expect(heap.peek()).toEqual(22); - expect(objHeap.peek()).toEqual({ value: 22 }); + expect(heap.remove(20)).toEqual(20); + expect(objHeap.remove({ value: 20 })).toEqual({ value: 20 }); + expect(heap.peek()).toEqual(22); + expect(objHeap.peek()).toEqual({ value: 22 }); - expect(heap.remove(22)).toEqual(22); - expect(objHeap.remove({ value: 22 })).toEqual({ value: 22 }); - expect(heap.peek()).toEqual(31); - expect(objHeap.peek()).toEqual({ value: 31 }); + expect(heap.remove(22)).toEqual(22); + expect(objHeap.remove({ value: 22 })).toEqual({ value: 22 }); + expect(heap.peek()).toEqual(31); + expect(objHeap.peek()).toEqual({ value: 31 }); - expect(heap.remove(31)).toEqual(31); - expect(objHeap.remove({ value: 31 })).toEqual({ value: 31 }); - expect(heap.peek()).toEqual(32); - expect(objHeap.peek()).toEqual({ value: 32 }); + expect(heap.remove(31)).toEqual(31); + expect(objHeap.remove({ value: 31 })).toEqual({ value: 31 }); + expect(heap.peek()).toEqual(32); + expect(objHeap.peek()).toEqual({ value: 32 }); - expect(heap.remove(32)).toEqual(32); - expect(objHeap.remove({ value: 32 })).toEqual({ value: 32 }); - expect(heap.peek()).toEqual(33); - expect(objHeap.peek()).toEqual({ value: 33 }); + expect(heap.remove(32)).toEqual(32); + expect(objHeap.remove({ value: 32 })).toEqual({ value: 32 }); + expect(heap.peek()).toEqual(33); + expect(objHeap.peek()).toEqual({ value: 33 }); - expect(heap.remove(33)).toEqual(33); - expect(objHeap.remove({ value: 33 })).toEqual({ value: 33 }); - expect(heap.peek()).toEqual(34); - expect(objHeap.peek()).toEqual({ value: 34 }); + expect(heap.remove(33)).toEqual(33); + expect(objHeap.remove({ value: 33 })).toEqual({ value: 33 }); + expect(heap.peek()).toEqual(34); + expect(objHeap.peek()).toEqual({ value: 34 }); - expect(heap.remove(34)).toEqual(34); - expect(objHeap.remove({ value: 34 })).toEqual({ value: 34 }); - expect(heap.peek()).not.toBeDefined(); - expect(objHeap.peek()).not.toBeDefined(); - }); -}); \ No newline at end of file + expect(heap.remove(34)).toEqual(34); + expect(objHeap.remove({ value: 34 })).toEqual({ value: 34 }); + expect(heap.peek()).not.toBeDefined(); + expect(objHeap.peek()).not.toBeDefined(); + }); +}); diff --git a/test/binaryHeap.removeAll-test.js b/test/binaryHeap.removeAll-test.js index 927a977..0ef9705 100644 --- a/test/binaryHeap.removeAll-test.js +++ b/test/binaryHeap.removeAll-test.js @@ -1,27 +1,27 @@ describe('BinaryHeap.removeAll()', function () { - it('should remove all items from the heap.', function () { - var heap = new BinaryHeap(); - var objHeap = new BinaryHeap(function (x) { - return x.value; - }); - var items = [20, 4, 33, 1, 0, 34, 22, 31, 32, 5, 6, 7]; - for (var i = 0; i < items.length; i++) { - heap.push(items[i]); - objHeap.push({ - value: items[i] - }); - } + it('should remove all items from the heap.', function () { + var heap = new BinaryHeap(); + var objHeap = new BinaryHeap(function (x) { + return x.value; + }); + var items = [20, 4, 33, 1, 0, 34, 22, 31, 32, 5, 6, 7]; + for (var i = 0; i < items.length; i++) { + heap.push(items[i]); + objHeap.push({ + value: items[i] + }); + } - expect(heap.size()).toEqual(12); - expect(objHeap.size()).toEqual(12); + expect(heap.size()).toEqual(12); + expect(objHeap.size()).toEqual(12); - heap.removeAll(); - objHeap.removeAll(); + heap.removeAll(); + objHeap.removeAll(); - expect(heap.size()).toEqual(0); - expect(objHeap.size()).toEqual(0); + expect(heap.size()).toEqual(0); + expect(objHeap.size()).toEqual(0); - expect(heap.peek()).not.toBeDefined(); - expect(objHeap.peek()).not.toBeDefined(); - }); -}); \ No newline at end of file + expect(heap.peek()).not.toBeDefined(); + expect(objHeap.peek()).not.toBeDefined(); + }); +}); diff --git a/test/karma.start.js b/test/karma.start.js index 48e6ac2..ec34cc2 100644 --- a/test/karma.start.js +++ b/test/karma.start.js @@ -1,42 +1,76 @@ var fail = function (msg) { - expect('should not reach this!: ' + msg).toEqual('failure'); - }, - TYPES_EXCEPT_STRING = [123, 123.123, null, undefined, {}, [], true, false, function () { - }], - TYPES_EXCEPT_STRING_OR_ARRAY = [123, 123.123, null, undefined, {}, true, false, function () { - }], - TYPES_EXCEPT_STRING_OR_NUMBER = [null, undefined, {}, [], true, false, function () { - }], - TYPES_EXCEPT_STRING_OR_ARRAY_OR_NUMBER = [null, undefined, {}, true, false, function () { - }], - TYPES_EXCEPT_NUMBER = ['string', null, undefined, {}, [], true, false, function () { - }], - TYPES_EXCEPT_OBJECT = ['string', 123, 123.123, null, undefined, true, false, function () { - }], - TYPES_EXCEPT_BOOLEAN = ['string', 123, 123.123, null, undefined, {}, [], function () { - }], - TYPES_EXCEPT_FUNCTION = ['string', 123, 123.123, null, undefined, {}, [], true, false], - CACHE_DEFAULTS = { - capacity: Number.MAX_VALUE, - maxAge: null, - deleteOnExpire: 'none', - onExpire: null, - cacheFlushInterval: null, - recycleFreq: 1000, - storageMode: 'none', - storageImpl: null, - verifyIntegrity: true, - disabled: false - }; + expect('should not reach this!: ' + msg).toEqual('failure'); + }, + TYPES_EXCEPT_STRING = [123, 123.123, null, undefined, {}, [], true, false, function () { + }], + TYPES_EXCEPT_STRING_OR_ARRAY = [123, 123.123, null, undefined, {}, true, false, function () { + }], + TYPES_EXCEPT_STRING_OR_NUMBER = [null, undefined, {}, [], true, false, function () { + }], + TYPES_EXCEPT_STRING_OR_ARRAY_OR_NUMBER = [null, undefined, {}, true, false, function () { + }], + TYPES_EXCEPT_NUMBER = ['string', null, undefined, {}, [], true, false, function () { + }], + TYPES_EXCEPT_OBJECT = ['string', 123, 123.123, null, undefined, true, false, function () { + }], + TYPES_EXCEPT_BOOLEAN = ['string', 123, 123.123, null, undefined, {}, [], function () { + }], + TYPES_EXCEPT_FUNCTION = ['string', 123, 123.123, null, undefined, {}, [], true, false], + CACHE_DEFAULTS = { + capacity: Number.MAX_VALUE, + maxAge: null, + deleteOnExpire: 'none', + onExpire: null, + cacheFlushInterval: null, + recycleFreq: 1000, + storageMode: 'none', + storageImpl: null, + verifyIntegrity: true, + disabled: false, + storePromises: false + }; -var $angularCacheFactoryProvider, $angularCacheFactory, BinaryHeap; +var $angularCacheFactoryProvider, $angularCacheFactory, BinaryHeap, $q, $rootScope, $httpBackend, $http; beforeEach(module('jmdobry.angular-cache', function (_$angularCacheFactoryProvider_) { - $angularCacheFactoryProvider = _$angularCacheFactoryProvider_; + $angularCacheFactoryProvider = _$angularCacheFactoryProvider_; })); -beforeEach(inject(function (_$angularCacheFactory_, _BinaryHeap_) { - $angularCacheFactory = _$angularCacheFactory_; - BinaryHeap = _BinaryHeap_; +beforeEach(inject(function (_$angularCacheFactory_, _BinaryHeap_, _$q_, _$rootScope_, _$httpBackend_, _$http_) { + $angularCacheFactory = _$angularCacheFactory_; + BinaryHeap = _BinaryHeap_; + $q = _$q_; + $rootScope = _$rootScope_; + $rootScope.$safeApply = function () { + var $scope, fn, force = false; + if (arguments.length === 1) { + var arg = arguments[0]; + if (typeof arg === 'function') { + fn = arg; + } else { + $scope = arg; + } + } else { + $scope = arguments[0]; + fn = arguments[1]; + if (arguments.length === 3) { + force = !!arguments[2]; + } + } + $scope = $scope || this; + fn = fn || function () { + }; + if (force || !$scope.$$phase) { + if ($scope.$apply) { + $scope.$apply(fn); + } else { + $scope.apply(fn); + } + } else { + fn(); + } + }; + $httpBackend = _$httpBackend_; + $http = _$http_; })); afterEach(function () { - $angularCacheFactory.removeAll(); + $angularCacheFactory.removeAll(); });