angular-local-storage.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. /**
  2. * An Angular module that gives you access to the browsers local storage
  3. * @version v0.2.6 - 2016-03-16
  4. * @link https://github.com/grevory/angular-local-storage
  5. * @author grevory <greg@gregpike.ca>
  6. * @license MIT License, http://www.opensource.org/licenses/MIT
  7. */
  8. (function (window, angular) {
  9. var isDefined = angular.isDefined,
  10. isUndefined = angular.isUndefined,
  11. isNumber = angular.isNumber,
  12. isObject = angular.isObject,
  13. isArray = angular.isArray,
  14. extend = angular.extend,
  15. toJson = angular.toJson;
  16. angular
  17. .module('LocalStorageModule', [])
  18. .provider('localStorageService', function() {
  19. // You should set a prefix to avoid overwriting any local storage variables from the rest of your app
  20. // e.g. localStorageServiceProvider.setPrefix('yourAppName');
  21. // With provider you can use config as this:
  22. // myApp.config(function (localStorageServiceProvider) {
  23. // localStorageServiceProvider.prefix = 'yourAppName';
  24. // });
  25. this.prefix = 'ls';
  26. // You could change web storage type localstorage or sessionStorage
  27. this.storageType = 'localStorage';
  28. // Cookie options (usually in case of fallback)
  29. // expiry = Number of days before cookies expire // 0 = Does not expire
  30. // path = The web path the cookie represents
  31. this.cookie = {
  32. expiry: 30,
  33. path: '/'
  34. };
  35. // Send signals for each of the following actions?
  36. this.notify = {
  37. setItem: true,
  38. removeItem: false
  39. };
  40. // Setter for the prefix
  41. this.setPrefix = function(prefix) {
  42. this.prefix = prefix;
  43. return this;
  44. };
  45. // Setter for the storageType
  46. this.setStorageType = function(storageType) {
  47. this.storageType = storageType;
  48. return this;
  49. };
  50. // Setter for cookie config
  51. this.setStorageCookie = function(exp, path) {
  52. this.cookie.expiry = exp;
  53. this.cookie.path = path;
  54. return this;
  55. };
  56. // Setter for cookie domain
  57. this.setStorageCookieDomain = function(domain) {
  58. this.cookie.domain = domain;
  59. return this;
  60. };
  61. // Setter for notification config
  62. // itemSet & itemRemove should be booleans
  63. this.setNotify = function(itemSet, itemRemove) {
  64. this.notify = {
  65. setItem: itemSet,
  66. removeItem: itemRemove
  67. };
  68. return this;
  69. };
  70. this.$get = ['$rootScope', '$window', '$document', '$parse', function($rootScope, $window, $document, $parse) {
  71. var self = this;
  72. var prefix = self.prefix;
  73. var cookie = self.cookie;
  74. var notify = self.notify;
  75. var storageType = self.storageType;
  76. var webStorage;
  77. // When Angular's $document is not available
  78. if (!$document) {
  79. $document = document;
  80. } else if ($document[0]) {
  81. $document = $document[0];
  82. }
  83. // If there is a prefix set in the config lets use that with an appended period for readability
  84. if (prefix.substr(-1) !== '.') {
  85. prefix = !!prefix ? prefix + '.' : '';
  86. }
  87. var deriveQualifiedKey = function(key) {
  88. return prefix + key;
  89. };
  90. // Checks the browser to see if local storage is supported
  91. var browserSupportsLocalStorage = (function () {
  92. try {
  93. var supported = (storageType in $window && $window[storageType] !== null);
  94. // When Safari (OS X or iOS) is in private browsing mode, it appears as though localStorage
  95. // is available, but trying to call .setItem throws an exception.
  96. //
  97. // "QUOTA_EXCEEDED_ERR: DOM Exception 22: An attempt was made to add something to storage
  98. // that exceeded the quota."
  99. var key = deriveQualifiedKey('__' + Math.round(Math.random() * 1e7));
  100. if (supported) {
  101. webStorage = $window[storageType];
  102. webStorage.setItem(key, '');
  103. webStorage.removeItem(key);
  104. }
  105. return supported;
  106. } catch (e) {
  107. storageType = 'cookie';
  108. $rootScope.$broadcast('LocalStorageModule.notification.error', e.message);
  109. return false;
  110. }
  111. }());
  112. // Directly adds a value to local storage
  113. // If local storage is not available in the browser use cookies
  114. // Example use: localStorageService.add('library','angular');
  115. var addToLocalStorage = function (key, value) {
  116. // Let's convert undefined values to null to get the value consistent
  117. if (isUndefined(value)) {
  118. value = null;
  119. } else {
  120. value = toJson(value);
  121. }
  122. // If this browser does not support local storage use cookies
  123. if (!browserSupportsLocalStorage || self.storageType === 'cookie') {
  124. if (!browserSupportsLocalStorage) {
  125. $rootScope.$broadcast('LocalStorageModule.notification.warning', 'LOCAL_STORAGE_NOT_SUPPORTED');
  126. }
  127. if (notify.setItem) {
  128. $rootScope.$broadcast('LocalStorageModule.notification.setitem', {key: key, newvalue: value, storageType: 'cookie'});
  129. }
  130. return addToCookies(key, value);
  131. }
  132. try {
  133. if (webStorage) {
  134. webStorage.setItem(deriveQualifiedKey(key), value);
  135. }
  136. if (notify.setItem) {
  137. $rootScope.$broadcast('LocalStorageModule.notification.setitem', {key: key, newvalue: value, storageType: self.storageType});
  138. }
  139. } catch (e) {
  140. $rootScope.$broadcast('LocalStorageModule.notification.error', e.message);
  141. return addToCookies(key, value);
  142. }
  143. return true;
  144. };
  145. // Directly get a value from local storage
  146. // Example use: localStorageService.get('library'); // returns 'angular'
  147. var getFromLocalStorage = function (key) {
  148. if (!browserSupportsLocalStorage || self.storageType === 'cookie') {
  149. if (!browserSupportsLocalStorage) {
  150. $rootScope.$broadcast('LocalStorageModule.notification.warning', 'LOCAL_STORAGE_NOT_SUPPORTED');
  151. }
  152. return getFromCookies(key);
  153. }
  154. var item = webStorage ? webStorage.getItem(deriveQualifiedKey(key)) : null;
  155. // angular.toJson will convert null to 'null', so a proper conversion is needed
  156. // FIXME not a perfect solution, since a valid 'null' string can't be stored
  157. if (!item || item === 'null') {
  158. return null;
  159. }
  160. try {
  161. return JSON.parse(item);
  162. } catch (e) {
  163. return item;
  164. }
  165. };
  166. // Remove an item from local storage
  167. // Example use: localStorageService.remove('library'); // removes the key/value pair of library='angular'
  168. var removeFromLocalStorage = function () {
  169. var i, key;
  170. for (i=0; i<arguments.length; i++) {
  171. key = arguments[i];
  172. if (!browserSupportsLocalStorage || self.storageType === 'cookie') {
  173. if (!browserSupportsLocalStorage) {
  174. $rootScope.$broadcast('LocalStorageModule.notification.warning', 'LOCAL_STORAGE_NOT_SUPPORTED');
  175. }
  176. if (notify.removeItem) {
  177. $rootScope.$broadcast('LocalStorageModule.notification.removeitem', {key: key, storageType: 'cookie'});
  178. }
  179. removeFromCookies(key);
  180. }
  181. else {
  182. try {
  183. webStorage.removeItem(deriveQualifiedKey(key));
  184. if (notify.removeItem) {
  185. $rootScope.$broadcast('LocalStorageModule.notification.removeitem', {
  186. key: key,
  187. storageType: self.storageType
  188. });
  189. }
  190. } catch (e) {
  191. $rootScope.$broadcast('LocalStorageModule.notification.error', e.message);
  192. removeFromCookies(key);
  193. }
  194. }
  195. }
  196. };
  197. // Return array of keys for local storage
  198. // Example use: var keys = localStorageService.keys()
  199. var getKeysForLocalStorage = function () {
  200. if (!browserSupportsLocalStorage) {
  201. $rootScope.$broadcast('LocalStorageModule.notification.warning', 'LOCAL_STORAGE_NOT_SUPPORTED');
  202. return [];
  203. }
  204. var prefixLength = prefix.length;
  205. var keys = [];
  206. for (var key in webStorage) {
  207. // Only return keys that are for this app
  208. if (key.substr(0, prefixLength) === prefix) {
  209. try {
  210. keys.push(key.substr(prefixLength));
  211. } catch (e) {
  212. $rootScope.$broadcast('LocalStorageModule.notification.error', e.Description);
  213. return [];
  214. }
  215. }
  216. }
  217. return keys;
  218. };
  219. // Remove all data for this app from local storage
  220. // Also optionally takes a regular expression string and removes the matching key-value pairs
  221. // Example use: localStorageService.clearAll();
  222. // Should be used mostly for development purposes
  223. var clearAllFromLocalStorage = function (regularExpression) {
  224. // Setting both regular expressions independently
  225. // Empty strings result in catchall RegExp
  226. var prefixRegex = !!prefix ? new RegExp('^' + prefix) : new RegExp();
  227. var testRegex = !!regularExpression ? new RegExp(regularExpression) : new RegExp();
  228. if (!browserSupportsLocalStorage || self.storageType === 'cookie') {
  229. if (!browserSupportsLocalStorage) {
  230. $rootScope.$broadcast('LocalStorageModule.notification.warning', 'LOCAL_STORAGE_NOT_SUPPORTED');
  231. }
  232. return clearAllFromCookies();
  233. }
  234. var prefixLength = prefix.length;
  235. for (var key in webStorage) {
  236. // Only remove items that are for this app and match the regular expression
  237. if (prefixRegex.test(key) && testRegex.test(key.substr(prefixLength))) {
  238. try {
  239. removeFromLocalStorage(key.substr(prefixLength));
  240. } catch (e) {
  241. $rootScope.$broadcast('LocalStorageModule.notification.error', e.message);
  242. return clearAllFromCookies();
  243. }
  244. }
  245. }
  246. return true;
  247. };
  248. // Checks the browser to see if cookies are supported
  249. var browserSupportsCookies = (function() {
  250. try {
  251. return $window.navigator.cookieEnabled ||
  252. ("cookie" in $document && ($document.cookie.length > 0 ||
  253. ($document.cookie = "test").indexOf.call($document.cookie, "test") > -1));
  254. } catch (e) {
  255. $rootScope.$broadcast('LocalStorageModule.notification.error', e.message);
  256. return false;
  257. }
  258. }());
  259. // Directly adds a value to cookies
  260. // Typically used as a fallback is local storage is not available in the browser
  261. // Example use: localStorageService.cookie.add('library','angular');
  262. var addToCookies = function (key, value, daysToExpiry) {
  263. if (isUndefined(value)) {
  264. return false;
  265. } else if(isArray(value) || isObject(value)) {
  266. value = toJson(value);
  267. }
  268. if (!browserSupportsCookies) {
  269. $rootScope.$broadcast('LocalStorageModule.notification.error', 'COOKIES_NOT_SUPPORTED');
  270. return false;
  271. }
  272. try {
  273. var expiry = '',
  274. expiryDate = new Date(),
  275. cookieDomain = '';
  276. if (value === null) {
  277. // Mark that the cookie has expired one day ago
  278. expiryDate.setTime(expiryDate.getTime() + (-1 * 24 * 60 * 60 * 1000));
  279. expiry = "; expires=" + expiryDate.toGMTString();
  280. value = '';
  281. } else if (isNumber(daysToExpiry) && daysToExpiry !== 0) {
  282. expiryDate.setTime(expiryDate.getTime() + (daysToExpiry * 24 * 60 * 60 * 1000));
  283. expiry = "; expires=" + expiryDate.toGMTString();
  284. } else if (cookie.expiry !== 0) {
  285. expiryDate.setTime(expiryDate.getTime() + (cookie.expiry * 24 * 60 * 60 * 1000));
  286. expiry = "; expires=" + expiryDate.toGMTString();
  287. }
  288. if (!!key) {
  289. var cookiePath = "; path=" + cookie.path;
  290. if(cookie.domain){
  291. cookieDomain = "; domain=" + cookie.domain;
  292. }
  293. $document.cookie = deriveQualifiedKey(key) + "=" + encodeURIComponent(value) + expiry + cookiePath + cookieDomain;
  294. }
  295. } catch (e) {
  296. $rootScope.$broadcast('LocalStorageModule.notification.error', e.message);
  297. return false;
  298. }
  299. return true;
  300. };
  301. // Directly get a value from a cookie
  302. // Example use: localStorageService.cookie.get('library'); // returns 'angular'
  303. var getFromCookies = function (key) {
  304. if (!browserSupportsCookies) {
  305. $rootScope.$broadcast('LocalStorageModule.notification.error', 'COOKIES_NOT_SUPPORTED');
  306. return false;
  307. }
  308. var cookies = $document.cookie && $document.cookie.split(';') || [];
  309. for(var i=0; i < cookies.length; i++) {
  310. var thisCookie = cookies[i];
  311. while (thisCookie.charAt(0) === ' ') {
  312. thisCookie = thisCookie.substring(1,thisCookie.length);
  313. }
  314. if (thisCookie.indexOf(deriveQualifiedKey(key) + '=') === 0) {
  315. var storedValues = decodeURIComponent(thisCookie.substring(prefix.length + key.length + 1, thisCookie.length));
  316. try {
  317. return JSON.parse(storedValues);
  318. } catch(e) {
  319. return storedValues;
  320. }
  321. }
  322. }
  323. return null;
  324. };
  325. var removeFromCookies = function (key) {
  326. addToCookies(key,null);
  327. };
  328. var clearAllFromCookies = function () {
  329. var thisCookie = null, thisKey = null;
  330. var prefixLength = prefix.length;
  331. var cookies = $document.cookie.split(';');
  332. for(var i = 0; i < cookies.length; i++) {
  333. thisCookie = cookies[i];
  334. while (thisCookie.charAt(0) === ' ') {
  335. thisCookie = thisCookie.substring(1, thisCookie.length);
  336. }
  337. var key = thisCookie.substring(prefixLength, thisCookie.indexOf('='));
  338. removeFromCookies(key);
  339. }
  340. };
  341. var getStorageType = function() {
  342. return storageType;
  343. };
  344. // Add a listener on scope variable to save its changes to local storage
  345. // Return a function which when called cancels binding
  346. var bindToScope = function(scope, key, def, lsKey) {
  347. lsKey = lsKey || key;
  348. var value = getFromLocalStorage(lsKey);
  349. if (value === null && isDefined(def)) {
  350. value = def;
  351. } else if (isObject(value) && isObject(def)) {
  352. value = extend(value, def);
  353. }
  354. $parse(key).assign(scope, value);
  355. return scope.$watch(key, function(newVal) {
  356. addToLocalStorage(lsKey, newVal);
  357. }, isObject(scope[key]));
  358. };
  359. // Return localStorageService.length
  360. // ignore keys that not owned
  361. var lengthOfLocalStorage = function() {
  362. var count = 0;
  363. var storage = $window[storageType];
  364. for(var i = 0; i < storage.length; i++) {
  365. if(storage.key(i).indexOf(prefix) === 0 ) {
  366. count++;
  367. }
  368. }
  369. return count;
  370. };
  371. return {
  372. isSupported: browserSupportsLocalStorage,
  373. getStorageType: getStorageType,
  374. set: addToLocalStorage,
  375. add: addToLocalStorage, //DEPRECATED
  376. get: getFromLocalStorage,
  377. keys: getKeysForLocalStorage,
  378. remove: removeFromLocalStorage,
  379. clearAll: clearAllFromLocalStorage,
  380. bind: bindToScope,
  381. deriveKey: deriveQualifiedKey,
  382. length: lengthOfLocalStorage,
  383. cookie: {
  384. isSupported: browserSupportsCookies,
  385. set: addToCookies,
  386. add: addToCookies, //DEPRECATED
  387. get: getFromCookies,
  388. remove: removeFromCookies,
  389. clearAll: clearAllFromCookies
  390. }
  391. };
  392. }];
  393. });
  394. })(window, window.angular);