root / trunk / web / dojo / dojox / storage / Storage.as
History | View | Annotate | Download (11.8 KB)
1 |
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 |
} |
402 |
|