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 | } |