Last active
August 11, 2016 14:43
-
-
Save codyromano/3b0106de3b77c1d314c070ba8b7d37e5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * @desc A type-aware wrapper around window.localStorage | |
| * @module Store | |
| */ | |
| (function(exports) { | |
| 'use strict'; | |
| var proto = Store.prototype; | |
| /* Name of the properties that store type annotations. These | |
| should be unique enough that they don't collide with the | |
| properties of other object literals in localStorage. */ | |
| var PROP_TYPE = '_ls_type', | |
| PROP_VALUE = '_ls_value'; | |
| function Store(options) { | |
| options = options || {}; | |
| // For keeping your data organized | |
| this.namespace = options.namespace || 'main'; | |
| if (!window.localStorage) { | |
| throw new Error('localStorage backend is unavailable'); | |
| } | |
| } | |
| function getType(value) { | |
| /* undefined needs its own type because it can't | |
| be parsed as an object. If a value is stored as | |
| undefined, we need to explicitly return undefined. */ | |
| if (value === undefined) { | |
| return 'undefined'; | |
| } | |
| /* typeof null will equal 'object,' but we want to | |
| differentiate between null and object literals */ | |
| if (value === null) { | |
| return 'null'; | |
| } | |
| if (Array.isArray(value)) { | |
| return 'array'; | |
| } | |
| return typeof value; | |
| } | |
| function isTypeEncodedObj(value) { | |
| return (typeof value === 'object' && | |
| value.hasOwnProperty(PROP_VALUE) && | |
| value.hasOwnProperty(PROP_TYPE)); | |
| } | |
| function typeDecode(value) { | |
| var typeObj, storedValue, storedType, objKey, objVal, decoded; | |
| // Don't decode objects unless they contain type info | |
| if (!isTypeEncodedObj(value)) { | |
| return value; | |
| } | |
| // An object with type annotations | |
| typeObj = value; | |
| storedValue = typeObj[PROP_VALUE]; | |
| storedType = typeObj[PROP_TYPE]; | |
| switch(storedType) { | |
| case 'undefined': | |
| decoded = undefined; | |
| break; | |
| case 'null': | |
| decoded = null; | |
| break; | |
| case 'number': | |
| decoded = parseFloat(storedValue); | |
| break; | |
| // Recursively decode object properties | |
| case 'array': | |
| decoded = storedValue.map(typeDecode); | |
| break; | |
| case 'object': | |
| for (objKey in storedValue) { | |
| objVal = storedValue[objKey]; | |
| typeObj[PROP_VALUE][objKey] = typeDecode(objVal); | |
| } | |
| decoded = typeObj[PROP_VALUE]; | |
| break; | |
| default: | |
| // Return numbers, strings and other data types as-is | |
| decoded = typeObj[PROP_VALUE]; | |
| break; | |
| } | |
| return decoded; | |
| } | |
| function typeEncode(value) { | |
| var encoded = {}, objKey, objVal; | |
| encoded[PROP_VALUE] = value; | |
| encoded[PROP_TYPE] = getType(value); | |
| switch (encoded[PROP_TYPE]) { | |
| /* If the value is an object (array or object literal), | |
| we need to recursively encode its properties. */ | |
| case 'array': | |
| encoded[PROP_VALUE] = encoded[PROP_VALUE].map(typeEncode); | |
| break; | |
| case 'object': | |
| for (objKey in value) { | |
| objVal = value[objKey]; | |
| encoded[PROP_VALUE][objKey] = typeEncode(objVal); | |
| } | |
| break; | |
| default: | |
| /* We don't need to do any processing for other data types | |
| such as numbers or strings. */ | |
| break; | |
| } | |
| return encoded; | |
| } | |
| /** | |
| * @returns {Object} | |
| */ | |
| function processJSON(string, storeKey, operation) { | |
| var processed = null; | |
| try { | |
| if (operation === 'parse') { | |
| processed = JSON.parse(string); | |
| } else if (operation === 'stringify') { | |
| processed = JSON.stringify(string); | |
| } else { | |
| throw new Error('Unrecognized operation: ' + operation); | |
| } | |
| } catch(err) { | |
| console.error("An exception occurred while processing " + | |
| "locally stored data for '" + storeKey + "': "); | |
| console.log(err); | |
| } | |
| return processed; | |
| } | |
| function getKey(key) { | |
| return [key, this.namespace].join('_'); | |
| } | |
| proto.setItem = function(key, value) { | |
| var storeKey = getKey.call(this, key); | |
| var encoded = processJSON(typeEncode(value), storeKey, 'stringify'); | |
| localStorage.setItem(storeKey, encoded); | |
| }; | |
| proto.getItem = function(key) { | |
| var storeKey = getKey.call(this, key), | |
| encoded = localStorage.getItem(storeKey), | |
| parsed = processJSON(encoded, storeKey, 'parse'); | |
| if (!encoded || !parsed) { | |
| return null; | |
| } | |
| return typeDecode(parsed); | |
| }; | |
| exports.Store = Store; | |
| })(window); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment