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.getItem("simpleStorage"); 88 89 try{ 90 _storage = JSON.parse(source) || {}; 91 }catch(E){ 92 _storage = {}; 93 } 94 95 _storage_size = _get_storage_size(); 96 } 97 98 function _save(){ 99 try{ 100 localStorage.setItem("simpleStorage", JSON.stringify(_storage)); 101 _storage_size = _get_storage_size(); 102 }catch(E){ 103 return E; 104 } 105 return true; 106 } 107 108 function _get_storage_size() { 109 var source = localStorage.getItem("simpleStorage"); 110 return source ? String(source).length : 0; 111 } 112 113 function _handleTTL(){ 114 var curtime, i, len, expire, keys, nextExpire = Infinity, expiredKeysCount = 0; 115 116 clearTimeout(_ttl_timeout); 117 118 if(!_storage || !_storage.__simpleStorage_meta || !_storage.__simpleStorage_meta.TTL){ 119 return; 120 } 121 122 curtime = +new Date(); 123 keys = _storage.__simpleStorage_meta.TTL.keys || []; 124 expire = _storage.__simpleStorage_meta.TTL.expire || {}; 125 126 for(i = 0, len = keys.length; i<len; i++){ 127 if(expire[keys[i]] <= curtime){ 128 expiredKeysCount++; 129 delete _storage[keys[i]]; 130 delete expire[keys[i]]; 131 }else{ 132 if(expire[keys[i]] < nextExpire){ 133 nextExpire = expire[keys[i]]; 134 } 135 break; 136 } 137 } 138 139 // set next check 140 if(nextExpire != Infinity){ 141 _ttl_timeout = setTimeout(_handleTTL, Math.min(nextExpire - curtime, 0x7FFFFFFF)); 142 } 143 144 // remove expired from TTL list and save changes 145 if(expiredKeysCount){ 146 keys.splice(0, expiredKeysCount); 147 148 _cleanMetaObject(); 149 _save(); 150 } 151 } 152 153 function _setTTL(key, ttl){ 154 var curtime = +new Date(), i, len, added = false; 155 156 ttl = Number(ttl) || 0; 157 158 // Set TTL value for the key 159 if(ttl !== 0){ 160 // If key exists, set TTL 161 if(_storage.hasOwnProperty(key)){ 162 163 if(!_storage.__simpleStorage_meta){ 164 _storage.__simpleStorage_meta = {}; 165 } 166 167 if(!_storage.__simpleStorage_meta.TTL){ 168 _storage.__simpleStorage_meta.TTL = {expire: {}, keys:[]}; 169 } 170 171 _storage.__simpleStorage_meta.TTL.expire[key] = curtime + ttl; 172 173 // remove from keys array 174 if(_storage.__simpleStorage_meta.TTL.expire.hasOwnProperty(key)){ 175 for(i = 0, len = _storage.__simpleStorage_meta.TTL.keys.length; i<len; i++){ 176 if(_storage.__simpleStorage_meta.TTL.keys[i] == key){ 177 _storage.__simpleStorage_meta.TTL.keys.splice(i); 178 } 179 } 180 } 181 182 // add to keys array, sorted by ttl 183 for(i = 0, len = _storage.__simpleStorage_meta.TTL.keys.length; i<len; i++){ 184 if(_storage.__simpleStorage_meta.TTL.expire[_storage.__simpleStorage_meta.TTL.keys[i]] > curtime + ttl){ 185 _storage.__simpleStorage_meta.TTL.keys.splice(i, 0, key); 186 } 187 } 188 189 if(!added){ 190 _storage.__simpleStorage_meta.TTL.keys.push(key); 191 } 192 }else{ 193 return false;
194 } 195 }else{ 196 // Remove TTL if set 197 if(_storage && _storage.__simpleStorage_meta && _storage.__simpleStorage_meta.TTL){ 198 199 if(_storage.__simpleStorage_meta.TTL.expire.hasOwnProperty(key)){ 200 delete _storage.__simpleStorage_meta.TTL.expire[key]; 201 for(i = 0, len = _storage.__simpleStorage_meta.TTL.keys.length; i<len; i++){ 202 if(_storage.__simpleStorage_meta.TTL.keys[i] == key){ 203 _storage.__simpleStorage_meta.TTL.keys.splice(i, 1); 204 break; 205 } 206 } 207 } 208 209 _cleanMetaObject(); 210 } 211 } 212 213 // schedule next TTL check 214 clearTimeout(_ttl_timeout); 215 if(_storage && _storage.__simpleStorage_meta && _storage.__simpleStorage_meta.TTL && _storage.__simpleStorage_meta.TTL.keys.length){ 216 _ttl_timeout = setTimeout(_handleTTL, Math.min(Math.max(_storage.__simpleStorage_meta.TTL.expire[_storage.__simpleStorage_meta.TTL.keys[0]] - curtime, 0), 0x7FFFFFFF)); 217 } 218 219 return true; 220 } 221 222 function _cleanMetaObject(){ 223 var updated = false, hasProperties = false, i; 224 225 if(!_storage || !_storage.__simpleStorage_meta){ 226 return updated; 227 } 228 229 // If nothing to TTL, remove the object 230 if(_storage.__simpleStorage_meta.TTL && !_storage.__simpleStorage_meta.TTL.keys.length){ 231 delete _storage.__simpleStorage_meta.TTL; 232 updated = true; 233 } 234 235 // If meta object is empty, remove it 236 for(i in _storage.__simpleStorage_meta){ 237 if(_storage.__simpleStorage_meta.hasOwnProperty(i)){ 238 hasProperties = true; 239 break; 240 } 241 } 242 243 if(!hasProperties){ 244 delete _storage.__simpleStorage_meta; 245 updated = true; 246 } 247 248 return updated; 249 } 250 251 ////////////////////////// PUBLIC INTERFACE ///////////////////////// 252 253 try{ 254 _init(); 255 }catch(E){} 256 257 return { 258 259 version: VERSION, 260 261 canUse: function(){ 262 return !!_storage_available; 263 }, 264 265 set: function(key, value, options){ 266 if(key == "__simpleStorage_meta"){ 267 return false; 268 } 269 270 if(!_storage){ 271 return false; 272 } 273 274 // undefined values are deleted automatically 275 if(typeof value == "undefined"){ 276 return this.deleteKey(key); 277 } 278 279 options = options || {}; 280 281 // Check if the value is JSON compatible (and remove reference to existing objects/arrays) 282 try{ 283 value = JSON.parse(JSON.stringify(value)); 284 }catch(E){ 285 return E; 286 } 287 288 _storage[key] = value; 289 290 _setTTL(key, options.TTL || 0); 291 292 return _save(); 293 }, 294 295 get: function(key){ 296 if(!_storage){ 297 return false; 298 } 299 300 if(_storage.hasOwnProperty(key) && key != "__simpleStorage_meta"){ 301 // TTL value for an existing key is either a positive number or an Infinity 302 if(this.getTTL(key)){ 303 return _storage[key]; 304 } 305 } 306 }, 307 308 deleteKey: function(key){ 309 310 if(!_storage){ 311 return false; 312 } 313 314 if(key in _storage){ 315 delete _storage[key]; 316 317 _setTTL(key, 0); 318 319 return _save(); 320 } 321 322 return false; 323 }, 324 325 setTTL: function(key, ttl){ 326 if(!_storage){ 327 return false; 328 } 329 330 _setTTL(key, ttl); 331 332 return _save(); 333 }, 334 335 getTTL: function(key){ 336 var ttl; 337 338 if(!_storage){ 339 return false; 340 } 341 342 if(_storage.hasOwnProperty(key)){ 343 if(_storage.__simpleStorage_meta && 344 _storage.__simpleStorage_meta.TTL && 345 _storage.__simpleStorage_meta.TTL.expire && 346 _storage.__simpleStorage_meta.TTL.expire.hasOwnProperty(key)){ 347 348 ttl = Math.max(_storage.__simpleStorage_meta.TTL.expire[key] - (+new Date()) || 0, 0); 349 350 return ttl || false; 351 }else{ 352 return Infinity; 353 } 354 } 355 356 return false;
357 }, 358 359 flush: function(){ 360 if(!_storage){ 361 return false; 362 } 363 364 _storage = {}; 365 try{ 366 localStorage.removeItem("simpleStorage"); 367 return true; 368 }catch(E){ 369 return E; 370 } 371 }, 372 373 index: function(){ 374 if(!_storage){ 375 return false; 376 } 377 378 var index = [], i; 379 for(i in _storage){ 380 if(_storage.hasOwnProperty(i) && i != "__simpleStorage_meta"){ 381 index.push(i); 382 } 383 } 384 return index; 385 }, 386 387 storageSize: function(){ 388 return _storage_size; 389 } 390 }; 391 392}));
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.