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