Subversion Repositories Applications.papyrus

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2150 mathias 1
if(!dojo._hasResource["dojox.off._common"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2
dojo._hasResource["dojox.off._common"] = true;
3
dojo.provide("dojox.off._common");
4
 
5
dojo.require("dojox.storage");
6
dojo.require("dojox.sql");
7
dojo.require("dojox.off.sync");
8
 
9
// Author: Brad Neuberg, bkn3@columbia.edu, http://codinginparadise.org
10
 
11
// summary:
12
//		dojox.off is the main object for offline applications.
13
dojo.mixin(dojox.off, {
14
	// isOnline: boolean
15
	//	true if we are online, false if not
16
	isOnline: false,
17
 
18
	// NET_CHECK: int
19
	//		For advanced usage; most developers can ignore this.
20
	//		Time in seconds on how often we should check the status of the
21
	//		network with an automatic background timer. The current default
22
	//		is 5 seconds.
23
	NET_CHECK: 5,
24
 
25
	// STORAGE_NAMESPACE: String
26
	//		For advanced usage; most developers can ignore this.
27
	//		The namespace we use to save core data into Dojo Storage.
28
	STORAGE_NAMESPACE: "_dot",
29
 
30
	// enabled: boolean
31
	//		For advanced usage; most developers can ignore this.
32
	//		Whether offline ability is enabled or not. Defaults to true.
33
	enabled: true,
34
 
35
	// availabilityURL: String
36
	//		For advanced usage; most developers can ignore this.
37
	//		The URL to check for site availability.  We do a GET request on
38
	//		this URL to check for site availability.  By default we check for a
39
	//		simple text file in src/off/network_check.txt that has one value
40
	//		it, the value '1'.
41
	availabilityURL: dojo.moduleUrl("dojox", "off/network_check.txt"),
42
 
43
	// goingOnline: boolean
44
	//		For advanced usage; most developers can ignore this.
45
	//		True if we are attempting to go online, false otherwise
46
	goingOnline: false,
47
 
48
	// coreOpFailed: boolean
49
	//		For advanced usage; most developers can ignore this.
50
	//		A flag set by the Dojo Offline framework that indicates that the
51
	//		user denied some operation that required the offline cache or an
52
	//		operation failed in some critical way that was unrecoverable. For
53
	//		example, if the offline cache is Google Gears and we try to get a
54
	//		Gears database, a popup window appears asking the user whether they
55
	//		will approve or deny this request. If the user denies the request,
56
	//		and we are doing some operation that is core to Dojo Offline, then
57
	//		we set this flag to 'true'.  This flag causes a 'fail fast'
58
	//		condition, turning off offline ability.
59
	coreOpFailed: false,
60
 
61
	// doNetChecking: boolean
62
	//		For advanced usage; most developers can ignore this.
63
	//		Whether to have a timing interval in the background doing automatic
64
	//		network checks at regular intervals; the length of time between
65
	//		checks is controlled by dojox.off.NET_CHECK. Defaults to true.
66
	doNetChecking: true,
67
 
68
	// hasOfflineCache: boolean
69
	//		For advanced usage; most developers can ignore this.
70
	//  	Determines if an offline cache is available or installed; an
71
	//  	offline cache is a facility that can truely cache offline
72
	//  	resources, such as JavaScript, HTML, etc. in such a way that they
73
	//  	won't be removed from the cache inappropriately like a browser
74
	//  	cache would. If this is false then an offline cache will be
75
	//  	installed. Only Google Gears is currently supported as an offline
76
	//  	cache. Future possible offline caches include Firefox 3.
77
	hasOfflineCache: null,
78
 
79
	// browserRestart: boolean
80
	//		For advanced usage; most developers can ignore this.
81
	//		If true, the browser must be restarted to register the existence of
82
	//		a new host added offline (from a call to addHostOffline); if false,
83
	//		then nothing is needed.
84
	browserRestart: false,
85
 
86
	_STORAGE_APP_NAME: window.location.href.replace(/[^0-9A-Za-z_]/g, "_"),
87
 
88
	_initializeCalled: false,
89
	_storageLoaded: false,
90
	_pageLoaded: false,
91
 
92
	onLoad: function(){
93
		// summary:
94
		//	Called when Dojo Offline can be used.
95
		// description:
96
		//	Do a dojo.connect to this to know when you can
97
		//	start using Dojo Offline:
98
		//		dojo.connect(dojox.off, "onLoad", myFunc);
99
	},
100
 
101
	onNetwork: function(type){
102
		// summary:
103
		//	Called when our on- or offline- status changes.
104
		// description:
105
		//	If we move online, then this method is called with the
106
		//	value "online". If we move offline, then this method is
107
		//	called with the value "offline". You can connect to this
108
		//	method to do add your own behavior:
109
		//
110
		//		dojo.connect(dojox.off, "onNetwork", someFunc)
111
		//
112
		//	Note that if you are using the default Dojo Offline UI
113
		//	widget that most of the on- and off-line notification
114
		//	and syncing is automatically handled and provided to the
115
		//	user.
116
		// type: String
117
		//	Either "online" or "offline".
118
	},
119
 
120
	initialize: function(){ /* void */
121
		// summary:
122
		//		Called when a Dojo Offline-enabled application is finished
123
		//		configuring Dojo Offline, and is ready for Dojo Offline to
124
		//		initialize itself.
125
		// description:
126
		//		When an application has finished filling out the variables Dojo
127
		//		Offline needs to work, such as dojox.off.ui.appName, it must
128
		//		this method to tell Dojo Offline to initialize itself.
129
 
130
		//		Note:
131
		//		This method is needed for a rare edge case. In some conditions,
132
		//		especially if we are dealing with a compressed Dojo build, the
133
		//		entire Dojo Offline subsystem might initialize itself and be
134
		//		running even before the JavaScript for an application has had a
135
		//		chance to run and configure Dojo Offline, causing Dojo Offline
136
		//		to have incorrect initialization parameters for a given app,
137
		//		such as no value for dojox.off.ui.appName. This method is
138
		//		provided to prevent this scenario, to slightly 'slow down' Dojo
139
		//		Offline so it can be configured before running off and doing
140
		//		its thing.
141
 
142
		//console.debug("dojox.off.initialize");
143
		this._initializeCalled = true;
144
 
145
		if(this._storageLoaded && this._pageLoaded){
146
			this._onLoad();
147
		}
148
	},
149
 
150
	goOffline: function(){ /* void */
151
		// summary:
152
		//		For advanced usage; most developers can ignore this.
153
		//		Manually goes offline, away from the network.
154
		if((dojox.off.sync.isSyncing)||(this.goingOnline)){ return; }
155
 
156
		this.goingOnline = false;
157
		this.isOnline = false;
158
	},
159
 
160
	goOnline: function(callback){ /* void */
161
		// summary:
162
		//		For advanced usage; most developers can ignore this.
163
		//		Attempts to go online.
164
		// description:
165
		//		Attempts to go online, making sure this web application's web
166
		//		site is available. 'callback' is called asychronously with the
167
		//		result of whether we were able to go online or not.
168
		// callback: Function
169
		//		An optional callback function that will receive one argument:
170
		//		whether the site is available or not and is boolean. If this
171
		//		function is not present we call dojo.xoff.onOnline instead if
172
		//		we are able to go online.
173
 
174
		//console.debug("goOnline");
175
 
176
		if(dojox.off.sync.isSyncing || dojox.off.goingOnline){
177
			return;
178
		}
179
 
180
		this.goingOnline = true;
181
		this.isOnline = false;
182
 
183
		// see if can reach our web application's web site
184
		this._isSiteAvailable(callback);
185
	},
186
 
187
	onFrameworkEvent: function(type /* String */, saveData /* Object? */){
188
		//	summary:
189
		//		For advanced usage; most developers can ignore this.
190
		//		A standard event handler that can be attached to to find out
191
		//		about low-level framework events. Most developers will not need to
192
		//		attach to this method; it is meant for low-level information
193
		//		that can be useful for updating offline user-interfaces in
194
		//		exceptional circumstances. The default Dojo Offline UI
195
		//		widget takes care of most of these situations.
196
		//	type: String
197
		//		The type of the event:
198
		//
199
		//		* "offlineCacheInstalled"
200
		//			An event that is fired when a user
201
		//			has installed an offline cache after the page has been loaded.
202
		//			If a user didn't have an offline cache when the page loaded, a
203
		//			UI of some kind might have prompted them to download one. This
204
		//			method is called if they have downloaded and installed an
205
		//			offline cache so a UI can reinitialize itself to begin using
206
		//			this offline cache.
207
		//		* "coreOperationFailed"
208
		//			Fired when a core operation during interaction with the
209
		//			offline cache is denied by the user. Some offline caches, such
210
		//			as Google Gears, prompts the user to approve or deny caching
211
		//			files, using the database, and more. If the user denies a
212
		//			request that is core to Dojo Offline's operation, we set
213
		//			dojox.off.coreOpFailed to true and call this method for
214
		//			listeners that would like to respond some how to Dojo Offline
215
		//			'failing fast'.
216
		//		* "save"
217
		//			Called whenever the framework saves data into persistent
218
		//			storage. This could be useful for providing save feedback
219
		//			or providing appropriate error feedback if saving fails
220
		//			due to a user not allowing the save to occur
221
		//	saveData: Object?
222
		//		If the type was 'save', then a saveData object is provided with
223
		//		further save information. This object has the following properties:
224
		//
225
		//		* status - dojox.storage.SUCCESS, dojox.storage.PENDING, dojox.storage.FAILED
226
		//		Whether the save succeeded, whether it is pending based on a UI
227
		//		dialog asking the user for permission, or whether it failed.
228
		//
229
		//		* isCoreSave - boolean
230
		//		If true, then this save was for a core piece of data necessary
231
		//		for the functioning of Dojo Offline. If false, then it is a
232
		//		piece of normal data being saved for offline access. Dojo
233
		//		Offline will 'fail fast' if some core piece of data could not
234
		//		be saved, automatically setting dojox.off.coreOpFailed to
235
		//		'true' and dojox.off.enabled to 'false'.
236
		//
237
		// 		* key - String
238
		//		The key that we are attempting to persist
239
		//
240
		// 		* value - Object
241
		//		The object we are trying to persist
242
		//
243
		// 		* namespace - String
244
		//		The Dojo Storage namespace we are saving this key/value pair
245
		//		into, such as "default", "Documents", "Contacts", etc.
246
		//		Optional.
247
		if(type == "save"){
248
			if(saveData.isCoreSave && (saveData.status == dojox.storage.FAILED)){
249
				dojox.off.coreOpFailed = true;
250
				dojox.off.enabled = false;
251
 
252
				// FIXME: Stop the background network thread
253
				dojox.off.onFrameworkEvent("coreOperationFailed");
254
			}
255
		}else if(type == "coreOperationFailed"){
256
			dojox.off.coreOpFailed = true;
257
			dojox.off.enabled = false;
258
			// FIXME: Stop the background network thread
259
		}
260
	},
261
 
262
	_checkOfflineCacheAvailable: function(callback){
263
		// is a true, offline cache running on this machine?
264
		this.hasOfflineCache = dojo.isGears;
265
 
266
		callback();
267
	},
268
 
269
	_onLoad: function(){
270
		//console.debug("dojox.off._onLoad");
271
 
272
		// both local storage and the page are finished loading
273
 
274
		// cache the Dojo JavaScript -- just use the default dojo.js
275
		// name for the most common scenario
276
		// FIXME: TEST: Make sure syncing doesn't break if dojo.js
277
		// can't be found, or report an error to developer
278
		dojox.off.files.cache(dojo.moduleUrl("dojo", "dojo.js"));
279
 
280
		// pull in the files needed by Dojo
281
		this._cacheDojoResources();
282
 
283
		// FIXME: need to pull in the firebug lite files here!
284
		// workaround or else we will get an error on page load
285
		// from Dojo that it can't find 'console.debug' for optimized builds
286
		// dojox.off.files.cache(djConfig.baseRelativePath + "src/debug.js");
287
 
288
		// make sure that resources needed by all of our underlying
289
		// Dojo Storage storage providers will be available
290
		// offline
291
		dojox.off.files.cache(dojox.storage.manager.getResourceList());
292
 
293
		// slurp the page if the end-developer wants that
294
		dojox.off.files._slurp();
295
 
296
		// see if we have an offline cache; when done, move
297
		// on to the rest of our startup tasks
298
		this._checkOfflineCacheAvailable(dojo.hitch(this, "_onOfflineCacheChecked"));
299
	},
300
 
301
	_onOfflineCacheChecked: function(){
302
		// this method is part of our _onLoad series of startup tasks
303
 
304
		// if we have an offline cache, see if we have been added to the
305
		// list of available offline web apps yet
306
		if(this.hasOfflineCache && this.enabled){
307
			// load framework data; when we are finished, continue
308
			// initializing ourselves
309
			this._load(dojo.hitch(this, "_finishStartingUp"));
310
		}else if(this.hasOfflineCache && !this.enabled){
311
			// we have an offline cache, but it is disabled for some reason
312
			// perhaps due to the user denying a core operation
313
			this._finishStartingUp();
314
		}else{
315
			this._keepCheckingUntilInstalled();
316
		}
317
	},
318
 
319
	_keepCheckingUntilInstalled: function(){
320
		// this method is part of our _onLoad series of startup tasks
321
 
322
		// kick off a background interval that keeps
323
		// checking to see if an offline cache has been
324
		// installed since this page loaded
325
 
326
		// FIXME: Gears: See if we are installed somehow after the
327
		// page has been loaded
328
 
329
		// now continue starting up
330
		this._finishStartingUp();
331
	},
332
 
333
	_finishStartingUp: function(){
334
		//console.debug("dojox.off._finishStartingUp");
335
 
336
		// this method is part of our _onLoad series of startup tasks
337
 
338
		if(!this.hasOfflineCache){
339
			this.onLoad();
340
		}else if(this.enabled){
341
			// kick off a thread to check network status on
342
			// a regular basis
343
			this._startNetworkThread();
344
 
345
			// try to go online
346
			this.goOnline(dojo.hitch(this, function(){
347
				//console.debug("Finished trying to go online");
348
				// indicate we are ready to be used
349
				dojox.off.onLoad();
350
			}));
351
		}else{ // we are disabled or a core operation failed
352
			if(this.coreOpFailed){
353
				this.onFrameworkEvent("coreOperationFailed");
354
			}else{
355
				this.onLoad();
356
			}
357
		}
358
	},
359
 
360
	_onPageLoad: function(){
361
		//console.debug("dojox.off._onPageLoad");
362
		this._pageLoaded = true;
363
 
364
		if(this._storageLoaded && this._initializeCalled){
365
			this._onLoad();
366
		}
367
	},
368
 
369
	_onStorageLoad: function(){
370
		//console.debug("dojox.off._onStorageLoad");
371
		this._storageLoaded = true;
372
 
373
		// were we able to initialize storage? if
374
		// not, then this is a core operation, and
375
		// let's indicate we will need to fail fast
376
		if(!dojox.storage.manager.isAvailable()
377
			&& dojox.storage.manager.isInitialized()){
378
			this.coreOpFailed = true;
379
			this.enabled = false;
380
		}
381
 
382
		if(this._pageLoaded && this._initializeCalled){
383
			this._onLoad();
384
		}
385
	},
386
 
387
	_isSiteAvailable: function(callback){
388
		// summary:
389
		//		Determines if our web application's website is available.
390
		// description:
391
		//		This method will asychronously determine if our web
392
		//		application's web site is available, which is a good proxy for
393
		//		network availability. The URL dojox.off.availabilityURL is
394
		//		used, which defaults to this site's domain name (ex:
395
		//		foobar.com). We check for dojox.off.AVAILABILITY_TIMEOUT (in
396
		//		seconds) and abort after that
397
		// callback: Function
398
		//		An optional callback function that will receive one argument:
399
		//		whether the site is available or not and is boolean. If this
400
		//		function is not present we call dojox.off.onNetwork instead if we
401
		//		are able to go online.
402
		dojo.xhrGet({
403
			url:		this._getAvailabilityURL(),
404
			handleAs:	"text",
405
			timeout:	this.NET_CHECK * 1000,
406
			error:		dojo.hitch(this, function(err){
407
				//console.debug("dojox.off._isSiteAvailable.error: " + err);
408
				this.goingOnline = false;
409
				this.isOnline = false;
410
				if(callback){ callback(false); }
411
			}),
412
			load:		dojo.hitch(this, function(data){
413
				//console.debug("dojox.off._isSiteAvailable.load, data="+data);
414
				this.goingOnline = false;
415
				this.isOnline = true;
416
 
417
				if(callback){ callback(true);
418
				}else{ this.onNetwork("online"); }
419
			})
420
		});
421
	},
422
 
423
	_startNetworkThread: function(){
424
		//console.debug("startNetworkThread");
425
 
426
		// kick off a thread that does periodic
427
		// checks on the status of the network
428
		if(!this.doNetChecking){
429
			return;
430
		}
431
 
432
		window.setInterval(dojo.hitch(this, function(){
433
			var d = dojo.xhrGet({
434
				url:	 	this._getAvailabilityURL(),
435
				handleAs:	"text",
436
				timeout: 	this.NET_CHECK * 1000,
437
				error:		dojo.hitch(this,
438
								function(err){
439
									if(this.isOnline){
440
										this.isOnline = false;
441
 
442
										// FIXME: xhrGet() is not
443
										// correctly calling abort
444
										// on the XHR object when
445
										// it times out; fix inside
446
										// there instead of externally
447
										// here
448
										try{
449
											if(typeof d.ioArgs.xhr.abort == "function"){
450
												d.ioArgs.xhr.abort();
451
											}
452
										}catch(e){}
453
 
454
										// if things fell in the middle of syncing,
455
										// stop syncing
456
										dojox.off.sync.isSyncing = false;
457
 
458
										this.onNetwork("offline");
459
									}
460
								}
461
							),
462
				load:		dojo.hitch(this,
463
								function(data){
464
									if(!this.isOnline){
465
										this.isOnline = true;
466
										this.onNetwork("online");
467
									}
468
								}
469
							)
470
			});
471
 
472
		}), this.NET_CHECK * 1000);
473
	},
474
 
475
	_getAvailabilityURL: function(){
476
		var url = this.availabilityURL.toString();
477
 
478
		// bust the browser's cache to make sure we are really talking to
479
		// the server
480
		if(url.indexOf("?") == -1){
481
			url += "?";
482
		}else{
483
			url += "&";
484
		}
485
		url += "browserbust=" + new Date().getTime();
486
 
487
		return url;
488
	},
489
 
490
	_onOfflineCacheInstalled: function(){
491
		this.onFrameworkEvent("offlineCacheInstalled");
492
	},
493
 
494
	_cacheDojoResources: function(){
495
		// if we are a non-optimized build, then the core Dojo bootstrap
496
		// system was loaded as separate JavaScript files;
497
		// add these to our offline cache list. these are
498
		// loaded before the dojo.require() system exists
499
 
500
		// FIXME: create a better mechanism in the Dojo core to
501
		// expose whether you are dealing with an optimized build;
502
		// right now we just scan the SCRIPT tags attached to this
503
		// page and see if there is one for _base/_loader/bootstrap.js
504
		var isOptimizedBuild = true;
505
		dojo.forEach(dojo.query("script"), function(i){
506
			var src = i.getAttribute("src");
507
			if(!src){ return; }
508
 
509
			if(src.indexOf("_base/_loader/bootstrap.js") != -1){
510
				isOptimizedBuild = false;
511
			}
512
		});
513
 
514
		if(!isOptimizedBuild){
515
			dojox.off.files.cache(dojo.moduleUrl("dojo", "_base.js").uri);
516
			dojox.off.files.cache(dojo.moduleUrl("dojo", "_base/_loader/loader.js").uri);
517
			dojox.off.files.cache(dojo.moduleUrl("dojo", "_base/_loader/bootstrap.js").uri);
518
 
519
			// FIXME: pull in the host environment file in a more generic way
520
			// for other host environments
521
			dojox.off.files.cache(dojo.moduleUrl("dojo", "_base/_loader/hostenv_browser.js").uri);
522
		}
523
 
524
		// add anything that was brought in with a
525
		// dojo.require() that resulted in a JavaScript
526
		// URL being fetched
527
 
528
		// FIXME: modify dojo/_base/_loader/loader.js to
529
		// expose a public API to get this information
530
 
531
		for(var i = 0; i < dojo._loadedUrls.length; i++){
532
			dojox.off.files.cache(dojo._loadedUrls[i]);
533
		}
534
 
535
		// FIXME: add the standard Dojo CSS file
536
	},
537
 
538
	_save: function(){
539
		// summary:
540
		//		Causes the Dojo Offline framework to save its configuration
541
		//		data into local storage.
542
	},
543
 
544
	_load: function(callback){
545
		// summary:
546
		//		Causes the Dojo Offline framework to load its configuration
547
		//		data from local storage
548
		dojox.off.sync._load(callback);
549
	}
550
});
551
 
552
 
553
// wait until the storage system is finished loading
554
dojox.storage.manager.addOnLoad(dojo.hitch(dojox.off, "_onStorageLoad"));
555
 
556
// wait until the page is finished loading
557
dojo.addOnLoad(dojox.off, "_onPageLoad");
558
 
559
}