Project

General

Profile

Statistics
| Revision:

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