Skip to content

Instantly share code, notes, and snippets.

@neotericneoteny
Last active April 24, 2018 10:41
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save neotericneoteny/3219776 to your computer and use it in GitHub Desktop.
Save neotericneoteny/3219776 to your computer and use it in GitHub Desktop.
StorageManager makes it super trivial to persist and retrieve arbitrary structured data for front-end HTML web applications. It is essentially a glorified wrapper class for the W3C Web Storage specification.
/**
* An adaptable cache manager class used to persist one or more arbitrary objects client-side. The default caching
* strategy is WEB_STORAGE. In the event WEB_STORAGE is unavailable (i.e., in older web browsers like IE6), the
* backup strategy, COOKIE_STORAGE, is used with one minor catch detailed next.
*
* COOKIE_STORAGE will only work when the jquery.cookie.js plugin is available in the jQuery plugin path. This decision was made
* in an effort to simplify this class' source and because it just didn't make sense to reinvent the wheel.
*
* For usage instructions, please see the comments above the class definition.
*
* External Dependencies:
*
* - Global JSON object which is available in all modern browsers. If your project has the liklihood of being used by a client with
* an outdated and extremely unsecure browser like IE6 or IE7, it is recommended that you include the JSON polyfill in your build
* process. You can find the JSON polyfill library here:
*
* https://github.com/douglascrockford/JSON-js
*
* - jquery.cookie.js project in order for COOKIE_STORAGE_STRATEGY to work as expected.
*
* - RequireJS. If you do not use RequireJS in your application, just comment out the line that begins with 'define( .. )' along
* with its corresponding closing tag. You'll then need to attach the StorageManager function to the global scope.
*
*
* Copyright (c) 2013 Matthew A. Zimmer
* http://neotericneoteny.com
*
* Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
*/
(function( window ) {
var $ = window.jQuery,
document = window.document;
define( [], function() {
'use strict';
var DEFAULT_VALUE = {},
WEB_STORAGE_STRATEGY = 'web_storage',
COOKIE_STORAGE_STRATEGY = 'cookie',
PER_REQUEST_STORAGE_STRATEGY = 'request',
PRIMARY_STORAGE_STRATEGY = WEB_STORAGE_STRATEGY,
BACKUP_STORAGE_STRATEGY = COOKIE_STORAGE_STRATEGY,
DEFAULT_STORAGE_STRATEGY = PER_REQUEST_STORAGE_STRATEGY;
/**
* If the style of function declaration used in this class is unfamiliar, no worries. The public API method names
* available to use on instances of this class are indicated with a prefix of 'local.' followed by the method name.
* Any functions declared within the scope of this class that begin with the 'function' keyword are private
* functions visible only within the scope of this class definition. This style is the closest implementation
* available to ultimately achieve conventional encapsulation within the JavaScript programming language. For
* readability purposes, all public methods are declared first followed by all private functions (similar to the
* Ruby convention).
*
* Public API Methods:
*
* read( $key );
* save( $data, $key );
* remove( $key );
* purge();
* getWebStorage();
*
*
* @param $storageKey {String} The root key corresponding to the location in the storage facility where cached objects reside.
* @param $cacheIndefinitely {Boolean} A boolean indicating whether to persist this store across browser sessions until manually purged through this API or by the end-user.
* @param $defaultValue {Object} The default value used to initialize the storage container. If you do not provide a value for this parameter, an empty object is used.
* @param $cacheStrategy {String} The caching strategy which determines where objects are persisted and retrieved from through the API methods. See the static properties which are attached to this class towards the bottom of the class.
*
* TODO:
*
* - If COOKIE_STORAGE_STRATEGY enabled, update expiration date appropriately based on cacheIndefinitely state.
*/
function StorageManager( $storageKey, $cacheIndefinitely, $defaultValue, $cacheStrategy ) {
var local = this,
cacheIndefinitely = false,
cacheStrategy = validateCacheStrategyOrUseDefault( $cacheStrategy || DEFAULT_STORAGE_STRATEGY );
/**
* @constructor
*/
function __construct__() {
cacheIndefinitely = $cacheIndefinitely == true;
$defaultValue = $defaultValue !== undefined ? $defaultValue : DEFAULT_VALUE;
}
/**
* Queries the storage for data based on an arbitrary key. If no key is provided, this
* method returns the entire storage contents for this StorageManager instance.
*
* @param $key An arbitrary key used to look up data from the storage container.
*/
local.read = function( $key ) {
var cache = getStorageCache( $storageKey );
return $key !== undefined ? ( typeof(cache) === 'object' && $key in cache ? cache[ $key ] : null ) : cache;
};
/**
* Writes the contents of <code>$data</code> to the underlying storage container. If <code>$key</code> is
* undefined, <code>$data</code> will consume the entire container. However, if you want to maintain
* multiple data entities within this storage container, you will need to pass an arbitrary value for the
* <code>$key</code> parameter, too.
*/
local.save = function( $data, $key ) {
var cache = getStorageCache( $storageKey );
if( cache && $key !== undefined ) {
cache[ $key ] = $data !== undefined ? $data : $defaultValue;
} else {
cache = $data !== undefined ? $data : $defaultValue;
}
saveCache( cache );
};
/**
* Removes data from the storage container for a given key.
*
* @param $key Required parameter corresponding to the <code>$key</code> to remove from the cache. If this storage
* manager is not designed to manage its own key/value pairs, then you must <code>#purge</code> if you want to clear
* your cache.
*/
local.remove = function( $key ) {
if( $key !== undefined ) {
var cache = null;
switch( cacheStrategy ) {
case WEB_STORAGE_STRATEGY:
if( typeof(Storage) !== 'undefined' ) {
cache = local.getWebStorage()[ $storageKey ];
}
break;
case COOKIE_STORAGE_STRATEGY:
if( $.cookie ) {
cache = $.cookie( $storageKey, {'path': '/'} );
}
break;
case PER_REQUEST_STORAGE_STRATEGY:
default:
cache = $.data( document.body, $storageKey );
break;
}
if( cache ) {
// In case an attempt to delete an undeletable property is made, let's
// handle the TypeError exception thrown
try {
cache = JSON.parse( cache );
delete cache[ $key ];
saveCache( cache );
}catch(e) {}
}
}
};
/**
* Wipes out the entire storage container and nulls it out completely.
*/
local.purge = function() {
initStorageCache( $storageKey );
};
/**
* If the caching strategy is WEB_STORAGE_STRATEGY, this function will be consulted to return the appropriate
* Web Storage store based on whether this provider has been told to cache the contents indefinitely or
* temporarily.
*
* @return localStorage if this provider has been told to cache the contents indefinitely. Otherwise sessionStorage.
*/
local.getWebStorage = function() {
return cacheIndefinitely ? localStorage : sessionStorage;
};
/**
* Returns the cache store where objects may be stored and persisted.
*
* @param $storageKey
* @return {*}
*/
function getStorageCache( $storageKey ) {
var cache = null;
switch( cacheStrategy ) {
case WEB_STORAGE_STRATEGY:
if( typeof(Storage) !== 'undefined' ) {
cache = local.getWebStorage()[ $storageKey ];
}
break;
case COOKIE_STORAGE_STRATEGY:
if( $.cookie ) {
cache = $.cookie( $storageKey, {'path': '/'} );
}
break;
case PER_REQUEST_STORAGE_STRATEGY:
default:
cache = $.data( document.body, $storageKey );
break;
}
if( !cache ) {
initStorageCache( $storageKey );
cache = JSON.stringify( getStorageCache( $storageKey ) );
}
return JSON.parse( cache );
}
/**
* Initialized the storage cached based on the caching strategy for this instance.
*
* @param $storageKey
*/
function initStorageCache( $storageKey ) {
var cache = null;
switch( cacheStrategy ) {
case WEB_STORAGE_STRATEGY:
if( typeof(Storage) !== 'undefined' ) {
cache = local.getWebStorage()[ $storageKey ];
if( !cache ) {
cache = $defaultValue;
local.getWebStorage()[ $storageKey ] = JSON.stringify( cache );
}
}
break;
case COOKIE_STORAGE_STRATEGY:
if( $.cookie ) {
cache = $.cookie( $storageKey, {'path': '/'} );
if( !cache ) {
cache = $defaultValue;
$.cookie( $storageKey, JSON.stringify( cache ), {'path': '/'} );
}
}
break;
case PER_REQUEST_STORAGE_STRATEGY:
default:
cache = $.data( document.body, $storageKey );
if( cache == null ) {
cache = $defaultValue;
$.data( document.body, $storageKey, JSON.stringify( cache ) );
}
break;
}
}
/**
* Persists the contents to the appropriate storage facility determined by the chosen caching strategy.
*
* @param cache The cache in which to persist.
*/
function saveCache( cache ) {
var serializedCache = JSON.stringify( cache );
switch( cacheStrategy ) {
case WEB_STORAGE_STRATEGY:
if( typeof(Storage) !== 'undefined' ) {
local.getWebStorage()[ $storageKey ] = serializedCache;
}
break;
case COOKIE_STORAGE_STRATEGY:
if( $.cookie ) {
$.cookie( $storageKey, serializedCache, {'path': '/'} );
}
break;
case PER_REQUEST_STORAGE_STRATEGY:
default:
$.data( document.body, $storageKey, serializedCache );
break;
}
}
/**
* This function checks whether the selected cache strategy is supported in the current user's browser.
* If the browser doesn't support the chosen strategy, the backup strategy is used instead. Note the
* backup strategy is guaranteed to work in all browsers.
*
* @param $cacheStrategy {String} The potential cache strategy to be used by this instance.
* @return {String} The actual cache strategy used by this instance.
*/
function validateCacheStrategyOrUseDefault( $cacheStrategy ) {
switch( true ) {
case isSupportedCacheStrategy( $cacheStrategy ):
case validateCacheStrategyOrUseDefault( $cacheStrategy = BACKUP_STORAGE_STRATEGY ):
return $cacheStrategy;
break;
}
return validateCacheStrategyOrUseDefault( PER_REQUEST_STORAGE_STRATEGY );
}
/**
* In the case of an unsupported cache strategy type injection into the constructor, we validate against our
* supported stretegy types here.
*
* @param $cacheStrategy The potential cache strategy to be used by this instance.
* @return {Boolean} true if the provided <code>$cacheStrategy</code> is valid; false otherwise;
*/
function isSupportedCacheStrategy( $cacheStrategy ) {
switch( true ) {
case $cacheStrategy === WEB_STORAGE_STRATEGY && typeof(Storage) !== 'undefined':
case $cacheStrategy === COOKIE_STORAGE_STRATEGY && $.cookie:
case $cacheStrategy === PER_REQUEST_STORAGE_STRATEGY:
return true;
break;
}
return false;
}
__construct__();
return local;
}
// class-level constants
StorageManager.WEB_STORAGE_STRATEGY = WEB_STORAGE_STRATEGY;
StorageManager.COOKIE_STORAGE_STRATEGY = COOKIE_STORAGE_STRATEGY;
StorageManager.PER_REQUEST_STORAGE_STRATEGY = PER_REQUEST_STORAGE_STRATEGY;
return StorageManager;
} );
})( window );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment