diff options
Diffstat (limited to 'js/dojo/dojox/storage')
19 files changed, 3616 insertions, 0 deletions
diff --git a/js/dojo/dojox/storage/AirDBStorageProvider.js b/js/dojo/dojox/storage/AirDBStorageProvider.js new file mode 100644 index 0000000..71b6cbe --- /dev/null +++ b/js/dojo/dojox/storage/AirDBStorageProvider.js @@ -0,0 +1,252 @@ +//>>built +// wrapped by build app +define("dojox/storage/AirDBStorageProvider", ["dijit","dojo","dojox","dojo/require!dojox/storage/manager,dojox/storage/Provider"], function(dijit,dojo,dojox){ +dojo.provide("dojox.storage.AirDBStorageProvider"); +dojo.require("dojox.storage.manager"); +dojo.require("dojox.storage.Provider"); + +if (dojo.isAIR) { + (function(){ + + if (!air) { + var air = {}; + } + air.File = window.runtime.flash.filesystem.File; + air.SQLConnection = window.runtime.flash.data.SQLConnection; + air.SQLStatement = window.runtime.flash.data.SQLStatement; + + // summary: + // Storage provider that uses features in the Adobe AIR runtime to achieve + // permanent storage + dojo.declare("dojox.storage.AirDBStorageProvider", [ dojox.storage.Provider ], { + DATABASE_FILE: "dojo.db", + TABLE_NAME: "__DOJO_STORAGE", + initialized: false, + + _db: null, + + initialize: function(){ + this.initialized = false; + + // need to initialize our storage database + try{ + this._db = new air.SQLConnection(); + this._db.open(air.File.applicationStorageDirectory.resolvePath(this.DATABASE_FILE)); + + this._sql("CREATE TABLE IF NOT EXISTS " + this.TABLE_NAME + "(namespace TEXT, key TEXT, value TEXT)"); + this._sql("CREATE UNIQUE INDEX IF NOT EXISTS namespace_key_index ON " + this.TABLE_NAME + " (namespace, key)"); + + this.initialized = true; + }catch(e){ + console.debug("dojox.storage.AirDBStorageProvider.initialize:", e); + } + + // indicate that this storage provider is now loaded + dojox.storage.manager.loaded(); + }, + + _sql: function(query, params){ + var stmt = new air.SQLStatement(); + stmt.sqlConnection = this._db; + stmt.text = query; + if (params){ + for (var param in params){ + stmt.parameters[param] = params[param]; + } + } + stmt.execute(); + return stmt.getResult(); + }, + + _beginTransaction: function(){ + this._db.begin(); + }, + + _commitTransaction: function(){ + this._db.commit(); + }, + + isAvailable: function(){ + return true; + }, + + put: function(key, value, resultsHandler, namespace){ + if(this.isValidKey(key) == false){ + throw new Error("Invalid key given: " + key); + } + namespace = namespace||this.DEFAULT_NAMESPACE; + if(this.isValidKey(namespace) == false){ + throw new Error("Invalid namespace given: " + namespace); + } + + // try to store the value + try{ + this._sql("DELETE FROM " + this.TABLE_NAME + " WHERE namespace = :namespace AND key = :key", + { ":namespace":namespace, ":key":key }); + this._sql("INSERT INTO " + this.TABLE_NAME + " VALUES (:namespace, :key, :value)", + { ":namespace":namespace, ":key":key, ":value":value }); + }catch(e){ + // indicate we failed + console.debug("dojox.storage.AirDBStorageProvider.put:", e); + resultsHandler(this.FAILED, key, e.toString()); + return; + } + + if(resultsHandler){ + resultsHandler(this.SUCCESS, key, null, namespace); + } + }, + + get: function(key, namespace){ + if(this.isValidKey(key) == false){ + throw new Error("Invalid key given: " + key); + } + namespace = namespace||this.DEFAULT_NAMESPACE; + + var results = this._sql("SELECT * FROM " + this.TABLE_NAME + " WHERE namespace = :namespace AND key = :key", + { ":namespace":namespace, ":key":key }); + + if(results.data && results.data.length){ + return results.data[0].value; + } + + return null; + }, + + getNamespaces: function(){ + var results = [ this.DEFAULT_NAMESPACE ]; + var rs = this._sql("SELECT namespace FROM " + this.TABLE_NAME + " DESC GROUP BY namespace"); + if (rs.data){ + for(var i = 0; i < rs.data.length; i++){ + if(rs.data[i].namespace != this.DEFAULT_NAMESPACE){ + results.push(rs.data[i].namespace); + } + } + } + return results; + }, + + getKeys: function(namespace){ + namespace = namespace||this.DEFAULT_NAMESPACE; + if(this.isValidKey(namespace) == false){ + throw new Error("Invalid namespace given: " + namespace); + } + + var results = []; + var rs = this._sql("SELECT key FROM " + this.TABLE_NAME + " WHERE namespace = :namespace", { ":namespace":namespace }); + if (rs.data){ + for(var i = 0; i < rs.data.length; i++){ + results.push(rs.data[i].key); + } + } + return results; + }, + + clear: function(namespace){ + if(this.isValidKey(namespace) == false){ + throw new Error("Invalid namespace given: " + namespace); + } + this._sql("DELETE FROM " + this.TABLE_NAME + " WHERE namespace = :namespace", { ":namespace":namespace }); + }, + + remove: function(key, namespace){ + namespace = namespace||this.DEFAULT_NAMESPACE; + this._sql("DELETE FROM " + this.TABLE_NAME + " WHERE namespace = :namespace AND key = :key", + { ":namespace":namespace, ":key":key }); + }, + + putMultiple: function(keys, values, resultsHandler, namespace) { + if(this.isValidKeyArray(keys) === false + || ! values instanceof Array + || keys.length != values.length){ + throw new Error("Invalid arguments: keys = [" + keys + "], values = [" + values + "]"); + } + + if(namespace == null || typeof namespace == "undefined"){ + namespace = this.DEFAULT_NAMESPACE; + } + + if(this.isValidKey(namespace) == false){ + throw new Error("Invalid namespace given: " + namespace); + } + + this._statusHandler = resultsHandler; + + // try to store the value + try{ + this._beginTransaction(); + for(var i=0;i<keys.length;i++) { + this._sql("DELETE FROM " + this.TABLE_NAME + " WHERE namespace = :namespace AND key = :key", + { ":namespace":namespace, ":key":keys[i] }); + this._sql("INSERT INTO " + this.TABLE_NAME + " VALUES (:namespace, :key, :value)", + { ":namespace":namespace, ":key":keys[i], ":value":values[i] }); + } + this._commitTransaction(); + }catch(e){ + // indicate we failed + console.debug("dojox.storage.AirDBStorageProvider.putMultiple:", e); + if(resultsHandler){ + resultsHandler(this.FAILED, keys, e.toString(), namespace); + } + return; + } + + if(resultsHandler){ + resultsHandler(this.SUCCESS, keys, null); + } + }, + + getMultiple: function(keys, namespace){ + if(this.isValidKeyArray(keys) === false){ + throw new Error("Invalid key array given: " + keys); + } + + if(namespace == null || typeof namespace == "undefined"){ + namespace = this.DEFAULT_NAMESPACE; + } + + if(this.isValidKey(namespace) == false){ + throw new Error("Invalid namespace given: " + namespace); + } + + var results = []; + for(var i=0;i<keys.length;i++){ + var result = this._sql("SELECT * FROM " + this.TABLE_NAME + " WHERE namespace = :namespace AND key = :key", + { ":namespace":namespace, ":key":keys[i] }); + results[i] = result.data && result.data.length ? result.data[0].value : null; + } + + return results; + }, + + removeMultiple: function(keys, namespace){ + namespace = namespace||this.DEFAULT_NAMESPACE; + + this._beginTransaction(); + for(var i=0;i<keys.length;i++){ + this._sql("DELETE FROM " + this.TABLE_NAME + " WHERE namespace = namespace = :namespace AND key = :key", + { ":namespace":namespace, ":key":keys[i] }); + } + this._commitTransaction(); + }, + + isPermanent: function(){ return true; }, + + getMaximumSize: function(){ return this.SIZE_NO_LIMIT; }, + + hasSettingsUI: function(){ return false; }, + + showSettingsUI: function(){ + throw new Error(this.declaredClass + " does not support a storage settings user-interface"); + }, + + hideSettingsUI: function(){ + throw new Error(this.declaredClass + " does not support a storage settings user-interface"); + } + }); + + dojox.storage.manager.register("dojox.storage.AirDBStorageProvider", new dojox.storage.AirDBStorageProvider()); + dojox.storage.manager.initialize(); + })(); +} +}); diff --git a/js/dojo/dojox/storage/AirEncryptedLocalStorageProvider.js b/js/dojo/dojox/storage/AirEncryptedLocalStorageProvider.js new file mode 100644 index 0000000..6e5f4a5 --- /dev/null +++ b/js/dojo/dojox/storage/AirEncryptedLocalStorageProvider.js @@ -0,0 +1,221 @@ +//>>built +// wrapped by build app +define("dojox/storage/AirEncryptedLocalStorageProvider", ["dijit","dojo","dojox","dojo/require!dojox/storage/manager,dojox/storage/Provider"], function(dijit,dojo,dojox){ +dojo.provide("dojox.storage.AirEncryptedLocalStorageProvider"); +dojo.require("dojox.storage.manager"); +dojo.require("dojox.storage.Provider"); + +if (dojo.isAIR) { + (function(){ + + if (!air) { + var air = {}; + } + air.ByteArray = window.runtime.flash.utils.ByteArray; + air.EncryptedLocalStore = window.runtime.flash.data.EncryptedLocalStore, + + // summary: + // Storage provider that uses features in the Adobe AIR runtime to achieve + // permanent storage + dojo.declare("dojox.storage.AirEncryptedLocalStorageProvider", [ dojox.storage.Provider ], { + initialize: function(){ + // indicate that this storage provider is now loaded + dojox.storage.manager.loaded(); + }, + + isAvailable: function(){ + return true; + }, + + _getItem: function(key){ + var storedValue = air.EncryptedLocalStore.getItem("__dojo_" + key); + return storedValue ? storedValue.readUTFBytes(storedValue.length) : ""; + }, + + _setItem: function(key, value){ + var bytes = new air.ByteArray(); + bytes.writeUTFBytes(value); + air.EncryptedLocalStore.setItem("__dojo_" + key, bytes); + }, + + _removeItem: function(key){ + air.EncryptedLocalStore.removeItem("__dojo_" + key); + }, + + put: function(key, value, resultsHandler, namespace){ + if(this.isValidKey(key) == false){ + throw new Error("Invalid key given: " + key); + } + namespace = namespace||this.DEFAULT_NAMESPACE; + if(this.isValidKey(namespace) == false){ + throw new Error("Invalid namespace given: " + namespace); + } + + // try to store the value + try{ + var namespaces = this._getItem("namespaces")||'|'; + if(namespaces.indexOf('|'+namespace+'|')==-1){ + this._setItem("namespaces", namespaces + namespace + '|'); + } + var keys = this._getItem(namespace + "_keys")||'|'; + if(keys.indexOf('|'+key+'|')==-1){ + this._setItem(namespace + "_keys", keys + key + '|'); + } + this._setItem('_' + namespace + '_' + key, value); + }catch(e){ + // indicate we failed + console.debug("dojox.storage.AirEncryptedLocalStorageProvider.put:", e); + resultsHandler(this.FAILED, key, e.toString(), namespace); + return; + } + + if(resultsHandler){ + resultsHandler(this.SUCCESS, key, null, namespace); + } + }, + + get: function(key, namespace){ + if(this.isValidKey(key) == false){ + throw new Error("Invalid key given: " + key); + } + namespace = namespace||this.DEFAULT_NAMESPACE; + return this._getItem('_' + namespace + '_' + key); + }, + + getNamespaces: function(){ + var results = [ this.DEFAULT_NAMESPACE ]; + var namespaces = (this._getItem("namespaces")||'|').split('|'); + for (var i=0;i<namespaces.length;i++){ + if(namespaces[i].length && namespaces[i] != this.DEFAULT_NAMESPACE){ + results.push(namespaces[i]); + } + } + return results; + }, + + getKeys: function(namespace){ + namespace = namespace||this.DEFAULT_NAMESPACE; + if(this.isValidKey(namespace) == false){ + throw new Error("Invalid namespace given: " + namespace); + } + + var results = []; + var keys = (this._getItem(namespace + "_keys")||'|').split('|'); + for (var i=0;i<keys.length;i++){ + if (keys[i].length){ + results.push(keys[i]); + } + } + return results; + }, + + clear: function(namespace){ + if(this.isValidKey(namespace) == false){ + throw new Error("Invalid namespace given: " + namespace); + } + var namespaces = this._getItem("namespaces")||'|'; + if(namespaces.indexOf('|'+namespace+'|')!=-1){ + this._setItem("namespaces", namespaces.replace('|' + namespace + '|', '|')); + } + var keys = (this._getItem(namespace + "_keys")||'|').split('|'); + for (var i=0;i<keys.length;i++){ + if (keys[i].length){ + this._removeItem(namespace + "_" + keys[i]); + } + } + this._removeItem(namespace + "_keys"); + }, + + remove: function(key, namespace){ + namespace = namespace||this.DEFAULT_NAMESPACE; + + var keys = this._getItem(namespace + "_keys")||'|'; + if(keys.indexOf('|'+key+'|')!=-1){ + this._setItem(namespace + "_keys", keys.replace('|' + key + '|', '|')); + } + this._removeItem('_' + namespace + '_' + key); + }, + + putMultiple: function(keys, values, resultsHandler, namespace) { + if(this.isValidKeyArray(keys) === false + || ! values instanceof Array + || keys.length != values.length){ + throw new Error("Invalid arguments: keys = [" + keys + "], values = [" + values + "]"); + } + + if(namespace == null || typeof namespace == "undefined"){ + namespace = this.DEFAULT_NAMESPACE; + } + + if(this.isValidKey(namespace) == false){ + throw new Error("Invalid namespace given: " + namespace); + } + + this._statusHandler = resultsHandler; + + // try to store the value + try{ + for(var i=0;i<keys.length;i++) { + this.put(keys[i], values[i], null, namespace); + } + }catch(e){ + // indicate we failed + console.debug("dojox.storage.AirEncryptedLocalStorageProvider.putMultiple:", e); + if(resultsHandler){ + resultsHandler(this.FAILED, keys, e.toString(), namespace); + } + return; + } + + if(resultsHandler){ + resultsHandler(this.SUCCESS, keys, null); + } + }, + + getMultiple: function(keys, namespace){ + if(this.isValidKeyArray(keys) === false){ + throw new Error("Invalid key array given: " + keys); + } + + if(namespace == null || typeof namespace == "undefined"){ + namespace = this.DEFAULT_NAMESPACE; + } + + if(this.isValidKey(namespace) == false){ + throw new Error("Invalid namespace given: " + namespace); + } + + var results = []; + for(var i=0;i<keys.length;i++){ + results[i] = this.get(keys[i], namespace); + } + return results; + }, + + removeMultiple: function(keys, namespace){ + namespace = namespace||this.DEFAULT_NAMESPACE; + for(var i=0;i<keys.length;i++){ + this.remove(keys[i], namespace); + } + }, + + isPermanent: function(){ return true; }, + + getMaximumSize: function(){ return this.SIZE_NO_LIMIT; }, + + hasSettingsUI: function(){ return false; }, + + showSettingsUI: function(){ + throw new Error(this.declaredClass + " does not support a storage settings user-interface"); + }, + + hideSettingsUI: function(){ + throw new Error(this.declaredClass + " does not support a storage settings user-interface"); + } + }); + + dojox.storage.manager.register("dojox.storage.AirEncryptedLocalStorageProvider", new dojox.storage.AirEncryptedLocalStorageProvider()); + dojox.storage.manager.initialize(); + })(); +} +}); diff --git a/js/dojo/dojox/storage/AirFileStorageProvider.js b/js/dojo/dojox/storage/AirFileStorageProvider.js new file mode 100644 index 0000000..9749913 --- /dev/null +++ b/js/dojo/dojox/storage/AirFileStorageProvider.js @@ -0,0 +1,232 @@ +//>>built +// wrapped by build app +define("dojox/storage/AirFileStorageProvider", ["dijit","dojo","dojox","dojo/require!dojox/storage/manager,dojox/storage/Provider"], function(dijit,dojo,dojox){ +dojo.provide("dojox.storage.AirFileStorageProvider"); +dojo.require("dojox.storage.manager"); +dojo.require("dojox.storage.Provider"); + +if (dojo.isAIR) { + (function(){ + + if (!air) { + var air = {}; + } + air.File = window.runtime.flash.filesystem.File; + air.FileStream = window.runtime.flash.filesystem.FileStream; + air.FileMode = window.runtime.flash.filesystem.FileMode; + + // summary: + // Storage provider that uses features in the Adobe AIR runtime to achieve + // permanent storage + dojo.declare("dojox.storage.AirFileStorageProvider", [ dojox.storage.Provider ], { + initialized: false, + + _storagePath: "__DOJO_STORAGE/", + + initialize: function(){ + this.initialized = false; + + // need to initialize our storage directory + try{ + var dir = air.File.applicationStorageDirectory.resolvePath(this._storagePath); + if (!dir.exists){ + dir.createDirectory(); + } + this.initialized = true; + }catch(e){ + console.debug("dojox.storage.AirFileStorageProvider.initialize:", e); + } + + // indicate that this storage provider is now loaded + dojox.storage.manager.loaded(); + }, + + isAvailable: function(){ + return true; + }, + + put: function(key, value, resultsHandler, namespace){ + if(this.isValidKey(key) == false){ + throw new Error("Invalid key given: " + key); + } + namespace = namespace||this.DEFAULT_NAMESPACE; + if(this.isValidKey(namespace) == false){ + throw new Error("Invalid namespace given: " + namespace); + } + + // try to store the value + try{ + this.remove(key, namespace); + + var dir = air.File.applicationStorageDirectory.resolvePath(this._storagePath + namespace); + if (!dir.exists){ + dir.createDirectory(); + } + + var file = dir.resolvePath(key); + var stream = new air.FileStream(); + stream.open(file, air.FileMode.WRITE); + stream.writeObject(value); + stream.close(); + }catch(e){ + // indicate we failed + console.debug("dojox.storage.AirFileStorageProvider.put:", e); + resultsHandler(this.FAILED, key, e.toString(), namespace); + return; + } + + if(resultsHandler){ + resultsHandler(this.SUCCESS, key, null, namespace); + } + }, + + get: function(key, namespace){ + if(this.isValidKey(key) == false){ + throw new Error("Invalid key given: " + key); + } + namespace = namespace||this.DEFAULT_NAMESPACE; + + var results = null; + + var file = air.File.applicationStorageDirectory.resolvePath(this._storagePath + namespace + '/' + key); + if (file.exists && !file.isDirectory){ + var stream = new air.FileStream(); + stream.open(file, air.FileMode.READ); + results = stream.readObject(); + stream.close(); + } + + return results; + }, + + getNamespaces: function(){ + var results = [ this.DEFAULT_NAMESPACE ]; + var dir = air.File.applicationStorageDirectory.resolvePath(this._storagePath); + var files = dir.getDirectoryListing(), i; + for (i = 0; i < files.length; i++) { + if(files[i].isDirectory && files[i].name != this.DEFAULT_NAMESPACE){ + results.push(files[i].name); + } + } + return results; + }, + + getKeys: function(namespace){ + namespace = namespace||this.DEFAULT_NAMESPACE; + if(this.isValidKey(namespace) == false){ + throw new Error("Invalid namespace given: " + namespace); + } + + var results = []; + var dir = air.File.applicationStorageDirectory.resolvePath(this._storagePath + namespace); + if (dir.exists && dir.isDirectory){ + var files = dir.getDirectoryListing(), i; + for (i = 0; i < files.length; i++) { + results.push(files[i].name); + } + } + return results; + }, + + clear: function(namespace){ + if(this.isValidKey(namespace) == false){ + throw new Error("Invalid namespace given: " + namespace); + } + var dir = air.File.applicationStorageDirectory.resolvePath(this._storagePath + namespace); + if (dir.exists && dir.isDirectory){ + dir.deleteDirectory(true); + } + }, + + remove: function(key, namespace){ + namespace = namespace||this.DEFAULT_NAMESPACE; + var file = air.File.applicationStorageDirectory.resolvePath(this._storagePath + namespace + '/' + key); + if (file.exists && !file.isDirectory){ + file.deleteFile(); + } + }, + + putMultiple: function(keys, values, resultsHandler, namespace) { + if(this.isValidKeyArray(keys) === false + || ! values instanceof Array + || keys.length != values.length){ + throw new Error("Invalid arguments: keys = [" + keys + "], values = [" + values + "]"); + } + + if(namespace == null || typeof namespace == "undefined"){ + namespace = this.DEFAULT_NAMESPACE; + } + + if(this.isValidKey(namespace) == false){ + throw new Error("Invalid namespace given: " + namespace); + } + + this._statusHandler = resultsHandler; + + // try to store the value + try{ + for(var i=0;i<keys.length;i++) { + this.put(keys[i], values[i], null, namespace); + } + }catch(e){ + // indicate we failed + console.debug("dojox.storage.AirFileStorageProvider.putMultiple:", e); + if(resultsHandler){ + resultsHandler(this.FAILED, keys, e.toString(), namespace); + } + return; + } + + if(resultsHandler){ + resultsHandler(this.SUCCESS, keys, null, namespace); + } + }, + + getMultiple: function(keys, namespace){ + if(this.isValidKeyArray(keys) === false){ + throw new Error("Invalid key array given: " + keys); + } + + if(namespace == null || typeof namespace == "undefined"){ + namespace = this.DEFAULT_NAMESPACE; + } + + if(this.isValidKey(namespace) == false){ + throw new Error("Invalid namespace given: " + namespace); + } + + var results = []; + for(var i=0;i<keys.length;i++){ + results[i] = this.get(keys[i], namespace); + } + return results; + }, + + removeMultiple: function(keys, namespace){ + namespace = namespace||this.DEFAULT_NAMESPACE; + + for(var i=0;i<keys.length;i++){ + this.remove(keys[i], namespace); + } + }, + + isPermanent: function(){ return true; }, + + getMaximumSize: function(){ return this.SIZE_NO_LIMIT; }, + + hasSettingsUI: function(){ return false; }, + + showSettingsUI: function(){ + throw new Error(this.declaredClass + " does not support a storage settings user-interface"); + }, + + hideSettingsUI: function(){ + throw new Error(this.declaredClass + " does not support a storage settings user-interface"); + } + }); + + dojox.storage.manager.register("dojox.storage.AirFileStorageProvider", new dojox.storage.AirFileStorageProvider()); + dojox.storage.manager.initialize(); + })(); +} +}); diff --git a/js/dojo/dojox/storage/BehaviorStorageProvider.js b/js/dojo/dojox/storage/BehaviorStorageProvider.js new file mode 100644 index 0000000..6ab2211 --- /dev/null +++ b/js/dojo/dojox/storage/BehaviorStorageProvider.js @@ -0,0 +1,241 @@ +//>>built +// wrapped by build app +define("dojox/storage/BehaviorStorageProvider", ["dijit","dojo","dojox","dojo/require!dojox/storage/Provider,dojox/storage/manager"], function(dijit,dojo,dojox){ +dojo.provide("dojox.storage.BehaviorStorageProvider"); + +dojo.require("dojox.storage.Provider"); +dojo.require("dojox.storage.manager"); + +dojo.declare( + "dojox.storage.BehaviorStorageProvider", + [dojox.storage.Provider], + { + store: null, + + storeName: '__dojox_BehaviorStorage', + + keys: [], + + initialize: function(){ + try{ + this.store = this._createStore(); + this.store.load(this.storeName); + }catch(e){ + throw new Error("Store is not available: " + e); + } + + var keys = this.get('keys','dojoxSystemNS'); + this.keys = keys || []; + + this.initialized = true; + dojox.storage.manager.loaded(); + + }, + + isAvailable: function(){ /*Boolean*/ + // This is not completely true. UserData may + // be disabled in security settings. To *really* + // check if this is available, one needs to wait + // until the store is successfully initialized... + return dojo.isIE && dojo.isIE >= 5; + }, + + _createStore: function() { + var storeNode = dojo.create( + 'link', + {id: this.storeName + 'Node', style: {'display':'none'}}, + dojo.query('head')[0] + ); + storeNode.addBehavior('#default#userdata'); + + return storeNode; + }, + + put: function( /*string*/ key, + /*object*/ value, + /*function*/ resultsHandler, + /*string?*/ namespace){ + + this._assertIsValidKey(key); + + namespace = namespace||this.DEFAULT_NAMESPACE; + this._assertIsValidNamespace(namespace); + + var fullKey = this.getFullKey(key,namespace); + value = dojo.toJson(value); + + this.store.setAttribute(fullKey, value); + this.store.save(this.storeName); + + var success = this.store.getAttribute(fullKey) === value; + if(success){ + this._addKey(fullKey); + this.store.setAttribute('__dojoxSystemNS_keys', dojo.toJson(this.keys)); + this.store.save(this.storeName); + } + + if(resultsHandler){ + resultsHandler(success ? this.SUCCESS : this.FAILED, key, null, namespace); + } + }, + + get: function(/*string*/ key, /*string?*/ namespace){ /*Object*/ + this._assertIsValidKey(key); + + namespace = namespace||this.DEFAULT_NAMESPACE; + this._assertIsValidNamespace(namespace); + + key = this.getFullKey(key, namespace); + + return dojo.fromJson(this.store.getAttribute(key)); + }, + + getKeys: function(/*string?*/ namespace){ /*Array*/ + namespace = namespace||this.DEFAULT_NAMESPACE; + this._assertIsValidNamespace(namespace); + + namespace = '__'+namespace+'_'; + + var keys = []; + for(var i = 0; i < this.keys.length; i++){ + var currentKey = this.keys[i]; + if(this._beginsWith(currentKey,namespace)){ + currentKey = currentKey.substring(namespace.length); + keys.push(currentKey); + } + } + + return keys; + }, + + clear: function(/*string?*/ namespace){ + namespace = namespace||this.DEFAULT_NAMESPACE; + this._assertIsValidNamespace(namespace); + + namespace = '__'+namespace+'_'; + + var keys = []; + for(var i = 0; i < this.keys.length; i++){ + var currentKey = this.keys[i]; + if(this._beginsWith(currentKey,namespace)){ + keys.push(currentKey); + } + } + + dojo.forEach(keys, function(key){ + this.store.removeAttribute(key); + this._removeKey(key); + }, this); + + this.put('keys', this.keys, null, 'dojoxSystemNS'); + this.store.save(this.storeName); + }, + + remove: function(/*string*/ key, /*string?*/ namespace){ + this._assertIsValidKey(key); + + namespace = namespace||this.DEFAULT_NAMESPACE; + this._assertIsValidNamespace(namespace); + + key = this.getFullKey(key, namespace); + this.store.removeAttribute(key); + + this._removeKey(key); + this.put('keys', this.keys, null, 'dojoxSystemNS'); + this.store.save(this.storeName); + + }, + + getNamespaces: function(){ /*string[]*/ + + + var results = [ this.DEFAULT_NAMESPACE]; + + var found = {}; + found[this.DEFAULT_NAMESPACE] = true; + var tester = /^__([^_]*)_/; + + for(var i = 0; i < this.keys.length; i++){ + var currentKey = this.keys[i]; + if(tester.test(currentKey) == true){ + var currentNS = currentKey.match(tester)[1]; + if(typeof found[currentNS] == "undefined"){ + found[currentNS] = true; + results.push(currentNS); + } + } + } + + return results; + + }, + + isPermanent: function(){ /*Boolean*/ + return true; + }, + + getMaximumSize: function(){ /* mixed */ + // this *might* be more, depending on the zone + // of the current site. But 64k is guaranteed. + return 64; + }, + + hasSettingsUI: function(){ /*Boolean*/ + return false; + }, + + isValidKey: function(/*string*/ keyName){ /*Boolean*/ + if(keyName === null || keyName === undefined){ + return false; + } + + return /^[0-9A-Za-z_-]*$/.test(keyName); + }, + + isValidNamespace: function(/*string*/ keyName){ /*Boolean*/ + + if(keyName === null || keyName === undefined){ + return false; + } + + return /^[0-9A-Za-z-]*$/.test(keyName); + }, + + getFullKey: function(key, namespace){ + // checks for valid namespace and + // key are already performed. + return "__" + namespace + "_" + key; + }, + + _beginsWith: function(/* string */ haystack, /* string */ needle) { + if(needle.length > haystack.length) { + return false; + } + return haystack.substring(0,needle.length) === needle; + }, + + _assertIsValidNamespace: function(/* string */ namespace){ + if(this.isValidNamespace(namespace) === false){ + throw new Error("Invalid namespace given: " + namespace); + } + }, + + _assertIsValidKey: function(/* string */ key){ + if(this.isValidKey(key) === false){ + throw new Error("Invalid key given: " + key); + } + }, + + _addKey: function(key){ + this._removeKey(key); + this.keys.push(key); + }, + + _removeKey: function(key){ + this.keys = dojo.filter(this.keys,function(item){ return item !== key;},this); + } + } +); + +dojox.storage.manager.register("dojox.storage.BehaviorStorageProvider", new dojox.storage.BehaviorStorageProvider()); +}); diff --git a/js/dojo/dojox/storage/CookieStorageProvider.js b/js/dojo/dojox/storage/CookieStorageProvider.js new file mode 100644 index 0000000..292a347 --- /dev/null +++ b/js/dojo/dojox/storage/CookieStorageProvider.js @@ -0,0 +1,207 @@ +//>>built +// wrapped by build app +define("dojox/storage/CookieStorageProvider", ["dijit","dojo","dojox","dojo/require!dojox/storage/Provider,dojox/storage/manager,dojo/cookie"], function(dijit,dojo,dojox){ +dojo.provide("dojox.storage.CookieStorageProvider"); + +dojo.require("dojox.storage.Provider"); +dojo.require("dojox.storage.manager"); +dojo.require("dojo.cookie"); + +dojo.declare( + "dojox.storage.CookieStorageProvider", + [dojox.storage.Provider], + { + store: null, + + cookieName: 'dojoxStorageCookie', + + storageLife: 730, // in days + + initialize: function(){ + + this.store = dojo.fromJson(dojo.cookie(this.cookieName)) || {}; + + this.initialized = true; + dojox.storage.manager.loaded(); + }, + + isAvailable: function(){ /*Boolean*/ + return dojo.cookie.isSupported(); + }, + + put: function( /*string*/ key, + /*object*/ value, + /*function*/ resultsHandler, + /*string?*/ namespace){ + + this._assertIsValidKey(key); + + namespace = namespace||this.DEFAULT_NAMESPACE; + this._assertIsValidNamespace(namespace); + + fullKey = this.getFullKey(key,namespace); + + this.store[fullKey] = dojo.toJson(value); + this._save(); + + var success = dojo.toJson(this.store) === dojo.cookie(this.cookieName); + + if(!success){ + this.remove(key,namespace); + } + + if(resultsHandler){ + resultsHandler(success ? this.SUCCESS : this.FAILED, key, null, namespace); + } + + }, + + get: function(/*string*/ key, /*string?*/ namespace){ /*Object*/ + this._assertIsValidKey(key); + + namespace = namespace||this.DEFAULT_NAMESPACE; + this._assertIsValidNamespace(namespace); + + // get our full key name, which is namespace + key + key = this.getFullKey(key, namespace); + + return this.store[key] ? dojo.fromJson(this.store[key]) : null; + }, + + getKeys: function(/*string?*/ namespace){ /*Array*/ + namespace = namespace||this.DEFAULT_NAMESPACE; + this._assertIsValidNamespace(namespace); + + namespace = '__'+namespace+'_'; + + var keys = []; + for(var currentKey in this.store){ + if(this._beginsWith(currentKey,namespace)){ + currentKey = currentKey.substring(namespace.length); + keys.push(currentKey); + } + } + + return keys; + }, + + clear: function(/*string?*/ namespace){ + namespace = namespace||this.DEFAULT_NAMESPACE; + this._assertIsValidNamespace(namespace); + + namespace = '__'+namespace+'_'; + + for(var currentKey in this.store){ + if(this._beginsWith(currentKey,namespace)){ + delete(this.store[currentKey]); + } + } + + this._save(); + }, + + remove: function(/*string*/ key, /*string?*/ namespace){ + namespace = namespace||this.DEFAULT_NAMESPACE; + this._assertIsValidNamespace(namespace); + + this._assertIsValidKey(key); + key = this.getFullKey(key, namespace); + + delete this.store[key]; + this._save(); + }, + + getNamespaces: function(){ /*string[]*/ + // There must be a better way than + // to execute a regex on *every* + // item in the store. + + var results = [this.DEFAULT_NAMESPACE]; + + var found = {}; + found[this.DEFAULT_NAMESPACE] = true; + var tester = /^__([^_]*)_/; + + for(var currentKey in this.store){ + if(tester.test(currentKey) == true){ + var currentNS = currentKey.match(tester)[1]; + if(typeof found[currentNS] == "undefined"){ + found[currentNS] = true; + results.push(currentNS); + } + } + } + + return results; + }, + + isPermanent: function(){ /*Boolean*/ + return true; + }, + + getMaximumSize: function(){ /* mixed */ + return 4; + }, + + hasSettingsUI: function(){ /*Boolean*/ + return false; + }, + + isValidKey: function(/*string*/ keyName){ /*Boolean*/ + if(keyName === null || keyName === undefined){ + return false; + } + + return /^[0-9A-Za-z_-]*$/.test(keyName); + }, + + isValidNamespace: function(/*string*/ keyName){ /*Boolean*/ + // we *must* prevent namespaces from having + // underscores - else lookup of namespaces + // via RegEx (e.g. in getNamespaces ) would + // return wrong results. + // + // The only way around this would be to + // disallow underscores in keys. + + if(keyName === null || keyName === undefined){ + return false; + } + + return /^[0-9A-Za-z-]*$/.test(keyName); + }, + + getFullKey: function(key, namespace){ + // checks for valid namespace and + // key are already performed. + return "__" + namespace + "_" + key; + }, + + _save: function(){ + dojo.cookie(this.cookieName,dojo.toJson(this.store),{expires: this.storageLife}); + }, + + _beginsWith: function(/* string */ haystack, /* string */ needle) { + if(needle.length > haystack.length) { + return false; + } + return haystack.substring(0,needle.length) === needle; + }, + + _assertIsValidNamespace: function(/* string */ namespace){ + if(this.isValidNamespace(namespace) === false){ + throw new Error("Invalid namespace given: " + namespace); + } + }, + + _assertIsValidKey: function(/* string */ key){ + if(this.isValidKey(key) === false){ + throw new Error("Invalid key given: " + key); + } + } + } +); + +dojox.storage.manager.register("dojox.storage.CookieStorageProvider", new dojox.storage.CookieStorageProvider()); + +}); diff --git a/js/dojo/dojox/storage/FlashStorageProvider.js b/js/dojo/dojox/storage/FlashStorageProvider.js new file mode 100644 index 0000000..7bc4d9c --- /dev/null +++ b/js/dojo/dojox/storage/FlashStorageProvider.js @@ -0,0 +1,347 @@ +//>>built +// wrapped by build app +define("dojox/storage/FlashStorageProvider", ["dijit","dojo","dojox","dojo/require!dojox/flash,dojox/storage/manager,dojox/storage/Provider"], function(dijit,dojo,dojox){ +dojo.provide("dojox.storage.FlashStorageProvider"); + +dojo.require("dojox.flash"); +dojo.require("dojox.storage.manager"); +dojo.require("dojox.storage.Provider"); + +// summary: +// Storage provider that uses features in Flash to achieve permanent +// storage +// description: +// Authors of this storage provider- +// Brad Neuberg, bkn3@columbia.edu +dojo.declare("dojox.storage.FlashStorageProvider", dojox.storage.Provider, { + initialized: false, + + _available: null, + _statusHandler: null, + _flashReady: false, + _pageReady: false, + + initialize: function(){ + //console.debug("FlashStorageProvider.initialize"); + if(dojo.config["disableFlashStorage"] == true){ + return; + } + + // initialize our Flash + dojox.flash.addLoadedListener(dojo.hitch(this, function(){ + //console.debug("flashReady"); + // indicate our Flash subsystem is now loaded + this._flashReady = true; + if(this._flashReady && this._pageReady){ + this._loaded(); + } + })); + var swfLoc = dojo.moduleUrl("dojox", "storage/Storage.swf").toString(); + dojox.flash.setSwf(swfLoc, false); + + // wait till page is finished loading + dojo.connect(dojo, "loaded", this, function(){ + //console.debug("pageReady"); + this._pageReady = true; + if(this._flashReady && this._pageReady){ + this._loaded(); + } + }); + }, + + // Set a new value for the flush delay timer. + // Possible values: + // 0 : Perform the flush synchronously after each "put" request + // > 0 : Wait until 'newDelay' ms have passed without any "put" request to flush + // -1 : Do not automatically flush + setFlushDelay: function(newDelay){ + if(newDelay === null || typeof newDelay === "undefined" || isNaN(newDelay)){ + throw new Error("Invalid argunment: " + newDelay); + } + + dojox.flash.comm.setFlushDelay(String(newDelay)); + }, + + getFlushDelay: function(){ + return Number(dojox.flash.comm.getFlushDelay()); + }, + + flush: function(namespace){ + //FIXME: is this test necessary? Just use !namespace + if(namespace == null || typeof namespace == "undefined"){ + namespace = dojox.storage.DEFAULT_NAMESPACE; + } + dojox.flash.comm.flush(namespace); + }, + + isAvailable: function(){ + return (this._available = !dojo.config["disableFlashStorage"]); + }, + + put: function(key, value, resultsHandler, namespace){ + if(!this.isValidKey(key)){ + throw new Error("Invalid key given: " + key); + } + + if(!namespace){ + namespace = dojox.storage.DEFAULT_NAMESPACE; + } + + if(!this.isValidKey(namespace)){ + throw new Error("Invalid namespace given: " + namespace); + } + + this._statusHandler = resultsHandler; + + // serialize the value; + // handle strings differently so they have better performance + if(dojo.isString(value)){ + value = "string:" + value; + }else{ + value = dojo.toJson(value); + } + + dojox.flash.comm.put(key, value, namespace); + }, + + putMultiple: function(keys, values, resultsHandler, namespace){ + if(!this.isValidKeyArray(keys) || ! values instanceof Array + || keys.length != values.length){ + throw new Error("Invalid arguments: keys = [" + keys + "], values = [" + values + "]"); + } + + if(!namespace){ + namespace = dojox.storage.DEFAULT_NAMESPACE; + } + + if(!this.isValidKey(namespace)){ + throw new Error("Invalid namespace given: " + namespace); + } + + this._statusHandler = resultsHandler; + + // Convert the arguments on strings we can pass along to Flash + var metaKey = keys.join(","); + var lengths = []; + for(var i=0;i<values.length;i++){ + if(dojo.isString(values[i])){ + values[i] = "string:" + values[i]; + }else{ + values[i] = dojo.toJson(values[i]); + } + lengths[i] = values[i].length; + } + var metaValue = values.join(""); + var metaLengths = lengths.join(","); + + dojox.flash.comm.putMultiple(metaKey, metaValue, metaLengths, namespace); + }, + + get: function(key, namespace){ + if(!this.isValidKey(key)){ + throw new Error("Invalid key given: " + key); + } + + if(!namespace){ + namespace = dojox.storage.DEFAULT_NAMESPACE; + } + + if(!this.isValidKey(namespace)){ + throw new Error("Invalid namespace given: " + namespace); + } + + var results = dojox.flash.comm.get(key, namespace); + + if(results == ""){ + return null; + } + + return this._destringify(results); + }, + + getMultiple: function(/*array*/ keys, /*string?*/ namespace){ /*Object*/ + if(!this.isValidKeyArray(keys)){ + throw new ("Invalid key array given: " + keys); + } + + if(!namespace){ + namespace = dojox.storage.DEFAULT_NAMESPACE; + } + + if(!this.isValidKey(namespace)){ + throw new Error("Invalid namespace given: " + namespace); + } + + var metaKey = keys.join(","); + var metaResults = dojox.flash.comm.getMultiple(metaKey, namespace); + var results = eval("(" + metaResults + ")"); + + // destringify each entry back into a real JS object + //FIXME: use dojo.map + for(var i = 0; i < results.length; i++){ + results[i] = (results[i] == "") ? null : this._destringify(results[i]); + } + + return results; + }, + + _destringify: function(results){ + // destringify the content back into a + // real JavaScript object; + // handle strings differently so they have better performance + if(dojo.isString(results) && (/^string:/.test(results))){ + results = results.substring("string:".length); + }else{ + results = dojo.fromJson(results); + } + + return results; + }, + + getKeys: function(namespace){ + if(!namespace){ + namespace = dojox.storage.DEFAULT_NAMESPACE; + } + + if(!this.isValidKey(namespace)){ + throw new Error("Invalid namespace given: " + namespace); + } + + var results = dojox.flash.comm.getKeys(namespace); + + // Flash incorrectly returns an empty string as "null" + if(results == null || results == "null"){ + results = ""; + } + + results = results.split(","); + results.sort(); + + return results; + }, + + getNamespaces: function(){ + var results = dojox.flash.comm.getNamespaces(); + + // Flash incorrectly returns an empty string as "null" + if(results == null || results == "null"){ + results = dojox.storage.DEFAULT_NAMESPACE; + } + + results = results.split(","); + results.sort(); + + return results; + }, + + clear: function(namespace){ + if(!namespace){ + namespace = dojox.storage.DEFAULT_NAMESPACE; + } + + if(!this.isValidKey(namespace)){ + throw new Error("Invalid namespace given: " + namespace); + } + + dojox.flash.comm.clear(namespace); + }, + + remove: function(key, namespace){ + if(!namespace){ + namespace = dojox.storage.DEFAULT_NAMESPACE; + } + + if(!this.isValidKey(namespace)){ + throw new Error("Invalid namespace given: " + namespace); + } + + dojox.flash.comm.remove(key, namespace); + }, + + removeMultiple: function(/*array*/ keys, /*string?*/ namespace){ /*Object*/ + if(!this.isValidKeyArray(keys)){ + dojo.raise("Invalid key array given: " + keys); + } + if(!namespace){ + namespace = dojox.storage.DEFAULT_NAMESPACE; + } + + if(!this.isValidKey(namespace)){ + throw new Error("Invalid namespace given: " + namespace); + } + + var metaKey = keys.join(","); + dojox.flash.comm.removeMultiple(metaKey, namespace); + }, + + isPermanent: function(){ + return true; + }, + + getMaximumSize: function(){ + return dojox.storage.SIZE_NO_LIMIT; + }, + + hasSettingsUI: function(){ + return true; + }, + + showSettingsUI: function(){ + dojox.flash.comm.showSettings(); + dojox.flash.obj.setVisible(true); + dojox.flash.obj.center(); + }, + + hideSettingsUI: function(){ + // hide the dialog + dojox.flash.obj.setVisible(false); + + // call anyone who wants to know the dialog is + // now hidden + if(dojo.isFunction(dojox.storage.onHideSettingsUI)){ + dojox.storage.onHideSettingsUI.call(null); + } + }, + + getResourceList: function(){ /* Array[] */ + // Dojo Offline no longer uses the FlashStorageProvider for offline + // storage; Gears is now required + return []; + }, + + /** Called when Flash and the page are finished loading. */ + _loaded: function(){ + // get available namespaces + this._allNamespaces = this.getNamespaces(); + + this.initialized = true; + + // indicate that this storage provider is now loaded + dojox.storage.manager.loaded(); + }, + + // Called if the storage system needs to tell us about the status + // of a put() request. + _onStatus: function(statusResult, key, namespace){ + //console.debug("onStatus, statusResult="+statusResult+", key="+key); + var ds = dojox.storage; + var dfo = dojox.flash.obj; + + if(statusResult == ds.PENDING){ + dfo.center(); + dfo.setVisible(true); + }else{ + dfo.setVisible(false); + } + + if(ds._statusHandler){ + ds._statusHandler.call(null, statusResult, key, null, namespace); + } + } + } +); + +dojox.storage.manager.register("dojox.storage.FlashStorageProvider", + new dojox.storage.FlashStorageProvider()); + +}); diff --git a/js/dojo/dojox/storage/GearsStorageProvider.js b/js/dojo/dojox/storage/GearsStorageProvider.js new file mode 100644 index 0000000..0ce0461 --- /dev/null +++ b/js/dojo/dojox/storage/GearsStorageProvider.js @@ -0,0 +1,388 @@ +//>>built +// wrapped by build app +define("dojox/storage/GearsStorageProvider", ["dijit","dojo","dojox","dojo/require!dojo/gears,dojox/storage/Provider,dojox/storage/manager,dojox/sql"], function(dijit,dojo,dojox){ +dojo.provide("dojox.storage.GearsStorageProvider"); +dojo.require("dojo.gears"); +dojo.require("dojox.storage.Provider"); +dojo.require("dojox.storage.manager"); +dojo.require("dojox.sql"); + +if(dojo.gears.available){ + + (function(){ + // make sure we don't define the gears provider if we're not gears + // enabled + + dojo.declare("dojox.storage.GearsStorageProvider", dojox.storage.Provider, { + // summary: + // Storage provider that uses the features of Google Gears + // to store data (it is saved into the local SQL database + // provided by Gears, using dojox.sql) + // description: + // You can disable this storage provider with the following djConfig + // variable: + // var djConfig = { disableGearsStorage: true }; + // + // Authors of this storage provider- + // Brad Neuberg, bkn3@columbia.edu + constructor: function(){ + }, + // instance methods and properties + TABLE_NAME: "__DOJO_STORAGE", + initialized: false, + + _available: null, + _storageReady: false, + + initialize: function(){ + //console.debug("dojox.storage.GearsStorageProvider.initialize"); + if(dojo.config["disableGearsStorage"] == true){ + return; + } + + // partition our storage data so that multiple apps + // on the same host won't collide + this.TABLE_NAME = "__DOJO_STORAGE"; + + // we delay creating our internal tables until an operation is + // actually called, to avoid having a Gears permission dialog + // on page load (bug #7538) + + // indicate that this storage provider is now loaded + this.initialized = true; + dojox.storage.manager.loaded(); + }, + + isAvailable: function(){ + // is Google Gears available and defined? + return this._available = dojo.gears.available; + }, + + put: function(key, value, resultsHandler, namespace){ + this._initStorage(); + + if(!this.isValidKey(key)){ + throw new Error("Invalid key given: " + key); + } + + namespace = namespace||this.DEFAULT_NAMESPACE; + if(!this.isValidKey(namespace)){ + throw new Error("Invalid namespace given: " + key); + } + + // serialize the value; + // handle strings differently so they have better performance + if(dojo.isString(value)){ + value = "string:" + value; + }else{ + value = dojo.toJson(value); + } + + // try to store the value + try{ + dojox.sql("DELETE FROM " + this.TABLE_NAME + + " WHERE namespace = ? AND key = ?", + namespace, key); + dojox.sql("INSERT INTO " + this.TABLE_NAME + + " VALUES (?, ?, ?)", + namespace, key, value); + }catch(e){ + // indicate we failed + console.debug("dojox.storage.GearsStorageProvider.put:", e); + resultsHandler(this.FAILED, key, e.toString(), namespace); + return; + } + + if(resultsHandler){ + resultsHandler(dojox.storage.SUCCESS, key, null, namespace); + } + }, + + get: function(key, namespace){ + this._initStorage(); + + if(!this.isValidKey(key)){ + throw new Error("Invalid key given: " + key); + } + + namespace = namespace||this.DEFAULT_NAMESPACE; + if(!this.isValidKey(namespace)){ + throw new Error("Invalid namespace given: " + key); + } + + // try to find this key in the database + var results = dojox.sql("SELECT * FROM " + this.TABLE_NAME + + " WHERE namespace = ? AND " + + " key = ?", + namespace, key); + if(!results.length){ + return null; + }else{ + results = results[0].value; + } + + // destringify the content back into a + // real JavaScript object; + // handle strings differently so they have better performance + if(dojo.isString(results) && (/^string:/.test(results))){ + results = results.substring("string:".length); + }else{ + results = dojo.fromJson(results); + } + + return results; + }, + + getNamespaces: function(){ + this._initStorage(); + + var results = [ dojox.storage.DEFAULT_NAMESPACE ]; + + var rs = dojox.sql("SELECT namespace FROM " + this.TABLE_NAME + + " DESC GROUP BY namespace"); + for(var i = 0; i < rs.length; i++){ + if(rs[i].namespace != dojox.storage.DEFAULT_NAMESPACE){ + results.push(rs[i].namespace); + } + } + + return results; + }, + + getKeys: function(namespace){ + this._initStorage(); + + namespace = namespace||this.DEFAULT_NAMESPACE; + if(!this.isValidKey(namespace)){ + throw new Error("Invalid namespace given: " + namespace); + } + + var rs = dojox.sql("SELECT key FROM " + this.TABLE_NAME + + " WHERE namespace = ?", + namespace); + + var results = []; + for(var i = 0; i < rs.length; i++){ + results.push(rs[i].key); + } + + return results; + }, + + clear: function(namespace){ + this._initStorage(); + + namespace = namespace||this.DEFAULT_NAMESPACE; + if(!this.isValidKey(namespace)){ + throw new Error("Invalid namespace given: " + namespace); + } + + dojox.sql("DELETE FROM " + this.TABLE_NAME + + " WHERE namespace = ?", + namespace); + }, + + remove: function(key, namespace){ + this._initStorage(); + + if(!this.isValidKey(key)){ + throw new Error("Invalid key given: " + key); + } + + namespace = namespace||this.DEFAULT_NAMESPACE; + if(!this.isValidKey(namespace)){ + throw new Error("Invalid namespace given: " + key); + } + + dojox.sql("DELETE FROM " + this.TABLE_NAME + + " WHERE namespace = ? AND" + + " key = ?", + namespace, + key); + }, + + putMultiple: function(keys, values, resultsHandler, namespace) { + this._initStorage(); + + if(!this.isValidKeyArray(keys) + || ! values instanceof Array + || keys.length != values.length){ + throw new Error("Invalid arguments: keys = [" + + keys + "], values = [" + values + "]"); + } + + if(namespace == null || typeof namespace == "undefined"){ + namespace = dojox.storage.DEFAULT_NAMESPACE; + } + if(!this.isValidKey(namespace)){ + throw new Error("Invalid namespace given: " + namespace); + } + + this._statusHandler = resultsHandler; + + // try to store the value + try{ + dojox.sql.open(); + dojox.sql.db.execute("BEGIN TRANSACTION"); + var _stmt = "REPLACE INTO " + this.TABLE_NAME + " VALUES (?, ?, ?)"; + for(var i=0;i<keys.length;i++) { + // serialize the value; + // handle strings differently so they have better performance + var value = values[i]; + if(dojo.isString(value)){ + value = "string:" + value; + }else{ + value = dojo.toJson(value); + } + + dojox.sql.db.execute( _stmt, + [namespace, keys[i], value]); + } + dojox.sql.db.execute("COMMIT TRANSACTION"); + dojox.sql.close(); + }catch(e){ + // indicate we failed + console.debug("dojox.storage.GearsStorageProvider.putMultiple:", e); + if(resultsHandler){ + resultsHandler(this.FAILED, keys, e.toString(), namespace); + } + return; + } + + if(resultsHandler){ + resultsHandler(dojox.storage.SUCCESS, keys, null, namespace); + } + }, + + getMultiple: function(keys, namespace){ + // TODO: Maybe use SELECT IN instead + this._initStorage(); + + if(!this.isValidKeyArray(keys)){ + throw new ("Invalid key array given: " + keys); + } + + if(namespace == null || typeof namespace == "undefined"){ + namespace = dojox.storage.DEFAULT_NAMESPACE; + } + if(!this.isValidKey(namespace)){ + throw new Error("Invalid namespace given: " + namespace); + } + + var _stmt = "SELECT * FROM " + this.TABLE_NAME + + " WHERE namespace = ? AND " + " key = ?"; + + var results = []; + for(var i=0;i<keys.length;i++){ + var result = dojox.sql( _stmt, namespace, keys[i]); + + if( ! result.length){ + results[i] = null; + }else{ + result = result[0].value; + + // destringify the content back into a + // real JavaScript object; + // handle strings differently so they have better performance + if(dojo.isString(result) && (/^string:/.test(result))){ + results[i] = result.substring("string:".length); + }else{ + results[i] = dojo.fromJson(result); + } + } + } + + return results; + }, + + removeMultiple: function(keys, namespace){ + this._initStorage(); + + if(!this.isValidKeyArray(keys)){ + throw new Error("Invalid arguments: keys = [" + keys + "]"); + } + + if(namespace == null || typeof namespace == "undefined"){ + namespace = dojox.storage.DEFAULT_NAMESPACE; + } + if(!this.isValidKey(namespace)){ + throw new Error("Invalid namespace given: " + namespace); + } + + dojox.sql.open(); + dojox.sql.db.execute("BEGIN TRANSACTION"); + var _stmt = "DELETE FROM " + this.TABLE_NAME + + " WHERE namespace = ? AND key = ?"; + + for(var i=0;i<keys.length;i++){ + dojox.sql.db.execute( _stmt, + [namespace, keys[i]]); + } + dojox.sql.db.execute("COMMIT TRANSACTION"); + dojox.sql.close(); + }, + + isPermanent: function(){ return true; }, + + getMaximumSize: function(){ return this.SIZE_NO_LIMIT; }, + + hasSettingsUI: function(){ return false; }, + + showSettingsUI: function(){ + throw new Error(this.declaredClass + + " does not support a storage settings user-interface"); + }, + + hideSettingsUI: function(){ + throw new Error(this.declaredClass + + " does not support a storage settings user-interface"); + }, + + _initStorage: function(){ + // we delay creating the tables until an operation is actually + // called so that we don't give a Gears dialog right on page + // load (bug #7538) + if (this._storageReady) { + return; + } + + if (!google.gears.factory.hasPermission) { + var siteName = null; + var icon = null; + var msg = 'This site would like to use Google Gears to enable ' + + 'enhanced functionality.'; + var allowed = google.gears.factory.getPermission(siteName, icon, msg); + if (!allowed) { + throw new Error('You must give permission to use Gears in order to ' + + 'store data'); + } + } + + // create the table that holds our data + try{ + dojox.sql("CREATE TABLE IF NOT EXISTS " + this.TABLE_NAME + "( " + + " namespace TEXT, " + + " key TEXT, " + + " value TEXT " + + ")" + ); + dojox.sql("CREATE UNIQUE INDEX IF NOT EXISTS namespace_key_index" + + " ON " + this.TABLE_NAME + + " (namespace, key)"); + }catch(e){ + console.debug("dojox.storage.GearsStorageProvider._createTables:", e); + throw new Error('Unable to create storage tables for Gears in ' + + 'Dojo Storage'); + } + + this._storageReady = true; + } + }); + + // register the existence of our storage providers + dojox.storage.manager.register("dojox.storage.GearsStorageProvider", + new dojox.storage.GearsStorageProvider()); + })(); +} + +}); diff --git a/js/dojo/dojox/storage/LocalStorageProvider.js b/js/dojo/dojox/storage/LocalStorageProvider.js new file mode 100644 index 0000000..d4a139b --- /dev/null +++ b/js/dojo/dojox/storage/LocalStorageProvider.js @@ -0,0 +1,209 @@ +//>>built +// wrapped by build app +define("dojox/storage/LocalStorageProvider", ["dijit","dojo","dojox","dojo/require!dojox/storage/Provider,dojox/storage/manager"], function(dijit,dojo,dojox){ +dojo.provide("dojox.storage.LocalStorageProvider"); + +dojo.require("dojox.storage.Provider"); +dojo.require("dojox.storage.manager"); + +dojo.declare( + "dojox.storage.LocalStorageProvider", + [dojox.storage.Provider], + { + store: null, + + initialize: function(){ + + this.store = localStorage; + + this.initialized = true; + dojox.storage.manager.loaded(); + }, + + isAvailable: function(){ /*Boolean*/ + return typeof localStorage != 'undefined'; + }, + + put: function( /*string*/ key, + /*object*/ value, + /*function*/ resultsHandler, + /*string?*/ namespace){ + + // TODO: Use the events as specified in http://dev.w3.org/html5/webstorage/#the-storage-event ? + // Currently, the storage event is not reliable around browsers. + + this._assertIsValidKey(key); + + namespace = namespace||this.DEFAULT_NAMESPACE; + this._assertIsValidNamespace(namespace); + + var fullKey = this.getFullKey(key,namespace); + + // prepending a prefix to a string value + // will result in that prefix not being + // usable as a value, so we better use + // toJson() always. + value = dojo.toJson(value); + + try { // ua may raise an QUOTA_EXCEEDED_ERR exception + this.store.setItem(fullKey,value); + + if(resultsHandler){ + resultsHandler(this.SUCCESS, key, null, namespace); + } + } catch(e) { + if(resultsHandler){ + resultsHandler(this.FAILED, key, e.toString(), namespace); + } + } + }, + + get: function(/*string*/ key, /*string?*/ namespace){ /*Object*/ + this._assertIsValidKey(key); + + namespace = namespace||this.DEFAULT_NAMESPACE; + this._assertIsValidNamespace(namespace); + + // get our full key name, which is namespace + key + key = this.getFullKey(key, namespace); + + return dojo.fromJson(this.store.getItem(key)); + }, + + getKeys: function(/*string?*/ namespace){ /*Array*/ + namespace = namespace||this.DEFAULT_NAMESPACE; + this._assertIsValidNamespace(namespace); + + namespace = '__'+namespace+'_' + + var keys = []; + for(var i = 0; i < this.store.length; i++){ + var currentKey = this.store.key(i); + if(this._beginsWith(currentKey,namespace)){ + currentKey = currentKey.substring(namespace.length); + keys.push(currentKey); + } + } + + return keys; + }, + + clear: function(/*string?*/ namespace){ + // Um, well, the 'specs' in Provider.js say that if + // no namespace is given, this method should nuke + // the *complete* storage. As other components might + // be using localStorage too, this might not be a + // good idea, so this method won't do it. + + namespace = namespace||this.DEFAULT_NAMESPACE; + this._assertIsValidNamespace(namespace); + + namespace = '__'+namespace+'_'; + + var keys = []; + for(var i = 0; i < this.store.length; i++){ + if(this._beginsWith(this.store.key(i),namespace)){ + keys.push(this.store.key(i)); + } + } + + dojo.forEach(keys, dojo.hitch(this.store, "removeItem")); + }, + + remove: function(/*string*/ key, /*string?*/ namespace){ + namespace = namespace||this.DEFAULT_NAMESPACE; + this._assertIsValidNamespace(namespace); + + this.store.removeItem(this.getFullKey(key, namespace)); + }, + + getNamespaces: function(){ /*string[]*/ + // There must be a better way than + // to execute a regex on *every* + // item in the store. + + var results = [ this.DEFAULT_NAMESPACE]; + + var found = {}; + found[this.DEFAULT_NAMESPACE] = true; + var tester = /^__([^_]*)_/; + + for(var i = 0; i < this.store.length; i++){ + var currentKey = this.store.key(i); + if(tester.test(currentKey) == true){ + var currentNS = currentKey.match(tester)[1]; + if(typeof found[currentNS] == "undefined"){ + found[currentNS] = true; + results.push(currentNS); + } + } + } + + return results; + }, + + isPermanent: function(){ /*Boolean*/ + return true; + }, + + getMaximumSize: function(){ /* mixed */ + return dojox.storage.SIZE_NO_LIMIT; + }, + + hasSettingsUI: function(){ /*Boolean*/ + return false; + }, + + isValidKey: function(/*string*/ keyName){ /*Boolean*/ + if(keyName === null || keyName === undefined){ + return false; + } + + return /^[0-9A-Za-z_-]*$/.test(keyName); + }, + + isValidNamespace: function(/*string*/ keyName){ /*Boolean*/ + // we *must* prevent namespaces from having + // underscores - else lookup of namespaces + // via RegEx (e.g. in getNamespaces ) would + // return wrong results. + // + // The only way around this would be to + // disallow underscores in keys. + + if(keyName === null || keyName === undefined){ + return false; + } + + return /^[0-9A-Za-z-]*$/.test(keyName); + }, + + getFullKey: function(key, namespace){ + // checks for valid namespace and + // key are already performed. + return "__" + namespace + "_" + key; + }, + + _beginsWith: function(/* string */ haystack, /* string */ needle) { + if(needle.length > haystack.length) { + return false; + } + return haystack.substring(0,needle.length) === needle; + }, + + _assertIsValidNamespace: function(/* string */ namespace){ + if(this.isValidNamespace(namespace) === false){ + throw new Error("Invalid namespace given: " + namespace); + } + }, + + _assertIsValidKey: function(/* string */ key){ + if(this.isValidKey(key) === false){ + throw new Error("Invalid key given: " + key); + } + } + } +); + +dojox.storage.manager.register("dojox.storage.LocalStorageProvider", new dojox.storage.LocalStorageProvider()); +}); diff --git a/js/dojo/dojox/storage/Provider.js b/js/dojo/dojox/storage/Provider.js new file mode 100644 index 0000000..1847e12 --- /dev/null +++ b/js/dojo/dojox/storage/Provider.js @@ -0,0 +1,345 @@ +//>>built +// wrapped by build app +define("dojox/storage/Provider", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.storage.Provider"); + +dojo.declare("dojox.storage.Provider", null, { + // summary: A singleton for working with dojox.storage. + // description: + // dojox.storage exposes the current available storage provider on this + // platform. It gives you methods such as dojox.storage.put(), + // dojox.storage.get(), etc. + // + // For more details on dojox.storage, see the primary documentation + // page at + // http://manual.dojotoolkit.org/storage.html + // + // Note for storage provider developers who are creating subclasses- + // This is the base class for all storage providers Specific kinds of + // Storage Providers should subclass this and implement these methods. + // You should avoid initialization in storage provider subclass's + // constructor; instead, perform initialization in your initialize() + // method. + constructor: function(){ + }, + + // SUCCESS: String + // Flag that indicates a put() call to a + // storage provider was succesful. + SUCCESS: "success", + + // FAILED: String + // Flag that indicates a put() call to + // a storage provider failed. + FAILED: "failed", + + // PENDING: String + // Flag that indicates a put() call to a + // storage provider is pending user approval. + PENDING: "pending", + + // SIZE_NOT_AVAILABLE: String + // Returned by getMaximumSize() if this storage provider can not determine + // the maximum amount of data it can support. + SIZE_NOT_AVAILABLE: "Size not available", + + // SIZE_NO_LIMIT: String + // Returned by getMaximumSize() if this storage provider has no theoretical + // limit on the amount of data it can store. + SIZE_NO_LIMIT: "No size limit", + + // DEFAULT_NAMESPACE: String + // The namespace for all storage operations. This is useful if several + // applications want access to the storage system from the same domain but + // want different storage silos. + DEFAULT_NAMESPACE: "default", + + // onHideSettingsUI: Function + // If a function is assigned to this property, then when the settings + // provider's UI is closed this function is called. Useful, for example, + // if the user has just cleared out all storage for this provider using + // the settings UI, and you want to update your UI. + onHideSettingsUI: null, + + initialize: function(){ + // summary: + // Allows this storage provider to initialize itself. This is + // called after the page has finished loading, so you can not do + // document.writes(). Storage Provider subclasses should initialize + // themselves inside of here rather than in their function + // constructor. + console.warn("dojox.storage.initialize not implemented"); + }, + + isAvailable: function(){ /*Boolean*/ + // summary: + // Returns whether this storage provider is available on this + // platform. + console.warn("dojox.storage.isAvailable not implemented"); + }, + + put: function( /*string*/ key, + /*object*/ value, + /*function*/ resultsHandler, + /*string?*/ namespace){ + // summary: + // Puts a key and value into this storage system. + // description: + // Example- + // var resultsHandler = function(status, key, message, namespace){ + // alert("status="+status+", key="+key+", message="+message); + // }; + // dojox.storage.put("test", "hello world", resultsHandler); + // + // Arguments: + // + // status - The status of the put operation, given by + // dojox.storage.FAILED, dojox.storage.SUCCEEDED, or + // dojox.storage.PENDING + // key - The key that was used for the put + // message - An optional message if there was an error or things failed. + // namespace - The namespace of the key. This comes at the end since + // it was added later. + // + // Important note: if you are using Dojo Storage in conjunction with + // Dojo Offline, then you don't need to provide + // a resultsHandler; this is because for Dojo Offline we + // use Google Gears to persist data, which has unlimited data + // once the user has given permission. If you are using Dojo + // Storage apart from Dojo Offline, then under the covers hidden + // Flash might be used, which is both asychronous and which might + // get denied; in this case you must provide a resultsHandler. + // key: + // A string key to use when retrieving this value in the future. + // value: + // A value to store; this can be any JavaScript type. + // resultsHandler: + // A callback function that will receive three arguments. The + // first argument is one of three values: dojox.storage.SUCCESS, + // dojox.storage.FAILED, or dojox.storage.PENDING; these values + // determine how the put request went. In some storage systems + // users can deny a storage request, resulting in a + // dojox.storage.FAILED, while in other storage systems a storage + // request must wait for user approval, resulting in a + // dojox.storage.PENDING status until the request is either + // approved or denied, resulting in another call back with + // dojox.storage.SUCCESS. + // The second argument in the call back is the key name that was being stored. + // The third argument in the call back is an optional message that + // details possible error messages that might have occurred during + // the storage process. + // namespace: + // Optional string namespace that this value will be placed into; + // if left off, the value will be placed into dojox.storage.DEFAULT_NAMESPACE + + console.warn("dojox.storage.put not implemented"); + }, + + get: function(/*string*/ key, /*string?*/ namespace){ /*Object*/ + // summary: + // Gets the value with the given key. Returns null if this key is + // not in the storage system. + // key: + // A string key to get the value of. + // namespace: + // Optional string namespace that this value will be retrieved from; + // if left off, the value will be retrieved from dojox.storage.DEFAULT_NAMESPACE + // return: Returns any JavaScript object type; null if the key is not present + console.warn("dojox.storage.get not implemented"); + }, + + hasKey: function(/*string*/ key, /*string?*/ namespace){ + // summary: Determines whether the storage has the given key. + return !!this.get(key, namespace); // Boolean + }, + + getKeys: function(/*string?*/ namespace){ /*Array*/ + // summary: Enumerates all of the available keys in this storage system. + // return: Array of available keys + console.warn("dojox.storage.getKeys not implemented"); + }, + + clear: function(/*string?*/ namespace){ + // summary: + // Completely clears this storage system of all of it's values and + // keys. If 'namespace' is provided just clears the keys in that + // namespace. + console.warn("dojox.storage.clear not implemented"); + }, + + remove: function(/*string*/ key, /*string?*/ namespace){ + // summary: Removes the given key from this storage system. + console.warn("dojox.storage.remove not implemented"); + }, + + getNamespaces: function(){ /*string[]*/ + console.warn("dojox.storage.getNamespaces not implemented"); + }, + + isPermanent: function(){ /*Boolean*/ + // summary: + // Returns whether this storage provider's values are persisted + // when this platform is shutdown. + console.warn("dojox.storage.isPermanent not implemented"); + }, + + getMaximumSize: function(){ /* mixed */ + // summary: The maximum storage allowed by this provider + // returns: + // Returns the maximum storage size + // supported by this provider, in + // thousands of bytes (i.e., if it + // returns 60 then this means that 60K + // of storage is supported). + // + // If this provider can not determine + // it's maximum size, then + // dojox.storage.SIZE_NOT_AVAILABLE is + // returned; if there is no theoretical + // limit on the amount of storage + // this provider can return, then + // dojox.storage.SIZE_NO_LIMIT is + // returned + console.warn("dojox.storage.getMaximumSize not implemented"); + }, + + putMultiple: function( /*array*/ keys, + /*array*/ values, + /*function*/ resultsHandler, + /*string?*/ namespace){ + // summary: + // Puts multiple keys and values into this storage system. + // description: + // Example- + // var resultsHandler = function(status, key, message){ + // alert("status="+status+", key="+key+", message="+message); + // }; + // dojox.storage.put(["test"], ["hello world"], resultsHandler); + // + // Important note: if you are using Dojo Storage in conjunction with + // Dojo Offline, then you don't need to provide + // a resultsHandler; this is because for Dojo Offline we + // use Google Gears to persist data, which has unlimited data + // once the user has given permission. If you are using Dojo + // Storage apart from Dojo Offline, then under the covers hidden + // Flash might be used, which is both asychronous and which might + // get denied; in this case you must provide a resultsHandler. + // keys: + // An array of string keys to use when retrieving this value in the future, + // one per value to be stored + // values: + // An array of values to store; this can be any JavaScript type, though the + // performance of plain strings is considerably better + // resultsHandler: + // A callback function that will receive three arguments. The + // first argument is one of three values: dojox.storage.SUCCESS, + // dojox.storage.FAILED, or dojox.storage.PENDING; these values + // determine how the put request went. In some storage systems + // users can deny a storage request, resulting in a + // dojox.storage.FAILED, while in other storage systems a storage + // request must wait for user approval, resulting in a + // dojox.storage.PENDING status until the request is either + // approved or denied, resulting in another call back with + // dojox.storage.SUCCESS. + // The second argument in the call back is the key name that was being stored. + // The third argument in the call back is an optional message that + // details possible error messages that might have occurred during + // the storage process. + // namespace: + // Optional string namespace that this value will be placed into; + // if left off, the value will be placed into dojox.storage.DEFAULT_NAMESPACE + + for(var i = 0; i < keys.length; i++){ + dojox.storage.put(keys[i], values[i], resultsHandler, namespace); + } + }, + + getMultiple: function(/*array*/ keys, /*string?*/ namespace){ /*Object*/ + // summary: + // Gets the valuse corresponding to each of the given keys. + // Returns a null array element for each given key that is + // not in the storage system. + // keys: + // An array of string keys to get the value of. + // namespace: + // Optional string namespace that this value will be retrieved from; + // if left off, the value will be retrieved from dojox.storage.DEFAULT_NAMESPACE + // return: Returns any JavaScript object type; null if the key is not present + + var results = []; + for(var i = 0; i < keys.length; i++){ + results.push(dojox.storage.get(keys[i], namespace)); + } + + return results; + }, + + removeMultiple: function(/*array*/ keys, /*string?*/ namespace) { + // summary: Removes the given keys from this storage system. + + for(var i = 0; i < keys.length; i++){ + dojox.storage.remove(keys[i], namespace); + } + }, + + isValidKeyArray: function( keys) { + if(keys === null || keys === undefined || !dojo.isArray(keys)){ + return false; + } + + // JAC: This could be optimized by running the key validity test + // directly over a joined string + return !dojo.some(keys, function(key){ + return !this.isValidKey(key); + }, this); // Boolean + }, + + hasSettingsUI: function(){ /*Boolean*/ + // summary: Determines whether this provider has a settings UI. + return false; + }, + + showSettingsUI: function(){ + // summary: If this provider has a settings UI, determined + // by calling hasSettingsUI(), it is shown. + console.warn("dojox.storage.showSettingsUI not implemented"); + }, + + hideSettingsUI: function(){ + // summary: If this provider has a settings UI, hides it. + console.warn("dojox.storage.hideSettingsUI not implemented"); + }, + + isValidKey: function(/*string*/ keyName){ /*Boolean*/ + // summary: + // Subclasses can call this to ensure that the key given is valid + // in a consistent way across different storage providers. We use + // the lowest common denominator for key values allowed: only + // letters, numbers, and underscores are allowed. No spaces. + if(keyName === null || keyName === undefined){ + return false; + } + + return /^[0-9A-Za-z_]*$/.test(keyName); + }, + + getResourceList: function(){ /* Array[] */ + // summary: + // Returns a list of URLs that this + // storage provider might depend on. + // description: + // This method returns a list of URLs that this + // storage provider depends on to do its work. + // This list is used by the Dojo Offline Toolkit + // to cache these resources to ensure the machinery + // used by this storage provider is available offline. + // What is returned is an array of URLs. + // Note that Dojo Offline uses Gears as its native + // storage provider, and does not support using other + // kinds of storage providers while offline anymore. + + return []; + } +}); + +}); diff --git a/js/dojo/dojox/storage/README b/js/dojo/dojox/storage/README new file mode 100644 index 0000000..7acf9b0 --- /dev/null +++ b/js/dojo/dojox/storage/README @@ -0,0 +1,115 @@ +------------------------------------------------------------------------------- +Dojo Storage +------------------------------------------------------------------------------- +Version X.XXX (does not have separate versioning -- versioned by release date) +Last Release date: January 2010 +------------------------------------------------------------------------------- +Project state: +experimental +------------------------------------------------------------------------------- +Credits + Brad Neuberg + Alex Russell + +LocalStorage, BehaviorStorage, CookieStorage: + Jens Arps +------------------------------------------------------------------------------- +Project description + +dojox.storage provides a JavaScript abstraction for persistent storage +as well as pluggable implementations which typically use native browser +extensions (e.g. Flash player, Gears) + +------------------------------------------------------------------------------- +Dependencies: + +FlashStorageProvider requires the Flash player +GearsStorageProvider requires the Gears extension +LocalStorageProvider does not require any plugins but will run only in certain +browsers (see below) +BehaviorStorageProvider does not require any plugins but will run only in IE 5+ +CookieStorageProvider has no requirements +The various Air*StorageProviders require Adobe's AIR software + +The open source mtasc compiler (www.mtasc.org) is needed to build the +ActionScript into SWF format. The SWF object is maintained within svn, so +this step is only necessary if Storage.as is modified. A sample build script +is provided (buildFlashStorage.sh) + +------------------------------------------------------------------------------- +Documentation + +See http://dojotoolkit.org/reference-guide/dojox/storage.html for Dojo Storage docs. + +See dojox/storage/demos/helloworld.html for a simple Hello World example +you can base your code off of. + +------------------------------------------------------------------------------- +Installation instructions + +If you want to use Dojo Storage in a web browser: + +These installation instructions are to use Dojo Storage in a web browser; at +runtime, Dojo Storage will autodetect and use the best available storage +option. This includes: + + * localStorage: HTML 5 Web Browsers (Firefox 3+, Safari 4+, IE 8+) + * Google Gears (Plugin) + * globalStorage (Firefox 2+) + * userData Behavior (IE 5+) + * Hidden Flash (Plugin) + * Cookies + +To have access to Dojo Storage, require "dojox.storage": + +dojo.require("dojox.storage"); + + +If you want to use Dojo Storage with Adobe AIR: + +[TBD! Why don't you write this and contribute!] + +------------------------------------------------------------------------------- +Additional Notes + + +STORAGE TABLE +------------- + + +Browser Used StorageProvider, in order of preference + +IE 6 / IE7 GearsStorageProvider + FlashStorageProvider + BehaviorStorageProvider + CookieStorageProvider + +IE 8 LocalStorageProvider + GearsStorageProvider + FlashStorageProvider + BehaviorStorageProvider + CookieStorageProvider + +Safari 3 FlashStorageProvider + CookieStorageProvider + +Safari 4 LocalStorageProvider + FlashStorageProvider + CookieStorageProvider + +Chromium 4 FlashStorageProvider +(Mac OS) CookieStorageProvider + +Firefox 2 WhatWGStorageProvider (= globalStorage) + GearsStorageProvider + FlashStorageProvider + CookieStorageProvider + +Firefox 3 LocalStorageProvider + GearsStorageProvider + FlashStorageProvider + CookieStorageProvider + +Opera 10 LocalStorageProvider + FlashStorageProvider + CookieStorageProvider diff --git a/js/dojo/dojox/storage/Storage.as b/js/dojo/dojox/storage/Storage.as new file mode 100644 index 0000000..89ec67d --- /dev/null +++ b/js/dojo/dojox/storage/Storage.as @@ -0,0 +1,402 @@ +import DojoExternalInterface; + +class Storage{ + public static var SUCCESS = "success"; + public static var FAILED = "failed"; + public static var PENDING = "pending"; + + // Wait the following number of milliseconds before flushing + public static var FLUSH_DELAY_DEFAULT = 500; + + public var flush_delay; + public var so; + public var timer; + + private var _NAMESPACE_KEY = "allNamespaces"; + + public function Storage(){ + flush_delay = Storage.FLUSH_DELAY_DEFAULT; + + DojoExternalInterface.initialize(); + DojoExternalInterface.addCallback("put", this, put); + DojoExternalInterface.addCallback("putMultiple", this, putMultiple); + DojoExternalInterface.addCallback("get", this, get); + DojoExternalInterface.addCallback("getMultiple", this, getMultiple); + DojoExternalInterface.addCallback("showSettings", this, showSettings); + DojoExternalInterface.addCallback("clear", this, clear); + DojoExternalInterface.addCallback("getKeys", this, getKeys); + DojoExternalInterface.addCallback("getNamespaces", this, getNamespaces); + DojoExternalInterface.addCallback("remove", this, remove); + DojoExternalInterface.addCallback("removeMultiple", this, removeMultiple); + DojoExternalInterface.addCallback("flush", this, flush); + DojoExternalInterface.addCallback("setFlushDelay", this, setFlushDelay); + DojoExternalInterface.addCallback("getFlushDelay", this, getFlushDelay); + DojoExternalInterface.loaded(); + + // preload the System Settings finished button movie for offline + // access so it is in the cache + _root.createEmptyMovieClip("_settingsBackground", 1); + _root._settingsBackground.loadMovie(DojoExternalInterface.dojoPath + + "../dojox/storage/storage_dialog.swf"); + } + + // FIXME: Whoever added this Flush code did not document why it + // exists. Please also put your name and a bug number so I know + // who to contact. -- Brad Neuberg + + // Set a new value for the flush delay timer. + // Possible values: + // 0 : Perform the flush synchronously after each "put" request + // > 0 : Wait until 'newDelay' ms have passed without any "put" request to flush + // -1 : Do not automatically flush + public function setFlushDelay(newDelay){ + flush_delay = Number(newDelay); + } + + public function getFlushDelay(){ + return String(flush_delay); + } + + public function flush(namespace){ + if(timer){ + _global.clearTimeout(timer); + delete timer; + } + + var so = SharedObject.getLocal(namespace); + var flushResults = so.flush(); + + // return results of this command to JavaScript + var statusResults; + if(flushResults == true){ + statusResults = Storage.SUCCESS; + }else if(flushResults == "pending"){ + statusResults = Storage.PENDING; + }else{ + statusResults = Storage.FAILED; + } + + DojoExternalInterface.call("dojox.storage._onStatus", statusResults, + null, namespace); + } + + public function put(keyName, keyValue, namespace){ + // Get the SharedObject for these values and save it + so = SharedObject.getLocal(namespace); + + // Save the key and value + so.data[keyName] = keyValue; + + // Save the namespace + // FIXME: Tie this into the flush/no-flush stuff below; right now + // we immediately write out this namespace. -- Brad Neuberg + addNamespace(namespace, keyName); + + // Do all the flush/no-flush stuff + var keyNames = new Array(); + keyNames[0] = keyName; + postWrite(so, keyNames, namespace); + } + + public function putMultiple(metaKey, metaValue, metaLengths, namespace){ + // Get the SharedObject for these values and save it + so = SharedObject.getLocal(namespace); + + // Create array of keys and value lengths + var keys = metaKey.split(","); + var lengths = metaLengths.split(","); + + // Loop through the array and write the values + for(var i = 0; i < keys.length; i++){ + so.data[keys[i]] = metaValue.slice(0,lengths[i]); + metaValue = metaValue.slice(lengths[i]); + } + + // Save the namespace + // FIXME: Tie this into the flush/no-flush stuff below; right now + // we immediately write out this namespace. -- Brad Neuberg + addNamespace(namespace, null); + + // Do all the flush/no-flush stuff + postWrite(so, keys, namespace); + } + + public function postWrite(so, keyNames, namespace){ + // TODO: Review all this 'handler' stuff. In particular, the flush + // could now be with keys pending from several different requests, not + // only the ones passed in this method call + + // prepare a storage status handler + var self = this; + so.onStatus = function(infoObject:Object){ + //trace("onStatus, infoObject="+infoObject.code); + + // delete the data value if the request was denied + if(infoObject.code == "SharedObject.Flush.Failed"){ + for(var i=0;i<keyNames.length;i++){ + delete self.so.data[keyNames[i]]; + } + } + + var statusResults; + if(infoObject.code == "SharedObject.Flush.Failed"){ + statusResults = Storage.FAILED; + }else if(infoObject.code == "SharedObject.Flush.Pending"){ + statusResults = Storage.PENDING; + }else if(infoObject.code == "SharedObject.Flush.Success"){ + // if we have succeeded saving our value, see if we + // need to update our list of namespaces + if(self.hasNamespace(namespace) == true){ + statusResults = Storage.SUCCESS; + }else{ + // we have a new namespace we must store + self.addNamespace(namespace, keyNames[0]); + return; + } + } + //trace("onStatus, statusResults="+statusResults); + + // give the status results to JavaScript + DojoExternalInterface.call("dojox.storage._onStatus", statusResults, + keyNames[0], namespace); + } + + // Clear any pending flush timers + if(timer){ + _global.clearTimeout(timer); + } + + // If we have a flush delay set, set a timer for its execution + if(flush_delay > 0){ + timer = _global.setTimeout(flush, flush_delay, namespace); + // With a flush_delay value of 0, execute the flush request synchronously + }else if(flush_delay == 0){ + flush(namespace); + } + // Otherwise just don't flush - will be probably be flushed manually + } + + public function get(keyName, namespace){ + // Get the SharedObject for these values and save it + so = SharedObject.getLocal(namespace); + var results = so.data[keyName]; + + return results; + } + + // Returns an array with the contents of each key value on the metaKeys array + public function getMultiple(metaKeys, namespace){ + // get the storage object + so = SharedObject.getLocal(namespace); + + // Create array of keys to read + var keys = metaKeys.split(","); + var results = new Array(); + + // Read from storage into results array + for(var i = 0;i < keys.length;i++){ + var val = so.data[keys[i]]; + val = val.split("\\").join("\\\\"); + val = val.split('"').join('\\"'); + results.push( val); + } + + // Make the results array into a string + var metaResults = '["' + results.join('","') + '"]'; + + return metaResults; + } + + public function showSettings(){ + // Show the configuration options for the Flash player, opened to the + // section for local storage controls (pane 1) + System.showSettings(1); + + // there is no way we can intercept when the Close button is pressed, allowing us + // to hide the Flash dialog. Instead, we need to load a movie in the + // background that we can show a close button on. + _root.createEmptyMovieClip("_settingsBackground", 1); + _root._settingsBackground.loadMovie(DojoExternalInterface.dojoPath + + "../dojox/storage/storage_dialog.swf"); + } + + public function clear(namespace){ + so = SharedObject.getLocal(namespace); + so.clear(); + so.flush(); + + // remove this namespace entry now + removeNamespace(namespace); + } + + public function getKeys(namespace) : String{ + // Returns a list of the available keys in this namespace + + // get the storage object + so = SharedObject.getLocal(namespace); + // get all of the keys + var results = []; + for(var i in so.data){ + results.push(i); + } + + // remove our key that records our list of namespaces + for(var i = 0; i < results.length; i++){ + if(results[i] == _NAMESPACE_KEY){ + results.splice(i, 1); + break; + } + } + + // a bug in ExternalInterface transforms Arrays into + // Strings, so we can't use those here! -- BradNeuberg + results = results.join(","); + + return results; + } + + public function getNamespaces() : String{ + var allNamespaces = SharedObject.getLocal(_NAMESPACE_KEY); + var results = []; + + for(var i in allNamespaces.data){ + results.push(i); + } + + // a bug in ExternalInterface transforms Arrays into + // Strings, so we can use those here! -- BradNeuberg + results = results.join(","); + + return results; + } + + public function remove(keyName, namespace){ + // Removes a key + + // get the storage object + so = SharedObject.getLocal(namespace); + + // delete this value + delete so.data[keyName]; + + // save the changes + so.flush(); + + // see if we are the last entry for this namespace + var availableKeys = getKeys(namespace); + if(availableKeys == ""){ + // we are empty + removeNamespace(namespace); + } + } + + // Removes all the values for each keys on the metaKeys array + public function removeMultiple(metaKeys, namespace){ + // get the storage object + so = SharedObject.getLocal(namespace); + + // Create array of keys to read + var keys = metaKeys.split(","); + var results = new Array(); + + // Delete elements + for(var i=0;i<keys.length;i++){ + delete so.data[keys[i]]; + } + + // see if there are no more entries for this namespace + var availableKeys = getKeys(namespace); + if(availableKeys == ""){ + // we are empty + removeNamespace(namespace); + } + } + + private function hasNamespace(namespace):Boolean{ + // Get the SharedObject for the namespace list + var allNamespaces = SharedObject.getLocal(_NAMESPACE_KEY); + + var results = false; + for(var i in allNamespaces.data){ + if(i == namespace){ + results = true; + break; + } + } + + return results; + } + + // FIXME: This code has gotten ugly -- refactor + private function addNamespace(namespace, keyName){ + if(hasNamespace(namespace) == true){ + return; + } + + // Get the SharedObject for the namespace list + var allNamespaces = SharedObject.getLocal(_NAMESPACE_KEY); + + // prepare a storage status handler if the keyName is + // not null + if(keyName != null && typeof keyName != "undefined"){ + var self = this; + allNamespaces.onStatus = function(infoObject:Object){ + // delete the data value if the request was denied + if(infoObject.code == "SharedObject.Flush.Failed"){ + delete self.so.data[keyName]; + } + + var statusResults; + if(infoObject.code == "SharedObject.Flush.Failed"){ + statusResults = Storage.FAILED; + }else if(infoObject.code == "SharedObject.Flush.Pending"){ + statusResults = Storage.PENDING; + }else if(infoObject.code == "SharedObject.Flush.Success"){ + statusResults = Storage.SUCCESS; + } + + // give the status results to JavaScript + DojoExternalInterface.call("dojox.storage._onStatus", statusResults, + keyName, namespace); + } + } + + // save the namespace list + allNamespaces.data[namespace] = true; + var flushResults = allNamespaces.flush(); + + // return results of this command to JavaScript + if(keyName != null && typeof keyName != "undefined"){ + var statusResults; + if(flushResults == true){ + statusResults = Storage.SUCCESS; + }else if(flushResults == "pending"){ + statusResults = Storage.PENDING; + }else{ + statusResults = Storage.FAILED; + } + + DojoExternalInterface.call("dojox.storage._onStatus", statusResults, + keyName, namespace); + } + } + + // FIXME: This code has gotten ugly -- refactor + private function removeNamespace(namespace){ + if(hasNamespace(namespace) == false){ + return; + } + + // try to save the namespace list; don't have a return + // callback; if we fail on this, the worst that will happen + // is that we have a spurious namespace entry + var allNamespaces = SharedObject.getLocal(_NAMESPACE_KEY); + delete allNamespaces.data[namespace]; + allNamespaces.flush(); + } + + static function main(mc){ + _root.app = new Storage(); + } +} + diff --git a/js/dojo/dojox/storage/Storage.swf b/js/dojo/dojox/storage/Storage.swf Binary files differnew file mode 100644 index 0000000..9a09f70 --- /dev/null +++ b/js/dojo/dojox/storage/Storage.swf diff --git a/js/dojo/dojox/storage/WhatWGStorageProvider.js b/js/dojo/dojox/storage/WhatWGStorageProvider.js new file mode 100644 index 0000000..61b9e57 --- /dev/null +++ b/js/dojo/dojox/storage/WhatWGStorageProvider.js @@ -0,0 +1,277 @@ +//>>built +// wrapped by build app +define("dojox/storage/WhatWGStorageProvider", ["dijit","dojo","dojox","dojo/require!dojox/storage/Provider,dojox/storage/manager"], function(dijit,dojo,dojox){ +dojo.provide("dojox.storage.WhatWGStorageProvider"); +dojo.require("dojox.storage.Provider"); +dojo.require("dojox.storage.manager"); + +dojo.declare("dojox.storage.WhatWGStorageProvider", [ dojox.storage.Provider ], { + // summary: + // Storage provider that uses WHAT Working Group features in Firefox 2 + // to achieve permanent storage. + // description: + // The WHAT WG storage API is documented at + // http://www.whatwg.org/specs/web-apps/current-work/#scs-client-side + // + // You can disable this storage provider with the following djConfig + // variable: + // var djConfig = { disableWhatWGStorage: true }; + // + // Authors of this storage provider- + // JB Boisseau, jb.boisseau@eutech-ssii.com + // Brad Neuberg, bkn3@columbia.edu + + initialized: false, + + _domain: null, + _available: null, + _statusHandler: null, + _allNamespaces: null, + _storageEventListener: null, + + initialize: function(){ + if(dojo.config["disableWhatWGStorage"] == true){ + return; + } + + // get current domain + this._domain = location.hostname; + // console.debug(this._domain); + + // indicate that this storage provider is now loaded + this.initialized = true; + dojox.storage.manager.loaded(); + }, + + isAvailable: function(){ + try{ + var myStorage = globalStorage[location.hostname]; + }catch(e){ + this._available = false; + return this._available; + } + + this._available = true; + return this._available; + }, + + put: function(key, value, resultsHandler, namespace){ + if(this.isValidKey(key) == false){ + throw new Error("Invalid key given: " + key); + } + namespace = namespace||this.DEFAULT_NAMESPACE; + + // get our full key name, which is namespace + key + key = this.getFullKey(key, namespace); + + this._statusHandler = resultsHandler; + + // serialize the value; + // handle strings differently so they have better performance + if(dojo.isString(value)){ + value = "string:" + value; + }else{ + value = dojo.toJson(value); + } + + // register for successful storage events. + var storageListener = dojo.hitch(this, function(evt){ + // remove any old storage event listener we might have added + // to the window on old put() requests; Firefox has a bug + // where it can occassionaly go into infinite loops calling + // our storage event listener over and over -- this is a + // workaround + // FIXME: Simplify this into a test case and submit it + // to Firefox + window.removeEventListener("storage", storageListener, false); + + // indicate we succeeded + if(resultsHandler){ + resultsHandler.call(null, this.SUCCESS, key, null, namespace); + } + }); + + window.addEventListener("storage", storageListener, false); + + // try to store the value + try{ + var myStorage = globalStorage[this._domain]; + myStorage.setItem(key, value); + }catch(e){ + // indicate we failed + this._statusHandler.call(null, this.FAILED, key, e.toString(), namespace); + } + }, + + get: function(key, namespace){ + if(this.isValidKey(key) == false){ + throw new Error("Invalid key given: " + key); + } + namespace = namespace||this.DEFAULT_NAMESPACE; + + // get our full key name, which is namespace + key + key = this.getFullKey(key, namespace); + + // sometimes, even if a key doesn't exist, Firefox + // will return a blank string instead of a null -- + // this _might_ be due to having underscores in the + // keyname, but I am not sure. + + // FIXME: Simplify this bug into a testcase and + // submit it to Firefox + var myStorage = globalStorage[this._domain]; + var results = myStorage.getItem(key); + + if(results == null || results == ""){ + return null; + } + + results = results.value; + + // destringify the content back into a + // real JavaScript object; + // handle strings differently so they have better performance + if(dojo.isString(results) && (/^string:/.test(results))){ + results = results.substring("string:".length); + }else{ + results = dojo.fromJson(results); + } + + return results; + }, + + getNamespaces: function(){ + var results = [ this.DEFAULT_NAMESPACE ]; + + // simply enumerate through our array and save any string + // that starts with __ + var found = {}; + var myStorage = globalStorage[this._domain]; + var tester = /^__([^_]*)_/; + for(var i = 0; i < myStorage.length; i++){ + var currentKey = myStorage.key(i); + if(tester.test(currentKey) == true){ + var currentNS = currentKey.match(tester)[1]; + // have we seen this namespace before? + if(typeof found[currentNS] == "undefined"){ + found[currentNS] = true; + results.push(currentNS); + } + } + } + + return results; + }, + + getKeys: function(namespace){ + namespace = namespace||this.DEFAULT_NAMESPACE; + + if(this.isValidKey(namespace) == false){ + throw new Error("Invalid namespace given: " + namespace); + } + + // create a regular expression to test the beginning + // of our key names to see if they match our namespace; + // if it is the default namespace then test for the presence + // of no namespace for compatibility with older versions + // of dojox.storage + var namespaceTester; + if(namespace == this.DEFAULT_NAMESPACE){ + namespaceTester = new RegExp("^([^_]{2}.*)$"); + }else{ + namespaceTester = new RegExp("^__" + namespace + "_(.*)$"); + } + + var myStorage = globalStorage[this._domain]; + var keysArray = []; + for(var i = 0; i < myStorage.length; i++){ + var currentKey = myStorage.key(i); + if(namespaceTester.test(currentKey) == true){ + // strip off the namespace portion + currentKey = currentKey.match(namespaceTester)[1]; + keysArray.push(currentKey); + } + } + + return keysArray; + }, + + clear: function(namespace){ + namespace = namespace||this.DEFAULT_NAMESPACE; + + if(this.isValidKey(namespace) == false){ + throw new Error("Invalid namespace given: " + namespace); + } + + // create a regular expression to test the beginning + // of our key names to see if they match our namespace; + // if it is the default namespace then test for the presence + // of no namespace for compatibility with older versions + // of dojox.storage + var namespaceTester; + if(namespace == this.DEFAULT_NAMESPACE){ + namespaceTester = new RegExp("^[^_]{2}"); + }else{ + namespaceTester = new RegExp("^__" + namespace + "_"); + } + + var myStorage = globalStorage[this._domain]; + var keys = []; + for(var i = 0; i < myStorage.length; i++){ + if(namespaceTester.test(myStorage.key(i)) == true){ + keys[keys.length] = myStorage.key(i); + } + } + + dojo.forEach(keys, dojo.hitch(myStorage, "removeItem")); + }, + + remove: function(key, namespace){ + // get our full key name, which is namespace + key + key = this.getFullKey(key, namespace); + + var myStorage = globalStorage[this._domain]; + myStorage.removeItem(key); + }, + + isPermanent: function(){ + return true; + }, + + getMaximumSize: function(){ + return this.SIZE_NO_LIMIT; + }, + + hasSettingsUI: function(){ + return false; + }, + + showSettingsUI: function(){ + throw new Error(this.declaredClass + " does not support a storage settings user-interface"); + }, + + hideSettingsUI: function(){ + throw new Error(this.declaredClass + " does not support a storage settings user-interface"); + }, + + getFullKey: function(key, namespace){ + namespace = namespace||this.DEFAULT_NAMESPACE; + + if(this.isValidKey(namespace) == false){ + throw new Error("Invalid namespace given: " + namespace); + } + + // don't append a namespace string for the default namespace, + // for compatibility with older versions of dojox.storage + if(namespace == this.DEFAULT_NAMESPACE){ + return key; + }else{ + return "__" + namespace + "_" + key; + } + } +}); + +dojox.storage.manager.register("dojox.storage.WhatWGStorageProvider", + new dojox.storage.WhatWGStorageProvider()); + +}); diff --git a/js/dojo/dojox/storage/_common.js b/js/dojo/dojox/storage/_common.js new file mode 100644 index 0000000..0c766ca --- /dev/null +++ b/js/dojo/dojox/storage/_common.js @@ -0,0 +1,24 @@ +//>>built +// wrapped by build app +define("dojox/storage/_common", ["dijit","dojo","dojox","dojo/require!dojox/storage/Provider,dojox/storage/manager,dojox/storage/LocalStorageProvider,dojox/storage/GearsStorageProvider,dojox/storage/WhatWGStorageProvider,dojox/storage/FlashStorageProvider,dojox/storage/BehaviorStorageProvider,dojox/storage/CookieStorageProvider"], function(dijit,dojo,dojox){ +dojo.provide("dojox.storage._common"); +dojo.require("dojox.storage.Provider"); +dojo.require("dojox.storage.manager"); + +/* + Note: if you are doing Dojo Offline builds you _must_ + have offlineProfile=true when you run the build script: + ./build.sh action=release profile=offline offlineProfile=true +*/ +dojo.require("dojox.storage.LocalStorageProvider"); +dojo.require("dojox.storage.GearsStorageProvider"); +dojo.require("dojox.storage.WhatWGStorageProvider"); +dojo.require("dojox.storage.FlashStorageProvider"); +dojo.require("dojox.storage.BehaviorStorageProvider"); +dojo.require("dojox.storage.CookieStorageProvider"); + +// now that we are loaded and registered tell the storage manager to +// initialize itself +dojox.storage.manager.initialize(); + +}); diff --git a/js/dojo/dojox/storage/buildFlashStorage.sh b/js/dojo/dojox/storage/buildFlashStorage.sh new file mode 100644 index 0000000..892dca1 --- /dev/null +++ b/js/dojo/dojox/storage/buildFlashStorage.sh @@ -0,0 +1,4 @@ +#!/bin/sh +# TODO: FIXME: Get rid of this and hook it into Dojo's general build script +# You must have mtasc to run this +mtasc -trace DojoExternalInterface.trace -main -cp ../flash -swf Storage.swf -version 8 -header 215:138:10 Storage.as diff --git a/js/dojo/dojox/storage/demos/helloworld.html b/js/dojo/dojox/storage/demos/helloworld.html new file mode 100644 index 0000000..44fd739 --- /dev/null +++ b/js/dojo/dojox/storage/demos/helloworld.html @@ -0,0 +1,90 @@ +<html> + <head> + <script src="../../../dojo/dojo.js"></script> + <script src="../storage-browser.js"></script> + + <script> + dojo.require("dojox.storage"); + + function runDemo(){ + // setup event handlers + dojo.byId("saveButton").onclick = saveValue; + + // write out what our storage provider is for debugging + dojo.byId("currentProvider").innerHTML = + dojox.storage.manager.currentProvider.declaredClass; + + loadValues(); + } + + function loadValues(){ + // get any values that were saved before and write them into the page + var results = dojox.storage.get("myValues"); + + if(results){ + var printMe = "<ul>"; + for(var i = 0; i < results.length; i++){ + printMe += "<li>" + results[i] + "</li>"; + } + printMe += "</ul>"; + dojo.byId("allValues").innerHTML = printMe; + } + } + + function saveValue(){ + var value = dojo.byId("saveValue").value; + if(value == undefined || value === ""){ + alert("Please enter a correct value"); + return; + } + + // get the old values first, since we are saving everything + // as one key + var results = dojox.storage.get("myValues"); + if(!results){ + results = new Array(); + } + + // add new value + results.push(value); + + dojox.storage.put("myValues", results, function(status, keyName){ + if(status == dojox.storage.FAILED){ + alert("You do not have permission to store data for this web site."); + }else if(status == dojox.storage.SUCCESS){ + loadValues(); + } + }); + } + + // wait until the storage system is finished loading + if(!dojox.storage.manager.isInitialized()){ + dojo.connect(dojox.storage.manager, "loaded", runDemo); + }else{ + dojo.connect(dojo, "loaded", runDemo); + } + </script> + </head> + + <body> + <h1>Dojo Storage Hello World</h1> + + <p>Simple Dojo Storage example. Enter values below to have them + persisted in Dojo Storage; refresh browser page or close browser + and then return to this page to see the values again. Note that + Dojo Storage will not work from file:// URLs.</p> + + <h2>Save Values:</h2> + <div> + <input id="saveValue" type="text"></input> + <button id="saveButton">Save Value</button> + </div> + + <h2>All Saved Values:</h2> + <p id="allValues"></p> + + <p>Using Dojo Storage Provider (autodetected): + <span id="currentProvider"></span> + <p> + </body> +</html>
\ No newline at end of file diff --git a/js/dojo/dojox/storage/manager.js b/js/dojo/dojox/storage/manager.js new file mode 100644 index 0000000..5d47296 --- /dev/null +++ b/js/dojo/dojox/storage/manager.js @@ -0,0 +1,262 @@ +//>>built +// wrapped by build app +define("dojox/storage/manager", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.storage.manager"); +//dojo.require("dojo.AdapterRegistry"); +// FIXME: refactor this to use an AdapterRegistry + +dojox.storage.manager = new function(){ + // summary: A singleton class in charge of the dojox.storage system + // description: + // Initializes the storage systems and figures out the best available + // storage options on this platform. + + // currentProvider: Object + // The storage provider that was automagically chosen to do storage + // on this platform, such as dojox.storage.FlashStorageProvider. + this.currentProvider = null; + + // available: Boolean + // Whether storage of some kind is available. + this.available = false; + + // providers: Array + // Array of all the static provider instances, useful if you want to + // loop through and see what providers have been registered. + this.providers = []; + + this._initialized = false; + + this._onLoadListeners = []; + + this.initialize = function(){ + // summary: + // Initializes the storage system and autodetects the best storage + // provider we can provide on this platform + this.autodetect(); + }; + + this.register = function(/*string*/ name, /*Object*/ instance){ + // summary: + // Registers the existence of a new storage provider; used by + // subclasses to inform the manager of their existence. The + // storage manager will select storage providers based on + // their ordering, so the order in which you call this method + // matters. + // name: + // The full class name of this provider, such as + // "dojox.storage.FlashStorageProvider". + // instance: + // An instance of this provider, which we will use to call + // isAvailable() on. + + // keep list of providers as a list so that we can know what order + // storage providers are preferred; also, store the providers hashed + // by name in case someone wants to get a provider that uses + // a particular storage backend + this.providers.push(instance); + this.providers[name] = instance; + }; + + this.setProvider = function(storageClass){ + // summary: + // Instructs the storageManager to use the given storage class for + // all storage requests. + // description: + // Example- + // dojox.storage.setProvider( + // dojox.storage.IEStorageProvider) + + }; + + this.autodetect = function(){ + // summary: + // Autodetects the best possible persistent storage provider + // available on this platform. + + //console.debug("dojox.storage.manager.autodetect"); + + if(this._initialized){ // already finished + return; + } + + // a flag to force the storage manager to use a particular + // storage provider type, such as + // djConfig = {forceStorageProvider: "dojox.storage.WhatWGStorageProvider"}; + var forceProvider = dojo.config["forceStorageProvider"] || false; + + // go through each provider, seeing if it can be used + var providerToUse; + //FIXME: use dojo.some + for(var i = 0; i < this.providers.length; i++){ + providerToUse = this.providers[i]; + if(forceProvider && forceProvider == providerToUse.declaredClass){ + // still call isAvailable for this provider, since this helps some + // providers internally figure out if they are available + // FIXME: This should be refactored since it is non-intuitive + // that isAvailable() would initialize some state + providerToUse.isAvailable(); + break; + }else if(!forceProvider && providerToUse.isAvailable()){ + break; + } + } + + if(!providerToUse){ // no provider available + this._initialized = true; + this.available = false; + this.currentProvider = null; + console.warn("No storage provider found for this platform"); + this.loaded(); + return; + } + + // create this provider and mix in it's properties + // so that developers can do dojox.storage.put rather + // than dojox.storage.currentProvider.put, for example + this.currentProvider = providerToUse; + dojo.mixin(dojox.storage, this.currentProvider); + + // have the provider initialize itself + dojox.storage.initialize(); + + this._initialized = true; + this.available = true; + }; + + this.isAvailable = function(){ /*Boolean*/ + // summary: Returns whether any storage options are available. + return this.available; + }; + + this.addOnLoad = function(func){ /* void */ + // summary: + // Adds an onload listener to know when Dojo Offline can be used. + // description: + // Adds a listener to know when Dojo Offline can be used. This + // ensures that the Dojo Offline framework is loaded and that the + // local dojox.storage system is ready to be used. This method is + // useful if you don't want to have a dependency on Dojo Events + // when using dojox.storage. + // func: Function + // A function to call when Dojo Offline is ready to go + this._onLoadListeners.push(func); + + if(this.isInitialized()){ + this._fireLoaded(); + } + }; + + this.removeOnLoad = function(func){ /* void */ + // summary: Removes the given onLoad listener + for(var i = 0; i < this._onLoadListeners.length; i++){ + if(func == this._onLoadListeners[i]){ + this._onLoadListeners.splice(i, 1); + break; + } + } + }; + + this.isInitialized = function(){ /*Boolean*/ + // summary: + // Returns whether the storage system is initialized and ready to + // be used. + + // FIXME: This should REALLY not be in here, but it fixes a tricky + // Flash timing bug. + // Confirm that this is still needed with the newly refactored Dojo + // Flash. Used to be for Internet Explorer. -- Brad Neuberg + if(this.currentProvider != null + && this.currentProvider.declaredClass == "dojox.storage.FlashStorageProvider" + && dojox.flash.ready == false){ + return false; + }else{ + return this._initialized; + } + }; + + this.supportsProvider = function(/*string*/ storageClass){ /* Boolean */ + // summary: Determines if this platform supports the given storage provider. + // description: + // Example- + // dojox.storage.manager.supportsProvider( + // "dojox.storage.InternetExplorerStorageProvider"); + + // construct this class dynamically + try{ + // dynamically call the given providers class level isAvailable() + // method + var provider = eval("new " + storageClass + "()"); + var results = provider.isAvailable(); + if(!results){ return false; } + return results; + }catch(e){ + return false; + } + }; + + this.getProvider = function(){ /* Object */ + // summary: Gets the current provider + return this.currentProvider; + }; + + this.loaded = function(){ + // summary: + // The storage provider should call this method when it is loaded + // and ready to be used. Clients who will use the provider will + // connect to this method to know when they can use the storage + // system. You can either use dojo.connect to connect to this + // function, or can use dojox.storage.manager.addOnLoad() to add + // a listener that does not depend on the dojo.event package. + // description: + // Example 1- + // if(dojox.storage.manager.isInitialized() == false){ + // dojo.connect(dojox.storage.manager, "loaded", TestStorage, "initialize"); + // }else{ + // dojo.connect(dojo, "loaded", TestStorage, "initialize"); + // } + // Example 2- + // dojox.storage.manager.addOnLoad(someFunction); + + + // FIXME: we should just provide a Deferred for this. That way you + // don't care when this happens or has happened. Deferreds are in Base + this._fireLoaded(); + }; + + this._fireLoaded = function(){ + //console.debug("dojox.storage.manager._fireLoaded"); + + dojo.forEach(this._onLoadListeners, function(i){ + try{ + i(); + }catch(e){ console.debug(e); } + }); + }; + + this.getResourceList = function(){ + // summary: + // Returns a list of whatever resources are necessary for storage + // providers to work. + // description: + // This will return all files needed by all storage providers for + // this particular environment type. For example, if we are in the + // browser environment, then this will return the hidden SWF files + // needed by the FlashStorageProvider, even if we don't need them + // for the particular browser we are working within. This is meant + // to faciliate Dojo Offline, which must retrieve all resources we + // need offline into the offline cache -- we retrieve everything + // needed, in case another browser that requires different storage + // mechanisms hits the local offline cache. For example, if we + // were to sync against Dojo Offline on Firefox 2, then we would + // not grab the FlashStorageProvider resources needed for Safari. + var results = []; + dojo.forEach(dojox.storage.manager.providers, function(currentProvider){ + results = results.concat(currentProvider.getResourceList()); + }); + + return results; + } +}; + +}); diff --git a/js/dojo/dojox/storage/storage_dialog.fla b/js/dojo/dojox/storage/storage_dialog.fla Binary files differnew file mode 100644 index 0000000..8e9a093 --- /dev/null +++ b/js/dojo/dojox/storage/storage_dialog.fla diff --git a/js/dojo/dojox/storage/storage_dialog.swf b/js/dojo/dojox/storage/storage_dialog.swf Binary files differnew file mode 100644 index 0000000..db6b217 --- /dev/null +++ b/js/dojo/dojox/storage/storage_dialog.swf |
