Files
server/usr/share/psa-horde/js/jstorage.js
2026-01-07 20:52:11 +01:00

426 lines
14 KiB
JavaScript

/*
* ----------------------------- JSTORAGE -------------------------------------
* Simple local storage wrapper to save data on the browser side, supporting
* all major browsers - IE6+, Firefox2+, Safari4+, Chrome4+ and Opera 10.5+
*
* Copyright (c) 2010 Andris Reinman, andris.reinman@gmail.com
* Project homepage: www.jstorage.info
*
* Licensed under MIT-style license:
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* $.jStorage
*
* USAGE:
*
* jStorage requires Prototype, MooTools or jQuery! If jQuery is used, then
* jQuery-JSON (http://code.google.com/p/jquery-json/) is also needed.
* (jQuery-JSON needs to be loaded BEFORE jStorage!)
*
* Methods:
*
* -set(key, value)
* $.jStorage.set(key, value) -> saves a value
*
* -get(key[, default])
* value = $.jStorage.get(key [, default]) ->
* retrieves value if key exists, or default if it doesn't
*
* -deleteKey(key)
* $.jStorage.deleteKey(key) -> removes a key from the storage
*
* -flush()
* $.jStorage.flush() -> clears the cache
*
* -storageObj()
* $.jStorage.storageObj() -> returns a read-ony copy of the actual storage
*
* -storageSize()
* $.jStorage.storageSize() -> returns the size of the storage in bytes
*
* -index()
* $.jStorage.index() -> returns the used keys as an array
*
* -storageAvailable()
* $.jStorage.storageAvailable() -> returns true if storage is available
*
* -reInit()
* $.jStorage.reInit() -> reloads the data from browser storage
*
* <value> can be any JSON-able value, including objects and arrays.
*
**/
(function($){
if(!$ || !($.toJSON || Object.toJSON || window.JSON)){
throw new Error("jQuery, MooTools or Prototype needs to be loaded before jStorage!");
}
var
/* This is the object, that holds the cached values */
_storage = {},
/* Actual browser storage (localStorage or globalStorage['domain']) */
_storage_service = {jStorage:"{}"},
/* DOM element for older IE versions, holds userData behavior */
_storage_elm = null,
/* How much space does the storage take */
_storage_size = 0,
/* function to encode objects to JSON strings */
json_encode = $.toJSON || Object.toJSON || (window.JSON && (JSON.encode || JSON.stringify)),
/* function to decode objects from JSON strings */
json_decode = $.evalJSON || (window.JSON && (JSON.decode || JSON.parse)) || function(str){
return String(str).evalJSON();
},
/* which backend is currently used */
_backend = false,
/**
* XML encoding and decoding as XML nodes can't be JSON'ized
* XML nodes are encoded and decoded if the node is the value to be saved
* but not if it's as a property of another object
* Eg. -
* $.jStorage.set("key", xmlNode); // IS OK
* $.jStorage.set("key", {xml: xmlNode}); // NOT OK
*/
_XMLService = {
/**
* Validates a XML node to be XML
* based on jQuery.isXML function
*/
isXML: function(elm){
var documentElement = (elm ? elm.ownerDocument || elm : 0).documentElement;
return documentElement ? documentElement.nodeName !== "HTML" : false;
},
/**
* Encodes a XML node to string
* based on http://www.mercurytide.co.uk/news/article/issues-when-working-ajax/
*/
encode: function(xmlNode) {
if(!this.isXML(xmlNode)){
return false;
}
try{ // Mozilla, Webkit, Opera
return new XMLSerializer().serializeToString(xmlNode);
}catch(E1) {
try { // IE
return xmlNode.xml;
}catch(E2){}
}
return false;
},
/**
* Decodes a XML node from string
* loosely based on http://outwestmedia.com/jquery-plugins/xmldom/
*/
decode: function(xmlString){
var dom_parser = ("DOMParser" in window && (new DOMParser()).parseFromString) ||
(window.ActiveXObject && function(_xmlString) {
var xml_doc = new ActiveXObject('Microsoft.XMLDOM');
xml_doc.async = 'false';
xml_doc.loadXML(_xmlString);
return xml_doc;
}),
resultXML;
if(!dom_parser){
return false;
}
resultXML = dom_parser.call("DOMParser" in window && (new DOMParser()) || window, xmlString, 'text/xml');
return this.isXML(resultXML)?resultXML:false;
}
};
////////////////////////// PRIVATE METHODS ////////////////////////
/**
* Initialization function. Detects if the browser supports DOM Storage
* or userData behavior and behaves accordingly.
* @returns undefined
*/
function _init(){
/* Check if browser supports localStorage */
var localStorageReallyWorks = false;
if("localStorage" in window){
try {
window.localStorage.setItem('_tmptest', 'tmpval');
localStorageReallyWorks = true;
window.localStorage.removeItem('_tmptest');
} catch(BogusQuotaExceededErrorOnIos5) {
// Thanks be to iOS5 Private Browsing mode which throws
// QUOTA_EXCEEDED_ERRROR DOM Exception 22.
}
}
if(localStorageReallyWorks){
try {
if(window.localStorage) {
_storage_service = window.localStorage;
_backend = "localStorage";
}
} catch(E3) {/* Firefox fails when touching localStorage and cookies are disabled */}
}
/* Check if browser supports globalStorage */
else if("globalStorage" in window){
try {
if(window.globalStorage) {
_storage_service = window.globalStorage[window.location.hostname];
_backend = "globalStorage";
}
} catch(E4) {/* Firefox fails when touching localStorage and cookies are disabled */}
}
/* Check if browser supports userData behavior */
else {
_storage_elm = document.createElement('link');
if(_storage_elm.addBehavior){
/* Use a DOM element to act as userData storage */
_storage_elm.style.behavior = 'url(#default#userData)';
/* userData element needs to be inserted into the DOM! */
document.getElementsByTagName('head')[0].appendChild(_storage_elm);
_storage_elm.load("jStorage");
var data = "{}";
try{
data = _storage_elm.getAttribute("jStorage");
}catch(E5){}
_storage_service.jStorage = data;
_backend = "userDataBehavior";
}else{
_storage_elm = null;
return;
}
}
_load_storage();
}
/**
* Loads the data from the storage based on the supported mechanism
* @returns undefined
*/
function _load_storage(){
/* if jStorage string is retrieved, then decode it */
if(_storage_service.jStorage){
try{
_storage = json_decode(String(_storage_service.jStorage));
}catch(E6){_storage_service.jStorage = "{}";}
}else{
_storage_service.jStorage = "{}";
}
_storage_size = _storage_service.jStorage?String(_storage_service.jStorage).length:0;
}
/**
* This functions provides the "save" mechanism to store the jStorage object
* @returns undefined
*/
function _save(){
try{
_storage_service.jStorage = json_encode(_storage);
// If userData is used as the storage engine, additional
if(_storage_elm) {
_storage_elm.setAttribute("jStorage",_storage_service.jStorage);
_storage_elm.save("jStorage");
}
_storage_size = _storage_service.jStorage?String(_storage_service.jStorage).length:0;
}catch(E7){/* probably cache is full, nothing is saved this way*/}
}
/**
* Function checks if a key is set and is string or numberic
*/
function _checkKey(key){
if(!key || (typeof key != "string" && typeof key != "number")){
throw new TypeError('Key name must be string or numeric');
}
return true;
}
////////////////////////// PUBLIC INTERFACE /////////////////////////
$.jStorage = {
/* Version number */
version: "0.1.5.4",
/**
* Sets a key's value.
*
* @param {String} key - Key to set. If this value is not set or not
* a string an exception is raised.
* @param value - Value to set. This can be any value that is JSON
* compatible (Numbers, Strings, Objects etc.).
* @returns the used value
*/
set: function(key, value){
_checkKey(key);
if(_XMLService.isXML(value)){
value = {_is_xml:true,xml:_XMLService.encode(value)};
}
_storage[key] = value;
_save();
return value;
},
/**
* Looks up a key in cache
*
* @param {String} key - Key to look up.
* @param {mixed} def - Default value to return, if key didn't exist.
* @returns the key value, default value or <null>
*/
get: function(key, def){
_checkKey(key);
if(key in _storage){
if(_storage[key] && typeof _storage[key] == "object" &&
_storage[key]._is_xml &&
_storage[key]._is_xml){
return _XMLService.decode(_storage[key].xml);
}else{
return _storage[key];
}
}
return typeof(def) == 'undefined' ? null : def;
},
/**
* Deletes a key from cache.
*
* @param {String} key - Key to delete.
* @returns true if key existed or false if it didn't
*/
deleteKey: function(key){
_checkKey(key);
if(key in _storage){
delete _storage[key];
_save();
return true;
}
return false;
},
/**
* Deletes everything in cache.
*
* @returns true
*/
flush: function(){
_storage = {};
_save();
return true;
},
/**
* Returns a read-only copy of _storage
*
* @returns Object
*/
storageObj: function(){
function F() {}
F.prototype = _storage;
return new F();
},
/**
* Returns an index of all used keys as an array
* ['key1', 'key2',..'keyN']
*
* @returns Array
*/
index: function(){
var index = [], i;
for(i in _storage){
if(_storage.hasOwnProperty(i)){
index.push(i);
}
}
return index;
},
/**
* How much space in bytes does the storage take?
*
* @returns Number
*/
storageSize: function(){
return _storage_size;
},
/**
* Which backend is currently in use?
*
* @returns String
*/
currentBackend: function(){
return _backend;
},
/**
* Test if storage is available
*
* @returns Boolean
*/
storageAvailable: function(){
return !!_backend;
},
/**
* Reloads the data from browser storage
*
* @returns undefined
*/
reInit: function(){
var new_storage_elm, data;
if(_storage_elm && _storage_elm.addBehavior){
new_storage_elm = document.createElement('link');
_storage_elm.parentNode.replaceChild(new_storage_elm, _storage_elm);
_storage_elm = new_storage_elm;
/* Use a DOM element to act as userData storage */
_storage_elm.style.behavior = 'url(#default#userData)';
/* userData element needs to be inserted into the DOM! */
document.getElementsByTagName('head')[0].appendChild(_storage_elm);
_storage_elm.load("jStorage");
data = "{}";
try{
data = _storage_elm.getAttribute("jStorage");
}catch(E5){}
_storage_service.jStorage = data;
_backend = "userDataBehavior";
}
_load_storage();
}
};
// Initialize jStorage
_init();
})(window.jQuery || window.$);