(function (root, factory) { 'use strict'; if (typeof define === 'function' && define.amd) { define(['angular'], factory); } else if (root.hasOwnProperty('angular')) { // Browser globals (root is window), we don't register it. factory(root.angular); } else if (typeof exports === 'object') { module.exports = factory(require('angular')); } }(this , function (angular) { 'use strict'; // In cases where Angular does not get passed or angular is a truthy value // but misses .module we can fall back to using window. angular = (angular && angular.module ) ? angular : window.angular; function isStorageSupported($window, storageType) { // Some installations of IE, for an unknown reason, throw "SCRIPT5: Error: Access is denied" // when accessing window.localStorage. This happens before you try to do anything with it. Catch // that error and allow execution to continue. // fix 'SecurityError: DOM Exception 18' exception in Desktop Safari, Mobile Safari // when "Block cookies": "Always block" is turned on var supported; try { supported = $window[storageType]; } catch(err) { supported = false; } // When Safari (OS X or iOS) is in private browsing mode, it appears as though localStorage and sessionStorage // is available, but trying to call .setItem throws an exception below: // "QUOTA_EXCEEDED_ERR: DOM Exception 22: An attempt was made to add something to storage that exceeded the quota." if(supported) { var key = '__' + Math.round(Math.random() * 1e7); try { $window[storageType].setItem(key, key); $window[storageType].removeItem(key, key); } catch(err) { supported = false; } } return supported; } /** * @ngdoc overview * @name ngStorage */ return angular.module('ngStorage', []) /** * @ngdoc object * @name ngStorage.$localStorage * @requires $rootScope * @requires $window */ .provider('$localStorage', _storageProvider('localStorage')) /** * @ngdoc object * @name ngStorage.$sessionStorage * @requires $rootScope * @requires $window */ .provider('$sessionStorage', _storageProvider('sessionStorage')); function _storageProvider(storageType) { var providerWebStorage = isStorageSupported(window, storageType); return function () { var storageKeyPrefix = 'ngStorage-'; this.setKeyPrefix = function (prefix) { if (typeof prefix !== 'string') { throw new TypeError('[ngStorage] - ' + storageType + 'Provider.setKeyPrefix() expects a String.'); } storageKeyPrefix = prefix; }; var serializer = angular.toJson; var deserializer = angular.fromJson; this.setSerializer = function (s) { if (typeof s !== 'function') { throw new TypeError('[ngStorage] - ' + storageType + 'Provider.setSerializer expects a function.'); } serializer = s; }; this.setDeserializer = function (d) { if (typeof d !== 'function') { throw new TypeError('[ngStorage] - ' + storageType + 'Provider.setDeserializer expects a function.'); } deserializer = d; }; this.supported = function() { return !!providerWebStorage; }; // Note: This is not very elegant at all. this.get = function (key) { return providerWebStorage && deserializer(providerWebStorage.getItem(storageKeyPrefix + key)); }; // Note: This is not very elegant at all. this.set = function (key, value) { return providerWebStorage && providerWebStorage.setItem(storageKeyPrefix + key, serializer(value)); }; this.remove = function (key) { providerWebStorage && providerWebStorage.removeItem(storageKeyPrefix + key); } this.$get = [ '$rootScope', '$window', '$log', '$timeout', '$document', function( $rootScope, $window, $log, $timeout, $document ){ // The magic number 10 is used which only works for some keyPrefixes... // See https://github.com/gsklee/ngStorage/issues/137 var prefixLength = storageKeyPrefix.length; // #9: Assign a placeholder object if Web Storage is unavailable to prevent breaking the entire AngularJS app // Note: recheck mainly for testing (so we can use $window[storageType] rather than window[storageType]) var isSupported = isStorageSupported($window, storageType), webStorage = isSupported || ($log.warn('This browser does not support Web Storage!'), {setItem: angular.noop, getItem: angular.noop, removeItem: angular.noop}), $storage = { $default: function(items) { for (var k in items) { angular.isDefined($storage[k]) || ($storage[k] = angular.copy(items[k]) ); } $storage.$sync(); return $storage; }, $reset: function(items) { for (var k in $storage) { '$' === k[0] || (delete $storage[k] && webStorage.removeItem(storageKeyPrefix + k)); } return $storage.$default(items); }, $sync: function () { for (var i = 0, l = webStorage.length, k; i < l; i++) { // #8, #10: `webStorage.key(i)` may be an empty string (or throw an exception in IE9 if `webStorage` is empty) (k = webStorage.key(i)) && storageKeyPrefix === k.slice(0, prefixLength) && ($storage[k.slice(prefixLength)] = deserializer(webStorage.getItem(k))); } }, $apply: function() { var temp$storage; _debounce = null; if (!angular.equals($storage, _last$storage)) { temp$storage = angular.copy(_last$storage); angular.forEach($storage, function(v, k) { if (angular.isDefined(v) && '$' !== k[0]) { webStorage.setItem(storageKeyPrefix + k, serializer(v)); delete temp$storage[k]; } }); for (var k in temp$storage) { webStorage.removeItem(storageKeyPrefix + k); } _last$storage = angular.copy($storage); } }, $supported: function() { return !!isSupported; } }, _last$storage, _debounce; $storage.$sync(); _last$storage = angular.copy($storage); $rootScope.$watch(function() { _debounce || (_debounce = $timeout($storage.$apply, 100, false)); }); // #6: Use `$window.addEventListener` instead of `angular.element` to avoid the jQuery-specific `event.originalEvent` $window.addEventListener && $window.addEventListener('storage', function(event) { if (!event.key) { return; } // Reference doc. var doc = $document[0]; if ( (!doc.hasFocus || !doc.hasFocus()) && storageKeyPrefix === event.key.slice(0, prefixLength) ) { event.newValue ? $storage[event.key.slice(prefixLength)] = deserializer(event.newValue) : delete $storage[event.key.slice(prefixLength)]; _last$storage = angular.copy($storage); $rootScope.$apply(); } }); $window.addEventListener && $window.addEventListener('beforeunload', function() { $storage.$apply(); }); return $storage; } ]; }; } }));