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