/static/scripts/jstorage.js

https://bitbucket.org/cistrome/cistrome-harvard/ · JavaScript · 420 lines · 195 code · 41 blank · 184 comment · 44 complexity · 1ed3525ccbd6baad44ed74b13096a156 MD5 · raw file

  1. /*
  2. * ----------------------------- JSTORAGE -------------------------------------
  3. * Simple local storage wrapper to save data on the browser side, supporting
  4. * all major browsers - IE6+, Firefox2+, Safari4+, Chrome4+ and Opera 10.5+
  5. *
  6. * Copyright (c) 2010 Andris Reinman, andris.reinman@gmail.com
  7. * Project homepage: www.jstorage.info
  8. *
  9. * Licensed under MIT-style license:
  10. *
  11. * Permission is hereby granted, free of charge, to any person obtaining a copy
  12. * of this software and associated documentation files (the "Software"), to deal
  13. * in the Software without restriction, including without limitation the rights
  14. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  15. * copies of the Software, and to permit persons to whom the Software is
  16. * furnished to do so, subject to the following conditions:
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  24. * SOFTWARE.
  25. */
  26. /**
  27. * $.jStorage
  28. *
  29. * USAGE:
  30. *
  31. * jStorage requires Prototype, MooTools or jQuery! If jQuery is used, then
  32. * jQuery-JSON (http://code.google.com/p/jquery-json/) is also needed.
  33. * (jQuery-JSON needs to be loaded BEFORE jStorage!)
  34. *
  35. * Methods:
  36. *
  37. * -set(key, value)
  38. * $.jStorage.set(key, value) -> saves a value
  39. *
  40. * -get(key[, default])
  41. * value = $.jStorage.get(key [, default]) ->
  42. * retrieves value if key exists, or default if it doesn't
  43. *
  44. * -deleteKey(key)
  45. * $.jStorage.deleteKey(key) -> removes a key from the storage
  46. *
  47. * -flush()
  48. * $.jStorage.flush() -> clears the cache
  49. *
  50. * -storageObj()
  51. * $.jStorage.storageObj() -> returns a read-ony copy of the actual storage
  52. *
  53. * -storageSize()
  54. * $.jStorage.storageSize() -> returns the size of the storage in bytes
  55. *
  56. * -index()
  57. * $.jStorage.index() -> returns the used keys as an array
  58. *
  59. * -storageAvailable()
  60. * $.jStorage.storageAvailable() -> returns true if storage is available
  61. *
  62. * -reInit()
  63. * $.jStorage.reInit() -> reloads the data from browser storage
  64. *
  65. * <value> can be any JSON-able value, including objects and arrays.
  66. *
  67. **/
  68. (function($){
  69. if(!$ || !($.toJSON || Object.toJSON || window.JSON)){
  70. throw new Error("jQuery, MooTools or Prototype needs to be loaded before jStorage!");
  71. }
  72. var
  73. /* This is the object, that holds the cached values */
  74. _storage = {},
  75. /* Actual browser storage (localStorage or globalStorage['domain']) */
  76. _storage_service = {jStorage:"{}"},
  77. /* DOM element for older IE versions, holds userData behavior */
  78. _storage_elm = null,
  79. /* How much space does the storage take */
  80. _storage_size = 0,
  81. /* function to encode objects to JSON strings */
  82. json_encode = $.toJSON || Object.toJSON || (window.JSON && (JSON.encode || JSON.stringify)),
  83. /* function to decode objects from JSON strings */
  84. json_decode = $.evalJSON || (window.JSON && (JSON.decode || JSON.parse)) || function(str){
  85. return String(str).evalJSON();
  86. },
  87. /* which backend is currently used */
  88. _backend = false;
  89. /**
  90. * XML encoding and decoding as XML nodes can't be JSON'ized
  91. * XML nodes are encoded and decoded if the node is the value to be saved
  92. * but not if it's as a property of another object
  93. * Eg. -
  94. * $.jStorage.set("key", xmlNode); // IS OK
  95. * $.jStorage.set("key", {xml: xmlNode}); // NOT OK
  96. */
  97. _XMLService = {
  98. /**
  99. * Validates a XML node to be XML
  100. * based on jQuery.isXML function
  101. */
  102. isXML: function(elm){
  103. var documentElement = (elm ? elm.ownerDocument || elm : 0).documentElement;
  104. return documentElement ? documentElement.nodeName !== "HTML" : false;
  105. },
  106. /**
  107. * Encodes a XML node to string
  108. * based on http://www.mercurytide.co.uk/news/article/issues-when-working-ajax/
  109. */
  110. encode: function(xmlNode) {
  111. if(!this.isXML(xmlNode)){
  112. return false;
  113. }
  114. try{ // Mozilla, Webkit, Opera
  115. return new XMLSerializer().serializeToString(xmlNode);
  116. }catch(E1) {
  117. try { // IE
  118. return xmlNode.xml;
  119. }catch(E2){}
  120. }
  121. return false;
  122. },
  123. /**
  124. * Decodes a XML node from string
  125. * loosely based on http://outwestmedia.com/jquery-plugins/xmldom/
  126. */
  127. decode: function(xmlString){
  128. var dom_parser = ("DOMParser" in window && (new DOMParser()).parseFromString) ||
  129. (window.ActiveXObject && function(_xmlString) {
  130. var xml_doc = new ActiveXObject('Microsoft.XMLDOM');
  131. xml_doc.async = 'false';
  132. xml_doc.loadXML(_xmlString);
  133. return xml_doc;
  134. }),
  135. resultXML;
  136. if(!dom_parser){
  137. return false;
  138. }
  139. resultXML = dom_parser.call("DOMParser" in window && (new DOMParser()) || window, xmlString, 'text/xml');
  140. return this.isXML(resultXML)?resultXML:false;
  141. }
  142. };
  143. ////////////////////////// PRIVATE METHODS ////////////////////////
  144. /**
  145. * Initialization function. Detects if the browser supports DOM Storage
  146. * or userData behavior and behaves accordingly.
  147. * @returns undefined
  148. */
  149. function _init(){
  150. /* Check if browser supports localStorage */
  151. if("localStorage" in window){
  152. try {
  153. if(window.localStorage) {
  154. _storage_service = window.localStorage;
  155. _backend = "localStorage";
  156. }
  157. } catch(E3) {/* Firefox fails when touching localStorage and cookies are disabled */}
  158. }
  159. /* Check if browser supports globalStorage */
  160. else if("globalStorage" in window){
  161. try {
  162. if(window.globalStorage) {
  163. _storage_service = window.globalStorage[window.location.hostname];
  164. _backend = "globalStorage";
  165. }
  166. } catch(E4) {/* Firefox fails when touching localStorage and cookies are disabled */}
  167. }
  168. /* Check if browser supports userData behavior */
  169. else {
  170. _storage_elm = document.createElement('link');
  171. if(_storage_elm.addBehavior){
  172. /* Use a DOM element to act as userData storage */
  173. _storage_elm.style.behavior = 'url(#default#userData)';
  174. /* userData element needs to be inserted into the DOM! */
  175. document.getElementsByTagName('head')[0].appendChild(_storage_elm);
  176. _storage_elm.load("jStorage");
  177. var data = "{}";
  178. try{
  179. data = _storage_elm.getAttribute("jStorage");
  180. }catch(E5){}
  181. _storage_service.jStorage = data;
  182. _backend = "userDataBehavior";
  183. }else{
  184. _storage_elm = null;
  185. return;
  186. }
  187. }
  188. _load_storage();
  189. }
  190. /**
  191. * Loads the data from the storage based on the supported mechanism
  192. * @returns undefined
  193. */
  194. function _load_storage(){
  195. /* if jStorage string is retrieved, then decode it */
  196. if(_storage_service.jStorage){
  197. try{
  198. _storage = json_decode(String(_storage_service.jStorage));
  199. }catch(E6){_storage_service.jStorage = "{}";}
  200. }else{
  201. _storage_service.jStorage = "{}";
  202. }
  203. _storage_size = _storage_service.jStorage?String(_storage_service.jStorage).length:0;
  204. }
  205. /**
  206. * This functions provides the "save" mechanism to store the jStorage object
  207. * @returns undefined
  208. */
  209. function _save(){
  210. try{
  211. _storage_service.jStorage = json_encode(_storage);
  212. // If userData is used as the storage engine, additional
  213. if(_storage_elm) {
  214. _storage_elm.setAttribute("jStorage",_storage_service.jStorage);
  215. _storage_elm.save("jStorage");
  216. }
  217. _storage_size = _storage_service.jStorage?String(_storage_service.jStorage).length:0;
  218. }catch(E7){/* probably cache is full, nothing is saved this way*/}
  219. }
  220. /**
  221. * Function checks if a key is set and is string or numberic
  222. */
  223. function _checkKey(key){
  224. if(!key || (typeof key != "string" && typeof key != "number")){
  225. throw new TypeError('Key name must be string or numeric');
  226. }
  227. return true;
  228. }
  229. ////////////////////////// PUBLIC INTERFACE /////////////////////////
  230. $.jStorage = {
  231. /* Version number */
  232. version: "0.1.5.0",
  233. /**
  234. * Sets a key's value.
  235. *
  236. * @param {String} key - Key to set. If this value is not set or not
  237. * a string an exception is raised.
  238. * @param value - Value to set. This can be any value that is JSON
  239. * compatible (Numbers, Strings, Objects etc.).
  240. * @returns the used value
  241. */
  242. set: function(key, value){
  243. _checkKey(key);
  244. if(_XMLService.isXML(value)){
  245. value = {_is_xml:true,xml:_XMLService.encode(value)};
  246. }
  247. _storage[key] = value;
  248. _save();
  249. return value;
  250. },
  251. /**
  252. * Looks up a key in cache
  253. *
  254. * @param {String} key - Key to look up.
  255. * @param {mixed} def - Default value to return, if key didn't exist.
  256. * @returns the key value, default value or <null>
  257. */
  258. get: function(key, def){
  259. _checkKey(key);
  260. if(key in _storage){
  261. if(typeof _storage[key] == "object" &&
  262. _storage[key]._is_xml &&
  263. _storage[key]._is_xml){
  264. return _XMLService.decode(_storage[key].xml);
  265. }else{
  266. return _storage[key];
  267. }
  268. }
  269. return typeof(def) == 'undefined' ? null : def;
  270. },
  271. /**
  272. * Deletes a key from cache.
  273. *
  274. * @param {String} key - Key to delete.
  275. * @returns true if key existed or false if it didn't
  276. */
  277. deleteKey: function(key){
  278. _checkKey(key);
  279. if(key in _storage){
  280. delete _storage[key];
  281. _save();
  282. return true;
  283. }
  284. return false;
  285. },
  286. /**
  287. * Deletes everything in cache.
  288. *
  289. * @returns true
  290. */
  291. flush: function(){
  292. _storage = {};
  293. _save();
  294. /*
  295. * Just to be sure - andris9/jStorage#3
  296. */
  297. try{
  298. window.localStorage.clear();
  299. }catch(E8){}
  300. return true;
  301. },
  302. /**
  303. * Returns a read-only copy of _storage
  304. *
  305. * @returns Object
  306. */
  307. storageObj: function(){
  308. function F() {}
  309. F.prototype = _storage;
  310. return new F();
  311. },
  312. /**
  313. * Returns an index of all used keys as an array
  314. * ['key1', 'key2',..'keyN']
  315. *
  316. * @returns Array
  317. */
  318. index: function(){
  319. var index = [], i;
  320. for(i in _storage){
  321. if(_storage.hasOwnProperty(i)){
  322. index.push(i);
  323. }
  324. }
  325. return index;
  326. },
  327. /**
  328. * How much space in bytes does the storage take?
  329. *
  330. * @returns Number
  331. */
  332. storageSize: function(){
  333. return _storage_size;
  334. },
  335. /**
  336. * Which backend is currently in use?
  337. *
  338. * @returns String
  339. */
  340. currentBackend: function(){
  341. return _backend;
  342. },
  343. /**
  344. * Test if storage is available
  345. *
  346. * @returns Boolean
  347. */
  348. storageAvailable: function(){
  349. return !!_backend;
  350. },
  351. /**
  352. * Reloads the data from browser storage
  353. *
  354. * @returns undefined
  355. */
  356. reInit: function(){
  357. var new_storage_elm, data;
  358. if(_storage_elm && _storage_elm.addBehavior){
  359. new_storage_elm = document.createElement('link');
  360. _storage_elm.parentNode.replaceChild(new_storage_elm, _storage_elm);
  361. _storage_elm = new_storage_elm;
  362. /* Use a DOM element to act as userData storage */
  363. _storage_elm.style.behavior = 'url(#default#userData)';
  364. /* userData element needs to be inserted into the DOM! */
  365. document.getElementsByTagName('head')[0].appendChild(_storage_elm);
  366. _storage_elm.load("jStorage");
  367. data = "{}";
  368. try{
  369. data = _storage_elm.getAttribute("jStorage");
  370. }catch(E5){}
  371. _storage_service.jStorage = data;
  372. _backend = "userDataBehavior";
  373. }
  374. _load_storage();
  375. }
  376. };
  377. // Initialize jStorage
  378. _init();
  379. })(window.jQuery || window.$);