thoughts/node_modules/ngstorage/ngStorage.js
node_modules/ngstorage/ngStorage.jsRaw
1(function (root, factory) {
2 'use strict';
3
4 if (typeof define === 'function' && define.amd) {
5 define(['angular'], factory);
6 } else if (root.hasOwnProperty('angular')) {
7 // Browser globals (root is window), we don't register it.
8 factory(root.angular);
9 } else if (typeof exports === 'object') {
10 module.exports = factory(require('angular'));
11 }
12}(this , function (angular) {
13 'use strict';
14
15 // In cases where Angular does not get passed or angular is a truthy value
16 // but misses .module we can fall back to using window.
17 angular = (angular && angular.module ) ? angular : window.angular;
18
19
20 function isStorageSupported($window, storageType) {
21
22 // Some installations of IE, for an unknown reason, throw "SCRIPT5: Error: Access is denied"
23 // when accessing window.localStorage. This happens before you try to do anything with it. Catch
24 // that error and allow execution to continue.
25
26 // fix 'SecurityError: DOM Exception 18' exception in Desktop Safari, Mobile Safari
27 // when "Block cookies": "Always block" is turned on
28 var supported;
29 try {
30 supported = $window[storageType];
31 }
32 catch(err) {
33 supported = false;
34 }
35
36 // When Safari (OS X or iOS) is in private browsing mode, it appears as though localStorage and sessionStorage
37 // is available, but trying to call .setItem throws an exception below:
38 // "QUOTA_EXCEEDED_ERR: DOM Exception 22: An attempt was made to add something to storage that exceeded the quota."
39 if(supported) {
40 var key = '__' + Math.round(Math.random() * 1e7);
41 try {
42 $window[storageType].setItem(key, key);
43 $window[storageType].removeItem(key, key);
44 }
45 catch(err) {
46 supported = false;
47 }
48 }
49
50 return supported;
51 }
52
53 /**
54 * @ngdoc overview
55 * @name ngStorage
56 */
57
58 return angular.module('ngStorage', [])
59
60 /**
61 * @ngdoc object
62 * @name ngStorage.$localStorage
63 * @requires $rootScope
64 * @requires $window
65 */
66
67 .provider('$localStorage', _storageProvider('localStorage'))
68
69 /**
70 * @ngdoc object
71 * @name ngStorage.$sessionStorage
72 * @requires $rootScope
73 * @requires $window
74 */
75
76 .provider('$sessionStorage', _storageProvider('sessionStorage'));
77
78 function _storageProvider(storageType) {
79 var providerWebStorage = isStorageSupported(window, storageType);
80
81 return function () {
82 var storageKeyPrefix = 'ngStorage-';
83
84 this.setKeyPrefix = function (prefix) {
85 if (typeof prefix !== 'string') {
86 throw new TypeError('[ngStorage] - ' + storageType + 'Provider.setKeyPrefix() expects a String.');
87 }
88 storageKeyPrefix = prefix;
89 };
90
91 var serializer = angular.toJson;
92 var deserializer = angular.fromJson;
93
94 this.setSerializer = function (s) {
95 if (typeof s !== 'function') {
96 throw new TypeError('[ngStorage] - ' + storageType + 'Provider.setSerializer expects a function.');
97 }
98
99 serializer = s;
100 };
101
102 this.setDeserializer = function (d) {
103 if (typeof d !== 'function') {
104 throw new TypeError('[ngStorage] - ' + storageType + 'Provider.setDeserializer expects a function.');
105 }
106
107 deserializer = d;
108 };
109
110 this.supported = function() {
111 return !!providerWebStorage;
112 };
113
114 // Note: This is not very elegant at all.
115 this.get = function (key) {
116 return providerWebStorage && deserializer(providerWebStorage.getItem(storageKeyPrefix + key));
117 };
118
119 // Note: This is not very elegant at all.
120 this.set = function (key, value) {
121 return providerWebStorage && providerWebStorage.setItem(storageKeyPrefix + key, serializer(value));
122 };
123
124 this.remove = function (key) {
125 providerWebStorage && providerWebStorage.removeItem(storageKeyPrefix + key);
126 }
127
128 this.$get = [
129 '$rootScope',
130 '$window',
131 '$log',
132 '$timeout',
133 '$document',
134
135 function(
136 $rootScope,
137 $window,
138 $log,
139 $timeout,
140 $document
141 ){
142
143 // The magic number 10 is used which only works for some keyPrefixes...
144 // See https://github.com/gsklee/ngStorage/issues/137
145 var prefixLength = storageKeyPrefix.length;
146
147 // #9: Assign a placeholder object if Web Storage is unavailable to prevent breaking the entire AngularJS app
148 // Note: recheck mainly for testing (so we can use $window[storageType] rather than window[storageType])
149 var isSupported = isStorageSupported($window, storageType),
150 webStorage = isSupported || ($log.warn('This browser does not support Web Storage!'), {setItem: angular.noop, getItem: angular.noop, removeItem: angular.noop}),
151 $storage = {
152 $default: function(items) {
153 for (var k in items) {
154 angular.isDefined($storage[k]) || ($storage[k] = angular.copy(items[k]) );
155 }
156
157 $storage.$sync();
158 return $storage;
159 },
160 $reset: function(items) {
161 for (var k in $storage) {
162 '$' === k[0] || (delete $storage[k] && webStorage.removeItem(storageKeyPrefix + k));
163 }
164
165 return $storage.$default(items);
166 },
167 $sync: function () {
168 for (var i = 0, l = webStorage.length, k; i < l; i++) {
169 // #8, #10: `webStorage.key(i)` may be an empty string (or throw an exception in IE9 if `webStorage` is empty)
170 (k = webStorage.key(i)) && storageKeyPrefix === k.slice(0, prefixLength) && ($storage[k.slice(prefixLength)] = deserializer(webStorage.getItem(k)));
171 }
172 },
173 $apply: function() {
174 var temp$storage;
175
176 _debounce = null;
177
178 if (!angular.equals($storage, _last$storage)) {
179 temp$storage = angular.copy(_last$storage);
180 angular.forEach($storage, function(v, k) {
181 if (angular.isDefined(v) && '$' !== k[0]) {
182 webStorage.setItem(storageKeyPrefix + k, serializer(v));
183 delete temp$storage[k];
184 }
185 });
186
187 for (var k in temp$storage) {
188 webStorage.removeItem(storageKeyPrefix + k);
189 }
190
191 _last$storage = angular.copy($storage);
192 }
193 },
194 $supported: function() {
195 return !!isSupported;
196 }
197 },
198 _last$storage,
199 _debounce;
200
201 $storage.$sync();
202
203 _last$storage = angular.copy($storage);
204
205 $rootScope.$watch(function() {
206 _debounce || (_debounce = $timeout($storage.$apply, 100, false));
207 });
208
209 // #6: Use `$window.addEventListener` instead of `angular.element` to avoid the jQuery-specific `event.originalEvent`
210 $window.addEventListener && $window.addEventListener('storage', function(event) {
211 if (!event.key) {
212 return;
213 }
214
215 // Reference doc.
216 var doc = $document[0];
217
218 if ( (!doc.hasFocus || !doc.hasFocus()) && storageKeyPrefix === event.key.slice(0, prefixLength) ) {
219 event.newValue ? $storage[event.key.slice(prefixLength)] = deserializer(event.newValue) : delete $storage[event.key.slice(prefixLength)];
220
221 _last$storage = angular.copy($storage);
222
223 $rootScope.$apply();
224 }
225 });
226
227 $window.addEventListener && $window.addEventListener('beforeunload', function() {
228 $storage.$apply();
229 });
230
231 return $storage;
232 }
233 ];
234 };
235 }
236
237}));