angular-local-storage.js 15 KB

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