Skip to content

Instantly share code, notes, and snippets.

@benekastah
Last active December 30, 2015 04:29
Show Gist options
  • Save benekastah/7775889 to your computer and use it in GitHub Desktop.
Save benekastah/7775889 to your computer and use it in GitHub Desktop.
(function (root, module) {
'use strict';
if (!module) {
module = root;
}
var _keys = Object.keys || function (o) {
var keys = [];
for (var prop in o) {
if (o.hasOwnProperty(prop)) {
keys.push(prop);
}
}
return keys;
};
var _toString = Object.prototype.toString;
var _type = function (x) {
return _toString.call(x);
};
/**
* @class Cache
*
* @param {Storage} storage An object implementing the Storage interface
* http://www.w3.org/TR/webstorage/#the-storage-interface
*/
function Cache(storage, options) {
this.options = options || {};
this.storage = storage;
this._length();
}
module.Cache = Cache;
Cache.localStorage = function (Fake) {
var storage;
try {
storage = window.localStorage;
} catch (e) {};
if (!storage) {
storage = new (Fake || Cache.LocalStorageCompat)();
}
return new Cache(storage);
};
Cache.prototype._getFullItem = function (key) {
var item = this.storage.getItem(key);
if (item) {
item = JSON.parse(item);
} else {
item = {value: null, expire: null};
}
if (_type(item) !== '[object Object]' || !('value' in item && 'expire' in item)) {
throw new Error('Can\'t get "' + key + '": invalid format');
}
return item;
};
/**
* @param {String} key The key to find the value for
*
* @return {*} The JSON-parsed value
*/
Cache.prototype.getItem = Cache.prototype.get = function (key) {
return this._getFullItem(key).value;
};
/**
* @param {String} key The key to save the value under
* @param {Object} value The JSON-encodable value to store
* @param {Date|Number} expire The date when this key should expire or the number of seconds from now this key should expire
*/
Cache.prototype.setItem = Cache.prototype.set = function (key, value, expire) {
var expireDate;
if (expire) {
if (expire.getTime) {
expireDate = expire.getTime();
} else if (typeof expire === 'number') {
expireDate = new Date().getTime() + (expire * 1000);
} else {
throw new Error('expire must be either a Date or a Number');
}
} else {
throw new Error('options.expire is a required option');
}
try {
this.storage.setItem(key, JSON.stringify({
expire: expireDate,
value: value
}));
this._length();
} catch (e) {
if (e === 'QUOTA_EXCEEDED_ERR') {
this.purge();
// Retry after the purge.
this.setItem.apply(this, arguments);
} else {
throw e;
}
}
};
Cache.prototype.removeItem = Cache.prototype.remove = function (key) {
this.storage.removeItem(key);
this._length();
};
Cache.prototype.clear = function () {
this.storage.clear();
this.length = this.storage.length;
};
Cache.prototype.purge = function () {
var now = new Date();
for (var i = 0, len = this.length; i < len; i++) {
var key = this.storage.key(i);
var item = this._getFullItem(key);
if (now > item.expire) {
this.removeItem(key);
if (this.options.conservativePurge) {
return;
}
}
}
};
Cache.prototype._length = function () {
return this.length = this.storage.length;
};
Cache.LocalStorageCompat = (function () {
function LocalStorageCompat() {
this.clear();
}
LocalStorageCompat.prototype.setItem = function (key, value) {
this.storage[key] = '' + value;
this._keys();
};
LocalStorageCompat.prototype.getItem = function (key) {
return '' + this.storage[key];
};
LocalStorageCompat.prototype.removeItem = function (key) {
delete this.storage[key];
this._keys();
};
LocalStorageCompat.prototype.clear = function () {
this.storage = {};
this._keys();
};
LocalStorageCompat.prototype.key = function (i) {
return this.keys[i];
};
LocalStorageCompat.prototype._keys = function () {
this.keys = _keys(this.storage);
this.length = this.keys.length;
};
return LocalStorageCompat;
})();
})(typeof window !== 'undefined' ? window : global, typeof exports !== 'undefined' ? exports : null);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment