Subversion Repositories Applications.papyrus

Rev

Blame | Last modification | View Log | RSS feed

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;
        
                //getURL("javascript:console.debug('FLASH:Storage constructor')");
                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);
                // getURL("javascript:alert('"+DojoExternalInterface.dojoPath+"');");
                _root._settingsBackground.loadMovie(DojoExternalInterface.dojoPath + "storage_dialog.swf");
        }

        //      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 st = (new Date()).getTime();
                var flushResults = so.flush();
                        //var end = (new Date()).getTime();
                        //getURL("javascript:dojo.debug('FLASH: flush - not a word game - took " + (end - st) + "ms')");

                // 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", null, statusResults, null);
        }

        // FIXME: This code has gotten ugly -- refactor
        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;

                //      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]);
                }
                
                //      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){
                        //getURL("javascript:console.debug('FLASH: 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;
                                }
                        }
                        //getURL("javascript:console.debug('FLASH: onStatus, statusResults="+statusResults+"')");
                        
                        // give the status results to JavaScript
                        DojoExternalInterface.call("dojox.storage._onStatus", null, statusResults, keyNames[0]);
                }
                
                //      Clear any pending flush timers
                if(timer){
                        //getURL("javascript:dojo.debug('FLASH: clearing 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){
                        //getURL("javascript:dojo.debug('FLASH: calling flush now')");
                        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){
                // 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 = new Array();
                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;
                        }
                }
                
                // join the keys together in a comma seperated string
                results = results.join(",");
                
                return results;
        }
        
        public function getNamespaces(){
                var allNamespaces = SharedObject.getLocal(_NAMESPACE_KEY);
                var results = new Array();
                for(var i in allNamespaces.data){
                        results.push(i);
                }
        
                return results.join(",");
        }
        
        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", null, statusResults, keyName);
                        }
                }
                
                // 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", null, statusResults, keyName);
                }
        }
        
        // 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){
                //getURL("javascript:console.debug('FLASH: storage loaded')");
                _root.app = new Storage(); 
        }
}