Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
329 changes: 171 additions & 158 deletions ngStorage.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
(function (root, factory) {
'use strict';

if (typeof define === 'function' && define.amd) {
define(['angular'], factory);
} else if (typeof exports === 'object') {
module.exports = factory(require('angular'));
} else {
// Browser globals (root is window), we don't register it.
factory(root.angular);
}
}(this , function (angular) {
'use strict';

if (typeof define === 'function' && define.amd) {
define(['angular'], factory);
} else if (typeof exports === 'object') {
module.exports = factory(require('angular'));
} else {
// Browser globals (root is window), we don't register it.
factory(root.angular);
}
}(this, function (angular) {
'use strict';

// In cases where Angular does not get passed or angular is a truthy value
Expand All @@ -30,7 +30,7 @@
* @requires $window
*/

.provider('$localStorage', _storageProvider('localStorage'))
.provider('$localStorage', _storageProvider('localStorage'))

/**
* @ngdoc object
Expand All @@ -39,183 +39,196 @@
* @requires $window
*/

.provider('$sessionStorage', _storageProvider('sessionStorage'));
.provider('$sessionStorage', _storageProvider('sessionStorage'));

function _storageProvider(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;
};

// Note: This is not very elegant at all.
this.get = function (key) {
return deserializer(window[storageType].getItem(storageKeyPrefix + key));
};

// Note: This is not very elegant at all.
this.set = function (key, value) {
return window[storageType].setItem(storageKeyPrefix + key, serializer(value));
};

this.$get = [
'$rootScope',
'$window',
'$log',
'$timeout',
'$document',

function(
$rootScope,
$window,
$log,
$timeout,
$document
){
function isStorageSupported(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;
}
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;
};

var objCopy = angular.copy;
var objEquals = angular.equals;

this.setObjCopy = function (f) {
if (typeof f !== 'function') {
throw new TypeError('[ngStorage] - ' + storageType + 'Provider.setObjCopy expects a function.');
}

objCopy = f;
};

this.setObjEquals = function (f) {
if (typeof f !== 'function') {
throw new TypeError('[ngStorage] - ' + storageType + 'Provider.setObjEquals expects a function.');
}

objEquals = f;
};

// Note: This is not very elegant at all.
this.get = function (key) {
return deserializer(window[storageType].getItem(storageKeyPrefix + key));
};

// Note: This is not very elegant at all.
this.set = function (key, value) {
return window[storageType].setItem(storageKeyPrefix + key, serializer(value));
};

this.$get = [
'$rootScope',
'$window',
'$log',
'$timeout',
'$document',

// When Safari (OS X or iOS) is in private browsing mode, it appears as though localStorage
// 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 && storageType === 'localStorage') {
var key = '__' + Math.round(Math.random() * 1e7);
function ($rootScope, $window, $log, $timeout, $document) {
function isStorageSupported(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 {
localStorage.setItem(key, key);
localStorage.removeItem(key);
supported = $window[storageType];
}
catch (err) {
supported = false;
}
}

return supported;
}

// The magic number 10 is used which only works for some keyPrefixes...
// See https://github.com/gsklee/ngStorage/issues/137
var prefixLength = storageKeyPrefix.length;
// When Safari (OS X or iOS) is in private browsing mode, it appears as though localStorage
// 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 && storageType === 'localStorage') {
var key = '__' + Math.round(Math.random() * 1e7);

// #9: Assign a placeholder object if Web Storage is unavailable to prevent breaking the entire AngularJS app
var webStorage = isStorageSupported(storageType) || ($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]) );
try {
localStorage.setItem(key, key);
localStorage.removeItem(key);
}

$storage.$sync();
return $storage;
},
$reset: function(items) {
for (var k in $storage) {
'$' === k[0] || (delete $storage[k] && webStorage.removeItem(storageKeyPrefix + k));
catch (err) {
supported = false;
}
}

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;
return supported;
}

_debounce = null;
// The magic number 10 is used which only works for some keyPrefixes...
// See https://github.com/gsklee/ngStorage/issues/137
var prefixLength = storageKeyPrefix.length;

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];
}
});
// #9: Assign a placeholder object if Web Storage is unavailable to prevent breaking the entire AngularJS app
var webStorage = isStorageSupported(storageType) || ($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] = objCopy(items[k]) );
}

for (var k in temp$storage) {
webStorage.removeItem(storageKeyPrefix + k);
$storage.$sync();
return $storage;
},
$reset: function (items) {
for (var k in $storage) {
'$' === k[0] || (delete $storage[k] && webStorage.removeItem(storageKeyPrefix + k));
}

_last$storage = angular.copy($storage);
}
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 (!objEquals($storage, _last$storage)) {
temp$storage = objCopy(_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 = objCopy($storage);
}
},
},
},
_last$storage,
_debounce;
_last$storage,
_debounce;

$storage.$sync();
$storage.$sync();

_last$storage = angular.copy($storage);
_last$storage = objCopy($storage);

$rootScope.$watch(function() {
_debounce || (_debounce = $timeout($storage.$apply, 100, false));
});
$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;
}
// #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];
// 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)];
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);
_last$storage = objCopy($storage);

$rootScope.$apply();
}
});
$rootScope.$apply();
}
});

$window.addEventListener && $window.addEventListener('beforeunload', function() {
$storage.$apply();
});
$window.addEventListener && $window.addEventListener('beforeunload', function () {
$storage.$apply();
});

return $storage;
}
];
};
return $storage;
}
];
};
}

}));
Loading