1/* jshint browser: true */ 2/* global define: false */ 3 4// AMD shim 5(function(root, factory) { 6 7 "use strict"; 8 9 if (typeof define === 'function' && define.amd) { 10 define(factory); 11 } else { 12 root.simpleStorage = factory(); 13 } 14 15}(this, function() { 16 17 "use strict"; 18 19 var 20 VERSION = "0.1.2", 21 22 /* This is the object, that holds the cached values */ 23 _storage = false, 24 25 /* How much space does the storage take */ 26 _storage_size = 0, 27 28 _storage_available = false, 29 30 _ttl_timeout = null; 31 32 // This method might throw as it touches localStorage and doing so 33 // can be prohibited in some environments 34 function _init(){ 35 36 // If localStorage does not exist, the following throws 37 // This is intentional 38 window.localStorage.setItem("__simpleStorageInitTest", "tmpval"); 39 window.localStorage.removeItem("__simpleStorageInitTest"); 40 41 // Load data from storage 42 _load_storage(); 43 44 // remove dead keys 45 _handleTTL(); 46 47 // start listening for changes 48 _setupUpdateObserver(); 49 50 // handle cached navigation 51 if("addEventListener" in window){ 52 window.addEventListener("pageshow", function(event){ 53 if(event.persisted){ 54 _reloadData(); 55 } 56 }, false); 57 } 58 59 _storage_available = true; 60 } 61 62 /** 63 * Sets up a storage change observer 64 */ 65 function _setupUpdateObserver(){ 66 if("addEventListener" in window){ 67 window.addEventListener("storage", _reloadData, false); 68 }else{ 69 document.attachEvent("onstorage", _reloadData); 70 } 71 } 72 73 /** 74 * Reload data from storage when needed 75 */ 76 function _reloadData(){ 77 try{ 78 _load_storage(); 79 }catch(E){ 80 _storage_available = false; 81 return; 82 } 83 _handleTTL(); 84 } 85 86 function _load_storage(){ 87 var source = localStorage.simpleStorage; 88 89 try{ 90 _storage = JSON.parse(source); 91 }catch(E){ 92 _storage = {}; 93 } 94 95 _storage_size = localStorage.simpleStorage ? String(localStorage.simpleStorage).length : 0; 96 } 97 98 function _save(){ 99 try{ 100 localStorage.simpleStorage = JSON.stringify(_storage); 101 _storage_size = localStorage.simpleStorage ? String(localStorage.simpleStorage).length : 0; 102 }catch(E){ 103 return E; 104 } 105 return true; 106 } 107 108 function _handleTTL(){ 109 var curtime, i, len, expire, keys, nextExpire = Infinity, expiredKeysCount = 0; 110 111 clearTimeout(_ttl_timeout); 112 113 if(!_storage || !_storage.__simpleStorage_meta || !_storage.__simpleStorage_meta.TTL){ 114 return; 115 } 116 117 curtime = +new Date(); 118 keys = _storage.__simpleStorage_meta.TTL.keys || []; 119 expire = _storage.__simpleStorage_meta.TTL.expire || {}; 120 121 for(i = 0, len = keys.length; i<len; i++){ 122 if(expire[keys[i]] <= curtime){ 123 expiredKeysCount++; 124 delete _storage[keys[i]]; 125 delete expire[keys[i]]; 126 }else{ 127 if(expire[keys[i]] < nextExpire){ 128 nextExpire = expire[keys[i]]; 129 } 130 break; 131 } 132 } 133 134 // set next check 135 if(nextExpire != Infinity){ 136 _ttl_timeout = setTimeout(_handleTTL, Math.min(nextExpire - curtime, 0x7FFFFFFF)); 137 } 138 139 // remove expired from TTL list and save changes 140 if(expiredKeysCount){ 141 keys.splice(0, expiredKeysCount); 142 143 _cleanMetaObject(); 144 _save(); 145 } 146 } 147 148 function _setTTL(key, ttl){ 149 var curtime = +new Date(), i, len, added = false; 150 151 ttl = Number(ttl) || 0; 152 153 // Set TTL value for the key 154 if(ttl !== 0){ 155 // If key exists, set TTL 156 if(_storage.hasOwnProperty(key)){ 157 158 if(!_storage.__simpleStorage_meta){ 159 _storage.__simpleStorage_meta = {}; 160 } 161 162 if(!_storage.__simpleStorage_meta.TTL){ 163 _storage.__simpleStorage_meta.TTL = {expire: {}, keys:[]}; 164 } 165 166 _storage.__simpleStorage_meta.TTL.expire[key] = curtime + ttl; 167 168 // remove from keys array 169 if(_storage.__simpleStorage_meta.TTL.expire.hasOwnProperty(key)){ 170 for(i = 0, len = _storage.__simpleStorage_meta.TTL.keys.length; i<len; i++){ 171 if(_storage.__simpleStorage_meta.TTL.keys[i] == key){ 172 _storage.__simpleStorage_meta.TTL.keys.splice(i); 173 } 174 } 175 } 176 177 // add to keys array, sorted by ttl 178 for(i = 0, len = _storage.__simpleStorage_meta.TTL.keys.length; i<len; i++){ 179 if(_storage.__simpleStorage_meta.TTL.expire[_storage.__simpleStorage_meta.TTL.keys[i]] > curtime + ttl){ 180 _storage.__simpleStorage_meta.TTL.keys.splice(i, 0, key); 181 } 182 } 183 184 if(!added){ 185 _storage.__simpleStorage_meta.TTL.keys.push(key); 186 } 187 }else{ 188 return false;
189 } 190 }else{ 191 // Remove TTL if set 192 if(_storage && _storage.__simpleStorage_meta && _storage.__simpleStorage_meta.TTL){ 193 194 if(_storage.__simpleStorage_meta.TTL.expire.hasOwnProperty(key)){ 195 delete _storage.__simpleStorage_meta.TTL.expire[key]; 196 for(i = 0, len = _storage.__simpleStorage_meta.TTL.keys.length; i<len; i++){ 197 if(_storage.__simpleStorage_meta.TTL.keys[i] == key){ 198 _storage.__simpleStorage_meta.TTL.keys.splice(i, 1); 199 break; 200 } 201 } 202 } 203 204 _cleanMetaObject(); 205 } 206 } 207 208 // schedule next TTL check 209 clearTimeout(_ttl_timeout); 210 if(_storage && _storage.__simpleStorage_meta && _storage.__simpleStorage_meta.TTL && _storage.__simpleStorage_meta.TTL.keys.length){ 211 _ttl_timeout = setTimeout(_handleTTL, Math.min(Math.max(_storage.__simpleStorage_meta.TTL.expire[_storage.__simpleStorage_meta.TTL.keys[0]] - curtime, 0), 0x7FFFFFFF)); 212 } 213 214 return true; 215 } 216 217 function _cleanMetaObject(){ 218 var updated = false, hasProperties = false, i; 219 220 if(!_storage || !_storage.__simpleStorage_meta){ 221 return updated; 222 } 223 224 // If nothing to TTL, remove the object 225 if(_storage.__simpleStorage_meta.TTL && !_storage.__simpleStorage_meta.TTL.keys.length){ 226 delete _storage.__simpleStorage_meta.TTL; 227 updated = true; 228 } 229 230 // If meta object is empty, remove it 231 for(i in _storage.__simpleStorage_meta){ 232 if(_storage.__simpleStorage_meta.hasOwnProperty(i)){ 233 hasProperties = true; 234 break; 235 } 236 } 237 238 if(!hasProperties){ 239 delete _storage.__simpleStorage_meta; 240 updated = true; 241 } 242 243 return updated; 244 } 245 246 ////////////////////////// PUBLIC INTERFACE ///////////////////////// 247 248 try{ 249 _init(); 250 }catch(E){} 251 252 return { 253 254 version: VERSION, 255 256 canUse: function(){ 257 return !!_storage_available; 258 }, 259 260 set: function(key, value, options){ 261 if(key == "__simpleStorage_meta"){ 262 return false; 263 } 264 265 if(!_storage){ 266 return false; 267 } 268 269 // undefined values are deleted automatically 270 if(typeof value == "undefined"){ 271 return this.deleteKey(key); 272 } 273 274 options = options || {}; 275 276 // Check if the value is JSON compatible (and remove reference to existing objects/arrays) 277 try{ 278 value = JSON.parse(JSON.stringify(value)); 279 }catch(E){ 280 return E; 281 } 282 283 _storage[key] = value; 284 285 _setTTL(key, options.TTL || 0); 286 287 return _save(); 288 }, 289 290 get: function(key){ 291 if(!_storage){ 292 return false; 293 } 294 295 if(_storage.hasOwnProperty(key) && key != "__simpleStorage_meta"){ 296 // TTL value for an existing key is either a positive number or an Infinity 297 if(this.getTTL(key)){ 298 return _storage[key]; 299 } 300 } 301 }, 302 303 deleteKey: function(key){ 304 305 if(!_storage){ 306 return false; 307 } 308 309 if(key in _storage){ 310 delete _storage[key]; 311 312 _setTTL(key, 0); 313 314 return _save(); 315 } 316 317 return false; 318 }, 319 320 setTTL: function(key, ttl){ 321 if(!_storage){ 322 return false; 323 } 324 325 _setTTL(key, ttl); 326 327 return _save(); 328 }, 329 330 getTTL: function(key){ 331 var ttl; 332 333 if(!_storage){ 334 return false; 335 } 336 337 if(_storage.hasOwnProperty(key)){ 338 if(_storage.__simpleStorage_meta && 339 _storage.__simpleStorage_meta.TTL && 340 _storage.__simpleStorage_meta.TTL.expire && 341 _storage.__simpleStorage_meta.TTL.expire.hasOwnProperty(key)){ 342 343 ttl = Math.max(_storage.__simpleStorage_meta.TTL.expire[key] - (+new Date()) || 0, 0); 344 345 return ttl || false; 346 }else{ 347 return Infinity; 348 } 349 } 350 351 return false;
352 }, 353 354 flush: function(){ 355 if(!_storage){ 356 return false; 357 } 358 359 _storage = {}; 360 try{ 361 localStorage.removeItem("simpleStorage"); 362 return true; 363 }catch(E){ 364 return E; 365 } 366 }, 367 368 index: function(){ 369 if(!_storage){ 370 return false; 371 } 372 373 var index = [], i; 374 for(i in _storage){ 375 if(_storage.hasOwnProperty(i) && i != "__simpleStorage_meta"){ 376 index.push(i); 377 } 378 } 379 return index; 380 }, 381 382 storageSize: function(){ 383 return _storage_size; 384 } 385 }; 386 387}));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.