400 lines
12 KiB
ActionScript
400 lines
12 KiB
ActionScript
|
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 + "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);
|
||
|
}
|
||
|
|
||
|
// FIXME: Whoever added this code did not document what the
|
||
|
// put/get multiple functionality is and why it exists. Please
|
||
|
// also put your name and a bug number so I know who to contact.
|
||
|
// -- Brad Neuberg
|
||
|
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]);
|
||
|
}
|
||
|
|
||
|
// 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 + "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();
|
||
|
}
|
||
|
}
|
||
|
|