root / trunk / web / dojo / dojox / storage / Storage.as
History | View | Annotate | Download (11.8 KB)
| 1 | 9 | andrej.cim | import DojoExternalInterface; |
|---|---|---|---|
| 2 | |||
| 3 | class Storage{
|
||
| 4 | public static var SUCCESS = "success"; |
||
| 5 | public static var FAILED = "failed"; |
||
| 6 | public static var PENDING = "pending"; |
||
| 7 | |||
| 8 | // Wait the following number of milliseconds before flushing |
||
| 9 | public static var FLUSH_DELAY_DEFAULT = 500; |
||
| 10 | |||
| 11 | public var flush_delay; |
||
| 12 | public var so; |
||
| 13 | public var timer; |
||
| 14 | |||
| 15 | private var _NAMESPACE_KEY = "allNamespaces"; |
||
| 16 | |||
| 17 | public function Storage(){
|
||
| 18 | flush_delay = Storage.FLUSH_DELAY_DEFAULT; |
||
| 19 | |||
| 20 | DojoExternalInterface.initialize(); |
||
| 21 | DojoExternalInterface.addCallback("put", this, put);
|
||
| 22 | DojoExternalInterface.addCallback("putMultiple", this, putMultiple);
|
||
| 23 | DojoExternalInterface.addCallback("get", this, get);
|
||
| 24 | DojoExternalInterface.addCallback("getMultiple", this, getMultiple);
|
||
| 25 | DojoExternalInterface.addCallback("showSettings", this, showSettings);
|
||
| 26 | DojoExternalInterface.addCallback("clear", this, clear);
|
||
| 27 | DojoExternalInterface.addCallback("getKeys", this, getKeys);
|
||
| 28 | DojoExternalInterface.addCallback("getNamespaces", this, getNamespaces);
|
||
| 29 | DojoExternalInterface.addCallback("remove", this, remove);
|
||
| 30 | DojoExternalInterface.addCallback("removeMultiple", this, removeMultiple);
|
||
| 31 | DojoExternalInterface.addCallback("flush", this, flush);
|
||
| 32 | DojoExternalInterface.addCallback("setFlushDelay", this, setFlushDelay);
|
||
| 33 | DojoExternalInterface.addCallback("getFlushDelay", this, getFlushDelay);
|
||
| 34 | DojoExternalInterface.loaded(); |
||
| 35 | |||
| 36 | // preload the System Settings finished button movie for offline |
||
| 37 | // access so it is in the cache |
||
| 38 | _root.createEmptyMovieClip("_settingsBackground", 1);
|
||
| 39 | _root._settingsBackground.loadMovie(DojoExternalInterface.dojoPath |
||
| 40 | + "../dojox/storage/storage_dialog.swf"); |
||
| 41 | } |
||
| 42 | |||
| 43 | // FIXME: Whoever added this Flush code did not document why it |
||
| 44 | // exists. Please also put your name and a bug number so I know |
||
| 45 | // who to contact. -- Brad Neuberg |
||
| 46 | |||
| 47 | // Set a new value for the flush delay timer. |
||
| 48 | // Possible values: |
||
| 49 | // 0 : Perform the flush synchronously after each "put" request |
||
| 50 | // > 0 : Wait until 'newDelay' ms have passed without any "put" request to flush |
||
| 51 | // -1 : Do not automatically flush |
||
| 52 | public function setFlushDelay(newDelay){
|
||
| 53 | flush_delay = Number(newDelay); |
||
| 54 | } |
||
| 55 | |||
| 56 | public function getFlushDelay(){
|
||
| 57 | return String(flush_delay); |
||
| 58 | } |
||
| 59 | |||
| 60 | public function flush(namespace){
|
||
| 61 | if(timer){
|
||
| 62 | _global.clearTimeout(timer); |
||
| 63 | delete timer; |
||
| 64 | } |
||
| 65 | |||
| 66 | var so = SharedObject.getLocal(namespace); |
||
| 67 | var flushResults = so.flush(); |
||
| 68 | |||
| 69 | // return results of this command to JavaScript |
||
| 70 | var statusResults; |
||
| 71 | if(flushResults == true){
|
||
| 72 | statusResults = Storage.SUCCESS; |
||
| 73 | }else if(flushResults == "pending"){
|
||
| 74 | statusResults = Storage.PENDING; |
||
| 75 | }else{
|
||
| 76 | statusResults = Storage.FAILED; |
||
| 77 | } |
||
| 78 | |||
| 79 | DojoExternalInterface.call("dojox.storage._onStatus", statusResults,
|
||
| 80 | null, namespace); |
||
| 81 | } |
||
| 82 | |||
| 83 | public function put(keyName, keyValue, namespace){
|
||
| 84 | // Get the SharedObject for these values and save it |
||
| 85 | so = SharedObject.getLocal(namespace); |
||
| 86 | |||
| 87 | // Save the key and value |
||
| 88 | so.data[keyName] = keyValue; |
||
| 89 | |||
| 90 | // Save the namespace |
||
| 91 | // FIXME: Tie this into the flush/no-flush stuff below; right now |
||
| 92 | // we immediately write out this namespace. -- Brad Neuberg |
||
| 93 | addNamespace(namespace, keyName); |
||
| 94 | |||
| 95 | // Do all the flush/no-flush stuff |
||
| 96 | var keyNames = new Array(); |
||
| 97 | keyNames[0] = keyName; |
||
| 98 | postWrite(so, keyNames, namespace); |
||
| 99 | } |
||
| 100 | |||
| 101 | public function putMultiple(metaKey, metaValue, metaLengths, namespace){
|
||
| 102 | // Get the SharedObject for these values and save it |
||
| 103 | so = SharedObject.getLocal(namespace); |
||
| 104 | |||
| 105 | // Create array of keys and value lengths |
||
| 106 | var keys = metaKey.split(",");
|
||
| 107 | var lengths = metaLengths.split(",");
|
||
| 108 | |||
| 109 | // Loop through the array and write the values |
||
| 110 | for(var i = 0; i < keys.length; i++){
|
||
| 111 | so.data[keys[i]] = metaValue.slice(0,lengths[i]); |
||
| 112 | metaValue = metaValue.slice(lengths[i]); |
||
| 113 | } |
||
| 114 | |||
| 115 | // Save the namespace |
||
| 116 | // FIXME: Tie this into the flush/no-flush stuff below; right now |
||
| 117 | // we immediately write out this namespace. -- Brad Neuberg |
||
| 118 | addNamespace(namespace, null); |
||
| 119 | |||
| 120 | // Do all the flush/no-flush stuff |
||
| 121 | postWrite(so, keys, namespace); |
||
| 122 | } |
||
| 123 | |||
| 124 | public function postWrite(so, keyNames, namespace){
|
||
| 125 | // TODO: Review all this 'handler' stuff. In particular, the flush |
||
| 126 | // could now be with keys pending from several different requests, not |
||
| 127 | // only the ones passed in this method call |
||
| 128 | |||
| 129 | // prepare a storage status handler |
||
| 130 | var self = this; |
||
| 131 | so.onStatus = function(infoObject:Object){
|
||
| 132 | //trace("onStatus, infoObject="+infoObject.code);
|
||
| 133 | |||
| 134 | // delete the data value if the request was denied |
||
| 135 | if(infoObject.code == "SharedObject.Flush.Failed"){
|
||
| 136 | for(var i=0;i<keyNames.length;i++){
|
||
| 137 | delete self.so.data[keyNames[i]]; |
||
| 138 | } |
||
| 139 | } |
||
| 140 | |||
| 141 | var statusResults; |
||
| 142 | if(infoObject.code == "SharedObject.Flush.Failed"){
|
||
| 143 | statusResults = Storage.FAILED; |
||
| 144 | }else if(infoObject.code == "SharedObject.Flush.Pending"){
|
||
| 145 | statusResults = Storage.PENDING; |
||
| 146 | }else if(infoObject.code == "SharedObject.Flush.Success"){
|
||
| 147 | // if we have succeeded saving our value, see if we |
||
| 148 | // need to update our list of namespaces |
||
| 149 | if(self.hasNamespace(namespace) == true){
|
||
| 150 | statusResults = Storage.SUCCESS; |
||
| 151 | }else{
|
||
| 152 | // we have a new namespace we must store |
||
| 153 | self.addNamespace(namespace, keyNames[0]); |
||
| 154 | return; |
||
| 155 | } |
||
| 156 | } |
||
| 157 | //trace("onStatus, statusResults="+statusResults);
|
||
| 158 | |||
| 159 | // give the status results to JavaScript |
||
| 160 | DojoExternalInterface.call("dojox.storage._onStatus", statusResults,
|
||
| 161 | keyNames[0], namespace); |
||
| 162 | } |
||
| 163 | |||
| 164 | // Clear any pending flush timers |
||
| 165 | if(timer){
|
||
| 166 | _global.clearTimeout(timer); |
||
| 167 | } |
||
| 168 | |||
| 169 | // If we have a flush delay set, set a timer for its execution |
||
| 170 | if(flush_delay > 0){
|
||
| 171 | timer = _global.setTimeout(flush, flush_delay, namespace); |
||
| 172 | // With a flush_delay value of 0, execute the flush request synchronously |
||
| 173 | }else if(flush_delay == 0){
|
||
| 174 | flush(namespace); |
||
| 175 | } |
||
| 176 | // Otherwise just don't flush - will be probably be flushed manually |
||
| 177 | } |
||
| 178 | |||
| 179 | public function get(keyName, namespace){
|
||
| 180 | // Get the SharedObject for these values and save it |
||
| 181 | so = SharedObject.getLocal(namespace); |
||
| 182 | var results = so.data[keyName]; |
||
| 183 | |||
| 184 | return results; |
||
| 185 | } |
||
| 186 | |||
| 187 | // Returns an array with the contents of each key value on the metaKeys array |
||
| 188 | public function getMultiple(metaKeys, namespace){
|
||
| 189 | // get the storage object |
||
| 190 | so = SharedObject.getLocal(namespace); |
||
| 191 | |||
| 192 | // Create array of keys to read |
||
| 193 | var keys = metaKeys.split(",");
|
||
| 194 | var results = new Array(); |
||
| 195 | |||
| 196 | // Read from storage into results array |
||
| 197 | for(var i = 0;i < keys.length;i++){
|
||
| 198 | var val = so.data[keys[i]]; |
||
| 199 | val = val.split("\\").join("\\\\");
|
||
| 200 | val = val.split('"').join('\\"');
|
||
| 201 | results.push( val); |
||
| 202 | } |
||
| 203 | |||
| 204 | // Make the results array into a string |
||
| 205 | var metaResults = '["' + results.join('","') + '"]';
|
||
| 206 | |||
| 207 | return metaResults; |
||
| 208 | } |
||
| 209 | |||
| 210 | public function showSettings(){
|
||
| 211 | // Show the configuration options for the Flash player, opened to the |
||
| 212 | // section for local storage controls (pane 1) |
||
| 213 | System.showSettings(1); |
||
| 214 | |||
| 215 | // there is no way we can intercept when the Close button is pressed, allowing us |
||
| 216 | // to hide the Flash dialog. Instead, we need to load a movie in the |
||
| 217 | // background that we can show a close button on. |
||
| 218 | _root.createEmptyMovieClip("_settingsBackground", 1);
|
||
| 219 | _root._settingsBackground.loadMovie(DojoExternalInterface.dojoPath |
||
| 220 | + "../dojox/storage/storage_dialog.swf"); |
||
| 221 | } |
||
| 222 | |||
| 223 | public function clear(namespace){
|
||
| 224 | so = SharedObject.getLocal(namespace); |
||
| 225 | so.clear(); |
||
| 226 | so.flush(); |
||
| 227 | |||
| 228 | // remove this namespace entry now |
||
| 229 | removeNamespace(namespace); |
||
| 230 | } |
||
| 231 | |||
| 232 | public function getKeys(namespace) : String{
|
||
| 233 | // Returns a list of the available keys in this namespace |
||
| 234 | |||
| 235 | // get the storage object |
||
| 236 | so = SharedObject.getLocal(namespace); |
||
| 237 | // get all of the keys |
||
| 238 | var results = []; |
||
| 239 | for(var i in so.data){
|
||
| 240 | results.push(i); |
||
| 241 | } |
||
| 242 | |||
| 243 | // remove our key that records our list of namespaces |
||
| 244 | for(var i = 0; i < results.length; i++){
|
||
| 245 | if(results[i] == _NAMESPACE_KEY){
|
||
| 246 | results.splice(i, 1); |
||
| 247 | break; |
||
| 248 | } |
||
| 249 | } |
||
| 250 | |||
| 251 | // a bug in ExternalInterface transforms Arrays into |
||
| 252 | // Strings, so we can't use those here! -- BradNeuberg |
||
| 253 | results = results.join(",");
|
||
| 254 | |||
| 255 | return results; |
||
| 256 | } |
||
| 257 | |||
| 258 | public function getNamespaces() : String{
|
||
| 259 | var allNamespaces = SharedObject.getLocal(_NAMESPACE_KEY); |
||
| 260 | var results = []; |
||
| 261 | |||
| 262 | for(var i in allNamespaces.data){
|
||
| 263 | results.push(i); |
||
| 264 | } |
||
| 265 | |||
| 266 | // a bug in ExternalInterface transforms Arrays into |
||
| 267 | // Strings, so we can use those here! -- BradNeuberg |
||
| 268 | results = results.join(",");
|
||
| 269 | |||
| 270 | return results; |
||
| 271 | } |
||
| 272 | |||
| 273 | public function remove(keyName, namespace){
|
||
| 274 | // Removes a key |
||
| 275 | |||
| 276 | // get the storage object |
||
| 277 | so = SharedObject.getLocal(namespace); |
||
| 278 | |||
| 279 | // delete this value |
||
| 280 | delete so.data[keyName]; |
||
| 281 | |||
| 282 | // save the changes |
||
| 283 | so.flush(); |
||
| 284 | |||
| 285 | // see if we are the last entry for this namespace |
||
| 286 | var availableKeys = getKeys(namespace); |
||
| 287 | if(availableKeys == ""){
|
||
| 288 | // we are empty |
||
| 289 | removeNamespace(namespace); |
||
| 290 | } |
||
| 291 | } |
||
| 292 | |||
| 293 | // Removes all the values for each keys on the metaKeys array |
||
| 294 | public function removeMultiple(metaKeys, namespace){
|
||
| 295 | // get the storage object |
||
| 296 | so = SharedObject.getLocal(namespace); |
||
| 297 | |||
| 298 | // Create array of keys to read |
||
| 299 | var keys = metaKeys.split(",");
|
||
| 300 | var results = new Array(); |
||
| 301 | |||
| 302 | // Delete elements |
||
| 303 | for(var i=0;i<keys.length;i++){
|
||
| 304 | delete so.data[keys[i]]; |
||
| 305 | } |
||
| 306 | |||
| 307 | // see if there are no more entries for this namespace |
||
| 308 | var availableKeys = getKeys(namespace); |
||
| 309 | if(availableKeys == ""){
|
||
| 310 | // we are empty |
||
| 311 | removeNamespace(namespace); |
||
| 312 | } |
||
| 313 | } |
||
| 314 | |||
| 315 | private function hasNamespace(namespace):Boolean{
|
||
| 316 | // Get the SharedObject for the namespace list |
||
| 317 | var allNamespaces = SharedObject.getLocal(_NAMESPACE_KEY); |
||
| 318 | |||
| 319 | var results = false; |
||
| 320 | for(var i in allNamespaces.data){
|
||
| 321 | if(i == namespace){
|
||
| 322 | results = true; |
||
| 323 | break; |
||
| 324 | } |
||
| 325 | } |
||
| 326 | |||
| 327 | return results; |
||
| 328 | } |
||
| 329 | |||
| 330 | // FIXME: This code has gotten ugly -- refactor |
||
| 331 | private function addNamespace(namespace, keyName){
|
||
| 332 | if(hasNamespace(namespace) == true){
|
||
| 333 | return; |
||
| 334 | } |
||
| 335 | |||
| 336 | // Get the SharedObject for the namespace list |
||
| 337 | var allNamespaces = SharedObject.getLocal(_NAMESPACE_KEY); |
||
| 338 | |||
| 339 | // prepare a storage status handler if the keyName is |
||
| 340 | // not null |
||
| 341 | if(keyName != null && typeof keyName != "undefined"){
|
||
| 342 | var self = this; |
||
| 343 | allNamespaces.onStatus = function(infoObject:Object){
|
||
| 344 | // delete the data value if the request was denied |
||
| 345 | if(infoObject.code == "SharedObject.Flush.Failed"){
|
||
| 346 | delete self.so.data[keyName]; |
||
| 347 | } |
||
| 348 | |||
| 349 | var statusResults; |
||
| 350 | if(infoObject.code == "SharedObject.Flush.Failed"){
|
||
| 351 | statusResults = Storage.FAILED; |
||
| 352 | }else if(infoObject.code == "SharedObject.Flush.Pending"){
|
||
| 353 | statusResults = Storage.PENDING; |
||
| 354 | }else if(infoObject.code == "SharedObject.Flush.Success"){
|
||
| 355 | statusResults = Storage.SUCCESS; |
||
| 356 | } |
||
| 357 | |||
| 358 | // give the status results to JavaScript |
||
| 359 | DojoExternalInterface.call("dojox.storage._onStatus", statusResults,
|
||
| 360 | keyName, namespace); |
||
| 361 | } |
||
| 362 | } |
||
| 363 | |||
| 364 | // save the namespace list |
||
| 365 | allNamespaces.data[namespace] = true; |
||
| 366 | var flushResults = allNamespaces.flush(); |
||
| 367 | |||
| 368 | // return results of this command to JavaScript |
||
| 369 | if(keyName != null && typeof keyName != "undefined"){
|
||
| 370 | var statusResults; |
||
| 371 | if(flushResults == true){
|
||
| 372 | statusResults = Storage.SUCCESS; |
||
| 373 | }else if(flushResults == "pending"){
|
||
| 374 | statusResults = Storage.PENDING; |
||
| 375 | }else{
|
||
| 376 | statusResults = Storage.FAILED; |
||
| 377 | } |
||
| 378 | |||
| 379 | DojoExternalInterface.call("dojox.storage._onStatus", statusResults,
|
||
| 380 | keyName, namespace); |
||
| 381 | } |
||
| 382 | } |
||
| 383 | |||
| 384 | // FIXME: This code has gotten ugly -- refactor |
||
| 385 | private function removeNamespace(namespace){
|
||
| 386 | if(hasNamespace(namespace) == false){
|
||
| 387 | return; |
||
| 388 | } |
||
| 389 | |||
| 390 | // try to save the namespace list; don't have a return |
||
| 391 | // callback; if we fail on this, the worst that will happen |
||
| 392 | // is that we have a spurious namespace entry |
||
| 393 | var allNamespaces = SharedObject.getLocal(_NAMESPACE_KEY); |
||
| 394 | delete allNamespaces.data[namespace]; |
||
| 395 | allNamespaces.flush(); |
||
| 396 | } |
||
| 397 | |||
| 398 | static function main(mc){
|
||
| 399 | _root.app = new Storage(); |
||
| 400 | } |
||
| 401 | } |