UNPKG

@meanie/angular-storage

Version:

An Angular service for interacting with local storage, session storage and cookie storage

664 lines (552 loc) 15.6 kB
/** * @meanie/angular-storage * https://github.com/meanie/angular-storage * * Copyright (c) 2020 Adam Reis <adam@reis.nz> * License: MIT */ (function (window, angular, undefined) { 'use strict'; /** * Module definition and dependencies */ angular.module('Storage.Service', ['Storage.MemoryStorage.Service', 'Storage.CookieStorage.Service', 'Storage.SessionStorage.Service', 'Storage.LocalStorage.Service']) /** * Provider definition */ .provider('$storage', function $storageProvider() { /** * Global prefix */ this.prefix = ''; /** * Enabled storage engines */ this.enabledStorageEngines = ['local', 'session', 'cookie', 'memory']; /** * Default storage engine */ this.defaultStorageEngine = 'local'; /** * Set prefix */ this.setPrefix = function (prefix) { //Append dot if there isn't one if (prefix && prefix.substr(-1) !== '.') { prefix += '.'; } //Set prefix this.prefix = prefix; return this; }; /** * Set enabled storage engines */ this.setEnabledStorageEngines = function (enabledStorageEngines) { if (angular.isArray(enabledStorageEngines)) { this.enabledStorageEngines = enabledStorageEngines; } return this; }; /** * Set default storage engine */ this.setDefaultStorageEngine = function (defaultStorageEngine) { this.defaultStorageEngine = defaultStorageEngine; return this; }; /** * Service getter */ this.$get = ['$parse', '$injector', '$log', function ($parse, $injector, $log) { //Set configuration vars locally var storagePrefix = this.prefix; var enabledStorageEngines = this.enabledStorageEngines; var defaultStorageEngine = this.defaultStorageEngine; //Cached engine service instances var engineServices = {}; /** * Get storage engine service name */ function getEngineServiceName(engine) { return '$' + engine + 'Storage'; } /** * Get storage engine service instance */ function getEngineService(engine) { //Cached? if (engineServices[engine]) { return engineServices[engine]; } //Get the engine service var serviceName = getEngineServiceName(engine); var engineService = $injector.get(serviceName); //Not supported if (engine !== 'memory' && !engineService.isSupported()) { //Determine fallback var fallback = void 0; if (angular.isFunction(engineService.getFallbackEngine)) { fallback = engineService.getFallbackEngine(); } //Validate fallback and log warning fallback = fallback || 'memory'; $log.warn('Storage engine', engine, 'not supported in this browser.', 'Using fallback engine', fallback, 'instead.'); //Get fallback engine engineService = getEngineService(fallback); } //Cache and return the service return engineServices[engine] = engineService; } /** * Get prefixed key */ function getPrefixedKey(key) { return key ? storagePrefix + key : ''; } /** * Test if string only contains numbers */ function isStringNumber(string) { return (/^-?\d+\.?\d*$/.test(string.replace(/["']/g, '')) ); } /** * Test if string is a boolean */ function isStringBoolean(string) { return string === 'false' || string === 'true'; } /** * Test if a string is an object/array */ function isStringObject(string) { return string.charAt(0) === '{' || string.charAt(0) === '['; } /** * Helper to parse a value for storage */ function parseValue(value) { //Normalize undefined values if (angular.isUndefined(value) || value === null) { return null; } //Convert to JSON if not a string if (angular.isObject(value) || angular.isArray(value) || typeof value === 'boolean' || angular.isNumber(Number(value) || value)) { return angular.toJson(value); } //Use as is return value; } /** * Helper to format a value from storage */ function formatValue(value) { //Null values (also convert string null value) if (!value || value === null || value === 'null') { return null; } //Parse from JSON if needed if (isStringObject(value) || isStringBoolean(value) || isStringNumber(value)) { try { return angular.fromJson(value); } catch (e) { return null; } } //Return as is return value; } /***************************************************************************** * Initialization ***/ //Make sure the default storage engine is enabled if (enabledStorageEngines.indexOf(defaultStorageEngine) === -1) { throw new Error('Default storage engine', defaultStorageEngine, 'is not enabled.'); } //Validate enabled storage engines angular.forEach(enabledStorageEngines, function (engine) { var serviceName = getEngineServiceName(engine); if (!$injector.has(serviceName)) { throw new Error('Storage engine', engine, 'does not exist.', 'Make sure the service', serviceName, 'is included as a dependency.'); } }); /***************************************************************************** * Storage engine instances class ***/ /** * Storage engine instance constructor */ var StorageEngine = function StorageEngine(engine) { this.engine = getEngineService(engine); }; /** * Setter */ StorageEngine.prototype.set = function (key, value) { //Must have a key key = getPrefixedKey(key); if (!key) { return false; } //Parse value value = parseValue(value); //Store value try { this.engine.set(key, value); return true; } catch (e) { return false; } }; /** * Getter */ StorageEngine.prototype.get = function (key, defaultValue) { //Default value of default value is set to null (defaultvalueception!) if (typeof defaultValue === 'undefined') { defaultValue = null; } //Must have a key key = getPrefixedKey(key); if (!key) { return defaultValue; } //Get value and return formatted try { var value = this.engine.get(key); if (value === null) { return defaultValue; } return formatValue(value); } catch (e) { return defaultValue; } }; /** * Remover */ StorageEngine.prototype.remove = function (key) { //Must have a key key = getPrefixedKey(key); if (!key) { return false; } //Remove try { this.engine.remove(key); return true; } catch (e) { return false; } }; /** * Clear, optionally prefixed */ StorageEngine.prototype.clear = function (prefix) { prefix = storagePrefix + (prefix || ''); this.engine.clear(prefix); }; /***************************************************************************** * Exposed storage service ***/ //Create default storage engine service and initialize engines cache var Storage = new StorageEngine(defaultStorageEngine); var StorageEngines = {}; //Store ourselves in the cache as the default storage engine StorageEngines[defaultStorageEngine] = Storage; //Create dynamic properties for all enabled storage engines angular.forEach(enabledStorageEngines, function (engine) { Object.defineProperty(Storage, engine, { /*jshint -W083 */ get: function get() { if (StorageEngines[engine]) { return StorageEngines[engine]; } return StorageEngines[engine] = new StorageEngine(engine); } }); }); //Return return Storage; }]; }); })(window, window.angular); (function (window, angular, undefined) { 'use strict'; /** * Module definition and dependencies */ angular.module('Storage.CookieStorage.Service', []) /** * Cookie storage engine service */ .factory('$cookieStorage', ['$injector', '$log', function $cookieStorage($injector, $log) { //Get cookies service var $cookies = void 0; if ($injector.has('$cookies')) { $cookies = $injector.get('$cookies'); } else { $log.warn('Cookie storage requires the `ngCookies` module as a dependency'); } /** * Storage engine interface */ return { /** * Check if supported */ isSupported: function isSupported() { return !!$cookies; }, /** * Get fallback engine */ getFallbackEngine: function getFallbackEngine() { return 'memory'; }, /** * Set an item */ set: function set(key, value) { $cookies.put(key, value); }, /** * Get an item */ get: function get(key) { return $cookies.get(key); }, /** * Remove an item */ remove: function remove(key) { $cookies.remove(key); }, /** * Clear items */ clear: function clear(prefix) { //Get all the cookies and corresponding keys var prefixRegex = prefix ? new RegExp('^' + prefix) : null; var cookies = $cookies.getAll(); var keys = cookies ? Object.keys(cookies) : []; //Loop keys for (var k = 0; k < keys.length; k++) { if (!prefix || prefixRegex.test(keys[k])) { $cookies.remove(keys[k]); } } } }; }]); })(window, window.angular); (function (window, angular, undefined) { 'use strict'; /** * Module definition and dependencies */ angular.module('Storage.LocalStorage.Service', []) /** * Local storage engine service */ .factory('$localStorage', ['$window', function $localStorage($window) { /** * Storage engine interface */ return { /** * Check if supported */ isSupported: function isSupported() { //Prepare test key var key = '___' + Math.round(Math.random() * 1e7); //Try local storage try { $window.localStorage.setItem(key, ''); $window.localStorage.removeItem(key); return true; } catch (e) { return false; } }, /** * Get fallback engine */ getFallbackEngine: function getFallbackEngine() { return 'cookie'; }, /** * Set an item */ set: function set(key, value) { $window.localStorage.setItem(key, value); }, /** * Get an item */ get: function get(key) { return $window.localStorage.getItem(key); }, /** * Remove an item */ remove: function remove(key) { $window.localStorage.removeItem(key); }, /** * Clear items */ clear: function clear(prefix) { //Get regex for prefix and keys var prefixRegex = prefix ? new RegExp('^' + prefix) : null; var keys = Object.keys($window.localStorage); //Loop keys for (var k = 0; k < keys.length; k++) { if (!prefix || prefixRegex.test(keys[k])) { $window.localStorage.removeItem(keys[k]); } } } }; }]); })(window, window.angular); (function (window, angular, undefined) { 'use strict'; /** * Module definition and dependencies */ angular.module('Storage.MemoryStorage.Service', []) /** * Memory storage engine service */ .factory('$memoryStorage', function $memoryStorage() { /** * Store */ var memoryStore = {}; /** * Storage engine interface */ return { /** * Always supported */ isSupported: function isSupported() { return true; }, /** * Get fallback engine */ getFallbackEngine: function getFallbackEngine() { return ''; }, /** * Set an item */ set: function set(key, value) { memoryStore[key] = value; }, /** * Get an item */ get: function get(key) { if (typeof memoryStore[key] === 'undefined') { return null; } return memoryStore[key]; }, /** * Remove an item */ remove: function remove(key) { if (typeof memoryStore[key] !== 'undefined') { delete memoryStore[key]; } }, /** * Clear items */ clear: function clear(prefix) { //Get regex for prefix and keys var prefixRegex = prefix ? new RegExp('^' + prefix) : null; var keys = Object.keys(memoryStore); //Loop keys for (var k = 0; k < keys.length; k++) { if (!prefix || prefixRegex.test(keys[k])) { delete memoryStore[keys[k]]; } } } }; }); })(window, window.angular); (function (window, angular, undefined) { 'use strict'; /** * Module definition and dependencies */ angular.module('Storage.SessionStorage.Service', []) /** * Session storage engine service */ .factory('$sessionStorage', ['$window', function $sessionStorage($window) { /** * Storage engine interface */ return { /** * Check if supported */ isSupported: function isSupported() { //Prepare test key var key = '___' + Math.round(Math.random() * 1e7); //Try session storage try { $window.sessionStorage.setItem(key, ''); $window.sessionStorage.removeItem(key); return true; } catch (e) { return false; } }, /** * Get fallback engine */ getFallbackEngine: function getFallbackEngine() { return 'memory'; }, /** * Set an item */ set: function set(key, value) { $window.sessionStorage.setItem(key, value); }, /** * Get an item */ get: function get(key) { return $window.sessionStorage.getItem(key); }, /** * Remove an item */ remove: function remove(key) { $window.sessionStorage.removeItem(key); }, /** * Clear items */ clear: function clear(prefix) { //Get regex for prefix and keys var prefixRegex = prefix ? new RegExp('^' + prefix) : null; var keys = Object.keys($window.sessionStorage); //Loop keys for (var k = 0; k < keys.length; k++) { if (!prefix || prefixRegex.test(keys[k])) { $window.sessionStorage.removeItem(keys[k]); } } } }; }]); })(window, window.angular);