Subversion Repositories Applications.papyrus

Rev

Rev 1372 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1318 alexandre_ 1
/*
2
	Copyright (c) 2004-2006, The Dojo Foundation
3
	All Rights Reserved.
4
 
5
	Licensed under the Academic Free License version 2.1 or above OR the
6
	modified BSD license. For more information on Dojo licensing, see:
7
 
8
		http://dojotoolkit.org/community/licensing.shtml
9
*/
10
 
11
if(typeof dojo == "undefined"){
12
 
13
// TODOC: HOW TO DOC THE BELOW?
14
// @global: djConfig
15
// summary:
16
//		Application code can set the global 'djConfig' prior to loading
17
//		the library to override certain global settings for how dojo works.
18
// description:  The variables that can be set are as follows:
19
//			- isDebug: false
20
//			- allowQueryConfig: false
21
//			- baseScriptUri: ""
22
//			- baseRelativePath: ""
23
//			- libraryScriptUri: ""
24
//			- iePreventClobber: false
25
//			- ieClobberMinimal: true
26
//			- locale: undefined
27
//			- extraLocale: undefined
28
//			- preventBackButtonFix: true
29
//			- searchIds: []
30
//			- parseWidgets: true
31
// TODOC: HOW TO DOC THESE VARIABLES?
32
// TODOC: IS THIS A COMPLETE LIST?
33
// note:
34
//		'djConfig' does not exist under 'dojo.*' so that it can be set before the
35
//		'dojo' variable exists.
36
// note:
37
//		Setting any of these variables *after* the library has loaded does nothing at all.
38
// TODOC: is this still true?  Release notes for 0.3 indicated they could be set after load.
39
//
40
 
41
 
42
//TODOC:  HOW TO DOC THIS?
43
// @global: dj_global
44
// summary:
45
//		an alias for the top-level global object in the host environment
46
//		(e.g., the window object in a browser).
47
// description:
48
//		Refer to 'dj_global' rather than referring to window to ensure your
49
//		code runs correctly in contexts other than web browsers (eg: Rhino on a server).
50
var dj_global = this;
51
 
52
//TODOC:  HOW TO DOC THIS?
53
// @global: dj_currentContext
54
// summary:
55
//		Private global context object. Where 'dj_global' always refers to the boot-time
56
//    global context, 'dj_currentContext' can be modified for temporary context shifting.
57
//    dojo.global() returns dj_currentContext.
58
// description:
59
//		Refer to dojo.global() rather than referring to dj_global to ensure your
60
//		code runs correctly in managed contexts.
61
var dj_currentContext = this;
62
 
63
 
64
// ****************************************************************
65
// global public utils
66
// TODOC: DO WE WANT TO NOTE THAT THESE ARE GLOBAL PUBLIC UTILS?
67
// ****************************************************************
68
 
69
function dj_undef(/*String*/ name, /*Object?*/ object){
70
	//summary: Returns true if 'name' is defined on 'object' (or globally if 'object' is null).
71
	//description: Note that 'defined' and 'exists' are not the same concept.
72
	return (typeof (object || dj_currentContext)[name] == "undefined");	// Boolean
73
}
74
 
75
// make sure djConfig is defined
76
if(dj_undef("djConfig", this)){
77
	var djConfig = {};
78
}
79
 
80
//TODOC:  HOW TO DOC THIS?
81
// dojo is the root variable of (almost all) our public symbols -- make sure it is defined.
82
if(dj_undef("dojo", this)){
83
	var dojo = {};
84
}
85
 
86
dojo.global = function(){
87
	// summary:
88
	//		return the current global context object
89
	//		(e.g., the window object in a browser).
90
	// description:
91
	//		Refer to 'dojo.global()' rather than referring to window to ensure your
92
	//		code runs correctly in contexts other than web browsers (eg: Rhino on a server).
93
	return dj_currentContext;
94
}
95
 
96
// Override locale setting, if specified
97
dojo.locale  = djConfig.locale;
98
 
99
//TODOC:  HOW TO DOC THIS?
100
dojo.version = {
101
	// summary: version number of this instance of dojo.
1422 alexandre_ 102
	major: 0, minor: 4, patch: 3, flag: "",
103
	revision: Number("$Rev: 8617 $".match(/[0-9]+/)[0]),
1318 alexandre_ 104
	toString: function(){
105
		with(dojo.version){
106
			return major + "." + minor + "." + patch + flag + " (" + revision + ")";	// String
107
		}
108
	}
109
}
110
 
111
dojo.evalProp = function(/*String*/ name, /*Object*/ object, /*Boolean?*/ create){
112
	// summary: Returns 'object[name]'.  If not defined and 'create' is true, will return a new Object.
113
	// description:
114
	//		Returns null if 'object[name]' is not defined and 'create' is not true.
115
	// 		Note: 'defined' and 'exists' are not the same concept.
116
	if((!object)||(!name)) return undefined; // undefined
117
	if(!dj_undef(name, object)) return object[name]; // mixed
118
	return (create ? (object[name]={}) : undefined);	// mixed
119
}
120
 
121
dojo.parseObjPath = function(/*String*/ path, /*Object?*/ context, /*Boolean?*/ create){
122
	// summary: Parse string path to an object, and return corresponding object reference and property name.
123
	// description:
124
	//		Returns an object with two properties, 'obj' and 'prop'.
125
	//		'obj[prop]' is the reference indicated by 'path'.
126
	// path: Path to an object, in the form "A.B.C".
127
	// context: Object to use as root of path.  Defaults to 'dojo.global()'.
128
	// create: If true, Objects will be created at any point along the 'path' that is undefined.
129
	var object = (context || dojo.global());
130
	var names = path.split('.');
131
	var prop = names.pop();
132
	for (var i=0,l=names.length;i<l && object;i++){
133
		object = dojo.evalProp(names[i], object, create);
134
	}
135
	return {obj: object, prop: prop};	// Object: {obj: Object, prop: String}
136
}
137
 
138
dojo.evalObjPath = function(/*String*/ path, /*Boolean?*/ create){
139
	// summary: Return the value of object at 'path' in the global scope, without using 'eval()'.
140
	// path: Path to an object, in the form "A.B.C".
141
	// create: If true, Objects will be created at any point along the 'path' that is undefined.
142
	if(typeof path != "string"){
143
		return dojo.global();
144
	}
145
	// fast path for no periods
146
	if(path.indexOf('.') == -1){
147
		return dojo.evalProp(path, dojo.global(), create);		// mixed
148
	}
149
 
150
	//MOW: old 'with' syntax was confusing and would throw an error if parseObjPath returned null.
151
	var ref = dojo.parseObjPath(path, dojo.global(), create);
152
	if(ref){
153
		return dojo.evalProp(ref.prop, ref.obj, create);	// mixed
154
	}
155
	return null;
156
}
157
 
158
dojo.errorToString = function(/*Error*/ exception){
159
	// summary: Return an exception's 'message', 'description' or text.
160
 
161
	// TODO: overriding Error.prototype.toString won't accomplish this?
162
 	// 		... since natively generated Error objects do not always reflect such things?
163
	if(!dj_undef("message", exception)){
164
		return exception.message;		// String
165
	}else if(!dj_undef("description", exception)){
166
		return exception.description;	// String
167
	}else{
168
		return exception;				// Error
169
	}
170
}
171
 
172
dojo.raise = function(/*String*/ message, /*Error?*/ exception){
173
	// summary: Common point for raising exceptions in Dojo to enable logging.
174
	//	Throws an error message with text of 'exception' if provided, or
175
	//	rethrows exception object.
176
 
177
	if(exception){
178
		message = message + ": "+dojo.errorToString(exception);
179
	}else{
180
		message = dojo.errorToString(message);
181
	}
182
 
183
	// print the message to the user if hostenv.println is defined
184
	try { if(djConfig.isDebug){ dojo.hostenv.println("FATAL exception raised: "+message); } } catch (e) {}
185
 
186
	throw exception || Error(message);
187
}
188
 
189
//Stub functions so things don't break.
190
//TODOC:  HOW TO DOC THESE?
191
dojo.debug = function(){};
192
dojo.debugShallow = function(obj){};
193
dojo.profile = { start: function(){}, end: function(){}, stop: function(){}, dump: function(){} };
194
 
195
function dj_eval(/*String*/ scriptFragment){
196
	// summary: Perform an evaluation in the global scope.  Use this rather than calling 'eval()' directly.
197
	// description: Placed in a separate function to minimize size of trapped evaluation context.
198
	// note:
199
	//	 - JSC eval() takes an optional second argument which can be 'unsafe'.
200
	//	 - Mozilla/SpiderMonkey eval() takes an optional second argument which is the
201
	//  	 scope object for new symbols.
202
	return dj_global.eval ? dj_global.eval(scriptFragment) : eval(scriptFragment); 	// mixed
203
}
204
 
205
dojo.unimplemented = function(/*String*/ funcname, /*String?*/ extra){
206
	// summary: Throw an exception because some function is not implemented.
207
	// extra: Text to append to the exception message.
208
	var message = "'" + funcname + "' not implemented";
209
	if (extra != null) { message += " " + extra; }
210
	dojo.raise(message);
211
}
212
 
213
dojo.deprecated = function(/*String*/ behaviour, /*String?*/ extra, /*String?*/ removal){
214
	// summary: Log a debug message to indicate that a behavior has been deprecated.
215
	// extra: Text to append to the message.
216
	// removal: Text to indicate when in the future the behavior will be removed.
217
	var message = "DEPRECATED: " + behaviour;
218
	if(extra){ message += " " + extra; }
219
	if(removal){ message += " -- will be removed in version: " + removal; }
220
	dojo.debug(message);
221
}
222
 
223
dojo.render = (function(){
224
	//TODOC: HOW TO DOC THIS?
225
	// summary: Details rendering support, OS and browser of the current environment.
226
	// TODOC: is this something many folks will interact with?  If so, we should doc the structure created...
227
	function vscaffold(prefs, names){
228
		var tmp = {
229
			capable: false,
230
			support: {
231
				builtin: false,
232
				plugin: false
233
			},
234
			prefixes: prefs
235
		};
236
		for(var i=0; i<names.length; i++){
237
			tmp[names[i]] = false;
238
		}
239
		return tmp;
240
	}
241
 
242
	return {
243
		name: "",
244
		ver: dojo.version,
245
		os: { win: false, linux: false, osx: false },
246
		html: vscaffold(["html"], ["ie", "opera", "khtml", "safari", "moz"]),
247
		svg: vscaffold(["svg"], ["corel", "adobe", "batik"]),
248
		vml: vscaffold(["vml"], ["ie"]),
249
		swf: vscaffold(["Swf", "Flash", "Mm"], ["mm"]),
250
		swt: vscaffold(["Swt"], ["ibm"])
251
	};
252
})();
253
 
254
// ****************************************************************
255
// dojo.hostenv methods that must be defined in hostenv_*.js
256
// ****************************************************************
257
 
258
/**
259
 * The interface definining the interaction with the EcmaScript host environment.
260
*/
261
 
262
/*
263
 * None of these methods should ever be called directly by library users.
264
 * Instead public methods such as loadModule should be called instead.
265
 */
266
dojo.hostenv = (function(){
267
	// TODOC:  HOW TO DOC THIS?
268
	// summary: Provides encapsulation of behavior that changes across different 'host environments'
269
	//			(different browsers, server via Rhino, etc).
270
	// description: None of these methods should ever be called directly by library users.
271
	//				Use public methods such as 'loadModule' instead.
272
 
273
	// default configuration options
274
	var config = {
275
		isDebug: false,
276
		allowQueryConfig: false,
277
		baseScriptUri: "",
278
		baseRelativePath: "",
279
		libraryScriptUri: "",
280
		iePreventClobber: false,
281
		ieClobberMinimal: true,
282
		preventBackButtonFix: true,
283
		delayMozLoadingFix: false,
284
		searchIds: [],
285
		parseWidgets: true
286
	};
287
 
288
	if (typeof djConfig == "undefined") { djConfig = config; }
289
	else {
290
		for (var option in config) {
291
			if (typeof djConfig[option] == "undefined") {
292
				djConfig[option] = config[option];
293
			}
294
		}
295
	}
296
 
297
	return {
298
		name_: '(unset)',
299
		version_: '(unset)',
300
 
301
 
302
		getName: function(){
303
			// sumary: Return the name of the host environment.
304
			return this.name_; 	// String
305
		},
306
 
307
 
308
		getVersion: function(){
309
			// summary: Return the version of the hostenv.
310
			return this.version_; // String
311
		},
312
 
313
		getText: function(/*String*/ uri){
314
			// summary:	Read the plain/text contents at the specified 'uri'.
315
			// description:
316
			//			If 'getText()' is not implemented, then it is necessary to override
317
			//			'loadUri()' with an implementation that doesn't rely on it.
318
 
319
			dojo.unimplemented('getText', "uri=" + uri);
320
		}
321
	};
322
})();
323
 
324
 
325
dojo.hostenv.getBaseScriptUri = function(){
326
	// summary: Return the base script uri that other scripts are found relative to.
327
	// TODOC: HUH?  This comment means nothing to me.  What other scripts? Is this the path to other dojo libraries?
328
	//		MAYBE:  Return the base uri to scripts in the dojo library.	 ???
329
	// return: Empty string or a path ending in '/'.
330
	if(djConfig.baseScriptUri.length){
331
		return djConfig.baseScriptUri;
332
	}
333
 
334
	// MOW: Why not:
335
	//			uri = djConfig.libraryScriptUri || djConfig.baseRelativePath
336
	//		??? Why 'new String(...)'
337
	var uri = new String(djConfig.libraryScriptUri||djConfig.baseRelativePath);
338
	if (!uri) { dojo.raise("Nothing returned by getLibraryScriptUri(): " + uri); }
339
 
340
	// MOW: uri seems to not be actually used.  Seems to be hard-coding to djConfig.baseRelativePath... ???
341
	var lastslash = uri.lastIndexOf('/');		// MOW ???
342
	djConfig.baseScriptUri = djConfig.baseRelativePath;
343
	return djConfig.baseScriptUri;	// String
344
}
345
 
346
/*
347
 * loader.js - A bootstrap module.  Runs before the hostenv_*.js file. Contains all of the package loading methods.
348
 */
349
 
350
//A semi-colon is at the start of the line because after doing a build, this function definition
351
//get compressed onto the same line as the last line in bootstrap1.js. That list line is just a
352
//curly bracket, and the browser complains about that syntax. The semicolon fixes it. Putting it
353
//here instead of at the end of bootstrap1.js, since it is more of an issue for this file, (using
354
//the closure), and bootstrap1.js could change in the future.
355
;(function(){
356
	//Additional properties for dojo.hostenv
357
	var _addHostEnv = {
358
		pkgFileName: "__package__",
359
 
360
		// for recursion protection
361
		loading_modules_: {},
362
		loaded_modules_: {},
363
		addedToLoadingCount: [],
364
		removedFromLoadingCount: [],
365
 
366
		inFlightCount: 0,
367
 
368
		// FIXME: it should be possible to pull module prefixes in from djConfig
369
		modulePrefixes_: {
370
			dojo: {name: "dojo", value: "src"}
371
		},
372
 
373
		setModulePrefix: function(/*String*/module, /*String*/prefix){
374
			// summary: establishes module/prefix pair
375
			this.modulePrefixes_[module] = {name: module, value: prefix};
376
		},
377
 
378
		moduleHasPrefix: function(/*String*/module){
379
			// summary: checks to see if module has been established
380
			var mp = this.modulePrefixes_;
381
			return Boolean(mp[module] && mp[module].value); // Boolean
382
		},
383
 
384
		getModulePrefix: function(/*String*/module){
385
			// summary: gets the prefix associated with module
386
			if(this.moduleHasPrefix(module)){
387
				return this.modulePrefixes_[module].value; // String
388
			}
389
			return module; // String
390
		},
391
 
392
		getTextStack: [],
393
		loadUriStack: [],
394
		loadedUris: [],
395
 
396
		//WARNING: This variable is referenced by packages outside of bootstrap: FloatingPane.js and undo/browser.js
397
		post_load_: false,
398
 
399
		//Egad! Lots of test files push on this directly instead of using dojo.addOnLoad.
400
		modulesLoadedListeners: [],
401
		unloadListeners: [],
402
		loadNotifying: false
403
	};
404
 
405
	//Add all of these properties to dojo.hostenv
406
	for(var param in _addHostEnv){
407
		dojo.hostenv[param] = _addHostEnv[param];
408
	}
409
})();
410
 
411
dojo.hostenv.loadPath = function(/*String*/relpath, /*String?*/module, /*Function?*/cb){
412
// summary:
413
//	Load a Javascript module given a relative path
414
//
415
// description:
416
//	Loads and interprets the script located at relpath, which is relative to the
417
//	script root directory.  If the script is found but its interpretation causes
418
//	a runtime exception, that exception is not caught by us, so the caller will
419
//	see it.  We return a true value if and only if the script is found.
420
//
421
//	For now, we do not have an implementation of a true search path.  We
422
//	consider only the single base script uri, as returned by getBaseScriptUri().
423
//
424
// relpath: A relative path to a script (no leading '/', and typically
425
// 	ending in '.js').
426
// module: A module whose existance to check for after loading a path.
427
//	Can be used to determine success or failure of the load.
428
// cb: a callback function to pass the result of evaluating the script
429
 
430
	var uri;
431
	if(relpath.charAt(0) == '/' || relpath.match(/^\w+:/)){
432
		// dojo.raise("relpath '" + relpath + "'; must be relative");
433
		uri = relpath;
434
	}else{
435
		uri = this.getBaseScriptUri() + relpath;
436
	}
437
	if(djConfig.cacheBust && dojo.render.html.capable){
438
		uri += "?" + String(djConfig.cacheBust).replace(/\W+/g,"");
439
	}
440
	try{
441
		return !module ? this.loadUri(uri, cb) : this.loadUriAndCheck(uri, module, cb); // Boolean
442
	}catch(e){
443
		dojo.debug(e);
444
		return false; // Boolean
445
	}
446
}
447
 
448
dojo.hostenv.loadUri = function(/*String (URL)*/uri, /*Function?*/cb){
449
// summary:
450
//	Loads JavaScript from a URI
451
//
452
// description:
453
//	Reads the contents of the URI, and evaluates the contents.  This is used to load modules as well
454
//	as resource bundles.  Returns true if it succeeded. Returns false if the URI reading failed.
455
//	Throws if the evaluation throws.
456
//
457
// uri: a uri which points at the script to be loaded
458
// cb: a callback function to process the result of evaluating the script as an expression, typically
459
//	used by the resource bundle loader to load JSON-style resources
460
 
461
	if(this.loadedUris[uri]){
462
		return true; // Boolean
463
	}
464
	var contents = this.getText(uri, null, true);
465
	if(!contents){ return false; } // Boolean
466
	this.loadedUris[uri] = true;
467
	if(cb){ contents = '('+contents+')'; }
468
	var value = dj_eval(contents);
469
	if(cb){ cb(value); }
470
	return true; // Boolean
471
}
472
 
473
// FIXME: probably need to add logging to this method
474
dojo.hostenv.loadUriAndCheck = function(/*String (URL)*/uri, /*String*/moduleName, /*Function?*/cb){
475
	// summary: calls loadUri then findModule and returns true if both succeed
476
	var ok = true;
477
	try{
478
		ok = this.loadUri(uri, cb);
479
	}catch(e){
480
		dojo.debug("failed loading ", uri, " with error: ", e);
481
	}
482
	return Boolean(ok && this.findModule(moduleName, false)); // Boolean
483
}
484
 
485
dojo.loaded = function(){ }
486
dojo.unloaded = function(){ }
487
 
488
dojo.hostenv.loaded = function(){
489
	this.loadNotifying = true;
490
	this.post_load_ = true;
491
	var mll = this.modulesLoadedListeners;
492
	for(var x=0; x<mll.length; x++){
493
		mll[x]();
494
	}
495
 
496
	//Clear listeners so new ones can be added
497
	//For other xdomain package loads after the initial load.
498
	this.modulesLoadedListeners = [];
499
	this.loadNotifying = false;
500
 
501
	dojo.loaded();
502
}
503
 
504
dojo.hostenv.unloaded = function(){
505
	var mll = this.unloadListeners;
506
	while(mll.length){
507
		(mll.pop())();
508
	}
509
	dojo.unloaded();
510
}
511
 
512
dojo.addOnLoad = function(/*Object?*/obj, /*String|Function*/functionName) {
513
// summary:
514
//	Registers a function to be triggered after the DOM has finished loading
515
//	and widgets declared in markup have been instantiated.  Images and CSS files
516
//	may or may not have finished downloading when the specified function is called.
517
//	(Note that widgets' CSS and HTML code is guaranteed to be downloaded before said
518
//	widgets are instantiated.)
519
//
520
// usage:
521
//	dojo.addOnLoad(functionPointer)
522
//	dojo.addOnLoad(object, "functionName")
523
 
524
	var dh = dojo.hostenv;
525
	if(arguments.length == 1) {
526
		dh.modulesLoadedListeners.push(obj);
527
	} else if(arguments.length > 1) {
528
		dh.modulesLoadedListeners.push(function() {
529
			obj[functionName]();
530
		});
531
	}
532
 
533
	//Added for xdomain loading. dojo.addOnLoad is used to
534
	//indicate callbacks after doing some dojo.require() statements.
535
	//In the xdomain case, if all the requires are loaded (after initial
536
	//page load), then immediately call any listeners.
537
	if(dh.post_load_ && dh.inFlightCount == 0 && !dh.loadNotifying){
538
		dh.callLoaded();
539
	}
540
}
541
 
542
dojo.addOnUnload = function(/*Object?*/obj, /*String|Function?*/functionName){
543
// summary: registers a function to be triggered when the page unloads
544
//
545
// usage:
546
//	dojo.addOnLoad(functionPointer)
547
//	dojo.addOnLoad(object, "functionName")
548
	var dh = dojo.hostenv;
549
	if(arguments.length == 1){
550
		dh.unloadListeners.push(obj);
551
	} else if(arguments.length > 1) {
552
		dh.unloadListeners.push(function() {
553
			obj[functionName]();
554
		});
555
	}
556
}
557
 
558
dojo.hostenv.modulesLoaded = function(){
559
	if(this.post_load_){ return; }
560
	if(this.loadUriStack.length==0 && this.getTextStack.length==0){
561
		if(this.inFlightCount > 0){
562
			dojo.debug("files still in flight!");
563
			return;
564
		}
565
		dojo.hostenv.callLoaded();
566
	}
567
}
568
 
569
dojo.hostenv.callLoaded = function(){
570
	//The "object" check is for IE, and the other opera check fixes an issue
571
	//in Opera where it could not find the body element in some widget test cases.
572
	//For 0.9, maybe route all browsers through the setTimeout (need protection
573
	//still for non-browser environments though). This might also help the issue with
574
	//FF 2.0 and freezing issues where we try to do sync xhr while background css images
575
	//are being loaded (trac #2572)? Consider for 0.9.
576
	if(typeof setTimeout == "object" || (djConfig["useXDomain"] && dojo.render.html.opera)){
577
		setTimeout("dojo.hostenv.loaded();", 0);
578
	}else{
579
		dojo.hostenv.loaded();
580
	}
581
}
582
 
583
dojo.hostenv.getModuleSymbols = function(/*String*/modulename){
584
// summary:
585
//	Converts a module name in dotted JS notation to an array representing the path in the source tree
586
	var syms = modulename.split(".");
587
	for(var i = syms.length; i>0; i--){
588
		var parentModule = syms.slice(0, i).join(".");
589
		if((i==1) && !this.moduleHasPrefix(parentModule)){
590
			// Support default module directory (sibling of dojo) for top-level modules
591
			syms[0] = "../" + syms[0];
592
		}else{
593
			var parentModulePath = this.getModulePrefix(parentModule);
594
			if(parentModulePath != parentModule){
595
				syms.splice(0, i, parentModulePath);
596
				break;
597
			}
598
		}
599
	}
600
	return syms; // Array
601
}
602
 
603
dojo.hostenv._global_omit_module_check = false;
604
dojo.hostenv.loadModule = function(/*String*/moduleName, /*Boolean?*/exactOnly, /*Boolean?*/omitModuleCheck){
605
// summary:
606
//	loads a Javascript module from the appropriate URI
607
//
608
// description:
609
//	loadModule("A.B") first checks to see if symbol A.B is defined.
610
//	If it is, it is simply returned (nothing to do).
611
//
612
//	If it is not defined, it will look for "A/B.js" in the script root directory,
613
//	followed by "A.js".
614
//
615
//	It throws if it cannot find a file to load, or if the symbol A.B is not
616
//	defined after loading.
617
//
618
//	It returns the object A.B.
619
//
620
//	This does nothing about importing symbols into the current package.
621
//	It is presumed that the caller will take care of that. For example, to import
622
//	all symbols:
623
//
624
//	   with (dojo.hostenv.loadModule("A.B")) {
625
//	      ...
626
//	   }
627
//
628
//	And to import just the leaf symbol:
629
//
630
//	   var B = dojo.hostenv.loadModule("A.B");
631
//	   ...
632
//
633
//	dj_load is an alias for dojo.hostenv.loadModule
634
 
635
	if(!moduleName){ return; }
636
	omitModuleCheck = this._global_omit_module_check || omitModuleCheck;
637
	var module = this.findModule(moduleName, false);
638
	if(module){
639
		return module;
640
	}
641
 
642
	// protect against infinite recursion from mutual dependencies
643
	if(dj_undef(moduleName, this.loading_modules_)){
644
		this.addedToLoadingCount.push(moduleName);
645
	}
646
	this.loading_modules_[moduleName] = 1;
647
 
648
	// convert periods to slashes
649
	var relpath = moduleName.replace(/\./g, '/') + '.js';
650
 
651
	var nsyms = moduleName.split(".");
652
 
653
	// this line allowed loading of a module manifest as if it were a namespace
654
	// it's an interesting idea, but shouldn't be combined with 'namespaces' proper
655
	// and leads to unwanted dependencies
656
	// the effect can be achieved in other (albeit less-flexible) ways now, so I am
657
	// removing this pending further design work
658
	// perhaps we can explicitly define this idea of a 'module manifest', and subclass
659
	// 'namespace manifest' from that
660
	//dojo.getNamespace(nsyms[0]);
661
 
662
	var syms = this.getModuleSymbols(moduleName);
663
	var startedRelative = ((syms[0].charAt(0) != '/') && !syms[0].match(/^\w+:/));
664
	var last = syms[syms.length - 1];
665
	var ok;
666
	// figure out if we're looking for a full package, if so, we want to do
667
	// things slightly diffrently
668
	if(last=="*"){
669
		moduleName = nsyms.slice(0, -1).join('.');
670
		while(syms.length){
671
			syms.pop();
672
			syms.push(this.pkgFileName);
673
			relpath = syms.join("/") + '.js';
674
			if(startedRelative && relpath.charAt(0)=="/"){
675
				relpath = relpath.slice(1);
676
			}
677
			ok = this.loadPath(relpath, !omitModuleCheck ? moduleName : null);
678
			if(ok){ break; }
679
			syms.pop();
680
		}
681
	}else{
682
		relpath = syms.join("/") + '.js';
683
		moduleName = nsyms.join('.');
684
		var modArg = !omitModuleCheck ? moduleName : null;
685
		ok = this.loadPath(relpath, modArg);
686
		if(!ok && !exactOnly){
687
			syms.pop();
688
			while(syms.length){
689
				relpath = syms.join('/') + '.js';
690
				ok = this.loadPath(relpath, modArg);
691
				if(ok){ break; }
692
				syms.pop();
693
				relpath = syms.join('/') + '/'+this.pkgFileName+'.js';
694
				if(startedRelative && relpath.charAt(0)=="/"){
695
					relpath = relpath.slice(1);
696
				}
697
				ok = this.loadPath(relpath, modArg);
698
				if(ok){ break; }
699
			}
700
		}
701
 
702
		if(!ok && !omitModuleCheck){
703
			dojo.raise("Could not load '" + moduleName + "'; last tried '" + relpath + "'");
704
		}
705
	}
706
 
707
	// check that the symbol was defined
708
	//Don't bother if we're doing xdomain (asynchronous) loading.
709
	if(!omitModuleCheck && !this["isXDomain"]){
710
		// pass in false so we can give better error
711
		module = this.findModule(moduleName, false);
712
		if(!module){
713
			dojo.raise("symbol '" + moduleName + "' is not defined after loading '" + relpath + "'");
714
		}
715
	}
716
 
717
	return module;
718
}
719
 
720
dojo.hostenv.startPackage = function(/*String*/packageName){
721
// summary:
722
//	Creates a JavaScript package
723
//
724
// description:
725
//	startPackage("A.B") follows the path, and at each level creates a new empty
726
//	object or uses what already exists. It returns the result.
727
//
728
// packageName: the package to be created as a String in dot notation
729
 
730
	//Make sure we have a string.
731
	var fullPkgName = String(packageName);
732
	var strippedPkgName = fullPkgName;
733
 
734
	var syms = packageName.split(/\./);
735
	if(syms[syms.length-1]=="*"){
736
		syms.pop();
737
		strippedPkgName = syms.join(".");
738
	}
739
	var evaledPkg = dojo.evalObjPath(strippedPkgName, true);
740
	this.loaded_modules_[fullPkgName] = evaledPkg;
741
	this.loaded_modules_[strippedPkgName] = evaledPkg;
742
 
743
	return evaledPkg; // Object
744
}
745
 
746
dojo.hostenv.findModule = function(/*String*/moduleName, /*Boolean?*/mustExist){
747
// summary:
748
//	Returns the Object representing the module, if it exists, otherwise null.
749
//
750
// moduleName A fully qualified module including package name, like 'A.B'.
751
// mustExist Optional, default false. throw instead of returning null
752
//	if the module does not currently exist.
753
 
754
	var lmn = String(moduleName);
755
 
756
	if(this.loaded_modules_[lmn]){
757
		return this.loaded_modules_[lmn]; // Object
758
	}
759
 
760
	if(mustExist){
761
		dojo.raise("no loaded module named '" + moduleName + "'");
762
	}
763
	return null; // null
764
}
765
 
766
//Start of old bootstrap2:
767
 
768
dojo.kwCompoundRequire = function(/*Object containing Arrays*/modMap){
769
// description:
770
//	This method taks a "map" of arrays which one can use to optionally load dojo
771
//	modules. The map is indexed by the possible dojo.hostenv.name_ values, with
772
//	two additional values: "default" and "common". The items in the "default"
773
//	array will be loaded if none of the other items have been choosen based on
774
//	the hostenv.name_ item. The items in the "common" array will _always_ be
775
//	loaded, regardless of which list is chosen.  Here's how it's normally
776
//	called:
777
//
778
//	dojo.kwCompoundRequire({
779
//		browser: [
780
//			["foo.bar.baz", true, true], // an example that passes multiple args to loadModule()
781
//			"foo.sample.*",
782
//			"foo.test,
783
//		],
784
//		default: [ "foo.sample.*" ],
785
//		common: [ "really.important.module.*" ]
786
//	});
787
 
788
	var common = modMap["common"]||[];
789
	var result = modMap[dojo.hostenv.name_] ? common.concat(modMap[dojo.hostenv.name_]||[]) : common.concat(modMap["default"]||[]);
790
 
791
	for(var x=0; x<result.length; x++){
792
		var curr = result[x];
793
		if(curr.constructor == Array){
794
			dojo.hostenv.loadModule.apply(dojo.hostenv, curr);
795
		}else{
796
			dojo.hostenv.loadModule(curr);
797
		}
798
	}
799
}
800
 
801
dojo.require = function(/*String*/ resourceName){
802
	// summary
803
	//	Ensure that the given resource (ie, javascript
804
	//	source file) has been loaded.
805
	// description
806
	//	dojo.require() is similar to C's #include command or java's "import" command.
807
	//	You call dojo.require() to pull in the resources (ie, javascript source files)
808
	//	that define the functions you are using.
809
	//
810
	//	Note that in the case of a build, many resources have already been included
811
	//	into dojo.js (ie, many of the javascript source files have been compressed and
812
	//	concatened into dojo.js), so many dojo.require() calls will simply return
813
	//	without downloading anything.
814
	dojo.hostenv.loadModule.apply(dojo.hostenv, arguments);
815
}
816
 
817
dojo.requireIf = function(/*Boolean*/ condition, /*String*/ resourceName){
818
	// summary
819
	//	If the condition is true then call dojo.require() for the specified resource
820
	var arg0 = arguments[0];
821
	if((arg0 === true)||(arg0=="common")||(arg0 && dojo.render[arg0].capable)){
822
		var args = [];
823
		for (var i = 1; i < arguments.length; i++) { args.push(arguments[i]); }
824
		dojo.require.apply(dojo, args);
825
	}
826
}
827
 
828
dojo.requireAfterIf = dojo.requireIf;
829
 
830
dojo.provide = function(/*String*/ resourceName){
831
	// summary
832
	//	Each javascript source file must have (exactly) one dojo.provide()
833
	//	call at the top of the file, corresponding to the file name.
834
	//	For example, dojo/src/foo.js must have dojo.provide("dojo.foo"); at the top of the file.
835
	//
836
	// description
837
	//	Each javascript source file is called a resource.  When a resource
838
	//	is loaded by the browser, dojo.provide() registers that it has
839
	//	been loaded.
840
	//
841
	//	For backwards compatibility reasons, in addition to registering the resource,
842
	//	dojo.provide() also ensures that the javascript object for the module exists.  For
843
	//	example, dojo.provide("dojo.html.common"), in addition to registering that common.js
844
	//	is a resource for the dojo.html module, will ensure that the dojo.html javascript object
845
	//	exists, so that calls like dojo.html.foo = function(){ ... } don't fail.
846
	//
847
	//	In the case of a build (or in the future, a rollup), where multiple javascript source
848
	//	files are combined into one bigger file (similar to a .lib or .jar file), that file
849
	//	will contain multiple dojo.provide() calls, to note that it includes
850
	//	multiple resources.
851
	return dojo.hostenv.startPackage.apply(dojo.hostenv, arguments);
852
}
853
 
854
dojo.registerModulePath = function(/*String*/module, /*String*/prefix){
855
	// summary: maps a module name to a path
856
	// description: An unregistered module is given the default path of ../<module>,
857
	//	relative to Dojo root. For example, module acme is mapped to ../acme.
858
	//	If you want to use a different module name, use dojo.registerModulePath.
859
	return dojo.hostenv.setModulePrefix(module, prefix);
860
}
861
 
862
if(djConfig["modulePaths"]){
863
	for(var param in djConfig["modulePaths"]){
864
		dojo.registerModulePath(param, djConfig["modulePaths"][param]);
865
	}
866
}
867
 
868
dojo.setModulePrefix = function(/*String*/module, /*String*/prefix){
869
	// summary: maps a module name to a path
870
	dojo.deprecated('dojo.setModulePrefix("' + module + '", "' + prefix + '")', "replaced by dojo.registerModulePath", "0.5");
871
	return dojo.registerModulePath(module, prefix);
872
}
873
 
874
dojo.exists = function(/*Object*/obj, /*String*/name){
875
	// summary: determine if an object supports a given method
876
	// description: useful for longer api chains where you have to test each object in the chain
877
	var p = name.split(".");
878
	for(var i = 0; i < p.length; i++){
879
		if(!obj[p[i]]){ return false; } // Boolean
880
		obj = obj[p[i]];
881
	}
882
	return true; // Boolean
883
}
884
 
885
// Localization routines
886
 
887
dojo.hostenv.normalizeLocale = function(/*String?*/locale){
888
//	summary:
889
//		Returns canonical form of locale, as used by Dojo.  All variants are case-insensitive and are separated by '-'
890
//		as specified in RFC 3066. If no locale is specified, the user agent's default is returned.
891
 
892
	var result = locale ? locale.toLowerCase() : dojo.locale;
893
	if(result == "root"){
894
		result = "ROOT";
895
	}
896
	return result;// String
897
};
898
 
899
dojo.hostenv.searchLocalePath = function(/*String*/locale, /*Boolean*/down, /*Function*/searchFunc){
900
//	summary:
901
//		A helper method to assist in searching for locale-based resources.  Will iterate through
902
//		the variants of a particular locale, either up or down, executing a callback function.
903
//		For example, "en-us" and true will try "en-us" followed by "en" and finally "ROOT".
904
 
905
	locale = dojo.hostenv.normalizeLocale(locale);
906
 
907
	var elements = locale.split('-');
908
	var searchlist = [];
909
	for(var i = elements.length; i > 0; i--){
910
		searchlist.push(elements.slice(0, i).join('-'));
911
	}
912
	searchlist.push(false);
913
	if(down){searchlist.reverse();}
914
 
915
	for(var j = searchlist.length - 1; j >= 0; j--){
916
		var loc = searchlist[j] || "ROOT";
917
		var stop = searchFunc(loc);
918
		if(stop){ break; }
919
	}
920
}
921
 
922
//These two functions are placed outside of preloadLocalizations
923
//So that the xd loading can use/override them.
924
dojo.hostenv.localesGenerated /***BUILD:localesGenerated***/; // value will be inserted here at build time, if necessary
925
 
926
dojo.hostenv.registerNlsPrefix = function(){
927
// summary:
928
//	Register module "nls" to point where Dojo can find pre-built localization files
929
	dojo.registerModulePath("nls","nls");
930
}
931
 
932
dojo.hostenv.preloadLocalizations = function(){
933
// summary:
934
//	Load built, flattened resource bundles, if available for all locales used in the page.
935
//	Execute only once.  Note that this is a no-op unless there is a build.
936
 
937
	if(dojo.hostenv.localesGenerated){
938
		dojo.hostenv.registerNlsPrefix();
939
 
940
		function preload(locale){
941
			locale = dojo.hostenv.normalizeLocale(locale);
942
			dojo.hostenv.searchLocalePath(locale, true, function(loc){
943
				for(var i=0; i<dojo.hostenv.localesGenerated.length;i++){
944
					if(dojo.hostenv.localesGenerated[i] == loc){
945
						dojo["require"]("nls.dojo_"+loc);
946
						return true; // Boolean
947
					}
948
				}
949
				return false; // Boolean
950
			});
951
		}
952
		preload();
953
		var extra = djConfig.extraLocale||[];
954
		for(var i=0; i<extra.length; i++){
955
			preload(extra[i]);
956
		}
957
	}
958
	dojo.hostenv.preloadLocalizations = function(){};
959
}
960
 
961
dojo.requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String?*/availableFlatLocales){
962
// summary:
963
//	Declares translated resources and loads them if necessary, in the same style as dojo.require.
964
//	Contents of the resource bundle are typically strings, but may be any name/value pair,
965
//	represented in JSON format.  See also dojo.i18n.getLocalization.
966
//
967
// moduleName: name of the package containing the "nls" directory in which the bundle is found
968
// bundleName: bundle name, i.e. the filename without the '.js' suffix
969
// locale: the locale to load (optional)  By default, the browser's user locale as defined by dojo.locale
970
// availableFlatLocales: A comma-separated list of the available, flattened locales for this bundle.
971
// This argument should only be set by the build process.
972
//
973
// description:
974
//	Load translated resource bundles provided underneath the "nls" directory within a package.
975
//	Translated resources may be located in different packages throughout the source tree.  For example,
976
//	a particular widget may define one or more resource bundles, structured in a program as follows,
977
//	where moduleName is mycode.mywidget and bundleNames available include bundleone and bundletwo:
978
//	...
979
//	mycode/
980
//	 mywidget/
981
//	  nls/
982
//	   bundleone.js (the fallback translation, English in this example)
983
//	   bundletwo.js (also a fallback translation)
984
//	   de/
985
//	    bundleone.js
986
//	    bundletwo.js
987
//	   de-at/
988
//	    bundleone.js
989
//	   en/
990
//	    (empty; use the fallback translation)
991
//	   en-us/
992
//	    bundleone.js
993
//	   en-gb/
994
//	    bundleone.js
995
//	   es/
996
//	    bundleone.js
997
//	    bundletwo.js
998
//	  ...etc
999
//	...
1000
//	Each directory is named for a locale as specified by RFC 3066, (http://www.ietf.org/rfc/rfc3066.txt),
1001
//	normalized in lowercase.  Note that the two bundles in the example do not define all the same variants.
1002
//	For a given locale, bundles will be loaded for that locale and all more general locales above it, including
1003
//	a fallback at the root directory.  For example, a declaration for the "de-at" locale will first
1004
//	load nls/de-at/bundleone.js, then nls/de/bundleone.js and finally nls/bundleone.js.  The data will
1005
//	be flattened into a single Object so that lookups will follow this cascading pattern.  An optional build
1006
//	step can preload the bundles to avoid data redundancy and the multiple network hits normally required to
1007
//	load these resources.
1008
 
1009
	dojo.hostenv.preloadLocalizations();
1010
	var targetLocale = dojo.hostenv.normalizeLocale(locale);
1011
 	var bundlePackage = [moduleName, "nls", bundleName].join(".");
1012
//NOTE: When loading these resources, the packaging does not match what is on disk.  This is an
1013
// implementation detail, as this is just a private data structure to hold the loaded resources.
1014
// e.g. tests/hello/nls/en-us/salutations.js is loaded as the object tests.hello.nls.salutations.en_us={...}
1015
// The structure on disk is intended to be most convenient for developers and translators, but in memory
1016
// it is more logical and efficient to store in a different order.  Locales cannot use dashes, since the
1017
// resulting path will not evaluate as valid JS, so we translate them to underscores.
1018
 
1019
	//Find the best-match locale to load if we have available flat locales.
1020
	var bestLocale = "";
1021
	if(availableFlatLocales){
1022
		var flatLocales = availableFlatLocales.split(",");
1023
		for(var i = 0; i < flatLocales.length; i++){
1024
			//Locale must match from start of string.
1025
			if(targetLocale.indexOf(flatLocales[i]) == 0){
1026
				if(flatLocales[i].length > bestLocale.length){
1027
					bestLocale = flatLocales[i];
1028
				}
1029
			}
1030
		}
1031
		if(!bestLocale){
1032
			bestLocale = "ROOT";
1033
		}
1034
	}
1035
 
1036
	//See if the desired locale is already loaded.
1037
	var tempLocale = availableFlatLocales ? bestLocale : targetLocale;
1038
	var bundle = dojo.hostenv.findModule(bundlePackage);
1039
	var localizedBundle = null;
1040
	if(bundle){
1041
		if(djConfig.localizationComplete && bundle._built){return;}
1042
		var jsLoc = tempLocale.replace('-', '_');
1043
		var translationPackage = bundlePackage+"."+jsLoc;
1044
		localizedBundle = dojo.hostenv.findModule(translationPackage);
1045
	}
1046
 
1047
	if(!localizedBundle){
1048
		bundle = dojo.hostenv.startPackage(bundlePackage);
1049
		var syms = dojo.hostenv.getModuleSymbols(moduleName);
1050
		var modpath = syms.concat("nls").join("/");
1051
		var parent;
1052
 
1053
		dojo.hostenv.searchLocalePath(tempLocale, availableFlatLocales, function(loc){
1054
			var jsLoc = loc.replace('-', '_');
1055
			var translationPackage = bundlePackage + "." + jsLoc;
1056
			var loaded = false;
1057
			if(!dojo.hostenv.findModule(translationPackage)){
1058
				// Mark loaded whether it's found or not, so that further load attempts will not be made
1059
				dojo.hostenv.startPackage(translationPackage);
1060
				var module = [modpath];
1061
				if(loc != "ROOT"){module.push(loc);}
1062
				module.push(bundleName);
1063
				var filespec = module.join("/") + '.js';
1064
				loaded = dojo.hostenv.loadPath(filespec, null, function(hash){
1065
					// Use singleton with prototype to point to parent bundle, then mix-in result from loadPath
1066
					var clazz = function(){};
1067
					clazz.prototype = parent;
1068
					bundle[jsLoc] = new clazz();
1069
					for(var j in hash){ bundle[jsLoc][j] = hash[j]; }
1070
				});
1071
			}else{
1072
				loaded = true;
1073
			}
1074
			if(loaded && bundle[jsLoc]){
1075
				parent = bundle[jsLoc];
1076
			}else{
1077
				bundle[jsLoc] = parent;
1078
			}
1079
 
1080
			if(availableFlatLocales){
1081
				//Stop the locale path searching if we know the availableFlatLocales, since
1082
				//the first call to this function will load the only bundle that is needed.
1083
				return true;
1084
			}
1085
		});
1086
	}
1087
 
1088
	//Save the best locale bundle as the target locale bundle when we know the
1089
	//the available bundles.
1090
	if(availableFlatLocales && targetLocale != bestLocale){
1091
		bundle[targetLocale.replace('-', '_')] = bundle[bestLocale.replace('-', '_')];
1092
	}
1093
};
1094
 
1095
(function(){
1096
	// If other locales are used, dojo.requireLocalization should load them as well, by default.
1097
	// Override dojo.requireLocalization to do load the default bundle, then iterate through the
1098
	// extraLocale list and load those translations as well, unless a particular locale was requested.
1099
 
1100
	var extra = djConfig.extraLocale;
1101
	if(extra){
1102
		if(!extra instanceof Array){
1103
			extra = [extra];
1104
		}
1105
 
1106
		var req = dojo.requireLocalization;
1107
		dojo.requireLocalization = function(m, b, locale, availableFlatLocales){
1108
			req(m,b,locale, availableFlatLocales);
1109
			if(locale){return;}
1110
			for(var i=0; i<extra.length; i++){
1111
				req(m,b,extra[i], availableFlatLocales);
1112
			}
1113
		};
1114
	}
1115
})();
1116
 
1117
};
1118
 
1119
if(typeof window != 'undefined'){
1120
 
1121
	// attempt to figure out the path to dojo if it isn't set in the config
1122
	(function(){
1123
		// before we get any further with the config options, try to pick them out
1124
		// of the URL. Most of this code is from NW
1125
		if(djConfig.allowQueryConfig){
1126
			var baseUrl = document.location.toString(); // FIXME: use location.query instead?
1127
			var params = baseUrl.split("?", 2);
1128
			if(params.length > 1){
1129
				var paramStr = params[1];
1130
				var pairs = paramStr.split("&");
1131
				for(var x in pairs){
1132
					var sp = pairs[x].split("=");
1133
					// FIXME: is this eval dangerous?
1134
					if((sp[0].length > 9)&&(sp[0].substr(0, 9) == "djConfig.")){
1135
						var opt = sp[0].substr(9);
1136
						try{
1137
							djConfig[opt]=eval(sp[1]);
1138
						}catch(e){
1139
							djConfig[opt]=sp[1];
1140
						}
1141
					}
1142
				}
1143
			}
1144
		}
1145
 
1146
		if(
1147
			((djConfig["baseScriptUri"] == "")||(djConfig["baseRelativePath"] == "")) &&
1148
			(document && document.getElementsByTagName)
1149
		){
1150
			var scripts = document.getElementsByTagName("script");
1151
			var rePkg = /(__package__|dojo|bootstrap1)\.js([\?\.]|$)/i;
1152
			for(var i = 0; i < scripts.length; i++) {
1153
				var src = scripts[i].getAttribute("src");
1154
				if(!src) { continue; }
1155
				var m = src.match(rePkg);
1156
				if(m) {
1157
					var root = src.substring(0, m.index);
1158
					if(src.indexOf("bootstrap1") > -1) { root += "../"; }
1159
					if(!this["djConfig"]) { djConfig = {}; }
1160
					if(djConfig["baseScriptUri"] == "") { djConfig["baseScriptUri"] = root; }
1161
					if(djConfig["baseRelativePath"] == "") { djConfig["baseRelativePath"] = root; }
1162
					break;
1163
				}
1164
			}
1165
		}
1166
 
1167
		// fill in the rendering support information in dojo.render.*
1168
		var dr = dojo.render;
1169
		var drh = dojo.render.html;
1170
		var drs = dojo.render.svg;
1171
		var dua = (drh.UA = navigator.userAgent);
1172
		var dav = (drh.AV = navigator.appVersion);
1173
		var t = true;
1174
		var f = false;
1175
		drh.capable = t;
1176
		drh.support.builtin = t;
1177
 
1178
		dr.ver = parseFloat(drh.AV);
1179
		dr.os.mac = dav.indexOf("Macintosh") >= 0;
1180
		dr.os.win = dav.indexOf("Windows") >= 0;
1181
		// could also be Solaris or something, but it's the same browser
1182
		dr.os.linux = dav.indexOf("X11") >= 0;
1183
 
1184
		drh.opera = dua.indexOf("Opera") >= 0;
1185
		drh.khtml = (dav.indexOf("Konqueror") >= 0)||(dav.indexOf("Safari") >= 0);
1186
		drh.safari = dav.indexOf("Safari") >= 0;
1187
		var geckoPos = dua.indexOf("Gecko");
1188
		drh.mozilla = drh.moz = (geckoPos >= 0)&&(!drh.khtml);
1189
		if (drh.mozilla) {
1190
			// gecko version is YYYYMMDD
1191
			drh.geckoVersion = dua.substring(geckoPos + 6, geckoPos + 14);
1192
		}
1193
		drh.ie = (document.all)&&(!drh.opera);
1194
		drh.ie50 = drh.ie && dav.indexOf("MSIE 5.0")>=0;
1195
		drh.ie55 = drh.ie && dav.indexOf("MSIE 5.5")>=0;
1196
		drh.ie60 = drh.ie && dav.indexOf("MSIE 6.0")>=0;
1197
		drh.ie70 = drh.ie && dav.indexOf("MSIE 7.0")>=0;
1198
 
1199
		var cm = document["compatMode"];
1200
		drh.quirks = (cm == "BackCompat")||(cm == "QuirksMode")||drh.ie55||drh.ie50;
1201
 
1202
		// TODO: is the HTML LANG attribute relevant?
1203
		dojo.locale = dojo.locale || (drh.ie ? navigator.userLanguage : navigator.language).toLowerCase();
1204
 
1205
		dr.vml.capable=drh.ie;
1206
		drs.capable = f;
1207
		drs.support.plugin = f;
1208
		drs.support.builtin = f;
1209
		var tdoc = window["document"];
1210
		var tdi = tdoc["implementation"];
1211
 
1212
		if((tdi)&&(tdi["hasFeature"])&&(tdi.hasFeature("org.w3c.dom.svg", "1.0"))){
1213
			drs.capable = t;
1214
			drs.support.builtin = t;
1215
			drs.support.plugin = f;
1216
		}
1217
		// webkits after 420 support SVG natively. The test string is "AppleWebKit/420+"
1218
		if(drh.safari){
1219
			var tmp = dua.split("AppleWebKit/")[1];
1220
			var ver = parseFloat(tmp.split(" ")[0]);
1221
			if(ver >= 420){
1222
				drs.capable = t;
1223
				drs.support.builtin = t;
1224
				drs.support.plugin = f;
1225
			}
1226
		}else{
1227
		}
1228
	})();
1229
 
1230
	dojo.hostenv.startPackage("dojo.hostenv");
1231
 
1232
	dojo.render.name = dojo.hostenv.name_ = 'browser';
1233
	dojo.hostenv.searchIds = [];
1234
 
1235
	// These are in order of decreasing likelihood; this will change in time.
1236
	dojo.hostenv._XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];
1237
 
1238
	dojo.hostenv.getXmlhttpObject = function(){
1239
		// summary: does the work of portably generating a new XMLHTTPRequest object.
1240
		var http = null;
1241
		var last_e = null;
1242
		try{ http = new XMLHttpRequest(); }catch(e){}
1243
		if(!http){
1244
			for(var i=0; i<3; ++i){
1245
				var progid = dojo.hostenv._XMLHTTP_PROGIDS[i];
1246
				try{
1247
					http = new ActiveXObject(progid);
1248
				}catch(e){
1249
					last_e = e;
1250
				}
1251
 
1252
				if(http){
1253
					dojo.hostenv._XMLHTTP_PROGIDS = [progid];  // so faster next time
1254
					break;
1255
				}
1256
			}
1257
 
1258
			/*if(http && !http.toString) {
1259
				http.toString = function() { "[object XMLHttpRequest]"; }
1260
			}*/
1261
		}
1262
 
1263
		if(!http){
1264
			return dojo.raise("XMLHTTP not available", last_e);
1265
		}
1266
 
1267
		return http; // XMLHTTPRequest instance
1268
	}
1269
 
1270
	dojo.hostenv._blockAsync = false;
1271
	dojo.hostenv.getText = function(uri, async_cb, fail_ok){
1272
		// summary: Read the contents of the specified uri and return those contents.
1273
		// uri:
1274
		//		A relative or absolute uri. If absolute, it still must be in
1275
		//		the same "domain" as we are.
1276
		// async_cb:
1277
		//		If not specified, load synchronously. If specified, load
1278
		//		asynchronously, and use async_cb as the progress handler which
1279
		//		takes the xmlhttp object as its argument. If async_cb, this
1280
		//		function returns null.
1281
		// fail_ok:
1282
		//		Default false. If fail_ok and !async_cb and loading fails,
1283
		//		return null instead of throwing.
1284
 
1285
		// need to block async callbacks from snatching this thread as the result
1286
		// of an async callback might call another sync XHR, this hangs khtml forever
1287
		// hostenv._blockAsync must also be checked in BrowserIO's watchInFlight()
1288
		// NOTE: must be declared before scope switches ie. this.getXmlhttpObject()
1289
		if(!async_cb){ this._blockAsync = true; }
1290
 
1291
		var http = this.getXmlhttpObject();
1292
 
1293
		function isDocumentOk(http){
1294
			var stat = http["status"];
1295
			// allow a 304 use cache, needed in konq (is this compliant with the http spec?)
1296
			return Boolean((!stat)||((200 <= stat)&&(300 > stat))||(stat==304));
1297
		}
1298
 
1299
		if(async_cb){
1300
			var _this = this, timer = null, gbl = dojo.global();
1301
			var xhr = dojo.evalObjPath("dojo.io.XMLHTTPTransport");
1302
			http.onreadystatechange = function(){
1303
				if(timer){ gbl.clearTimeout(timer); timer = null; }
1304
				if(_this._blockAsync || (xhr && xhr._blockAsync)){
1305
					timer = gbl.setTimeout(function () { http.onreadystatechange.apply(this); }, 10);
1306
				}else{
1307
					if(4==http.readyState){
1308
						if(isDocumentOk(http)){
1309
							// dojo.debug("LOADED URI: "+uri);
1310
							async_cb(http.responseText);
1311
						}
1312
					}
1313
				}
1314
			}
1315
		}
1316
 
1317
		http.open('GET', uri, async_cb ? true : false);
1318
		try{
1319
			http.send(null);
1320
			if(async_cb){
1321
				return null;
1322
			}
1323
			if(!isDocumentOk(http)){
1324
				var err = Error("Unable to load "+uri+" status:"+ http.status);
1325
				err.status = http.status;
1326
				err.responseText = http.responseText;
1327
				throw err;
1328
			}
1329
		}catch(e){
1330
			this._blockAsync = false;
1331
			if((fail_ok)&&(!async_cb)){
1332
				return null;
1333
			}else{
1334
				throw e;
1335
			}
1336
		}
1337
 
1338
		this._blockAsync = false;
1339
		return http.responseText; // String
1340
	}
1341
 
1342
	dojo.hostenv.defaultDebugContainerId = 'dojoDebug';
1343
	dojo.hostenv._println_buffer = [];
1344
	dojo.hostenv._println_safe = false;
1345
	dojo.hostenv.println = function(/*String*/line){
1346
		// summary:
1347
		//		prints the provided line to whatever logging container is
1348
		//		available. If the page isn't loaded yet, the line may be added
1349
		//		to a buffer for printing later.
1350
		if(!dojo.hostenv._println_safe){
1351
			dojo.hostenv._println_buffer.push(line);
1352
		}else{
1353
			try {
1354
				var console = document.getElementById(djConfig.debugContainerId ?
1355
					djConfig.debugContainerId : dojo.hostenv.defaultDebugContainerId);
1356
				if(!console) { console = dojo.body(); }
1357
 
1358
				var div = document.createElement("div");
1359
				div.appendChild(document.createTextNode(line));
1360
				console.appendChild(div);
1361
			} catch (e) {
1362
				try{
1363
					// safari needs the output wrapped in an element for some reason
1364
					document.write("<div>" + line + "</div>");
1365
				}catch(e2){
1366
					window.status = line;
1367
				}
1368
			}
1369
		}
1370
	}
1371
 
1372
	dojo.addOnLoad(function(){
1373
		dojo.hostenv._println_safe = true;
1374
		while(dojo.hostenv._println_buffer.length > 0){
1375
			dojo.hostenv.println(dojo.hostenv._println_buffer.shift());
1376
		}
1377
	});
1378
 
1379
	function dj_addNodeEvtHdlr(/*DomNode*/node, /*String*/evtName, /*Function*/fp){
1380
		// summary:
1381
		//		non-destructively adds the specified function to the node's
1382
		//		evtName handler.
1383
		// node: the DomNode to add the handler to
1384
		// evtName: should be in the form "click" for "onclick" handlers
1385
		var oldHandler = node["on"+evtName] || function(){};
1386
		node["on"+evtName] = function(){
1387
			fp.apply(node, arguments);
1388
			oldHandler.apply(node, arguments);
1389
		}
1390
		return true;
1391
	}
1392
 
1422 alexandre_ 1393
	dojo.hostenv._djInitFired = false;
1318 alexandre_ 1394
	//	BEGIN DOMContentLoaded, from Dean Edwards (http://dean.edwards.name/weblog/2006/06/again/)
1395
	function dj_load_init(e){
1422 alexandre_ 1396
		dojo.hostenv._djInitFired = true;
1318 alexandre_ 1397
		// allow multiple calls, only first one will take effect
1398
		// A bug in khtml calls events callbacks for document for event which isnt supported
1399
		// for example a created contextmenu event calls DOMContentLoaded, workaround
1400
		var type = (e && e.type) ? e.type.toLowerCase() : "load";
1401
		if(arguments.callee.initialized || (type!="domcontentloaded" && type!="load")){ return; }
1402
		arguments.callee.initialized = true;
1403
		if(typeof(_timer) != 'undefined'){
1404
			clearInterval(_timer);
1405
			delete _timer;
1406
		}
1407
 
1408
		var initFunc = function(){
1409
			//perform initialization
1410
			if(dojo.render.html.ie){
1411
				dojo.hostenv.makeWidgets();
1412
			}
1413
		};
1414
 
1415
		if(dojo.hostenv.inFlightCount == 0){
1416
			initFunc();
1417
			dojo.hostenv.modulesLoaded();
1418
		}else{
1419
			//This else case should be xdomain loading.
1420
			//Make sure this is the first thing in the load listener array.
1421
			//Part of the dojo.addOnLoad guarantee is that when the listeners are notified,
1422
			//It means the DOM (or page) has loaded and that widgets have been parsed.
1423
			dojo.hostenv.modulesLoadedListeners.unshift(initFunc);
1424
		}
1425
	}
1426
 
1427
	//	START DOMContentLoaded
1428
	// Mozilla and Opera 9 expose the event we could use
1429
	if(document.addEventListener){
1430
		// NOTE:
1431
		//		due to a threading issue in Firefox 2.0, we can't enable
1432
		//		DOMContentLoaded on that platform. For more information, see:
1433
		//		http://trac.dojotoolkit.org/ticket/1704
1434
		if(dojo.render.html.opera || (dojo.render.html.moz && (djConfig["enableMozDomContentLoaded"] === true))){
1435
			document.addEventListener("DOMContentLoaded", dj_load_init, null);
1436
		}
1437
 
1438
		//	mainly for Opera 8.5, won't be fired if DOMContentLoaded fired already.
1439
		//  also used for Mozilla because of trac #1640
1440
		window.addEventListener("load", dj_load_init, null);
1441
	}
1442
 
1443
	// 	for Internet Explorer. readyState will not be achieved on init call, but dojo doesn't need it
1444
	//	however, we'll include it because we don't know if there are other functions added that might.
1445
	//	Note that this has changed because the build process strips all comments--including conditional
1446
	//		ones.
1447
	if(dojo.render.html.ie && dojo.render.os.win){
1448
		document.attachEvent("onreadystatechange", function(e){
1449
			if(document.readyState == "complete"){
1450
				dj_load_init();
1451
			}
1452
		});
1453
	}
1454
 
1455
	if (/(WebKit|khtml)/i.test(navigator.userAgent)) { // sniff
1456
		var _timer = setInterval(function() {
1457
			if (/loaded|complete/.test(document.readyState)) {
1458
				dj_load_init(); // call the onload handler
1459
			}
1460
		}, 10);
1461
	}
1462
	//	END DOMContentLoaded
1463
 
1464
	// IE WebControl hosted in an application can fire "beforeunload" and "unload"
1465
	// events when control visibility changes, causing Dojo to unload too soon. The
1466
	// following code fixes the problem
1467
	// Reference: http://support.microsoft.com/default.aspx?scid=kb;en-us;199155
1468
	if(dojo.render.html.ie){
1469
		dj_addNodeEvtHdlr(window, "beforeunload", function(){
1470
			dojo.hostenv._unloading = true;
1471
			window.setTimeout(function() {
1472
				dojo.hostenv._unloading = false;
1473
			}, 0);
1474
		});
1475
	}
1476
 
1477
	dj_addNodeEvtHdlr(window, "unload", function(){
1478
		dojo.hostenv.unloaded();
1479
		if((!dojo.render.html.ie)||(dojo.render.html.ie && dojo.hostenv._unloading)){
1480
			dojo.hostenv.unloaded();
1481
		}
1482
	});
1483
 
1484
	dojo.hostenv.makeWidgets = function(){
1485
		// you can put searchIds in djConfig and dojo.hostenv at the moment
1486
		// we should probably eventually move to one or the other
1487
		var sids = [];
1488
		if(djConfig.searchIds && djConfig.searchIds.length > 0) {
1489
			sids = sids.concat(djConfig.searchIds);
1490
		}
1491
		if(dojo.hostenv.searchIds && dojo.hostenv.searchIds.length > 0) {
1492
			sids = sids.concat(dojo.hostenv.searchIds);
1493
		}
1494
 
1495
		if((djConfig.parseWidgets)||(sids.length > 0)){
1496
			if(dojo.evalObjPath("dojo.widget.Parse")){
1497
				// we must do this on a delay to avoid:
1498
				//	http://www.shaftek.org/blog/archives/000212.html
1499
				// (IE bug)
1500
					var parser = new dojo.xml.Parse();
1501
					if(sids.length > 0){
1502
						for(var x=0; x<sids.length; x++){
1503
							var tmpNode = document.getElementById(sids[x]);
1504
							if(!tmpNode){ continue; }
1505
							var frag = parser.parseElement(tmpNode, null, true);
1506
							dojo.widget.getParser().createComponents(frag);
1507
						}
1508
					}else if(djConfig.parseWidgets){
1509
						var frag  = parser.parseElement(dojo.body(), null, true);
1510
						dojo.widget.getParser().createComponents(frag);
1511
					}
1512
			}
1513
		}
1514
	}
1515
 
1516
	dojo.addOnLoad(function(){
1517
		if(!dojo.render.html.ie) {
1518
			dojo.hostenv.makeWidgets();
1519
		}
1520
	});
1521
 
1522
	try{
1523
		if(dojo.render.html.ie){
1524
			document.namespaces.add("v","urn:schemas-microsoft-com:vml");
1525
			document.createStyleSheet().addRule("v\\:*", "behavior:url(#default#VML)");
1526
		}
1527
	}catch(e){ }
1528
 
1529
	// stub, over-ridden by debugging code. This will at least keep us from
1530
	// breaking when it's not included
1531
	dojo.hostenv.writeIncludes = function(){}
1532
 
1533
	//TODOC:  HOW TO DOC THIS?
1534
	// @global: dj_currentDocument
1535
	// summary:
1536
	//		Current document object. 'dj_currentDocument' can be modified for temporary context shifting.
1537
	// description:
1538
	//    dojo.doc() returns dojo.currentDocument.
1539
	//		Refer to dojo.doc() rather than referring to 'window.document' to ensure your
1540
	//		code runs correctly in managed contexts.
1541
	if(!dj_undef("document", this)){
1542
		dj_currentDocument = this.document;
1543
	}
1544
 
1545
	dojo.doc = function(){
1546
		// summary:
1547
		//		return the document object associated with the dojo.global()
1548
		return dj_currentDocument;
1549
	}
1550
 
1551
	dojo.body = function(){
1552
		// summary:
1553
		//		return the body object associated with dojo.doc()
1554
		// Note: document.body is not defined for a strict xhtml document
1555
		return dojo.doc().body || dojo.doc().getElementsByTagName("body")[0];
1556
	}
1557
 
1558
	dojo.byId = function(/*String*/id, /*DocumentElement*/doc){
1559
		// summary:
1560
		// 		similar to other library's "$" function, takes a string
1561
		// 		representing a DOM id or a DomNode and returns the
1562
		// 		corresponding DomNode. If a Node is passed, this function is a
1563
		// 		no-op. Returns a single DOM node or null, working around
1564
		// 		several browser-specific bugs to do so.
1565
		// id: DOM id or DOM Node
1566
		// doc:
1567
		//		optional, defaults to the current value of dj_currentDocument.
1568
		//		Can be used to retreive node references from other documents.
1569
		if((id)&&((typeof id == "string")||(id instanceof String))){
1570
			if(!doc){ doc = dj_currentDocument; }
1571
			var ele = doc.getElementById(id);
1572
			// workaround bug in IE and Opera 8.2 where getElementById returns wrong element
1573
			if(ele && (ele.id != id) && doc.all){
1574
				ele = null;
1575
				// get all matching elements with this id
1576
				eles = doc.all[id];
1577
				if(eles){
1578
					// if more than 1, choose first with the correct id
1579
					if(eles.length){
1580
						for(var i=0; i<eles.length; i++){
1581
							if(eles[i].id == id){
1582
								ele = eles[i];
1583
								break;
1584
							}
1585
						}
1586
					// return 1 and only element
1587
					}else{
1588
						ele = eles;
1589
					}
1590
				}
1591
			}
1592
			return ele; // DomNode
1593
		}
1594
		return id; // DomNode
1595
	}
1596
 
1597
	dojo.setContext = function(/*Object*/globalObject, /*DocumentElement*/globalDocument){
1598
		// summary:
1599
		//		changes the behavior of many core Dojo functions that deal with
1600
		//		namespace and DOM lookup, changing them to work in a new global
1601
		//		context. The varibles dj_currentContext and dj_currentDocument
1602
		//		are modified as a result of calling this function.
1603
		dj_currentContext = globalObject;
1604
		dj_currentDocument = globalDocument;
1605
	};
1606
 
1607
	dojo._fireCallback = function(callback, context, cbArguments){
1608
		if((context)&&((typeof callback == "string")||(callback instanceof String))){
1609
			callback=context[callback];
1610
		}
1611
		return (context ? callback.apply(context, cbArguments || [ ]) : callback());
1612
	}
1613
 
1614
	dojo.withGlobal = function(/*Object*/globalObject, /*Function*/callback, /*Object?*/thisObject, /*Array?*/cbArguments){
1615
		// summary:
1616
		//		Call callback with globalObject as dojo.global() and globalObject.document
1617
		//		as dojo.doc(). If provided, globalObject will be executed in the context of
1618
		//		object thisObject
1619
		// description:
1620
		//		When callback() returns or throws an error, the dojo.global() and dojo.doc() will
1621
		//		be restored to its previous state.
1622
		var rval;
1623
		var oldGlob = dj_currentContext;
1624
		var oldDoc = dj_currentDocument;
1625
		try{
1626
			dojo.setContext(globalObject, globalObject.document);
1627
			rval = dojo._fireCallback(callback, thisObject, cbArguments);
1628
		}finally{
1629
			dojo.setContext(oldGlob, oldDoc);
1630
		}
1631
		return rval;
1632
	}
1633
 
1634
	dojo.withDoc = function (/*Object*/documentObject, /*Function*/callback, /*Object?*/thisObject, /*Array?*/cbArguments) {
1635
		// summary:
1636
		//		Call callback with documentObject as dojo.doc(). If provided, callback will be executed
1637
		//		in the context of object thisObject
1638
		// description:
1639
		//		When callback() returns or throws an error, the dojo.doc() will
1640
		//		be restored to its previous state.
1641
		var rval;
1642
		var oldDoc = dj_currentDocument;
1643
		try{
1644
			dj_currentDocument = documentObject;
1645
			rval = dojo._fireCallback(callback, thisObject, cbArguments);
1646
		}finally{
1647
			dj_currentDocument = oldDoc;
1648
		}
1649
		return rval;
1650
	}
1651
 
1652
} //if (typeof window != 'undefined')
1653
 
1654
//Load debug code if necessary.
1655
dojo.requireIf((djConfig["isDebug"] || djConfig["debugAtAllCosts"]), "dojo.debug");
1656
 
1657
//window.widget is for Dashboard detection
1658
//The full conditionals are spelled out to avoid issues during builds.
1659
//Builds may be looking for require/requireIf statements and processing them.
1660
dojo.requireIf(djConfig["debugAtAllCosts"] && !window.widget && !djConfig["useXDomain"], "dojo.browser_debug");
1661
dojo.requireIf(djConfig["debugAtAllCosts"] && !window.widget && djConfig["useXDomain"], "dojo.browser_debug_xd");
1662
 
1663
dojo.provide("dojo.string.common");
1664
 
1665
dojo.string.trim = function(/* string */str, /* integer? */wh){
1666
	//	summary
1667
	//	Trim whitespace from str.  If wh > 0, trim from start, if wh < 0, trim from end, else both
1668
	if(!str.replace){ return str; }
1669
	if(!str.length){ return str; }
1670
	var re = (wh > 0) ? (/^\s+/) : (wh < 0) ? (/\s+$/) : (/^\s+|\s+$/g);
1671
	return str.replace(re, "");	//	string
1672
}
1673
 
1674
dojo.string.trimStart = function(/* string */str) {
1675
	//	summary
1676
	//	Trim whitespace at the beginning of 'str'
1677
	return dojo.string.trim(str, 1);	//	string
1678
}
1679
 
1680
dojo.string.trimEnd = function(/* string */str) {
1681
	//	summary
1682
	//	Trim whitespace at the end of 'str'
1683
	return dojo.string.trim(str, -1);
1684
}
1685
 
1686
dojo.string.repeat = function(/* string */str, /* integer */count, /* string? */separator) {
1687
	//	summary
1688
	//	Return 'str' repeated 'count' times, optionally placing 'separator' between each rep
1689
	var out = "";
1690
	for(var i = 0; i < count; i++) {
1691
		out += str;
1692
		if(separator && i < count - 1) {
1693
			out += separator;
1694
		}
1695
	}
1696
	return out;	//	string
1697
}
1698
 
1699
dojo.string.pad = function(/* string */str, /* integer */len/*=2*/, /* string */ c/*='0'*/, /* integer */dir/*=1*/) {
1700
	//	summary
1701
	//	Pad 'str' to guarantee that it is at least 'len' length with the character 'c' at either the
1702
	//	start (dir=1) or end (dir=-1) of the string
1703
	var out = String(str);
1704
	if(!c) {
1705
		c = '0';
1706
	}
1707
	if(!dir) {
1708
		dir = 1;
1709
	}
1710
	while(out.length < len) {
1711
		if(dir > 0) {
1712
			out = c + out;
1713
		} else {
1714
			out += c;
1715
		}
1716
	}
1717
	return out;	//	string
1718
}
1719
 
1720
dojo.string.padLeft = function(/* string */str, /* integer */len, /* string */c) {
1721
	//	summary
1722
	//	same as dojo.string.pad(str, len, c, 1)
1723
	return dojo.string.pad(str, len, c, 1);	//	string
1724
}
1725
 
1726
dojo.string.padRight = function(/* string */str, /* integer */len, /* string */c) {
1727
	//	summary
1728
	//	same as dojo.string.pad(str, len, c, -1)
1729
	return dojo.string.pad(str, len, c, -1);	//	string
1730
}
1731
 
1732
dojo.provide("dojo.string");
1733
 
1734
 
1735
dojo.provide("dojo.lang.common");
1736
 
1737
dojo.lang.inherits = function(/*Function*/subclass, /*Function*/superclass){
1738
	// summary: Set up inheritance between two classes.
1739
	if(!dojo.lang.isFunction(superclass)){
1740
		dojo.raise("dojo.inherits: superclass argument ["+superclass+"] must be a function (subclass: ["+subclass+"']");
1741
	}
1742
	subclass.prototype = new superclass();
1743
	subclass.prototype.constructor = subclass;
1744
	subclass.superclass = superclass.prototype;
1745
	// DEPRECATED: super is a reserved word, use 'superclass'
1746
	subclass['super'] = superclass.prototype;
1747
}
1748
 
1749
dojo.lang._mixin = function(/*Object*/ obj, /*Object*/ props){
1750
	// summary:
1751
	//		Adds all properties and methods of props to obj. This addition is
1752
	//		"prototype extension safe", so that instances of objects will not
1753
	//		pass along prototype defaults.
1754
	var tobj = {};
1755
	for(var x in props){
1756
		// the "tobj" condition avoid copying properties in "props"
1757
		// inherited from Object.prototype.  For example, if obj has a custom
1758
		// toString() method, don't overwrite it with the toString() method
1759
		// that props inherited from Object.protoype
1760
		if((typeof tobj[x] == "undefined") || (tobj[x] != props[x])){
1761
			obj[x] = props[x];
1762
		}
1763
	}
1764
	// IE doesn't recognize custom toStrings in for..in
1765
	if(dojo.render.html.ie
1766
		&& (typeof(props["toString"]) == "function")
1767
		&& (props["toString"] != obj["toString"])
1768
		&& (props["toString"] != tobj["toString"]))
1769
	{
1770
		obj.toString = props.toString;
1771
	}
1772
	return obj; // Object
1773
}
1774
 
1775
dojo.lang.mixin = function(/*Object*/obj, /*Object...*/props){
1776
	// summary:	Adds all properties and methods of props to obj.
1777
	for(var i=1, l=arguments.length; i<l; i++){
1778
		dojo.lang._mixin(obj, arguments[i]);
1779
	}
1780
	return obj; // Object
1781
}
1782
 
1783
dojo.lang.extend = function(/*Object*/ constructor, /*Object...*/ props){
1784
	// summary:
1785
	//		Adds all properties and methods of props to constructor's
1786
	//		prototype, making them available to all instances created with
1787
	//		constructor.
1788
	for(var i=1, l=arguments.length; i<l; i++){
1789
		dojo.lang._mixin(constructor.prototype, arguments[i]);
1790
	}
1791
	return constructor; // Object
1792
}
1793
 
1794
// Promote to dojo module
1795
dojo.inherits = dojo.lang.inherits;
1796
//dojo.lang._mixin = dojo.lang._mixin;
1797
dojo.mixin = dojo.lang.mixin;
1798
dojo.extend = dojo.lang.extend;
1799
 
1800
dojo.lang.find = function(	/*Array*/		array,
1801
							/*Object*/		value,
1802
							/*Boolean?*/	identity,
1803
							/*Boolean?*/	findLast){
1804
	// summary:
1805
	//		Return the index of value in array, returning -1 if not found.
1806
	// array: just what you think
1807
	// value: the value to locate
1808
	// identity:
1809
	//		If true, matches with identity comparison (===). If false, uses
1810
	//		normal comparison (==).
1811
	// findLast:
1812
	//		If true, returns index of last instance of value.
1813
	// examples:
1814
	//		find(array, value[, identity [findLast]]) // recommended
1815
 	//		find(value, array[, identity [findLast]]) // deprecated
1816
 
1817
	// support both (array, value) and (value, array)
1818
	if(!dojo.lang.isArrayLike(array) && dojo.lang.isArrayLike(value)) {
1819
		dojo.deprecated('dojo.lang.find(value, array)', 'use dojo.lang.find(array, value) instead', "0.5");
1820
		var temp = array;
1821
		array = value;
1822
		value = temp;
1823
	}
1824
	var isString = dojo.lang.isString(array);
1825
	if(isString) { array = array.split(""); }
1826
 
1827
	if(findLast) {
1828
		var step = -1;
1829
		var i = array.length - 1;
1830
		var end = -1;
1831
	} else {
1832
		var step = 1;
1833
		var i = 0;
1834
		var end = array.length;
1835
	}
1836
	if(identity){
1837
		while(i != end) {
1838
			if(array[i] === value){ return i; }
1839
			i += step;
1840
		}
1841
	}else{
1842
		while(i != end) {
1843
			if(array[i] == value){ return i; }
1844
			i += step;
1845
		}
1846
	}
1847
	return -1;	// number
1848
}
1849
 
1850
dojo.lang.indexOf = dojo.lang.find;
1851
 
1852
dojo.lang.findLast = function(/*Array*/array, /*Object*/value, /*boolean?*/identity){
1853
	// summary:
1854
	//		Return index of last occurance of value in array, returning -1 if
1855
	//		not found. This is a shortcut for dojo.lang.find() with a true
1856
	//		value for its "findLast" parameter.
1857
	// identity:
1858
	//		If true, matches with identity comparison (===). If false, uses
1859
	//		normal comparison (==).
1860
	return dojo.lang.find(array, value, identity, true); // number
1861
}
1862
 
1863
dojo.lang.lastIndexOf = dojo.lang.findLast;
1864
 
1865
dojo.lang.inArray = function(array /*Array*/, value /*Object*/){
1866
	// summary:	Return true if value is present in array.
1867
	return dojo.lang.find(array, value) > -1; // boolean
1868
}
1869
 
1870
/**
1871
 * Partial implmentation of is* functions from
1872
 * http://www.crockford.com/javascript/recommend.html
1873
 * NOTE: some of these may not be the best thing to use in all situations
1874
 * as they aren't part of core JS and therefore can't work in every case.
1875
 * See WARNING messages inline for tips.
1876
 *
1877
 * The following is* functions are fairly "safe"
1878
 */
1879
 
1880
dojo.lang.isObject = function(/*anything*/ it){
1881
	// summary:	Return true if it is an Object, Array or Function.
1882
	if(typeof it == "undefined"){ return false; }
1883
	return (typeof it == "object" || it === null || dojo.lang.isArray(it) || dojo.lang.isFunction(it)); // Boolean
1884
}
1885
 
1886
dojo.lang.isArray = function(/*anything*/ it){
1887
	// summary:	Return true if it is an Array.
1888
	return (it && it instanceof Array || typeof it == "array"); // Boolean
1889
}
1890
 
1891
dojo.lang.isArrayLike = function(/*anything*/ it){
1892
	// summary:
1893
	//		Return true if it can be used as an array (i.e. is an object with
1894
	//		an integer length property).
1895
	if((!it)||(dojo.lang.isUndefined(it))){ return false; }
1896
	if(dojo.lang.isString(it)){ return false; }
1897
	if(dojo.lang.isFunction(it)){ return false; } // keeps out built-in constructors (Number, String, ...) which have length properties
1898
	if(dojo.lang.isArray(it)){ return true; }
1899
	// form node itself is ArrayLike, but not always iterable. Use form.elements instead.
1900
	if((it.tagName)&&(it.tagName.toLowerCase()=='form')){ return false; }
1901
	if(dojo.lang.isNumber(it.length) && isFinite(it.length)){ return true; }
1902
	return false; // Boolean
1903
}
1904
 
1905
dojo.lang.isFunction = function(/*anything*/ it){
1906
	// summary:	Return true if it is a Function.
1907
	return (it instanceof Function || typeof it == "function"); // Boolean
1908
};
1909
 
1910
(function(){
1911
	// webkit treats NodeList as a function, which is bad
1912
	if((dojo.render.html.capable)&&(dojo.render.html["safari"])){
1913
		dojo.lang.isFunction = function(/*anything*/ it){
1914
			if((typeof(it) == "function") && (it == "[object NodeList]")) { return false; }
1915
			return (it instanceof Function || typeof it == "function"); // Boolean
1916
		}
1917
	}
1918
})();
1919
 
1920
dojo.lang.isString = function(/*anything*/ it){
1921
	// summary:	Return true if it is a String.
1922
	return (typeof it == "string" || it instanceof String);
1923
}
1924
 
1925
dojo.lang.isAlien = function(/*anything*/ it){
1926
	// summary: Return true if it is not a built-in function. False if not.
1927
	if(!it){ return false; }
1928
	return !dojo.lang.isFunction(it) && /\{\s*\[native code\]\s*\}/.test(String(it)); // Boolean
1929
}
1930
 
1931
dojo.lang.isBoolean = function(/*anything*/ it){
1932
	// summary:	Return true if it is a Boolean.
1933
	return (it instanceof Boolean || typeof it == "boolean"); // Boolean
1934
}
1935
 
1936
/**
1937
 * The following is***() functions are somewhat "unsafe". Fortunately,
1938
 * there are workarounds the the language provides and are mentioned
1939
 * in the WARNING messages.
1940
 *
1941
 */
1942
dojo.lang.isNumber = function(/*anything*/ it){
1943
	// summary:	Return true if it is a number.
1944
	// description:
1945
	//		WARNING - In most cases, isNaN(it) is sufficient to determine whether or not
1946
	// 		something is a number or can be used as such. For example, a number or string
1947
	// 		can be used interchangably when accessing array items (array["1"] is the same as
1948
	// 		array[1]) and isNaN will return false for both values ("1" and 1). However,
1949
	// 		isNumber("1")  will return false, which is generally not too useful.
1950
	// 		Also, isNumber(NaN) returns true, again, this isn't generally useful, but there
1951
	// 		are corner cases (like when you want to make sure that two things are really
1952
	// 		the same type of thing). That is really where isNumber "shines".
1953
	//
1954
	// Recommendation - Use isNaN(it) when possible
1955
 
1956
	return (it instanceof Number || typeof it == "number"); // Boolean
1957
}
1958
 
1959
/*
1960
 * FIXME: Should isUndefined go away since it is error prone?
1961
 */
1962
dojo.lang.isUndefined = function(/*anything*/ it){
1963
	// summary: Return true if it is not defined.
1964
	// description:
1965
	//		WARNING - In some cases, isUndefined will not behave as you
1966
	// 		might expect. If you do isUndefined(foo) and there is no earlier
1967
	// 		reference to foo, an error will be thrown before isUndefined is
1968
	// 		called. It behaves correctly if you scope yor object first, i.e.
1969
	// 		isUndefined(foo.bar) where foo is an object and bar isn't a
1970
	// 		property of the object.
1971
	//
1972
	// Recommendation - Use typeof foo == "undefined" when possible
1973
 
1974
	return ((typeof(it) == "undefined")&&(it == undefined)); // Boolean
1975
}
1976
 
1977
// end Crockford functions
1978
 
1979
dojo.provide("dojo.lang.extras");
1980
 
1981
 
1982
 
1983
dojo.lang.setTimeout = function(/*Function*/func, /*int*/delay /*, ...*/){
1984
	// summary:
1985
	//		Sets a timeout in milliseconds to execute a function in a given
1986
	//		context with optional arguments.
1987
	// usage:
1988
	//		dojo.lang.setTimeout(Object context, function func, number delay[, arg1[, ...]]);
1989
	//		dojo.lang.setTimeout(function func, number delay[, arg1[, ...]]);
1990
 
1991
	var context = window, argsStart = 2;
1992
	if(!dojo.lang.isFunction(func)){
1993
		context = func;
1994
		func = delay;
1995
		delay = arguments[2];
1996
		argsStart++;
1997
	}
1998
 
1999
	if(dojo.lang.isString(func)){
2000
		func = context[func];
2001
	}
2002
 
2003
	var args = [];
2004
	for (var i = argsStart; i < arguments.length; i++){
2005
		args.push(arguments[i]);
2006
	}
2007
	return dojo.global().setTimeout(function(){ func.apply(context, args); }, delay); // int
2008
}
2009
 
2010
dojo.lang.clearTimeout = function(/*int*/timer){
2011
	// summary: clears timer by number from the execution queue
2012
 
2013
	// FIXME:
2014
	//		why do we have this function? It's not portable outside of browser
2015
	//		environments and it's a stupid wrapper on something that browsers
2016
	//		provide anyway.
2017
	dojo.global().clearTimeout(timer);
2018
}
2019
 
2020
dojo.lang.getNameInObj = function(/*Object*/ns, /*unknown*/item){
2021
	// summary:
2022
	//		looks for a value in the object ns with a value matching item and
2023
	//		returns the property name
2024
	// ns: if null, dj_global is used
2025
	// item: value to return a name for
2026
	if(!ns){ ns = dj_global; }
2027
 
2028
	for(var x in ns){
2029
		if(ns[x] === item){
2030
			return new String(x); // String
2031
		}
2032
	}
2033
	return null; // null
2034
}
2035
 
2036
dojo.lang.shallowCopy = function(/*Object*/obj, /*Boolean?*/deep){
2037
	// summary:
2038
	//		copies object obj one level deep, or full depth if deep is true
2039
	var i, ret;
2040
 
2041
	if(obj === null){ /*obj: null*/ return null; } // null
2042
 
2043
	if(dojo.lang.isObject(obj)){
2044
		// obj: Object
2045
		ret = new obj.constructor();
2046
		for(i in obj){
2047
			if(dojo.lang.isUndefined(ret[i])){
2048
				ret[i] = deep ? dojo.lang.shallowCopy(obj[i], deep) : obj[i];
2049
			}
2050
		}
2051
	}else if(dojo.lang.isArray(obj)){
2052
		// obj: Array
2053
		ret = [];
2054
		for(i=0; i<obj.length; i++){
2055
			ret[i] = deep ? dojo.lang.shallowCopy(obj[i], deep) : obj[i];
2056
		}
2057
	}else{
2058
		// obj: Object
2059
		ret = obj;
2060
	}
2061
 
2062
	return ret; // Object
2063
}
2064
 
2065
dojo.lang.firstValued = function(/* ... */){
2066
	// summary: Return the first argument that isn't undefined
2067
 
2068
	for(var i = 0; i < arguments.length; i++){
2069
		if(typeof arguments[i] != "undefined"){
2070
			return arguments[i]; // Object
2071
		}
2072
	}
2073
	return undefined; // undefined
2074
}
2075
 
2076
dojo.lang.getObjPathValue = function(/*String*/objpath, /*Object?*/context, /*Boolean?*/create){
2077
	// summary:
2078
	//		Gets a value from a reference specified as a string descriptor,
2079
	//		(e.g. "A.B") in the given context.
2080
	// context: if not specified, dj_global is used
2081
	// create: if true, undefined objects in the path are created.
2082
	with(dojo.parseObjPath(objpath, context, create)){
2083
		return dojo.evalProp(prop, obj, create); // Object
2084
	}
2085
}
2086
 
2087
dojo.lang.setObjPathValue = function(/*String*/objpath, /*anything*/value, /*Object?*/context, /*Boolean?*/create){
2088
	// summary:
2089
	//		Sets a value on a reference specified as a string descriptor.
2090
	//		(e.g. "A.B") in the given context. This is similar to straight
2091
	//		assignment, except that the object structure in question can
2092
	//		optionally be created if it does not exist.
2093
	//	context: if not specified, dj_global is used
2094
	//	create: if true, undefined objects in the path are created.
2095
 
2096
	// FIXME: why is this function valuable? It should be scheduled for
2097
	// removal on the grounds that dojo.parseObjPath does most of it's work and
2098
	// is more straightforward and has fewer dependencies. Also, the order of
2099
	// arguments is bone-headed. "context" should clearly come after "create".
2100
	// *sigh*
2101
	dojo.deprecated("dojo.lang.setObjPathValue", "use dojo.parseObjPath and the '=' operator", "0.6");
2102
 
2103
	if(arguments.length < 4){
2104
		create = true;
2105
	}
2106
	with(dojo.parseObjPath(objpath, context, create)){
2107
		if(obj && (create || (prop in obj))){
2108
			obj[prop] = value;
2109
		}
2110
	}
2111
}
2112
 
2113
dojo.provide("dojo.io.common");
2114
 
2115
 
2116
 
2117
/******************************************************************************
2118
 *	Notes about dojo.io design:
2119
 *
2120
 *	The dojo.io.* package has the unenviable task of making a lot of different
2121
 *	types of I/O feel natural, despite a universal lack of good (or even
2122
 *	reasonable!) I/O capability in the host environment. So lets pin this down
2123
 *	a little bit further.
2124
 *
2125
 *	Rhino:
2126
 *		perhaps the best situation anywhere. Access to Java classes allows you
2127
 *		to do anything one might want in terms of I/O, both synchronously and
2128
 *		async. Can open TCP sockets and perform low-latency client/server
2129
 *		interactions. HTTP transport is available through Java HTTP client and
2130
 *		server classes. Wish it were always this easy.
2131
 *
2132
 *	xpcshell:
2133
 *		XPCOM for I/O.
2134
 *
2135
 *	spidermonkey:
2136
 *		S.O.L.
2137
 *
2138
 *	Browsers:
2139
 *		Browsers generally do not provide any useable filesystem access. We are
2140
 *		therefore limited to HTTP for moving information to and from Dojo
2141
 *		instances living in a browser.
2142
 *
2143
 *		XMLHTTP:
2144
 *			Sync or async, allows reading of arbitrary text files (including
2145
 *			JS, which can then be eval()'d), writing requires server
2146
 *			cooperation and is limited to HTTP mechanisms (POST and GET).
2147
 *
2148
 *		<iframe> hacks:
2149
 *			iframe document hacks allow browsers to communicate asynchronously
2150
 *			with a server via HTTP POST and GET operations. With significant
2151
 *			effort and server cooperation, low-latency data transit between
2152
 *			client and server can be acheived via iframe mechanisms (repubsub).
2153
 *
2154
 *		SVG:
2155
 *			Adobe's SVG viewer implements helpful primitives for XML-based
2156
 *			requests, but receipt of arbitrary text data seems unlikely w/o
2157
 *			<![CDATA[]]> sections.
2158
 *
2159
 *
2160
 *	A discussion between Dylan, Mark, Tom, and Alex helped to lay down a lot
2161
 *	the IO API interface. A transcript of it can be found at:
2162
 *		http://dojotoolkit.org/viewcvs/viewcvs.py/documents/irc/irc_io_api_log.txt?rev=307&view=auto
2163
 *
2164
 *	Also referenced in the design of the API was the DOM 3 L&S spec:
2165
 *		http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/load-save.html
2166
 ******************************************************************************/
2167
 
2168
// a map of the available transport options. Transports should add themselves
2169
// by calling add(name)
2170
dojo.io.transports = [];
2171
dojo.io.hdlrFuncNames = [ "load", "error", "timeout" ]; // we're omitting a progress() event for now
2172
 
2173
dojo.io.Request = function(/*String*/ url, /*String*/ mimetype, /*String*/ transport, /*String or Boolean*/ changeUrl){
2174
// summary:
2175
//		Constructs a Request object that is used by dojo.io.bind().
2176
// description:
2177
//		dojo.io.bind() will create one of these for you if
2178
//		you call dojo.io.bind() with an plain object containing the bind parameters.
2179
//		This method can either take the arguments specified, or an Object containing all of the parameters that you
2180
//		want to use to create the dojo.io.Request (similar to how dojo.io.bind() is called.
2181
//		The named parameters to this constructor represent the minimum set of parameters need
2182
	if((arguments.length == 1)&&(arguments[0].constructor == Object)){
2183
		this.fromKwArgs(arguments[0]);
2184
	}else{
2185
		this.url = url;
2186
		if(mimetype){ this.mimetype = mimetype; }
2187
		if(transport){ this.transport = transport; }
2188
		if(arguments.length >= 4){ this.changeUrl = changeUrl; }
2189
	}
2190
}
2191
 
2192
dojo.lang.extend(dojo.io.Request, {
2193
 
2194
	/** The URL to hit */
2195
	url: "",
2196
 
2197
	/** The mime type used to interrpret the response body */
2198
	mimetype: "text/plain",
2199
 
2200
	/** The HTTP method to use */
2201
	method: "GET",
2202
 
2203
	/** An Object containing key-value pairs to be included with the request */
2204
	content: undefined, // Object
2205
 
2206
	/** The transport medium to use */
2207
	transport: undefined, // String
2208
 
2209
	/** If defined the URL of the page is physically changed */
2210
	changeUrl: undefined, // String
2211
 
2212
	/** A form node to use in the request */
2213
	formNode: undefined, // HTMLFormElement
2214
 
2215
	/** Whether the request should be made synchronously */
2216
	sync: false,
2217
 
2218
	bindSuccess: false,
2219
 
2220
	/** Cache/look for the request in the cache before attempting to request?
2221
	 *  NOTE: this isn't a browser cache, this is internal and would only cache in-page
2222
	 */
2223
	useCache: false,
2224
 
2225
	/** Prevent the browser from caching this by adding a query string argument to the URL */
2226
	preventCache: false,
1422 alexandre_ 2227
 
2228
	jsonFilter: function(value){
2229
		if(	(this.mimetype == "text/json-comment-filtered")||
2230
			(this.mimetype == "application/json-comment-filtered")
2231
		){
2232
			var cStartIdx = value.indexOf("\/*");
2233
			var cEndIdx = value.lastIndexOf("*\/");
2234
			if((cStartIdx == -1)||(cEndIdx == -1)){
2235
				dojo.debug("your JSON wasn't comment filtered!"); // FIXME: throw exception instead?
2236
				return "";
2237
			}
2238
			return value.substring(cStartIdx+2, cEndIdx);
2239
		}
2240
		dojo.debug("please consider using a mimetype of text/json-comment-filtered to avoid potential security issues with JSON endpoints");
2241
		return value;
2242
	},
1318 alexandre_ 2243
 
2244
	// events stuff
2245
	load: function(/*String*/type, /*Object*/data, /*Object*/transportImplementation, /*Object*/kwArgs){
2246
		// summary:
2247
		//		Called on successful completion of a bind.
2248
		//		type: String
2249
		//				A string with value "load"
2250
		//		data: Object
2251
		//				The object representing the result of the bind. The actual structure
2252
		//				of the data object will depend on the mimetype that was given to bind
2253
		//				in the bind arguments.
2254
		//		transportImplementation: Object
2255
		//				The object that implements a particular transport. Structure is depedent
2256
		//				on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
2257
		//				XMLHttpRequest object from the browser.
2258
		//		kwArgs: Object
2259
		//				Object that contains the request parameters that were given to the
2260
		//				bind call. Useful for storing and retrieving state from when bind
2261
		//				was called.
2262
	},
2263
	error: function(/*String*/type, /*Object*/error, /*Object*/transportImplementation, /*Object*/kwArgs){
2264
		// summary:
2265
		//		Called when there is an error with a bind.
2266
		//		type: String
2267
		//				A string with value "error"
2268
		//		error: Object
2269
		//				The error object. Should be a dojo.io.Error object, but not guaranteed.
2270
		//		transportImplementation: Object
2271
		//				The object that implements a particular transport. Structure is depedent
2272
		//				on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
2273
		//				XMLHttpRequest object from the browser.
2274
		//		kwArgs: Object
2275
		//				Object that contains the request parameters that were given to the
2276
		//				bind call. Useful for storing and retrieving state from when bind
2277
		//				was called.
2278
	},
2279
	timeout: function(/*String*/type, /*Object*/empty, /*Object*/transportImplementation, /*Object*/kwArgs){
2280
		// summary:
2281
		//		Called when there is an error with a bind. Only implemented in certain transports at this time.
2282
		//		type: String
2283
		//				A string with value "timeout"
2284
		//		empty: Object
2285
		//				Should be null. Just a spacer argument so that load, error, timeout and handle have the
2286
		//				same signatures.
2287
		//		transportImplementation: Object
2288
		//				The object that implements a particular transport. Structure is depedent
2289
		//				on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
2290
		//				XMLHttpRequest object from the browser. May be null for the timeout case for
2291
		//				some transports.
2292
		//		kwArgs: Object
2293
		//				Object that contains the request parameters that were given to the
2294
		//				bind call. Useful for storing and retrieving state from when bind
2295
		//				was called.
2296
	},
2297
	handle: function(/*String*/type, /*Object*/data, /*Object*/transportImplementation, /*Object*/kwArgs){
2298
		// summary:
2299
		//		The handle method can be defined instead of defining separate load, error and timeout
2300
		//		callbacks.
2301
		//		type: String
2302
		//				A string with the type of callback: "load", "error", or "timeout".
2303
		//		data: Object
2304
		//				See the above callbacks for what this parameter could be.
2305
		//		transportImplementation: Object
2306
		//				The object that implements a particular transport. Structure is depedent
2307
		//				on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
2308
		//				XMLHttpRequest object from the browser.
2309
		//		kwArgs: Object
2310
		//				Object that contains the request parameters that were given to the
2311
		//				bind call. Useful for storing and retrieving state from when bind
2312
		//				was called.
2313
	},
2314
 
2315
	//FIXME: change IframeIO.js to use timeouts?
2316
	// The number of seconds to wait until firing a timeout callback.
2317
	// If it is zero, that means, don't do a timeout check.
2318
	timeoutSeconds: 0,
2319
 
2320
	// the abort method needs to be filled in by the transport that accepts the
2321
	// bind() request
2322
	abort: function(){ },
2323
 
2324
	// backButton: function(){ },
2325
	// forwardButton: function(){ },
2326
 
2327
	fromKwArgs: function(/*Object*/ kwArgs){
2328
		// summary:
2329
		//		Creates a dojo.io.Request from a simple object (kwArgs object).
2330
 
2331
		// normalize args
2332
		if(kwArgs["url"]){ kwArgs.url = kwArgs.url.toString(); }
2333
		if(kwArgs["formNode"]) { kwArgs.formNode = dojo.byId(kwArgs.formNode); }
2334
		if(!kwArgs["method"] && kwArgs["formNode"] && kwArgs["formNode"].method) {
2335
			kwArgs.method = kwArgs["formNode"].method;
2336
		}
2337
 
2338
		// backwards compatibility
2339
		if(!kwArgs["handle"] && kwArgs["handler"]){ kwArgs.handle = kwArgs.handler; }
2340
		if(!kwArgs["load"] && kwArgs["loaded"]){ kwArgs.load = kwArgs.loaded; }
2341
		if(!kwArgs["changeUrl"] && kwArgs["changeURL"]) { kwArgs.changeUrl = kwArgs.changeURL; }
2342
 
2343
		// encoding fun!
2344
		kwArgs.encoding = dojo.lang.firstValued(kwArgs["encoding"], djConfig["bindEncoding"], "");
2345
 
2346
		kwArgs.sendTransport = dojo.lang.firstValued(kwArgs["sendTransport"], djConfig["ioSendTransport"], false);
2347
 
2348
		var isFunction = dojo.lang.isFunction;
2349
		for(var x=0; x<dojo.io.hdlrFuncNames.length; x++){
2350
			var fn = dojo.io.hdlrFuncNames[x];
2351
			if(kwArgs[fn] && isFunction(kwArgs[fn])){ continue; }
2352
			if(kwArgs["handle"] && isFunction(kwArgs["handle"])){
2353
				kwArgs[fn] = kwArgs.handle;
2354
			}
2355
			// handler is aliased above, shouldn't need this check
2356
			/* else if(dojo.lang.isObject(kwArgs.handler)){
2357
				if(isFunction(kwArgs.handler[fn])){
2358
					kwArgs[fn] = kwArgs.handler[fn]||kwArgs.handler["handle"]||function(){};
2359
				}
2360
			}*/
2361
		}
2362
		dojo.lang.mixin(this, kwArgs);
2363
	}
2364
 
2365
});
2366
 
2367
dojo.io.Error = function(/*String*/ msg, /*String*/ type, /*Number*/num){
2368
	// summary:
2369
	//		Constructs an object representing a bind error.
2370
	this.message = msg;
2371
	this.type =  type || "unknown"; // must be one of "io", "parse", "unknown"
2372
	this.number = num || 0; // per-substrate error number, not normalized
2373
}
2374
 
2375
dojo.io.transports.addTransport = function(/*String*/name){
2376
	// summary:
2377
	//		Used to register transports that can support bind calls.
2378
	this.push(name);
2379
	// FIXME: do we need to handle things that aren't direct children of the
2380
	// dojo.io module? (say, dojo.io.foo.fooTransport?)
2381
	this[name] = dojo.io[name];
2382
}
2383
 
2384
// binding interface, the various implementations register their capabilities
2385
// and the bind() method dispatches
2386
dojo.io.bind = function(/*dojo.io.Request or Object*/request){
2387
	// summary:
2388
	//		Binding interface for IO. Loading different IO transports, like
2389
	//		dojo.io.BrowserIO or dojo.io.IframeIO, will register with bind
2390
	//		to handle particular types of bind calls.
2391
	//		request: Object
2392
	//				Object containing bind arguments. This object is converted to
2393
	//				a dojo.io.Request object, and that request object is the return
2394
	//				value for this method.
2395
	if(!(request instanceof dojo.io.Request)){
2396
		try{
2397
			request = new dojo.io.Request(request);
2398
		}catch(e){ dojo.debug(e); }
2399
	}
2400
 
2401
	// if the request asks for a particular implementation, use it
2402
	var tsName = "";
2403
	if(request["transport"]){
2404
		tsName = request["transport"];
2405
		if(!this[tsName]){
2406
			dojo.io.sendBindError(request, "No dojo.io.bind() transport with name '"
2407
				+ request["transport"] + "'.");
2408
			return request; //dojo.io.Request
2409
		}
2410
		if(!this[tsName].canHandle(request)){
2411
			dojo.io.sendBindError(request, "dojo.io.bind() transport with name '"
2412
				+ request["transport"] + "' cannot handle this type of request.");
2413
			return request;	//dojo.io.Request
2414
		}
2415
	}else{
2416
		// otherwise we do our best to auto-detect what available transports
2417
		// will handle
2418
		for(var x=0; x<dojo.io.transports.length; x++){
2419
			var tmp = dojo.io.transports[x];
2420
			if((this[tmp])&&(this[tmp].canHandle(request))){
2421
				tsName = tmp;
2422
				break;
2423
			}
2424
		}
2425
		if(tsName == ""){
2426
			dojo.io.sendBindError(request, "None of the loaded transports for dojo.io.bind()"
2427
				+ " can handle the request.");
2428
			return request; //dojo.io.Request
2429
		}
2430
	}
2431
	this[tsName].bind(request);
2432
	request.bindSuccess = true;
2433
	return request; //dojo.io.Request
2434
}
2435
 
2436
dojo.io.sendBindError = function(/* Object */request, /* String */message){
2437
	// summary:
2438
	//		Used internally by dojo.io.bind() to return/raise a bind error.
2439
 
2440
	//Need to be careful since not all hostenvs support setTimeout.
2441
	if((typeof request.error == "function" || typeof request.handle == "function")
2442
		&& (typeof setTimeout == "function" || typeof setTimeout == "object")){
2443
		var errorObject = new dojo.io.Error(message);
2444
		setTimeout(function(){
2445
			request[(typeof request.error == "function") ? "error" : "handle"]("error", errorObject, null, request);
2446
		}, 50);
2447
	}else{
2448
		dojo.raise(message);
2449
	}
2450
}
2451
 
2452
dojo.io.queueBind = function(/*dojo.io.Request or Object*/request){
2453
	// summary:
2454
	//		queueBind will use dojo.io.bind() but guarantee that only one bind
2455
	//		call is handled at a time.
2456
	// description:
2457
	//		If queueBind is called while a bind call
2458
	//		is in process, it will queue up the other calls to bind and call them
2459
	//		in order as bind calls complete.
2460
	//		request: Object
2461
	//			Same sort of request object as used for dojo.io.bind().
2462
	if(!(request instanceof dojo.io.Request)){
2463
		try{
2464
			request = new dojo.io.Request(request);
2465
		}catch(e){ dojo.debug(e); }
2466
	}
2467
 
2468
	// make sure we get called if/when we get a response
2469
	var oldLoad = request.load;
2470
	request.load = function(){
2471
		dojo.io._queueBindInFlight = false;
2472
		var ret = oldLoad.apply(this, arguments);
2473
		dojo.io._dispatchNextQueueBind();
2474
		return ret;
2475
	}
2476
 
2477
	var oldErr = request.error;
2478
	request.error = function(){
2479
		dojo.io._queueBindInFlight = false;
2480
		var ret = oldErr.apply(this, arguments);
2481
		dojo.io._dispatchNextQueueBind();
2482
		return ret;
2483
	}
2484
 
2485
	dojo.io._bindQueue.push(request);
2486
	dojo.io._dispatchNextQueueBind();
2487
	return request; //dojo.io.Request
2488
}
2489
 
2490
dojo.io._dispatchNextQueueBind = function(){
2491
	// summary:
2492
	//	Private method used by dojo.io.queueBind().
2493
	if(!dojo.io._queueBindInFlight){
2494
		dojo.io._queueBindInFlight = true;
2495
		if(dojo.io._bindQueue.length > 0){
2496
			dojo.io.bind(dojo.io._bindQueue.shift());
2497
		}else{
2498
			dojo.io._queueBindInFlight = false;
2499
		}
2500
	}
2501
}
2502
dojo.io._bindQueue = [];
2503
dojo.io._queueBindInFlight = false;
2504
 
2505
dojo.io.argsFromMap = function(/*Object*/map, /*String?*/encoding, /*String?*/last){
2506
	// summary:
2507
	//		Converts name/values pairs in the map object to an URL-encoded string
2508
	//		with format of name1=value1&name2=value2...
2509
	//		map: Object
2510
	//			Object that has the contains the names and values.
2511
	//		encoding: String?
2512
	//			String to specify how to encode the name and value. If the encoding string
2513
	//			contains "utf" (case-insensitive), then encodeURIComponent is used. Otherwise
2514
	//			dojo.string.encodeAscii is used.
2515
	//		last: String?
2516
	//			The last parameter in the list. Helps with final string formatting?
2517
	var enc = /utf/i.test(encoding||"") ? encodeURIComponent : dojo.string.encodeAscii;
2518
	var mapped = [];
2519
	var control = new Object();
2520
	for(var name in map){
2521
		var domap = function(elt){
2522
			var val = enc(name)+"="+enc(elt);
2523
			mapped[(last == name) ? "push" : "unshift"](val);
2524
		}
2525
		if(!control[name]){
2526
			var value = map[name];
2527
			// FIXME: should be isArrayLike?
2528
			if (dojo.lang.isArray(value)){
2529
				dojo.lang.forEach(value, domap);
2530
			}else{
2531
				domap(value);
2532
			}
2533
		}
2534
	}
2535
	return mapped.join("&"); //String
2536
}
2537
 
2538
dojo.io.setIFrameSrc = function(/*DOMNode*/ iframe, /*String*/ src, /*Boolean*/ replace){
2539
	//summary:
2540
	//		Sets the URL that is loaded in an IFrame. The replace parameter indicates whether
2541
	//		location.replace() should be used when changing the location of the iframe.
2542
	try{
2543
		var r = dojo.render.html;
2544
		// dojo.debug(iframe);
2545
		if(!replace){
2546
			if(r.safari){
2547
				iframe.location = src;
2548
			}else{
2549
				frames[iframe.name].location = src;
2550
			}
2551
		}else{
2552
			// Fun with DOM 0 incompatibilities!
2553
			var idoc;
2554
			if(r.ie){
2555
				idoc = iframe.contentWindow.document;
2556
			}else if(r.safari){
2557
				idoc = iframe.document;
2558
			}else{ //  if(r.moz){
2559
				idoc = iframe.contentWindow;
2560
			}
2561
 
2562
			//For Safari (at least 2.0.3) and Opera, if the iframe
2563
			//has just been created but it doesn't have content
2564
			//yet, then iframe.document may be null. In that case,
2565
			//use iframe.location and return.
2566
			if(!idoc){
2567
				iframe.location = src;
2568
				return;
2569
			}else{
2570
				idoc.location.replace(src);
2571
			}
2572
		}
2573
	}catch(e){
2574
		dojo.debug(e);
2575
		dojo.debug("setIFrameSrc: "+e);
2576
	}
2577
}
2578
 
2579
/*
2580
dojo.io.sampleTranport = new function(){
2581
	this.canHandle = function(kwArgs){
2582
		// canHandle just tells dojo.io.bind() if this is a good transport to
2583
		// use for the particular type of request.
2584
		if(
2585
			(
2586
				(kwArgs["mimetype"] == "text/plain") ||
2587
				(kwArgs["mimetype"] == "text/html") ||
2588
				(kwArgs["mimetype"] == "text/javascript")
2589
			)&&(
2590
				(kwArgs["method"] == "get") ||
2591
				( (kwArgs["method"] == "post") && (!kwArgs["formNode"]) )
2592
			)
2593
		){
2594
			return true;
2595
		}
2596
 
2597
		return false;
2598
	}
2599
 
2600
	this.bind = function(kwArgs){
2601
		var hdlrObj = {};
2602
 
2603
		// set up a handler object
2604
		for(var x=0; x<dojo.io.hdlrFuncNames.length; x++){
2605
			var fn = dojo.io.hdlrFuncNames[x];
2606
			if(typeof kwArgs.handler == "object"){
2607
				if(typeof kwArgs.handler[fn] == "function"){
2608
					hdlrObj[fn] = kwArgs.handler[fn]||kwArgs.handler["handle"];
2609
				}
2610
			}else if(typeof kwArgs[fn] == "function"){
2611
				hdlrObj[fn] = kwArgs[fn];
2612
			}else{
2613
				hdlrObj[fn] = kwArgs["handle"]||function(){};
2614
			}
2615
		}
2616
 
2617
		// build a handler function that calls back to the handler obj
2618
		var hdlrFunc = function(evt){
2619
			if(evt.type == "onload"){
2620
				hdlrObj.load("load", evt.data, evt);
2621
			}else if(evt.type == "onerr"){
2622
				var errObj = new dojo.io.Error("sampleTransport Error: "+evt.msg);
2623
				hdlrObj.error("error", errObj);
2624
			}
2625
		}
2626
 
2627
		// the sample transport would attach the hdlrFunc() when sending the
2628
		// request down the pipe at this point
2629
		var tgtURL = kwArgs.url+"?"+dojo.io.argsFromMap(kwArgs.content);
2630
		// sampleTransport.sendRequest(tgtURL, hdlrFunc);
2631
	}
2632
 
2633
	dojo.io.transports.addTransport("sampleTranport");
2634
}
2635
*/
2636
 
2637
dojo.provide("dojo.lang.array");
2638
 
2639
 
2640
 
2641
// FIXME: Is this worthless since you can do: if(name in obj)
2642
// is this the right place for this?
2643
 
2644
dojo.lang.mixin(dojo.lang, {
2645
	has: function(/*Object*/obj, /*String*/name){
2646
		// summary: is there a property with the passed name in obj?
2647
		try{
2648
			return typeof obj[name] != "undefined"; // Boolean
2649
		}catch(e){ return false; } // Boolean
2650
	},
2651
 
2652
	isEmpty: function(/*Object*/obj){
2653
		// summary:
2654
		//		can be used to determine if the passed object is "empty". In
2655
		//		the case of array-like objects, the length, property is
2656
		//		examined, but for other types of objects iteration is used to
2657
		//		examine the iterable "surface area" to determine if any
2658
		//		non-prototypal properties have been assigned. This iteration is
2659
		//		prototype-extension safe.
2660
		if(dojo.lang.isObject(obj)){
2661
			var tmp = {};
2662
			var count = 0;
2663
			for(var x in obj){
2664
				if(obj[x] && (!tmp[x])){
2665
					count++;
2666
					break;
2667
				}
2668
			}
2669
			return count == 0; // boolean
2670
		}else if(dojo.lang.isArrayLike(obj) || dojo.lang.isString(obj)){
2671
			return obj.length == 0; // boolean
2672
		}
2673
	},
2674
 
2675
	map: function(/*Array*/arr, /*Object|Function*/obj, /*Function?*/unary_func){
2676
		// summary:
2677
		//		returns a new array constituded from the return values of
2678
		//		passing each element of arr into unary_func. The obj parameter
2679
		//		may be passed to enable the passed function to be called in
2680
		//		that scope. In environments that support JavaScript 1.6, this
2681
		//		function is a passthrough to the built-in map() function
2682
		//		provided by Array instances. For details on this, see:
2683
		// 			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:map
2684
		// examples:
2685
		//		dojo.lang.map([1, 2, 3, 4], function(item){ return item+1 });
2686
		//		// returns [2, 3, 4, 5]
2687
		var isString = dojo.lang.isString(arr);
2688
		if(isString){
2689
			// arr: String
2690
			arr = arr.split("");
2691
		}
2692
		if(dojo.lang.isFunction(obj)&&(!unary_func)){
2693
			unary_func = obj;
2694
			obj = dj_global;
2695
		}else if(dojo.lang.isFunction(obj) && unary_func){
2696
			// ff 1.5 compat
2697
			var tmpObj = obj;
2698
			obj = unary_func;
2699
			unary_func = tmpObj;
2700
		}
2701
		if(Array.map){
2702
			var outArr = Array.map(arr, unary_func, obj);
2703
		}else{
2704
			var outArr = [];
2705
			for(var i=0;i<arr.length;++i){
2706
				outArr.push(unary_func.call(obj, arr[i]));
2707
			}
2708
		}
2709
		if(isString) {
2710
			return outArr.join(""); // String
2711
		} else {
2712
			return outArr; // Array
2713
		}
2714
	},
2715
 
2716
	reduce: function(/*Array*/arr, initialValue, /*Object|Function*/obj, /*Function*/binary_func){
2717
		// summary:
2718
		// 		similar to Python's builtin reduce() function. The result of
2719
		// 		the previous computation is passed as the first argument to
2720
		// 		binary_func along with the next value from arr. The result of
2721
		// 		this call is used along with the subsequent value from arr, and
2722
		// 		this continues until arr is exhausted. The return value is the
2723
		// 		last result. The "obj" and "initialValue" parameters may be
2724
		// 		safely omitted and the order of obj and binary_func may be
2725
		// 		reversed. The default order of the obj and binary_func argument
2726
		// 		will probably be reversed in a future release, and this call
2727
		// 		order is supported today.
2728
		// examples:
2729
		//		dojo.lang.reduce([1, 2, 3, 4], function(last, next){ return last+next});
2730
		//		returns 10
2731
		var reducedValue = initialValue;
2732
		if(arguments.length == 2){
2733
			binary_func = initialValue;
2734
			reducedValue = arr[0];
2735
			arr = arr.slice(1);
2736
		}else if(arguments.length == 3){
2737
			if(dojo.lang.isFunction(obj)){
2738
				binary_func = obj;
2739
				obj = null;
2740
			}
2741
		}else{
2742
			// un-fsck the default order
2743
			// FIXME:
2744
			//		could be wrong for some strange function object cases. Not
2745
			//		sure how to test for them.
2746
			if(dojo.lang.isFunction(obj)){
2747
				var tmp = binary_func;
2748
				binary_func = obj;
2749
				obj = tmp;
2750
			}
2751
		}
2752
 
2753
		var ob = obj || dj_global;
2754
		dojo.lang.map(arr,
2755
			function(val){
2756
				reducedValue = binary_func.call(ob, reducedValue, val);
2757
			}
2758
		);
2759
		return reducedValue;
2760
	},
2761
 
2762
	forEach: function(/*Array*/anArray, /*Function*/callback, /*Object?*/thisObject){
2763
		// summary:
2764
		//		for every item in anArray, call callback with that item as its
2765
		//		only parameter. Return values are ignored. This funciton
2766
		//		corresponds (and wraps) the JavaScript 1.6 forEach method. For
2767
		//		more details, see:
2768
		//			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:forEach
2769
		if(dojo.lang.isString(anArray)){
2770
			// anArray: String
2771
			anArray = anArray.split("");
2772
		}
2773
		if(Array.forEach){
2774
			Array.forEach(anArray, callback, thisObject);
2775
		}else{
2776
			// FIXME: there are several ways of handilng thisObject. Is dj_global always the default context?
2777
			if(!thisObject){
2778
				thisObject=dj_global;
2779
			}
2780
			for(var i=0,l=anArray.length; i<l; i++){
2781
				callback.call(thisObject, anArray[i], i, anArray);
2782
			}
2783
		}
2784
	},
2785
 
2786
	_everyOrSome: function(/*Boolean*/every, /*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
2787
		if(dojo.lang.isString(arr)){
2788
			//arr: String
2789
			arr = arr.split("");
2790
		}
2791
		if(Array.every){
2792
			return Array[ every ? "every" : "some" ](arr, callback, thisObject);
2793
		}else{
2794
			if(!thisObject){
2795
				thisObject = dj_global;
2796
			}
2797
			for(var i=0,l=arr.length; i<l; i++){
2798
				var result = callback.call(thisObject, arr[i], i, arr);
2799
				if(every && !result){
2800
					return false; // Boolean
2801
				}else if((!every)&&(result)){
2802
					return true; // Boolean
2803
				}
2804
			}
2805
			return Boolean(every); // Boolean
2806
		}
2807
	},
2808
 
2809
	every: function(/*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
2810
		// summary:
2811
		//		determines whether or not every item in the array satisfies the
2812
		//		condition implemented by callback. thisObject may be used to
2813
		//		scope the call to callback. The function signature is derived
2814
		//		from the JavaScript 1.6 Array.every() function. More
2815
		//		information on this can be found here:
2816
		//			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:every
2817
		// examples:
2818
		//		dojo.lang.every([1, 2, 3, 4], function(item){ return item>1; });
2819
		//		// returns false
2820
		//		dojo.lang.every([1, 2, 3, 4], function(item){ return item>0; });
2821
		//		// returns true
2822
		return this._everyOrSome(true, arr, callback, thisObject); // Boolean
2823
	},
2824
 
2825
	some: function(/*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
2826
		// summary:
2827
		//		determines whether or not any item in the array satisfies the
2828
		//		condition implemented by callback. thisObject may be used to
2829
		//		scope the call to callback. The function signature is derived
2830
		//		from the JavaScript 1.6 Array.some() function. More
2831
		//		information on this can be found here:
2832
		//			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:some
2833
		// examples:
2834
		//		dojo.lang.some([1, 2, 3, 4], function(item){ return item>1; });
2835
		//		// returns true
2836
		//		dojo.lang.some([1, 2, 3, 4], function(item){ return item<1; });
2837
		//		// returns false
2838
		return this._everyOrSome(false, arr, callback, thisObject); // Boolean
2839
	},
2840
 
2841
	filter: function(/*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
2842
		// summary:
2843
		//		returns a new Array with those items from arr that match the
2844
		//		condition implemented by callback.thisObject may be used to
2845
		//		scope the call to callback. The function signature is derived
2846
		//		from the JavaScript 1.6 Array.filter() function, although
2847
		//		special accomidation is made in our implementation for strings.
2848
		//		More information on the JS 1.6 API can be found here:
2849
		//			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:filter
2850
		// examples:
2851
		//		dojo.lang.some([1, 2, 3, 4], function(item){ return item>1; });
2852
		//		// returns [2, 3, 4]
2853
		var isString = dojo.lang.isString(arr);
2854
		if(isString){ /*arr: String*/arr = arr.split(""); }
2855
		var outArr;
2856
		if(Array.filter){
2857
			outArr = Array.filter(arr, callback, thisObject);
2858
		}else{
2859
			if(!thisObject){
2860
				if(arguments.length >= 3){ dojo.raise("thisObject doesn't exist!"); }
2861
				thisObject = dj_global;
2862
			}
2863
 
2864
			outArr = [];
2865
			for(var i = 0; i < arr.length; i++){
2866
				if(callback.call(thisObject, arr[i], i, arr)){
2867
					outArr.push(arr[i]);
2868
				}
2869
			}
2870
		}
2871
		if(isString){
2872
			return outArr.join(""); // String
2873
		} else {
2874
			return outArr; // Array
2875
		}
2876
	},
2877
 
2878
	unnest: function(/* ... */){
2879
		// summary:
2880
		//		Creates a 1-D array out of all the arguments passed,
2881
		//		unravelling any array-like objects in the process
2882
		// usage:
2883
		//		unnest(1, 2, 3) ==> [1, 2, 3]
2884
		//		unnest(1, [2, [3], [[[4]]]]) ==> [1, 2, 3, 4]
2885
 
2886
		var out = [];
2887
		for(var i = 0; i < arguments.length; i++){
2888
			if(dojo.lang.isArrayLike(arguments[i])){
2889
				var add = dojo.lang.unnest.apply(this, arguments[i]);
2890
				out = out.concat(add);
2891
			}else{
2892
				out.push(arguments[i]);
2893
			}
2894
		}
2895
		return out; // Array
2896
	},
2897
 
2898
	toArray: function(/*Object*/arrayLike, /*Number*/startOffset){
2899
		// summary:
2900
		//		Converts an array-like object (i.e. arguments, DOMCollection)
2901
		//		to an array. Returns a new Array object.
2902
		var array = [];
2903
		for(var i = startOffset||0; i < arrayLike.length; i++){
2904
			array.push(arrayLike[i]);
2905
		}
2906
		return array; // Array
2907
	}
2908
});
2909
 
2910
dojo.provide("dojo.lang.func");
2911
 
2912
 
1422 alexandre_ 2913
dojo.lang.hitch = function(/*Object*/thisObject, /*Function|String*/method /*, ...*/){
1318 alexandre_ 2914
	// summary:
2915
	//		Returns a function that will only ever execute in the a given scope
2916
	//		(thisObject). This allows for easy use of object member functions
2917
	//		in callbacks and other places in which the "this" keyword may
1422 alexandre_ 2918
	//		otherwise not reference the expected scope. Any number of default
2919
	//		positional arguments may be passed as parameters beyond "method".
2920
	//		Each of these values will be used to "placehold" (similar to curry)
2921
	//		for the hitched function. Note that the order of arguments may be
2922
	//		reversed in a future version.
1318 alexandre_ 2923
	// thisObject: the scope to run the method in
2924
	// method:
2925
	//		a function to be "bound" to thisObject or the name of the method in
2926
	//		thisObject to be used as the basis for the binding
2927
	// usage:
2928
	//		dojo.lang.hitch(foo, "bar")(); // runs foo.bar() in the scope of foo
2929
	//		dojo.lang.hitch(foo, myFunction); // returns a function that runs myFunction in the scope of foo
2930
 
1422 alexandre_ 2931
	var args = [];
2932
	for(var x=2; x<arguments.length; x++){
2933
		args.push(arguments[x]);
2934
	}
1318 alexandre_ 2935
	var fcn = (dojo.lang.isString(method) ? thisObject[method] : method) || function(){};
2936
	return function(){
1422 alexandre_ 2937
		var ta = args.concat([]); // make a copy
2938
		for(var x=0; x<arguments.length; x++){
2939
			ta.push(arguments[x]);
2940
		}
2941
		return fcn.apply(thisObject, ta); // Function
2942
		// return fcn.apply(thisObject, arguments); // Function
1318 alexandre_ 2943
	};
2944
}
2945
 
2946
dojo.lang.anonCtr = 0;
2947
dojo.lang.anon = {};
2948
 
2949
dojo.lang.nameAnonFunc = function(/*Function*/anonFuncPtr, /*Object*/thisObj, /*Boolean*/searchForNames){
2950
	// summary:
2951
	//		Creates a reference to anonFuncPtr in thisObj with a completely
2952
	//		unique name. The new name is returned as a String.  If
2953
	//		searchForNames is true, an effort will be made to locate an
2954
	//		existing reference to anonFuncPtr in thisObj, and if one is found,
2955
	//		the existing name will be returned instead. The default is for
2956
	//		searchForNames to be false.
2957
	var nso = (thisObj|| dojo.lang.anon);
2958
	if( (searchForNames) ||
2959
		((dj_global["djConfig"])&&(djConfig["slowAnonFuncLookups"] == true)) ){
2960
		for(var x in nso){
2961
			try{
2962
				if(nso[x] === anonFuncPtr){
2963
					return x;
2964
				}
2965
			}catch(e){} // window.external fails in IE embedded in Eclipse (Eclipse bug #151165)
2966
		}
2967
	}
2968
	var ret = "__"+dojo.lang.anonCtr++;
2969
	while(typeof nso[ret] != "undefined"){
2970
		ret = "__"+dojo.lang.anonCtr++;
2971
	}
2972
	nso[ret] = anonFuncPtr;
2973
	return ret; // String
2974
}
2975
 
2976
dojo.lang.forward = function(funcName){
2977
	// summary:
2978
	// 		Returns a function that forwards a method call to
2979
	// 		this.funcName(...).  Unlike dojo.lang.hitch(), the "this" scope is
2980
	// 		not fixed on a single object. Ported from MochiKit.
2981
	return function(){
2982
		return this[funcName].apply(this, arguments);
2983
	}; // Function
2984
}
2985
 
2986
dojo.lang.curry = function(thisObj, func /* args ... */){
2987
	// summary:
2988
	//		similar to the curry() method found in many functional programming
2989
	//		environments, this function returns an "argument accumulator"
2990
	//		function, bound to a particular scope, and "primed" with a variable
2991
	//		number of arguments. The curry method is unique in that it returns
2992
	//		a function that may return other "partial" function which can be
2993
	//		called repeatedly. New functions are returned until the arity of
2994
	//		the original function is reached, at which point the underlying
2995
	//		function (func) is called in the scope thisObj with all of the
2996
	//		accumulated arguments (plus any extras) in positional order.
2997
	// examples:
2998
	//		assuming a function defined like this:
2999
	//			var foo = {
3000
	//				bar: function(arg1, arg2, arg3){
3001
	//					dojo.debug.apply(dojo, arguments);
3002
	//				}
3003
	//			};
3004
	//
3005
	//		dojo.lang.curry() can be used most simply in this way:
3006
	//
3007
	//			tmp = dojo.lang.curry(foo, foo.bar, "arg one", "thinger");
3008
	//			tmp("blah", "this is superfluous");
3009
	//			// debugs: "arg one thinger blah this is superfluous"
3010
	//			tmp("blah");
3011
	//			// debugs: "arg one thinger blah"
3012
	//			tmp();
3013
	//			// returns a function exactly like tmp that expects one argument
3014
	//
3015
	//		other intermittent functions could be created until the 3
3016
	//		positional arguments are filled:
3017
	//
3018
	//			tmp = dojo.lang.curry(foo, foo.bar, "arg one");
3019
	//			tmp2 = tmp("arg two");
3020
	//			tmp2("blah blah");
3021
	//			// debugs: "arg one arg two blah blah"
3022
	//			tmp2("oy");
3023
	//			// debugs: "arg one arg two oy"
3024
	//
3025
	//		curry() can also be used to call the function if enough arguments
3026
	//		are passed in the initial invocation:
3027
	//
3028
	//			dojo.lang.curry(foo, foo.bar, "one", "two", "three", "four");
3029
	//			// debugs: "one two three four"
3030
	//			dojo.lang.curry(foo, foo.bar, "one", "two", "three");
3031
	//			// debugs: "one two three"
3032
 
3033
 
3034
	// FIXME: the order of func and thisObj should be changed!!!
3035
	var outerArgs = [];
3036
	thisObj = thisObj||dj_global;
3037
	if(dojo.lang.isString(func)){
3038
		func = thisObj[func];
3039
	}
3040
	for(var x=2; x<arguments.length; x++){
3041
		outerArgs.push(arguments[x]);
3042
	}
3043
	// since the event system replaces the original function with a new
3044
	// join-point runner with an arity of 0, we check to see if it's left us
3045
	// any clues about the original arity in lieu of the function's actual
3046
	// length property
3047
	var ecount = (func["__preJoinArity"]||func.length) - outerArgs.length;
3048
	// borrowed from svend tofte
3049
	function gather(nextArgs, innerArgs, expected){
3050
		var texpected = expected;
3051
		var totalArgs = innerArgs.slice(0); // copy
3052
		for(var x=0; x<nextArgs.length; x++){
3053
			totalArgs.push(nextArgs[x]);
3054
		}
3055
		// check the list of provided nextArgs to see if it, plus the
3056
		// number of innerArgs already supplied, meets the total
3057
		// expected.
3058
		expected = expected-nextArgs.length;
3059
		if(expected<=0){
3060
			var res = func.apply(thisObj, totalArgs);
3061
			expected = texpected;
3062
			return res;
3063
		}else{
3064
			return function(){
3065
				return gather(arguments,// check to see if we've been run
3066
										// with enough args
3067
							totalArgs,	// a copy
3068
							expected);	// how many more do we need to run?;
3069
			};
3070
		}
3071
	}
3072
	return gather([], outerArgs, ecount);
3073
}
3074
 
3075
dojo.lang.curryArguments = function(/*Object*/thisObj, /*Function*/func, /*Array*/args, /*Integer, optional*/offset){
3076
	// summary:
3077
	//		similar to dojo.lang.curry(), except that a list of arguments to
3078
	//		start the curry with may be provided as an array instead of as
3079
	//		positional arguments. An offset may be specified from the 0 index
3080
	//		to skip some elements in args.
3081
	var targs = [];
3082
	var x = offset||0;
3083
	for(x=offset; x<args.length; x++){
3084
		targs.push(args[x]); // ensure that it's an arr
3085
	}
3086
	return dojo.lang.curry.apply(dojo.lang, [thisObj, func].concat(targs));
3087
}
3088
 
3089
dojo.lang.tryThese = function(/*...*/){
3090
	// summary:
3091
	//		executes each function argument in turn, returning the return value
3092
	//		from the first one which does not throw an exception in execution.
3093
	//		Any number of functions may be passed.
3094
	for(var x=0; x<arguments.length; x++){
3095
		try{
3096
			if(typeof arguments[x] == "function"){
3097
				var ret = (arguments[x]());
3098
				if(ret){
3099
					return ret;
3100
				}
3101
			}
3102
		}catch(e){
3103
			dojo.debug(e);
3104
		}
3105
	}
3106
}
3107
 
3108
dojo.lang.delayThese = function(/*Array*/farr, /*Function, optional*/cb, /*Integer*/delay, /*Function, optional*/onend){
3109
	// summary:
3110
	//		executes a series of functions contained in farr, but spaces out
3111
	//		calls to each function by the millisecond delay provided. If cb is
3112
	//		provided, it will be called directly after each item in farr is
3113
	//		called and if onend is passed, it will be called when all items
3114
	//		have completed executing.
3115
 
3116
	/**
3117
	 * alternate: (array funcArray, function callback, function onend)
3118
	 * alternate: (array funcArray, function callback)
3119
	 * alternate: (array funcArray)
3120
	 */
3121
	if(!farr.length){
3122
		if(typeof onend == "function"){
3123
			onend();
3124
		}
3125
		return;
3126
	}
3127
	if((typeof delay == "undefined")&&(typeof cb == "number")){
3128
		delay = cb;
3129
		cb = function(){};
3130
	}else if(!cb){
3131
		cb = function(){};
3132
		if(!delay){ delay = 0; }
3133
	}
3134
	setTimeout(function(){
3135
		(farr.shift())();
3136
		cb();
3137
		dojo.lang.delayThese(farr, cb, delay, onend);
3138
	}, delay);
3139
}
3140
 
3141
dojo.provide("dojo.string.extras");
3142
 
3143
 
3144
 
3145
 
3146
 
3147
//TODO: should we use ${} substitution syntax instead, like widgets do?
3148
dojo.string.substituteParams = function(/*string*/template, /* object - optional or ... */hash){
3149
// summary:
3150
//	Performs parameterized substitutions on a string. Throws an exception if any parameter is unmatched.
3151
//
3152
// description:
3153
//	For example,
3154
//		dojo.string.substituteParams("File '%{0}' is not found in directory '%{1}'.","foo.html","/temp");
3155
//	returns
3156
//		"File 'foo.html' is not found in directory '/temp'."
3157
//
3158
// template: the original string template with %{values} to be replaced
3159
// hash: name/value pairs (type object) to provide substitutions.  Alternatively, substitutions may be
3160
//	included as arguments 1..n to this function, corresponding to template parameters 0..n-1
3161
 
3162
	var map = (typeof hash == 'object') ? hash : dojo.lang.toArray(arguments, 1);
3163
 
3164
	return template.replace(/\%\{(\w+)\}/g, function(match, key){
3165
		if(typeof(map[key]) != "undefined" && map[key] != null){
3166
			return map[key];
3167
		}
3168
		dojo.raise("Substitution not found: " + key);
3169
	}); // string
3170
};
3171
 
3172
dojo.string.capitalize = function(/*string*/str){
3173
// summary:
3174
//	Uppercases the first letter of each word
3175
 
3176
	if(!dojo.lang.isString(str)){ return ""; }
3177
	if(arguments.length == 0){ str = this; }
3178
 
3179
	var words = str.split(' ');
3180
	for(var i=0; i<words.length; i++){
3181
		words[i] = words[i].charAt(0).toUpperCase() + words[i].substring(1);
3182
	}
3183
	return words.join(" "); // string
3184
}
3185
 
3186
dojo.string.isBlank = function(/*string*/str){
3187
// summary:
3188
//	Return true if the entire string is whitespace characters
3189
 
3190
	if(!dojo.lang.isString(str)){ return true; }
3191
	return (dojo.string.trim(str).length == 0); // boolean
3192
}
3193
 
3194
//FIXME: not sure exactly what encodeAscii is trying to do, or if it's working right
3195
dojo.string.encodeAscii = function(/*string*/str){
3196
	if(!dojo.lang.isString(str)){ return str; } // unknown
3197
	var ret = "";
3198
	var value = escape(str);
3199
	var match, re = /%u([0-9A-F]{4})/i;
3200
	while((match = value.match(re))){
3201
		var num = Number("0x"+match[1]);
3202
		var newVal = escape("&#" + num + ";");
3203
		ret += value.substring(0, match.index) + newVal;
3204
		value = value.substring(match.index+match[0].length);
3205
	}
3206
	ret += value.replace(/\+/g, "%2B");
3207
	return ret; // string
3208
}
3209
 
3210
dojo.string.escape = function(/*string*/type, /*string*/str){
3211
// summary:
3212
//	Adds escape sequences for special characters according to the convention of 'type'
3213
//
3214
// type: one of xml|html|xhtml|sql|regexp|regex|javascript|jscript|js|ascii
3215
// str: the string to be escaped
3216
 
3217
	var args = dojo.lang.toArray(arguments, 1);
3218
	switch(type.toLowerCase()){
3219
		case "xml":
3220
		case "html":
3221
		case "xhtml":
3222
			return dojo.string.escapeXml.apply(this, args); // string
3223
		case "sql":
3224
			return dojo.string.escapeSql.apply(this, args); // string
3225
		case "regexp":
3226
		case "regex":
3227
			return dojo.string.escapeRegExp.apply(this, args); // string
3228
		case "javascript":
3229
		case "jscript":
3230
		case "js":
3231
			return dojo.string.escapeJavaScript.apply(this, args); // string
3232
		case "ascii":
3233
			// so it's encode, but it seems useful
3234
			return dojo.string.encodeAscii.apply(this, args); // string
3235
		default:
3236
			return str; // string
3237
	}
3238
}
3239
 
3240
dojo.string.escapeXml = function(/*string*/str, /*boolean*/noSingleQuotes){
3241
//summary:
3242
//	Adds escape sequences for special characters in XML: &<>"'
3243
//  Optionally skips escapes for single quotes
3244
 
3245
	str = str.replace(/&/gm, "&amp;").replace(/</gm, "&lt;")
3246
		.replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
3247
	if(!noSingleQuotes){ str = str.replace(/'/gm, "&#39;"); }
3248
	return str; // string
3249
}
3250
 
3251
dojo.string.escapeSql = function(/*string*/str){
3252
//summary:
3253
//	Adds escape sequences for single quotes in SQL expressions
3254
 
3255
	return str.replace(/'/gm, "''"); //string
3256
}
3257
 
3258
dojo.string.escapeRegExp = function(/*string*/str){
3259
//summary:
3260
//	Adds escape sequences for special characters in regular expressions
3261
 
3262
	return str.replace(/\\/gm, "\\\\").replace(/([\f\b\n\t\r[\^$|?*+(){}])/gm, "\\$1"); // string
3263
}
3264
 
3265
//FIXME: should this one also escape backslash?
3266
dojo.string.escapeJavaScript = function(/*string*/str){
3267
//summary:
3268
//	Adds escape sequences for single and double quotes as well
3269
//	as non-visible characters in JavaScript string literal expressions
3270
 
3271
	return str.replace(/(["'\f\b\n\t\r])/gm, "\\$1"); // string
3272
}
3273
 
3274
//FIXME: looks a lot like escapeJavaScript, just adds quotes? deprecate one?
3275
dojo.string.escapeString = function(/*string*/str){
3276
//summary:
3277
//	Adds escape sequences for non-visual characters, double quote and backslash
3278
//	and surrounds with double quotes to form a valid string literal.
3279
	return ('"' + str.replace(/(["\\])/g, '\\$1') + '"'
3280
		).replace(/[\f]/g, "\\f"
3281
		).replace(/[\b]/g, "\\b"
3282
		).replace(/[\n]/g, "\\n"
3283
		).replace(/[\t]/g, "\\t"
3284
		).replace(/[\r]/g, "\\r"); // string
3285
}
3286
 
3287
// TODO: make an HTML version
3288
dojo.string.summary = function(/*string*/str, /*number*/len){
3289
// summary:
3290
//	Truncates 'str' after 'len' characters and appends periods as necessary so that it ends with "..."
3291
 
3292
	if(!len || str.length <= len){
3293
		return str; // string
3294
	}
3295
 
3296
	return str.substring(0, len).replace(/\.+$/, "") + "..."; // string
3297
}
3298
 
3299
dojo.string.endsWith = function(/*string*/str, /*string*/end, /*boolean*/ignoreCase){
3300
// summary:
3301
//	Returns true if 'str' ends with 'end'
3302
 
3303
	if(ignoreCase){
3304
		str = str.toLowerCase();
3305
		end = end.toLowerCase();
3306
	}
3307
	if((str.length - end.length) < 0){
3308
		return false; // boolean
3309
	}
3310
	return str.lastIndexOf(end) == str.length - end.length; // boolean
3311
}
3312
 
3313
dojo.string.endsWithAny = function(/*string*/str /* , ... */){
3314
// summary:
3315
//	Returns true if 'str' ends with any of the arguments[2 -> n]
3316
 
3317
	for(var i = 1; i < arguments.length; i++) {
3318
		if(dojo.string.endsWith(str, arguments[i])) {
3319
			return true; // boolean
3320
		}
3321
	}
3322
	return false; // boolean
3323
}
3324
 
3325
dojo.string.startsWith = function(/*string*/str, /*string*/start, /*boolean*/ignoreCase){
3326
// summary:
3327
//	Returns true if 'str' starts with 'start'
3328
 
3329
	if(ignoreCase) {
3330
		str = str.toLowerCase();
3331
		start = start.toLowerCase();
3332
	}
3333
	return str.indexOf(start) == 0; // boolean
3334
}
3335
 
3336
dojo.string.startsWithAny = function(/*string*/str /* , ... */){
3337
// summary:
3338
//	Returns true if 'str' starts with any of the arguments[2 -> n]
3339
 
3340
	for(var i = 1; i < arguments.length; i++) {
3341
		if(dojo.string.startsWith(str, arguments[i])) {
3342
			return true; // boolean
3343
		}
3344
	}
3345
	return false; // boolean
3346
}
3347
 
3348
dojo.string.has = function(/*string*/str /* , ... */) {
3349
// summary:
3350
//	Returns true if 'str' contains any of the arguments 2 -> n
3351
 
3352
	for(var i = 1; i < arguments.length; i++) {
3353
		if(str.indexOf(arguments[i]) > -1){
3354
			return true; // boolean
3355
		}
3356
	}
3357
	return false; // boolean
3358
}
3359
 
3360
dojo.string.normalizeNewlines = function(/*string*/text, /*string? (\n or \r)*/newlineChar){
3361
// summary:
3362
//	Changes occurences of CR and LF in text to CRLF, or if newlineChar is provided as '\n' or '\r',
3363
//	substitutes newlineChar for occurrences of CR/LF and CRLF
3364
 
3365
	if (newlineChar == "\n"){
3366
		text = text.replace(/\r\n/g, "\n");
3367
		text = text.replace(/\r/g, "\n");
3368
	} else if (newlineChar == "\r"){
3369
		text = text.replace(/\r\n/g, "\r");
3370
		text = text.replace(/\n/g, "\r");
3371
	}else{
3372
		text = text.replace(/([^\r])\n/g, "$1\r\n").replace(/\r([^\n])/g, "\r\n$1");
3373
	}
3374
	return text; // string
3375
}
3376
 
3377
dojo.string.splitEscaped = function(/*string*/str, /*string of length=1*/charac){
3378
// summary:
3379
//	Splits 'str' into an array separated by 'charac', but skips characters escaped with a backslash
3380
 
3381
	var components = [];
3382
	for (var i = 0, prevcomma = 0; i < str.length; i++){
3383
		if (str.charAt(i) == '\\'){ i++; continue; }
3384
		if (str.charAt(i) == charac){
3385
			components.push(str.substring(prevcomma, i));
3386
			prevcomma = i + 1;
3387
		}
3388
	}
3389
	components.push(str.substr(prevcomma));
3390
	return components; // array
3391
}
3392
 
3393
dojo.provide("dojo.dom");
3394
 
3395
dojo.dom.ELEMENT_NODE                  = 1;
3396
dojo.dom.ATTRIBUTE_NODE                = 2;
3397
dojo.dom.TEXT_NODE                     = 3;
3398
dojo.dom.CDATA_SECTION_NODE            = 4;
3399
dojo.dom.ENTITY_REFERENCE_NODE         = 5;
3400
dojo.dom.ENTITY_NODE                   = 6;
3401
dojo.dom.PROCESSING_INSTRUCTION_NODE   = 7;
3402
dojo.dom.COMMENT_NODE                  = 8;
3403
dojo.dom.DOCUMENT_NODE                 = 9;
3404
dojo.dom.DOCUMENT_TYPE_NODE            = 10;
3405
dojo.dom.DOCUMENT_FRAGMENT_NODE        = 11;
3406
dojo.dom.NOTATION_NODE                 = 12;
3407
 
3408
dojo.dom.dojoml = "http://www.dojotoolkit.org/2004/dojoml";
3409
 
3410
/**
3411
 *	comprehensive list of XML namespaces
3412
**/
3413
dojo.dom.xmlns = {
3414
	//	summary
3415
	//	aliases for various common XML namespaces
3416
	svg : "http://www.w3.org/2000/svg",
3417
	smil : "http://www.w3.org/2001/SMIL20/",
3418
	mml : "http://www.w3.org/1998/Math/MathML",
3419
	cml : "http://www.xml-cml.org",
3420
	xlink : "http://www.w3.org/1999/xlink",
3421
	xhtml : "http://www.w3.org/1999/xhtml",
3422
	xul : "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
3423
	xbl : "http://www.mozilla.org/xbl",
3424
	fo : "http://www.w3.org/1999/XSL/Format",
3425
	xsl : "http://www.w3.org/1999/XSL/Transform",
3426
	xslt : "http://www.w3.org/1999/XSL/Transform",
3427
	xi : "http://www.w3.org/2001/XInclude",
3428
	xforms : "http://www.w3.org/2002/01/xforms",
3429
	saxon : "http://icl.com/saxon",
3430
	xalan : "http://xml.apache.org/xslt",
3431
	xsd : "http://www.w3.org/2001/XMLSchema",
3432
	dt: "http://www.w3.org/2001/XMLSchema-datatypes",
3433
	xsi : "http://www.w3.org/2001/XMLSchema-instance",
3434
	rdf : "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
3435
	rdfs : "http://www.w3.org/2000/01/rdf-schema#",
3436
	dc : "http://purl.org/dc/elements/1.1/",
3437
	dcq: "http://purl.org/dc/qualifiers/1.0",
3438
	"soap-env" : "http://schemas.xmlsoap.org/soap/envelope/",
3439
	wsdl : "http://schemas.xmlsoap.org/wsdl/",
3440
	AdobeExtensions : "http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
3441
};
3442
 
3443
dojo.dom.isNode = function(/* object */wh){
3444
	//	summary:
3445
	//		checks to see if wh is actually a node.
3446
	if(typeof Element == "function") {
3447
		try {
3448
			return wh instanceof Element;	//	boolean
3449
		} catch(e) {}
3450
	} else {
3451
		// best-guess
3452
		return wh && !isNaN(wh.nodeType);	//	boolean
3453
	}
3454
}
3455
 
3456
dojo.dom.getUniqueId = function(){
3457
	//	summary:
3458
	//		returns a unique string for use with any DOM element
3459
	var _document = dojo.doc();
3460
	do {
3461
		var id = "dj_unique_" + (++arguments.callee._idIncrement);
3462
	}while(_document.getElementById(id));
3463
	return id;	//	string
3464
}
3465
dojo.dom.getUniqueId._idIncrement = 0;
3466
 
3467
dojo.dom.firstElement = dojo.dom.getFirstChildElement = function(/* Element */parentNode, /* string? */tagName){
3468
	//	summary:
3469
	//		returns the first child element matching tagName
3470
	var node = parentNode.firstChild;
3471
	while(node && node.nodeType != dojo.dom.ELEMENT_NODE){
3472
		node = node.nextSibling;
3473
	}
3474
	if(tagName && node && node.tagName && node.tagName.toLowerCase() != tagName.toLowerCase()) {
3475
		node = dojo.dom.nextElement(node, tagName);
3476
	}
3477
	return node;	//	Element
3478
}
3479
 
3480
dojo.dom.lastElement = dojo.dom.getLastChildElement = function(/* Element */parentNode, /* string? */tagName){
3481
	//	summary:
3482
	//		returns the last child element matching tagName
3483
	var node = parentNode.lastChild;
3484
	while(node && node.nodeType != dojo.dom.ELEMENT_NODE) {
3485
		node = node.previousSibling;
3486
	}
3487
	if(tagName && node && node.tagName && node.tagName.toLowerCase() != tagName.toLowerCase()) {
3488
		node = dojo.dom.prevElement(node, tagName);
3489
	}
3490
	return node;	//	Element
3491
}
3492
 
3493
dojo.dom.nextElement = dojo.dom.getNextSiblingElement = function(/* Node */node, /* string? */tagName){
3494
	//	summary:
3495
	//		returns the next sibling element matching tagName
3496
	if(!node) { return null; }
3497
	do {
3498
		node = node.nextSibling;
3499
	} while(node && node.nodeType != dojo.dom.ELEMENT_NODE);
3500
 
3501
	if(node && tagName && tagName.toLowerCase() != node.tagName.toLowerCase()) {
3502
		return dojo.dom.nextElement(node, tagName);
3503
	}
3504
	return node;	//	Element
3505
}
3506
 
3507
dojo.dom.prevElement = dojo.dom.getPreviousSiblingElement = function(/* Node */node, /* string? */tagName){
3508
	//	summary:
3509
	//		returns the previous sibling element matching tagName
3510
	if(!node) { return null; }
3511
	if(tagName) { tagName = tagName.toLowerCase(); }
3512
	do {
3513
		node = node.previousSibling;
3514
	} while(node && node.nodeType != dojo.dom.ELEMENT_NODE);
3515
 
3516
	if(node && tagName && tagName.toLowerCase() != node.tagName.toLowerCase()) {
3517
		return dojo.dom.prevElement(node, tagName);
3518
	}
3519
	return node;	//	Element
3520
}
3521
 
3522
// TODO: hmph
3523
/*this.forEachChildTag = function(node, unaryFunc) {
3524
	var child = this.getFirstChildTag(node);
3525
	while(child) {
3526
		if(unaryFunc(child) == "break") { break; }
3527
		child = this.getNextSiblingTag(child);
3528
	}
3529
}*/
3530
 
3531
dojo.dom.moveChildren = function(/*Element*/srcNode, /*Element*/destNode, /*boolean?*/trim){
3532
	//	summary:
3533
	//		Moves children from srcNode to destNode and returns the count of
3534
	//		children moved; will trim off text nodes if trim == true
3535
	var count = 0;
3536
	if(trim) {
3537
		while(srcNode.hasChildNodes() &&
3538
			srcNode.firstChild.nodeType == dojo.dom.TEXT_NODE) {
3539
			srcNode.removeChild(srcNode.firstChild);
3540
		}
3541
		while(srcNode.hasChildNodes() &&
3542
			srcNode.lastChild.nodeType == dojo.dom.TEXT_NODE) {
3543
			srcNode.removeChild(srcNode.lastChild);
3544
		}
3545
	}
3546
	while(srcNode.hasChildNodes()){
3547
		destNode.appendChild(srcNode.firstChild);
3548
		count++;
3549
	}
3550
	return count;	//	number
3551
}
3552
 
3553
dojo.dom.copyChildren = function(/*Element*/srcNode, /*Element*/destNode, /*boolean?*/trim){
3554
	//	summary:
3555
	//		Copies children from srcNde to destNode and returns the count of
3556
	//		children copied; will trim off text nodes if trim == true
3557
	var clonedNode = srcNode.cloneNode(true);
3558
	return this.moveChildren(clonedNode, destNode, trim);	//	number
3559
}
3560
 
3561
dojo.dom.replaceChildren = function(/*Element*/node, /*Node*/newChild){
3562
	//	summary:
3563
	//		Removes all children of node and appends newChild. All the existing
3564
	//		children will be destroyed.
3565
	// FIXME: what if newChild is an array-like object?
3566
	var nodes = [];
3567
	if(dojo.render.html.ie){
3568
		for(var i=0;i<node.childNodes.length;i++){
3569
			nodes.push(node.childNodes[i]);
3570
		}
3571
	}
3572
	dojo.dom.removeChildren(node);
3573
	node.appendChild(newChild);
3574
	for(var i=0;i<nodes.length;i++){
3575
		dojo.dom.destroyNode(nodes[i]);
3576
	}
3577
}
3578
 
3579
dojo.dom.removeChildren = function(/*Element*/node){
3580
	//	summary:
3581
	//		removes all children from node and returns the count of children removed.
3582
	//		The children nodes are not destroyed. Be sure to call destroyNode on them
3583
	//		after they are not used anymore.
3584
	var count = node.childNodes.length;
3585
	while(node.hasChildNodes()){ dojo.dom.removeNode(node.firstChild); }
3586
	return count; // int
3587
}
3588
 
3589
dojo.dom.replaceNode = function(/*Element*/node, /*Element*/newNode){
3590
	//	summary:
3591
	//		replaces node with newNode and returns a reference to the removed node.
3592
	//		To prevent IE memory leak, call destroyNode on the returned node when
3593
	//		it is no longer needed.
3594
	return node.parentNode.replaceChild(newNode, node); // Node
3595
}
3596
 
3597
dojo.dom.destroyNode = function(/*Node*/node){
3598
	// summary:
3599
	//		destroy a node (it can not be used any more). For IE, this is the
3600
	//		right function to call to prevent memory leaks. While for other
3601
	//		browsers, this is identical to dojo.dom.removeNode
3602
	if(node.parentNode){
3603
		node = dojo.dom.removeNode(node);
3604
	}
3605
	if(node.nodeType != 3){ // ingore TEXT_NODE
3606
		if(dojo.evalObjPath("dojo.event.browser.clean", false)){
3607
			dojo.event.browser.clean(node);
3608
		}
3609
		if(dojo.render.html.ie){
3610
			node.outerHTML=''; //prevent ugly IE mem leak associated with Node.removeChild (ticket #1727)
3611
		}
3612
	}
3613
}
3614
 
3615
dojo.dom.removeNode = function(/*Node*/node){
3616
	// summary:
3617
	//		if node has a parent, removes node from parent and returns a
3618
	//		reference to the removed child.
3619
	//		To prevent IE memory leak, call destroyNode on the returned node when
3620
	//		it is no longer needed.
3621
	//	node:
3622
	//		the node to remove from its parent.
3623
 
3624
	if(node && node.parentNode){
3625
		// return a ref to the removed child
3626
		return node.parentNode.removeChild(node); //Node
3627
	}
3628
}
3629
 
3630
dojo.dom.getAncestors = function(/*Node*/node, /*function?*/filterFunction, /*boolean?*/returnFirstHit){
3631
	//	summary:
3632
	//		returns all ancestors matching optional filterFunction; will return
3633
	//		only the first if returnFirstHit
3634
	var ancestors = [];
3635
	var isFunction = (filterFunction && (filterFunction instanceof Function || typeof filterFunction == "function"));
3636
	while(node){
3637
		if(!isFunction || filterFunction(node)){
3638
			ancestors.push(node);
3639
		}
3640
		if(returnFirstHit && ancestors.length > 0){
3641
			return ancestors[0]; 	//	Node
3642
		}
3643
 
3644
		node = node.parentNode;
3645
	}
3646
	if(returnFirstHit){ return null; }
3647
	return ancestors;	//	array
3648
}
3649
 
3650
dojo.dom.getAncestorsByTag = function(/*Node*/node, /*String*/tag, /*boolean?*/returnFirstHit){
3651
	//	summary:
3652
	//		returns all ancestors matching tag (as tagName), will only return
3653
	//		first one if returnFirstHit
3654
	tag = tag.toLowerCase();
3655
	return dojo.dom.getAncestors(node, function(el){
3656
		return ((el.tagName)&&(el.tagName.toLowerCase() == tag));
3657
	}, returnFirstHit);	//	Node || array
3658
}
3659
 
3660
dojo.dom.getFirstAncestorByTag = function(/*Node*/node, /*string*/tag){
3661
	//	summary:
3662
	//		Returns first ancestor of node with tag tagName
3663
	return dojo.dom.getAncestorsByTag(node, tag, true);	//	Node
3664
}
3665
 
3666
dojo.dom.isDescendantOf = function(/* Node */node, /* Node */ancestor, /* boolean? */guaranteeDescendant){
3667
	//	summary
3668
	//	Returns boolean if node is a descendant of ancestor
3669
	// guaranteeDescendant allows us to be a "true" isDescendantOf function
3670
	if(guaranteeDescendant && node) { node = node.parentNode; }
3671
	while(node) {
3672
		if(node == ancestor){
3673
			return true; 	//	boolean
3674
		}
3675
		node = node.parentNode;
3676
	}
3677
	return false;	//	boolean
3678
}
3679
 
3680
dojo.dom.innerXML = function(/*Node*/node){
3681
	//	summary:
3682
	//		Implementation of MS's innerXML function.
3683
	if(node.innerXML){
3684
		return node.innerXML;	//	string
3685
	}else if (node.xml){
3686
		return node.xml;		//	string
3687
	}else if(typeof XMLSerializer != "undefined"){
3688
		return (new XMLSerializer()).serializeToString(node);	//	string
3689
	}
3690
}
3691
 
3692
dojo.dom.createDocument = function(){
3693
	//	summary:
3694
	//		cross-browser implementation of creating an XML document object.
3695
	var doc = null;
3696
	var _document = dojo.doc();
3697
 
3698
	if(!dj_undef("ActiveXObject")){
3699
		var prefixes = [ "MSXML2", "Microsoft", "MSXML", "MSXML3" ];
3700
		for(var i = 0; i<prefixes.length; i++){
3701
			try{
3702
				doc = new ActiveXObject(prefixes[i]+".XMLDOM");
3703
			}catch(e){ /* squelch */ };
3704
 
3705
			if(doc){ break; }
3706
		}
3707
	}else if((_document.implementation)&&
3708
		(_document.implementation.createDocument)){
3709
		doc = _document.implementation.createDocument("", "", null);
3710
	}
3711
 
3712
	return doc;	//	DOMDocument
3713
}
3714
 
3715
dojo.dom.createDocumentFromText = function(/*string*/str, /*string?*/mimetype){
3716
	//	summary:
3717
	//		attempts to create a Document object based on optional mime-type,
3718
	//		using str as the contents of the document
3719
	if(!mimetype){ mimetype = "text/xml"; }
3720
	if(!dj_undef("DOMParser")){
3721
		var parser = new DOMParser();
3722
		return parser.parseFromString(str, mimetype);	//	DOMDocument
3723
	}else if(!dj_undef("ActiveXObject")){
3724
		var domDoc = dojo.dom.createDocument();
3725
		if(domDoc){
3726
			domDoc.async = false;
3727
			domDoc.loadXML(str);
3728
			return domDoc;	//	DOMDocument
3729
		}else{
3730
			dojo.debug("toXml didn't work?");
3731
		}
3732
	/*
3733
	}else if((dojo.render.html.capable)&&(dojo.render.html.safari)){
3734
		// FIXME: this doesn't appear to work!
3735
		// from: http://web-graphics.com/mtarchive/001606.php
3736
		// var xml = '<?xml version="1.0"?>'+str;
3737
		var mtype = "text/xml";
3738
		var xml = '<?xml version="1.0"?>'+str;
3739
		var url = "data:"+mtype+";charset=utf-8,"+encodeURIComponent(xml);
3740
		var req = new XMLHttpRequest();
3741
		req.open("GET", url, false);
3742
		req.overrideMimeType(mtype);
3743
		req.send(null);
3744
		return req.responseXML;
3745
	*/
3746
	}else{
3747
		var _document = dojo.doc();
3748
		if(_document.createElement){
3749
			// FIXME: this may change all tags to uppercase!
3750
			var tmp = _document.createElement("xml");
3751
			tmp.innerHTML = str;
3752
			if(_document.implementation && _document.implementation.createDocument){
3753
				var xmlDoc = _document.implementation.createDocument("foo", "", null);
3754
				for(var i = 0; i < tmp.childNodes.length; i++) {
3755
					xmlDoc.importNode(tmp.childNodes.item(i), true);
3756
				}
3757
				return xmlDoc;	//	DOMDocument
3758
			}
3759
			// FIXME: probably not a good idea to have to return an HTML fragment
3760
			// FIXME: the tmp.doc.firstChild is as tested from IE, so it may not
3761
			// work that way across the board
3762
			return ((tmp.document)&&
3763
				(tmp.document.firstChild ?  tmp.document.firstChild : tmp));	//	DOMDocument
3764
		}
3765
	}
3766
	return null;
3767
}
3768
 
3769
dojo.dom.prependChild = function(/*Element*/node, /*Element*/parent){
3770
	//	summary:
3771
	//		prepends node to parent's children nodes
3772
	if(parent.firstChild) {
3773
		parent.insertBefore(node, parent.firstChild);
3774
	} else {
3775
		parent.appendChild(node);
3776
	}
3777
	return true;	//	boolean
3778
}
3779
 
3780
dojo.dom.insertBefore = function(/*Node*/node, /*Node*/ref, /*boolean?*/force){
3781
	//	summary:
3782
	//		Try to insert node before ref
3783
	if(	(force != true)&&
3784
		(node === ref || node.nextSibling === ref)){ return false; }
3785
	var parent = ref.parentNode;
3786
	parent.insertBefore(node, ref);
3787
	return true;	//	boolean
3788
}
3789
 
3790
dojo.dom.insertAfter = function(/*Node*/node, /*Node*/ref, /*boolean?*/force){
3791
	//	summary:
3792
	//		Try to insert node after ref
3793
	var pn = ref.parentNode;
3794
	if(ref == pn.lastChild){
3795
		if((force != true)&&(node === ref)){
3796
			return false;	//	boolean
3797
		}
3798
		pn.appendChild(node);
3799
	}else{
3800
		return this.insertBefore(node, ref.nextSibling, force);	//	boolean
3801
	}
3802
	return true;	//	boolean
3803
}
3804
 
3805
dojo.dom.insertAtPosition = function(/*Node*/node, /*Node*/ref, /*string*/position){
3806
	//	summary:
3807
	//		attempt to insert node in relation to ref based on position
3808
	if((!node)||(!ref)||(!position)){
3809
		return false;	//	boolean
3810
	}
3811
	switch(position.toLowerCase()){
3812
		case "before":
3813
			return dojo.dom.insertBefore(node, ref);	//	boolean
3814
		case "after":
3815
			return dojo.dom.insertAfter(node, ref);		//	boolean
3816
		case "first":
3817
			if(ref.firstChild){
3818
				return dojo.dom.insertBefore(node, ref.firstChild);	//	boolean
3819
			}else{
3820
				ref.appendChild(node);
3821
				return true;	//	boolean
3822
			}
3823
			break;
3824
		default: // aka: last
3825
			ref.appendChild(node);
3826
			return true;	//	boolean
3827
	}
3828
}
3829
 
3830
dojo.dom.insertAtIndex = function(/*Node*/node, /*Element*/containingNode, /*number*/insertionIndex){
3831
	//	summary:
3832
	//		insert node into child nodes nodelist of containingNode at
3833
	//		insertionIndex. insertionIndex should be between 0 and
3834
	//		the number of the childNodes in containingNode. insertionIndex
3835
	//		specifys after how many childNodes in containingNode the node
3836
	//		shall be inserted. If 0 is given, node will be appended to
3837
	//		containingNode.
3838
	var siblingNodes = containingNode.childNodes;
3839
 
3840
	// if there aren't any kids yet, just add it to the beginning
3841
 
3842
	if (!siblingNodes.length || siblingNodes.length == insertionIndex){
3843
		containingNode.appendChild(node);
3844
		return true;	//	boolean
3845
	}
3846
 
3847
	if(insertionIndex == 0){
3848
		return dojo.dom.prependChild(node, containingNode);	//	boolean
3849
	}
3850
	// otherwise we need to walk the childNodes
3851
	// and find our spot
3852
 
3853
	return dojo.dom.insertAfter(node, siblingNodes[insertionIndex-1]);	//	boolean
3854
}
3855
 
3856
dojo.dom.textContent = function(/*Node*/node, /*string*/text){
3857
	//	summary:
3858
	//		implementation of the DOM Level 3 attribute; scan node for text
3859
	if (arguments.length>1) {
3860
		var _document = dojo.doc();
3861
		dojo.dom.replaceChildren(node, _document.createTextNode(text));
3862
		return text;	//	string
3863
	} else {
3864
		if(node.textContent != undefined){ //FF 1.5
3865
			return node.textContent;	//	string
3866
		}
3867
		var _result = "";
3868
		if (node == null) { return _result; }
3869
		for (var i = 0; i < node.childNodes.length; i++) {
3870
			switch (node.childNodes[i].nodeType) {
3871
				case 1: // ELEMENT_NODE
3872
				case 5: // ENTITY_REFERENCE_NODE
3873
					_result += dojo.dom.textContent(node.childNodes[i]);
3874
					break;
3875
				case 3: // TEXT_NODE
3876
				case 2: // ATTRIBUTE_NODE
3877
				case 4: // CDATA_SECTION_NODE
3878
					_result += node.childNodes[i].nodeValue;
3879
					break;
3880
				default:
3881
					break;
3882
			}
3883
		}
3884
		return _result;	//	string
3885
	}
3886
}
3887
 
3888
dojo.dom.hasParent = function(/*Node*/node){
3889
	//	summary:
3890
	//		returns whether or not node is a child of another node.
3891
	return Boolean(node && node.parentNode && dojo.dom.isNode(node.parentNode));	//	boolean
3892
}
3893
 
3894
/**
3895
 * Examples:
3896
 *
3897
 * myFooNode = <foo />
3898
 * isTag(myFooNode, "foo"); // returns "foo"
3899
 * isTag(myFooNode, "bar"); // returns ""
3900
 * isTag(myFooNode, "FOO"); // returns ""
3901
 * isTag(myFooNode, "hey", "foo", "bar"); // returns "foo"
3902
**/
3903
dojo.dom.isTag = function(/* Node */node /* ... */){
3904
	//	summary:
3905
	//		determines if node has any of the provided tag names and returns
3906
	//		the tag name that matches, empty string otherwise.
3907
	if(node && node.tagName) {
3908
		for(var i=1; i<arguments.length; i++){
3909
			if(node.tagName==String(arguments[i])){
3910
				return String(arguments[i]);	//	string
3911
			}
3912
		}
3913
	}
3914
	return "";	//	string
3915
}
3916
 
3917
dojo.dom.setAttributeNS = function(	/*Element*/elem, /*string*/namespaceURI,
3918
									/*string*/attrName, /*string*/attrValue){
3919
	//	summary:
3920
	//		implementation of DOM2 setAttributeNS that works cross browser.
3921
	if(elem == null || ((elem == undefined)&&(typeof elem == "undefined"))){
3922
		dojo.raise("No element given to dojo.dom.setAttributeNS");
3923
	}
3924
 
3925
	if(!((elem.setAttributeNS == undefined)&&(typeof elem.setAttributeNS == "undefined"))){ // w3c
3926
		elem.setAttributeNS(namespaceURI, attrName, attrValue);
3927
	}else{ // IE
3928
		// get a root XML document
3929
		var ownerDoc = elem.ownerDocument;
3930
		var attribute = ownerDoc.createNode(
3931
			2, // node type
3932
			attrName,
3933
			namespaceURI
3934
		);
3935
 
3936
		// set value
3937
		attribute.nodeValue = attrValue;
3938
 
3939
		// attach to element
3940
		elem.setAttributeNode(attribute);
3941
	}
3942
}
3943
 
3944
dojo.provide("dojo.undo.browser");
3945
 
3946
 
3947
try{
3948
	if((!djConfig["preventBackButtonFix"])&&(!dojo.hostenv.post_load_)){
3949
		document.write("<iframe style='border: 0px; width: 1px; height: 1px; position: absolute; bottom: 0px; right: 0px; visibility: visible;' name='djhistory' id='djhistory' src='" + (djConfig["dojoIframeHistoryUrl"] || dojo.hostenv.getBaseScriptUri()+'iframe_history.html') + "'></iframe>");
3950
	}
3951
}catch(e){/* squelch */}
3952
 
3953
if(dojo.render.html.opera){
3954
	dojo.debug("Opera is not supported with dojo.undo.browser, so back/forward detection will not work.");
3955
}
3956
 
3957
dojo.undo.browser = {
3958
	initialHref: (!dj_undef("window")) ? window.location.href : "",
3959
	initialHash: (!dj_undef("window")) ? window.location.hash : "",
3960
 
3961
	moveForward: false,
3962
	historyStack: [],
3963
	forwardStack: [],
3964
	historyIframe: null,
3965
	bookmarkAnchor: null,
3966
	locationTimer: null,
3967
 
3968
	/**
3969
	 *
3970
	 */
3971
	setInitialState: function(/*Object*/args){
3972
		//summary: Sets the state object and back callback for the very first page that is loaded.
3973
		//description: It is recommended that you call this method as part of an event listener that is registered via
3974
		//dojo.addOnLoad().
3975
		//args: Object
3976
		//		See the addToHistory() function for the list of valid args properties.
3977
		this.initialState = this._createState(this.initialHref, args, this.initialHash);
3978
	},
3979
 
3980
	//FIXME: Would like to support arbitrary back/forward jumps. Have to rework iframeLoaded among other things.
3981
	//FIXME: is there a slight race condition in moz using change URL with the timer check and when
3982
	//       the hash gets set? I think I have seen a back/forward call in quick succession, but not consistent.
3983
	addToHistory: function(args){
3984
		//summary: adds a state object (args) to the history list. You must set
3985
		//djConfig.preventBackButtonFix = false to use dojo.undo.browser.
3986
 
3987
		//args: Object
3988
		//		args can have the following properties:
3989
		//		To support getting back button notifications, the object argument should implement a
3990
		//		function called either "back", "backButton", or "handle". The string "back" will be
3991
		//		passed as the first and only argument to this callback.
3992
		//		- To support getting forward button notifications, the object argument should implement a
3993
		//		function called either "forward", "forwardButton", or "handle". The string "forward" will be
3994
		//		passed as the first and only argument to this callback.
3995
		//		- If you want the browser location string to change, define "changeUrl" on the object. If the
3996
		//		value of "changeUrl" is true, then a unique number will be appended to the URL as a fragment
3997
		//		identifier (http://some.domain.com/path#uniquenumber). If it is any other value that does
3998
		//		not evaluate to false, that value will be used as the fragment identifier. For example,
3999
		//		if changeUrl: 'page1', then the URL will look like: http://some.domain.com/path#page1
4000
	 	//		Full example:
4001
		//		dojo.undo.browser.addToHistory({
4002
		//		  back: function() { alert('back pressed'); },
4003
		//		  forward: function() { alert('forward pressed'); },
4004
		//		  changeUrl: true
4005
		//		});
4006
		//
4007
		//	BROWSER NOTES:
4008
		//  Safari 1.2:
4009
		//	back button "works" fine, however it's not possible to actually
4010
		//	DETECT that you've moved backwards by inspecting window.location.
4011
		//	Unless there is some other means of locating.
4012
		//	FIXME: perhaps we can poll on history.length?
4013
		//	Safari 2.0.3+ (and probably 1.3.2+):
4014
		//	works fine, except when changeUrl is used. When changeUrl is used,
4015
		//	Safari jumps all the way back to whatever page was shown before
4016
		//	the page that uses dojo.undo.browser support.
4017
		//	IE 5.5 SP2:
4018
		//	back button behavior is macro. It does not move back to the
4019
		//	previous hash value, but to the last full page load. This suggests
4020
		//	that the iframe is the correct way to capture the back button in
4021
		//	these cases.
4022
		//	Don't test this page using local disk for MSIE. MSIE will not create
4023
		//	a history list for iframe_history.html if served from a file: URL.
4024
		//	The XML served back from the XHR tests will also not be properly
4025
		//	created if served from local disk. Serve the test pages from a web
4026
		//	server to test in that browser.
4027
		//	IE 6.0:
4028
		//	same behavior as IE 5.5 SP2
4029
		//	Firefox 1.0+:
4030
		//	the back button will return us to the previous hash on the same
4031
		//	page, thereby not requiring an iframe hack, although we do then
4032
		//	need to run a timer to detect inter-page movement.
4033
 
4034
		//If addToHistory is called, then that means we prune the
4035
		//forward stack -- the user went back, then wanted to
4036
		//start a new forward path.
4037
		this.forwardStack = [];
4038
 
4039
		var hash = null;
4040
		var url = null;
4041
		if(!this.historyIframe){
4042
			if(djConfig["useXDomain"] && !djConfig["dojoIframeHistoryUrl"]){
4043
				dojo.debug("dojo.undo.browser: When using cross-domain Dojo builds,"
4044
					+ " please save iframe_history.html to your domain and set djConfig.dojoIframeHistoryUrl"
4045
					+ " to the path on your domain to iframe_history.html");
4046
			}
4047
			this.historyIframe = window.frames["djhistory"];
4048
		}
4049
		if(!this.bookmarkAnchor){
4050
			this.bookmarkAnchor = document.createElement("a");
4051
			dojo.body().appendChild(this.bookmarkAnchor);
4052
			this.bookmarkAnchor.style.display = "none";
4053
		}
4054
		if(args["changeUrl"]){
4055
			hash = "#"+ ((args["changeUrl"]!==true) ? args["changeUrl"] : (new Date()).getTime());
4056
 
4057
			//If the current hash matches the new one, just replace the history object with
4058
			//this new one. It doesn't make sense to track different state objects for the same
4059
			//logical URL. This matches the browser behavior of only putting in one history
4060
			//item no matter how many times you click on the same #hash link, at least in Firefox
4061
			//and Safari, and there is no reliable way in those browsers to know if a #hash link
4062
			//has been clicked on multiple times. So making this the standard behavior in all browsers
4063
			//so that dojo.undo.browser's behavior is the same in all browsers.
4064
			if(this.historyStack.length == 0 && this.initialState.urlHash == hash){
4065
				this.initialState = this._createState(url, args, hash);
4066
				return;
4067
			}else if(this.historyStack.length > 0 && this.historyStack[this.historyStack.length - 1].urlHash == hash){
4068
				this.historyStack[this.historyStack.length - 1] = this._createState(url, args, hash);
4069
				return;
4070
			}
4071
 
4072
			this.changingUrl = true;
4073
			setTimeout("window.location.href = '"+hash+"'; dojo.undo.browser.changingUrl = false;", 1);
4074
			this.bookmarkAnchor.href = hash;
4075
 
4076
			if(dojo.render.html.ie){
4077
				url = this._loadIframeHistory();
4078
 
4079
				var oldCB = args["back"]||args["backButton"]||args["handle"];
4080
 
4081
				//The function takes handleName as a parameter, in case the
4082
				//callback we are overriding was "handle". In that case,
4083
				//we will need to pass the handle name to handle.
4084
				var tcb = function(handleName){
4085
					if(window.location.hash != ""){
4086
						setTimeout("window.location.href = '"+hash+"';", 1);
4087
					}
4088
					//Use apply to set "this" to args, and to try to avoid memory leaks.
4089
					oldCB.apply(this, [handleName]);
4090
				}
4091
 
4092
				//Set interceptor function in the right place.
4093
				if(args["back"]){
4094
					args.back = tcb;
4095
				}else if(args["backButton"]){
4096
					args.backButton = tcb;
4097
				}else if(args["handle"]){
4098
					args.handle = tcb;
4099
				}
4100
 
4101
				var oldFW = args["forward"]||args["forwardButton"]||args["handle"];
4102
 
4103
				//The function takes handleName as a parameter, in case the
4104
				//callback we are overriding was "handle". In that case,
4105
				//we will need to pass the handle name to handle.
4106
				var tfw = function(handleName){
4107
					if(window.location.hash != ""){
4108
						window.location.href = hash;
4109
					}
4110
					if(oldFW){ // we might not actually have one
4111
						//Use apply to set "this" to args, and to try to avoid memory leaks.
4112
						oldFW.apply(this, [handleName]);
4113
					}
4114
				}
4115
 
4116
				//Set interceptor function in the right place.
4117
				if(args["forward"]){
4118
					args.forward = tfw;
4119
				}else if(args["forwardButton"]){
4120
					args.forwardButton = tfw;
4121
				}else if(args["handle"]){
4122
					args.handle = tfw;
4123
				}
4124
 
4125
			}else if(dojo.render.html.moz){
4126
				// start the timer
4127
				if(!this.locationTimer){
4128
					this.locationTimer = setInterval("dojo.undo.browser.checkLocation();", 200);
4129
				}
4130
			}
4131
		}else{
4132
			url = this._loadIframeHistory();
4133
		}
4134
 
4135
		this.historyStack.push(this._createState(url, args, hash));
4136
	},
4137
 
4138
	checkLocation: function(){
4139
		//summary: private method. Do not call this directly.
4140
		if (!this.changingUrl){
4141
			var hsl = this.historyStack.length;
4142
 
4143
			if((window.location.hash == this.initialHash||window.location.href == this.initialHref)&&(hsl == 1)){
4144
				// FIXME: could this ever be a forward button?
4145
				// we can't clear it because we still need to check for forwards. Ugg.
4146
				// clearInterval(this.locationTimer);
4147
				this.handleBackButton();
4148
				return;
4149
			}
4150
 
4151
			// first check to see if we could have gone forward. We always halt on
4152
			// a no-hash item.
4153
			if(this.forwardStack.length > 0){
4154
				if(this.forwardStack[this.forwardStack.length-1].urlHash == window.location.hash){
4155
					this.handleForwardButton();
4156
					return;
4157
				}
4158
			}
4159
 
4160
			// ok, that didn't work, try someplace back in the history stack
4161
			if((hsl >= 2)&&(this.historyStack[hsl-2])){
4162
				if(this.historyStack[hsl-2].urlHash==window.location.hash){
4163
					this.handleBackButton();
4164
					return;
4165
				}
4166
			}
4167
		}
4168
	},
4169
 
4170
	iframeLoaded: function(evt, ifrLoc){
4171
		//summary: private method. Do not call this directly.
4172
		if(!dojo.render.html.opera){
4173
			var query = this._getUrlQuery(ifrLoc.href);
4174
			if(query == null){
4175
				// alert("iframeLoaded");
4176
				// we hit the end of the history, so we should go back
4177
				if(this.historyStack.length == 1){
4178
					this.handleBackButton();
4179
				}
4180
				return;
4181
			}
4182
			if(this.moveForward){
4183
				// we were expecting it, so it's not either a forward or backward movement
4184
				this.moveForward = false;
4185
				return;
4186
			}
4187
 
4188
			//Check the back stack first, since it is more likely.
4189
			//Note that only one step back or forward is supported.
4190
			if(this.historyStack.length >= 2 && query == this._getUrlQuery(this.historyStack[this.historyStack.length-2].url)){
4191
				this.handleBackButton();
4192
			}
4193
			else if(this.forwardStack.length > 0 && query == this._getUrlQuery(this.forwardStack[this.forwardStack.length-1].url)){
4194
				this.handleForwardButton();
4195
			}
4196
		}
4197
	},
4198
 
4199
	handleBackButton: function(){
4200
		//summary: private method. Do not call this directly.
4201
 
4202
		//The "current" page is always at the top of the history stack.
4203
		var current = this.historyStack.pop();
4204
		if(!current){ return; }
4205
		var last = this.historyStack[this.historyStack.length-1];
4206
		if(!last && this.historyStack.length == 0){
4207
			last = this.initialState;
4208
		}
4209
		if (last){
4210
			if(last.kwArgs["back"]){
4211
				last.kwArgs["back"]();
4212
			}else if(last.kwArgs["backButton"]){
4213
				last.kwArgs["backButton"]();
4214
			}else if(last.kwArgs["handle"]){
4215
				last.kwArgs.handle("back");
4216
			}
4217
		}
4218
		this.forwardStack.push(current);
4219
	},
4220
 
4221
	handleForwardButton: function(){
4222
		//summary: private method. Do not call this directly.
4223
 
4224
		var last = this.forwardStack.pop();
4225
		if(!last){ return; }
4226
		if(last.kwArgs["forward"]){
4227
			last.kwArgs.forward();
4228
		}else if(last.kwArgs["forwardButton"]){
4229
			last.kwArgs.forwardButton();
4230
		}else if(last.kwArgs["handle"]){
4231
			last.kwArgs.handle("forward");
4232
		}
4233
		this.historyStack.push(last);
4234
	},
4235
 
4236
	_createState: function(url, args, hash){
4237
		//summary: private method. Do not call this directly.
4238
 
4239
		return {"url": url, "kwArgs": args, "urlHash": hash};	//Object
4240
	},
4241
 
4242
	_getUrlQuery: function(url){
4243
		//summary: private method. Do not call this directly.
4244
		var segments = url.split("?");
4245
		if (segments.length < 2){
4246
			return null; //null
4247
		}
4248
		else{
4249
			return segments[1]; //String
4250
		}
4251
	},
4252
 
4253
	_loadIframeHistory: function(){
4254
		//summary: private method. Do not call this directly.
4255
		var url = (djConfig["dojoIframeHistoryUrl"] || dojo.hostenv.getBaseScriptUri()+'iframe_history.html')
4256
			+ "?" + (new Date()).getTime();
4257
		this.moveForward = true;
4258
		dojo.io.setIFrameSrc(this.historyIframe, url, false);
4259
		return url; //String
4260
	}
4261
}
4262
 
4263
dojo.provide("dojo.io.BrowserIO");
4264
 
4265
 
4266
 
4267
 
4268
 
4269
 
4270
 
4271
 
4272
if(!dj_undef("window")) {
4273
 
4274
dojo.io.checkChildrenForFile = function(/*DOMNode*/node){
4275
	//summary: Checks any child nodes of node for an input type="file" element.
4276
	var hasFile = false;
4277
	var inputs = node.getElementsByTagName("input");
4278
	dojo.lang.forEach(inputs, function(input){
4279
		if(hasFile){ return; }
4280
		if(input.getAttribute("type")=="file"){
4281
			hasFile = true;
4282
		}
4283
	});
4284
	return hasFile; //boolean
4285
}
4286
 
4287
dojo.io.formHasFile = function(/*DOMNode*/formNode){
4288
	//summary: Just calls dojo.io.checkChildrenForFile().
4289
	return dojo.io.checkChildrenForFile(formNode); //boolean
4290
}
4291
 
4292
dojo.io.updateNode = function(/*DOMNode*/node, /*String or Object*/urlOrArgs){
4293
	//summary: Updates a DOMnode with the result of a dojo.io.bind() call.
4294
	//node: DOMNode
4295
	//urlOrArgs: String or Object
4296
	//		Either a String that has an URL, or an object containing dojo.io.bind()
4297
	//		arguments.
4298
	node = dojo.byId(node);
4299
	var args = urlOrArgs;
4300
	if(dojo.lang.isString(urlOrArgs)){
4301
		args = { url: urlOrArgs };
4302
	}
4303
	args.mimetype = "text/html";
4304
	args.load = function(t, d, e){
4305
		while(node.firstChild){
4306
			dojo.dom.destroyNode(node.firstChild);
4307
		}
4308
		node.innerHTML = d;
4309
	};
4310
	dojo.io.bind(args);
4311
}
4312
 
4313
dojo.io.formFilter = function(/*DOMNode*/node) {
4314
	//summary: Returns true if the node is an input element that is enabled, has
4315
	//a name, and whose type is one of the following values: ["file", "submit", "image", "reset", "button"]
4316
	var type = (node.type||"").toLowerCase();
4317
	return !node.disabled && node.name
4318
		&& !dojo.lang.inArray(["file", "submit", "image", "reset", "button"], type); //boolean
4319
}
4320
 
4321
// TODO: Move to htmlUtils
4322
dojo.io.encodeForm = function(/*DOMNode*/formNode, /*String?*/encoding, /*Function?*/formFilter){
4323
	//summary: Converts the names and values of form elements into an URL-encoded
4324
	//string (name=value&name=value...).
4325
	//formNode: DOMNode
4326
	//encoding: String?
4327
	//		The encoding to use for the values. Specify a string that starts with
4328
	//		"utf" (for instance, "utf8"), to use encodeURIComponent() as the encoding
4329
	//		function. Otherwise, dojo.string.encodeAscii will be used.
4330
	//formFilter: Function?
4331
	//	A function used to filter out form elements. The element node will be passed
4332
	//	to the formFilter function, and a boolean result is expected (true indicating
4333
	//	indicating that the element should have its name/value included in the output).
4334
	//	If no formFilter is specified, then dojo.io.formFilter() will be used.
4335
	if((!formNode)||(!formNode.tagName)||(!formNode.tagName.toLowerCase() == "form")){
4336
		dojo.raise("Attempted to encode a non-form element.");
4337
	}
4338
	if(!formFilter) { formFilter = dojo.io.formFilter; }
4339
	var enc = /utf/i.test(encoding||"") ? encodeURIComponent : dojo.string.encodeAscii;
4340
	var values = [];
4341
 
4342
	for(var i = 0; i < formNode.elements.length; i++){
4343
		var elm = formNode.elements[i];
4344
		if(!elm || elm.tagName.toLowerCase() == "fieldset" || !formFilter(elm)) { continue; }
4345
		var name = enc(elm.name);
4346
		var type = elm.type.toLowerCase();
4347
 
4348
		if(type == "select-multiple"){
4349
			for(var j = 0; j < elm.options.length; j++){
4350
				if(elm.options[j].selected) {
4351
					values.push(name + "=" + enc(elm.options[j].value));
4352
				}
4353
			}
4354
		}else if(dojo.lang.inArray(["radio", "checkbox"], type)){
4355
			if(elm.checked){
4356
				values.push(name + "=" + enc(elm.value));
4357
			}
4358
		}else{
4359
			values.push(name + "=" + enc(elm.value));
4360
		}
4361
	}
4362
 
4363
	// now collect input type="image", which doesn't show up in the elements array
4364
	var inputs = formNode.getElementsByTagName("input");
4365
	for(var i = 0; i < inputs.length; i++) {
4366
		var input = inputs[i];
4367
		if(input.type.toLowerCase() == "image" && input.form == formNode
4368
			&& formFilter(input)) {
4369
			var name = enc(input.name);
4370
			values.push(name + "=" + enc(input.value));
4371
			values.push(name + ".x=0");
4372
			values.push(name + ".y=0");
4373
		}
4374
	}
4375
	return values.join("&") + "&"; //String
4376
}
4377
 
4378
dojo.io.FormBind = function(/*DOMNode or Object*/args) {
4379
	//summary: constructor for a dojo.io.FormBind object. See the Dojo Book for
4380
	//some information on usage: http://manual.dojotoolkit.org/WikiHome/DojoDotBook/Book23
4381
	//args: DOMNode or Object
4382
	//		args can either be the DOMNode for a form element, or an object containing
4383
	//		dojo.io.bind() arguments, one of which should be formNode with the value of
4384
	//		a form element DOMNode.
4385
	this.bindArgs = {};
4386
 
4387
	if(args && args.formNode) {
4388
		this.init(args);
4389
	} else if(args) {
4390
		this.init({formNode: args});
4391
	}
4392
}
4393
dojo.lang.extend(dojo.io.FormBind, {
4394
	form: null,
4395
 
4396
	bindArgs: null,
4397
 
4398
	clickedButton: null,
4399
 
4400
	init: function(/*DOMNode or Object*/args) {
4401
		//summary: Internal function called by the dojo.io.FormBind() constructor
4402
		//do not call this method directly.
4403
		var form = dojo.byId(args.formNode);
4404
 
4405
		if(!form || !form.tagName || form.tagName.toLowerCase() != "form") {
4406
			throw new Error("FormBind: Couldn't apply, invalid form");
4407
		} else if(this.form == form) {
4408
			return;
4409
		} else if(this.form) {
4410
			throw new Error("FormBind: Already applied to a form");
4411
		}
4412
 
4413
		dojo.lang.mixin(this.bindArgs, args);
4414
		this.form = form;
4415
 
4416
		this.connect(form, "onsubmit", "submit");
4417
 
4418
		for(var i = 0; i < form.elements.length; i++) {
4419
			var node = form.elements[i];
4420
			if(node && node.type && dojo.lang.inArray(["submit", "button"], node.type.toLowerCase())) {
4421
				this.connect(node, "onclick", "click");
4422
			}
4423
		}
4424
 
4425
		var inputs = form.getElementsByTagName("input");
4426
		for(var i = 0; i < inputs.length; i++) {
4427
			var input = inputs[i];
4428
			if(input.type.toLowerCase() == "image" && input.form == form) {
4429
				this.connect(input, "onclick", "click");
4430
			}
4431
		}
4432
	},
4433
 
4434
	onSubmit: function(/*DOMNode*/form) {
4435
		//summary: Function used to verify that the form is OK to submit.
4436
		//Override this function if you want specific form validation done.
4437
		return true; //boolean
4438
	},
4439
 
4440
	submit: function(/*Event*/e) {
4441
		//summary: internal function that is connected as a listener to the
4442
		//form's onsubmit event.
4443
		e.preventDefault();
4444
		if(this.onSubmit(this.form)) {
4445
			dojo.io.bind(dojo.lang.mixin(this.bindArgs, {
4446
				formFilter: dojo.lang.hitch(this, "formFilter")
4447
			}));
4448
		}
4449
	},
4450
 
4451
	click: function(/*Event*/e) {
4452
		//summary: internal method that is connected as a listener to the
4453
		//form's elements whose click event can submit a form.
4454
		var node = e.currentTarget;
4455
		if(node.disabled) { return; }
4456
		this.clickedButton = node;
4457
	},
4458
 
4459
	formFilter: function(/*DOMNode*/node) {
4460
		//summary: internal function used to know which form element values to include
4461
		//		in the dojo.io.bind() request.
4462
		var type = (node.type||"").toLowerCase();
4463
		var accept = false;
4464
		if(node.disabled || !node.name) {
4465
			accept = false;
4466
		} else if(dojo.lang.inArray(["submit", "button", "image"], type)) {
4467
			if(!this.clickedButton) { this.clickedButton = node; }
4468
			accept = node == this.clickedButton;
4469
		} else {
4470
			accept = !dojo.lang.inArray(["file", "submit", "reset", "button"], type);
4471
		}
4472
		return accept; //boolean
4473
	},
4474
 
4475
	// in case you don't have dojo.event.* pulled in
4476
	connect: function(/*Object*/srcObj, /*Function*/srcFcn, /*Function*/targetFcn) {
4477
		//summary: internal function used to connect event listeners to form elements
4478
		//that trigger events. Used in case dojo.event is not loaded.
4479
		if(dojo.evalObjPath("dojo.event.connect")) {
4480
			dojo.event.connect(srcObj, srcFcn, this, targetFcn);
4481
		} else {
4482
			var fcn = dojo.lang.hitch(this, targetFcn);
4483
			srcObj[srcFcn] = function(e) {
4484
				if(!e) { e = window.event; }
4485
				if(!e.currentTarget) { e.currentTarget = e.srcElement; }
4486
				if(!e.preventDefault) { e.preventDefault = function() { window.event.returnValue = false; } }
4487
				fcn(e);
4488
			}
4489
		}
4490
	}
4491
});
4492
 
4493
dojo.io.XMLHTTPTransport = new function(){
4494
	//summary: The object that implements the dojo.io.bind transport for XMLHttpRequest.
4495
	var _this = this;
4496
 
4497
	var _cache = {}; // FIXME: make this public? do we even need to?
4498
	this.useCache = false; // if this is true, we'll cache unless kwArgs.useCache = false
4499
	this.preventCache = false; // if this is true, we'll always force GET requests to cache
4500
 
4501
	// FIXME: Should this even be a function? or do we just hard code it in the next 2 functions?
4502
	function getCacheKey(url, query, method) {
4503
		return url + "|" + query + "|" + method.toLowerCase();
4504
	}
4505
 
4506
	function addToCache(url, query, method, http) {
4507
		_cache[getCacheKey(url, query, method)] = http;
4508
	}
4509
 
4510
	function getFromCache(url, query, method) {
4511
		return _cache[getCacheKey(url, query, method)];
4512
	}
4513
 
4514
	this.clearCache = function() {
4515
		_cache = {};
4516
	}
4517
 
4518
	// moved successful load stuff here
4519
	function doLoad(kwArgs, http, url, query, useCache) {
4520
		if(	((http.status>=200)&&(http.status<300))|| 	// allow any 2XX response code
4521
			(http.status==304)|| 						// get it out of the cache
1422 alexandre_ 4522
			(http.status==1223)|| 						// Internet Explorer mangled the status code
1318 alexandre_ 4523
			(location.protocol=="file:" && (http.status==0 || http.status==undefined))||
4524
			(location.protocol=="chrome:" && (http.status==0 || http.status==undefined))
4525
		){
4526
			var ret;
4527
			if(kwArgs.method.toLowerCase() == "head"){
4528
				var headers = http.getAllResponseHeaders();
4529
				ret = {};
4530
				ret.toString = function(){ return headers; }
4531
				var values = headers.split(/[\r\n]+/g);
4532
				for(var i = 0; i < values.length; i++) {
4533
					var pair = values[i].match(/^([^:]+)\s*:\s*(.+)$/i);
4534
					if(pair) {
4535
						ret[pair[1]] = pair[2];
4536
					}
4537
				}
4538
			}else if(kwArgs.mimetype == "text/javascript"){
4539
				try{
4540
					ret = dj_eval(http.responseText);
4541
				}catch(e){
4542
					dojo.debug(e);
4543
					dojo.debug(http.responseText);
4544
					ret = null;
4545
				}
1422 alexandre_ 4546
			}else if(kwArgs.mimetype.substr(0, 9) == "text/json" || kwArgs.mimetype.substr(0, 16) == "application/json"){
1318 alexandre_ 4547
				try{
1422 alexandre_ 4548
					ret = dj_eval("("+kwArgs.jsonFilter(http.responseText)+")");
1318 alexandre_ 4549
				}catch(e){
4550
					dojo.debug(e);
4551
					dojo.debug(http.responseText);
4552
					ret = false;
4553
				}
4554
			}else if((kwArgs.mimetype == "application/xml")||
4555
						(kwArgs.mimetype == "text/xml")){
4556
				ret = http.responseXML;
4557
				if(!ret || typeof ret == "string" || !http.getResponseHeader("Content-Type")) {
4558
					ret = dojo.dom.createDocumentFromText(http.responseText);
4559
				}
4560
			}else{
4561
				ret = http.responseText;
4562
			}
4563
 
4564
			if(useCache){ // only cache successful responses
4565
				addToCache(url, query, kwArgs.method, http);
4566
			}
4567
			kwArgs[(typeof kwArgs.load == "function") ? "load" : "handle"]("load", ret, http, kwArgs);
4568
		}else{
4569
			var errObj = new dojo.io.Error("XMLHttpTransport Error: "+http.status+" "+http.statusText);
4570
			kwArgs[(typeof kwArgs.error == "function") ? "error" : "handle"]("error", errObj, http, kwArgs);
4571
		}
4572
	}
4573
 
4574
	// set headers (note: Content-Type will get overriden if kwArgs.contentType is set)
4575
	function setHeaders(http, kwArgs){
4576
		if(kwArgs["headers"]) {
4577
			for(var header in kwArgs["headers"]) {
4578
				if(header.toLowerCase() == "content-type" && !kwArgs["contentType"]) {
4579
					kwArgs["contentType"] = kwArgs["headers"][header];
4580
				} else {
4581
					http.setRequestHeader(header, kwArgs["headers"][header]);
4582
				}
4583
			}
4584
		}
4585
	}
4586
 
4587
	this.inFlight = [];
4588
	this.inFlightTimer = null;
4589
 
4590
	this.startWatchingInFlight = function(){
4591
		//summary: internal method used to trigger a timer to watch all inflight
4592
		//XMLHttpRequests.
4593
		if(!this.inFlightTimer){
4594
			// setInterval broken in mozilla x86_64 in some circumstances, see
4595
			// https://bugzilla.mozilla.org/show_bug.cgi?id=344439
4596
			// using setTimeout instead
4597
			this.inFlightTimer = setTimeout("dojo.io.XMLHTTPTransport.watchInFlight();", 10);
4598
		}
4599
	}
4600
 
4601
	this.watchInFlight = function(){
4602
		//summary: internal method that checks each inflight XMLHttpRequest to see
4603
		//if it has completed or if the timeout situation applies.
4604
		var now = null;
4605
		// make sure sync calls stay thread safe, if this callback is called during a sync call
4606
		// and this results in another sync call before the first sync call ends the browser hangs
4607
		if(!dojo.hostenv._blockAsync && !_this._blockAsync){
4608
			for(var x=this.inFlight.length-1; x>=0; x--){
4609
				try{
4610
					var tif = this.inFlight[x];
4611
					if(!tif || tif.http._aborted || !tif.http.readyState){
4612
						this.inFlight.splice(x, 1); continue;
4613
					}
4614
					if(4==tif.http.readyState){
4615
						// remove it so we can clean refs
4616
						this.inFlight.splice(x, 1);
4617
						doLoad(tif.req, tif.http, tif.url, tif.query, tif.useCache);
4618
					}else if (tif.startTime){
4619
						//See if this is a timeout case.
4620
						if(!now){
4621
							now = (new Date()).getTime();
4622
						}
4623
						if(tif.startTime + (tif.req.timeoutSeconds * 1000) < now){
4624
							//Stop the request.
4625
							if(typeof tif.http.abort == "function"){
4626
								tif.http.abort();
4627
							}
4628
 
4629
							// remove it so we can clean refs
4630
							this.inFlight.splice(x, 1);
4631
							tif.req[(typeof tif.req.timeout == "function") ? "timeout" : "handle"]("timeout", null, tif.http, tif.req);
4632
						}
4633
					}
4634
				}catch(e){
4635
					try{
4636
						var errObj = new dojo.io.Error("XMLHttpTransport.watchInFlight Error: " + e);
4637
						tif.req[(typeof tif.req.error == "function") ? "error" : "handle"]("error", errObj, tif.http, tif.req);
4638
					}catch(e2){
4639
						dojo.debug("XMLHttpTransport error callback failed: " + e2);
4640
					}
4641
				}
4642
			}
4643
		}
4644
 
4645
		clearTimeout(this.inFlightTimer);
4646
		if(this.inFlight.length == 0){
4647
			this.inFlightTimer = null;
4648
			return;
4649
		}
4650
		this.inFlightTimer = setTimeout("dojo.io.XMLHTTPTransport.watchInFlight();", 10);
4651
	}
4652
 
4653
	var hasXmlHttp = dojo.hostenv.getXmlhttpObject() ? true : false;
4654
	this.canHandle = function(/*dojo.io.Request*/kwArgs){
4655
		//summary: Tells dojo.io.bind() if this is a good transport to
4656
		//use for the particular type of request. This type of transport cannot
4657
		//handle forms that have an input type="file" element.
4658
 
4659
		// FIXME: we need to determine when form values need to be
4660
		// multi-part mime encoded and avoid using this transport for those
4661
		// requests.
1422 alexandre_ 4662
		var mlc = kwArgs["mimetype"].toLowerCase()||"";
1318 alexandre_ 4663
		return hasXmlHttp
1422 alexandre_ 4664
			&& (
4665
				(
4666
					dojo.lang.inArray([
4667
						"text/plain", "text/html", "application/xml",
4668
						"text/xml", "text/javascript"
4669
						], mlc
4670
					)
4671
				) || (
4672
					mlc.substr(0, 9) == "text/json" || mlc.substr(0, 16) == "application/json"
4673
				)
4674
			)
1318 alexandre_ 4675
			&& !( kwArgs["formNode"] && dojo.io.formHasFile(kwArgs["formNode"]) ); //boolean
4676
	}
4677
 
4678
	this.multipartBoundary = "45309FFF-BD65-4d50-99C9-36986896A96F";	// unique guid as a boundary value for multipart posts
4679
 
4680
	this.bind = function(/*dojo.io.Request*/kwArgs){
4681
		//summary: function that sends the request to the server.
4682
 
4683
		//This function will attach an abort() function to the kwArgs dojo.io.Request object,
4684
		//so if you need to abort the request, you can call that method on the request object.
4685
		//The following are acceptable properties in kwArgs (in addition to the
4686
		//normal dojo.io.Request object properties).
4687
		//url: String: URL the server URL to use for the request.
4688
		//method: String: the HTTP method to use (GET, POST, etc...).
4689
		//mimetype: Specifies what format the result data should be given to the load/handle callback. Valid values are:
4690
		//		text/javascript, text/json, application/json, application/xml, text/xml. Any other mimetype will give back a text
4691
		//		string.
4692
		//transport: String: specify "XMLHTTPTransport" to force the use of this XMLHttpRequest transport.
4693
		//headers: Object: The object property names and values will be sent as HTTP request header
4694
		//		names and values.
4695
		//sendTransport: boolean: If true, then dojo.transport=xmlhttp will be added to the request.
4696
		//encoding: String: The type of encoding to use when dealing with the content kwArgs property.
4697
		//content: Object: The content object is converted into a name=value&name=value string, by
4698
		//		using dojo.io.argsFromMap(). The encoding kwArgs property is passed to dojo.io.argsFromMap()
4699
		//		for use in encoding the names and values. The resulting string is added to the request.
4700
		//formNode: DOMNode: a form element node. This should not normally be used. Use new dojo.io.FormBind() instead.
4701
		//		If formNode is used, then the names and values of the form elements will be converted
4702
		//		to a name=value&name=value string and added to the request. The encoding kwArgs property is used
4703
		//		to encode the names and values.
4704
		//postContent: String: Raw name=value&name=value string to be included as part of the request.
4705
		//back or backButton: Function: A function to be called if the back button is pressed. If this kwArgs property
4706
		//		is used, then back button support via dojo.undo.browser will be used. See notes for dojo.undo.browser on usage.
4707
		//		You need to set djConfig.preventBackButtonFix = false to enable back button support.
4708
		//changeUrl: boolean or String: Used as part of back button support. See notes for dojo.undo.browser on usage.
4709
		//user: String: The user name. Used in conjuction with password. Passed to XMLHttpRequest.open().
4710
		//password: String: The user's password. Used in conjuction with user. Passed to XMLHttpRequest.open().
4711
		//file: Object or Array of Objects: an object simulating a file to be uploaded. file objects should have the following properties:
4712
		//		name or fileName: the name of the file
4713
		//		contentType: the MIME content type for the file.
4714
		//		content: the actual content of the file.
4715
		//multipart: boolean: indicates whether this should be a multipart mime request. If kwArgs.file exists, then this
4716
		//		property is set to true automatically.
4717
		//sync: boolean: if true, then a synchronous XMLHttpRequest call is done,
4718
		//		if false (the default), then an asynchronous call is used.
4719
		//preventCache: boolean: If true, then a cache busting parameter is added to the request URL.
4720
		//		default value is false.
4721
		//useCache: boolean: If true, then XMLHttpTransport will keep an internal cache of the server
4722
		//		response and use that response if a similar request is done again.
4723
		//		A similar request is one that has the same URL, query string and HTTP method value.
4724
		//		default is false.
4725
		if(!kwArgs["url"]){
4726
			// are we performing a history action?
4727
			if( !kwArgs["formNode"]
4728
				&& (kwArgs["backButton"] || kwArgs["back"] || kwArgs["changeUrl"] || kwArgs["watchForURL"])
4729
				&& (!djConfig.preventBackButtonFix)) {
4730
        dojo.deprecated("Using dojo.io.XMLHTTPTransport.bind() to add to browser history without doing an IO request",
4731
        				"Use dojo.undo.browser.addToHistory() instead.", "0.4");
4732
				dojo.undo.browser.addToHistory(kwArgs);
4733
				return true;
4734
			}
4735
		}
4736
 
4737
		// build this first for cache purposes
4738
		var url = kwArgs.url;
4739
		var query = "";
4740
		if(kwArgs["formNode"]){
4741
			var ta = kwArgs.formNode.getAttribute("action");
4742
			if((ta)&&(!kwArgs["url"])){ url = ta; }
4743
			var tp = kwArgs.formNode.getAttribute("method");
4744
			if((tp)&&(!kwArgs["method"])){ kwArgs.method = tp; }
4745
			query += dojo.io.encodeForm(kwArgs.formNode, kwArgs.encoding, kwArgs["formFilter"]);
4746
		}
4747
 
4748
		if(url.indexOf("#") > -1) {
4749
			dojo.debug("Warning: dojo.io.bind: stripping hash values from url:", url);
4750
			url = url.split("#")[0];
4751
		}
4752
 
4753
		if(kwArgs["file"]){
4754
			// force post for file transfer
4755
			kwArgs.method = "post";
4756
		}
4757
 
4758
		if(!kwArgs["method"]){
4759
			kwArgs.method = "get";
4760
		}
4761
 
4762
		// guess the multipart value
4763
		if(kwArgs.method.toLowerCase() == "get"){
4764
			// GET cannot use multipart
4765
			kwArgs.multipart = false;
4766
		}else{
4767
			if(kwArgs["file"]){
4768
				// enforce multipart when sending files
4769
				kwArgs.multipart = true;
4770
			}else if(!kwArgs["multipart"]){
4771
				// default
4772
				kwArgs.multipart = false;
4773
			}
4774
		}
4775
 
4776
		if(kwArgs["backButton"] || kwArgs["back"] || kwArgs["changeUrl"]){
4777
			dojo.undo.browser.addToHistory(kwArgs);
4778
		}
4779
 
4780
		var content = kwArgs["content"] || {};
4781
 
4782
		if(kwArgs.sendTransport) {
4783
			content["dojo.transport"] = "xmlhttp";
4784
		}
4785
 
4786
		do { // break-block
4787
			if(kwArgs.postContent){
4788
				query = kwArgs.postContent;
4789
				break;
4790
			}
4791
 
4792
			if(content) {
4793
				query += dojo.io.argsFromMap(content, kwArgs.encoding);
4794
			}
4795
 
4796
			if(kwArgs.method.toLowerCase() == "get" || !kwArgs.multipart){
4797
				break;
4798
			}
4799
 
4800
			var	t = [];
4801
			if(query.length){
4802
				var q = query.split("&");
4803
				for(var i = 0; i < q.length; ++i){
4804
					if(q[i].length){
4805
						var p = q[i].split("=");
4806
						t.push(	"--" + this.multipartBoundary,
4807
								"Content-Disposition: form-data; name=\"" + p[0] + "\"",
4808
								"",
4809
								p[1]);
4810
					}
4811
				}
4812
			}
4813
 
4814
			if(kwArgs.file){
4815
				if(dojo.lang.isArray(kwArgs.file)){
4816
					for(var i = 0; i < kwArgs.file.length; ++i){
4817
						var o = kwArgs.file[i];
4818
						t.push(	"--" + this.multipartBoundary,
4819
								"Content-Disposition: form-data; name=\"" + o.name + "\"; filename=\"" + ("fileName" in o ? o.fileName : o.name) + "\"",
4820
								"Content-Type: " + ("contentType" in o ? o.contentType : "application/octet-stream"),
4821
								"",
4822
								o.content);
4823
					}
4824
				}else{
4825
					var o = kwArgs.file;
4826
					t.push(	"--" + this.multipartBoundary,
4827
							"Content-Disposition: form-data; name=\"" + o.name + "\"; filename=\"" + ("fileName" in o ? o.fileName : o.name) + "\"",
4828
							"Content-Type: " + ("contentType" in o ? o.contentType : "application/octet-stream"),
4829
							"",
4830
							o.content);
4831
				}
4832
			}
4833
 
4834
			if(t.length){
4835
				t.push("--"+this.multipartBoundary+"--", "");
4836
				query = t.join("\r\n");
4837
			}
4838
		}while(false);
4839
 
4840
		// kwArgs.Connection = "close";
4841
 
4842
		var async = kwArgs["sync"] ? false : true;
4843
 
4844
		var preventCache = kwArgs["preventCache"] ||
4845
			(this.preventCache == true && kwArgs["preventCache"] != false);
4846
		var useCache = kwArgs["useCache"] == true ||
4847
			(this.useCache == true && kwArgs["useCache"] != false );
4848
 
4849
		// preventCache is browser-level (add query string junk), useCache
4850
		// is for the local cache. If we say preventCache, then don't attempt
4851
		// to look in the cache, but if useCache is true, we still want to cache
4852
		// the response
4853
		if(!preventCache && useCache){
4854
			var cachedHttp = getFromCache(url, query, kwArgs.method);
4855
			if(cachedHttp){
4856
				doLoad(kwArgs, cachedHttp, url, query, false);
4857
				return;
4858
			}
4859
		}
4860
 
4861
		// much of this is from getText, but reproduced here because we need
4862
		// more flexibility
4863
		var http = dojo.hostenv.getXmlhttpObject(kwArgs);
4864
		var received = false;
4865
 
4866
		// build a handler function that calls back to the handler obj
4867
		if(async){
4868
			var startTime =
4869
			// FIXME: setting up this callback handler leaks on IE!!!
4870
			this.inFlight.push({
4871
				"req":		kwArgs,
4872
				"http":		http,
4873
				"url":	 	url,
4874
				"query":	query,
4875
				"useCache":	useCache,
4876
				"startTime": kwArgs.timeoutSeconds ? (new Date()).getTime() : 0
4877
			});
4878
			this.startWatchingInFlight();
4879
		}else{
4880
			// block async callbacks until sync is in, needed in khtml, others?
4881
			_this._blockAsync = true;
4882
		}
4883
 
4884
		if(kwArgs.method.toLowerCase() == "post"){
4885
			// FIXME: need to hack in more flexible Content-Type setting here!
4886
			if (!kwArgs.user) {
4887
				http.open("POST", url, async);
4888
			}else{
4889
        http.open("POST", url, async, kwArgs.user, kwArgs.password);
4890
			}
4891
			setHeaders(http, kwArgs);
4892
			http.setRequestHeader("Content-Type", kwArgs.multipart ? ("multipart/form-data; boundary=" + this.multipartBoundary) :
4893
				(kwArgs.contentType || "application/x-www-form-urlencoded"));
4894
			try{
4895
				http.send(query);
4896
			}catch(e){
4897
				if(typeof http.abort == "function"){
4898
					http.abort();
4899
				}
4900
				doLoad(kwArgs, {status: 404}, url, query, useCache);
4901
			}
4902
		}else{
4903
			var tmpUrl = url;
4904
			if(query != "") {
4905
				tmpUrl += (tmpUrl.indexOf("?") > -1 ? "&" : "?") + query;
4906
			}
4907
			if(preventCache) {
4908
				tmpUrl += (dojo.string.endsWithAny(tmpUrl, "?", "&")
4909
					? "" : (tmpUrl.indexOf("?") > -1 ? "&" : "?")) + "dojo.preventCache=" + new Date().valueOf();
4910
			}
4911
			if (!kwArgs.user) {
4912
				http.open(kwArgs.method.toUpperCase(), tmpUrl, async);
4913
			}else{
4914
				http.open(kwArgs.method.toUpperCase(), tmpUrl, async, kwArgs.user, kwArgs.password);
4915
			}
4916
			setHeaders(http, kwArgs);
4917
			try {
4918
				http.send(null);
4919
			}catch(e)	{
4920
				if(typeof http.abort == "function"){
4921
					http.abort();
4922
				}
4923
				doLoad(kwArgs, {status: 404}, url, query, useCache);
4924
			}
4925
		}
4926
 
4927
		if( !async ) {
4928
			doLoad(kwArgs, http, url, query, useCache);
4929
			_this._blockAsync = false;
4930
		}
4931
 
4932
		kwArgs.abort = function(){
4933
			try{// khtml doesent reset readyState on abort, need this workaround
4934
				http._aborted = true;
4935
			}catch(e){/*squelsh*/}
4936
			return http.abort();
4937
		}
4938
 
4939
		return;
4940
	}
4941
	dojo.io.transports.addTransport("XMLHTTPTransport");
4942
}
4943
 
4944
}
4945
 
4946
dojo.provide("dojo.io.cookie");
4947
 
4948
dojo.io.cookie.setCookie = function(/*String*/name, /*String*/value,
4949
									/*Number?*/days, /*String?*/path,
4950
									/*String?*/domain, /*boolean?*/secure){
4951
	//summary: sets a cookie.
4952
	var expires = -1;
4953
	if((typeof days == "number")&&(days >= 0)){
4954
		var d = new Date();
4955
		d.setTime(d.getTime()+(days*24*60*60*1000));
4956
		expires = d.toGMTString();
4957
	}
4958
	value = escape(value);
4959
	document.cookie = name + "=" + value + ";"
4960
		+ (expires != -1 ? " expires=" + expires + ";" : "")
4961
		+ (path ? "path=" + path : "")
4962
		+ (domain ? "; domain=" + domain : "")
4963
		+ (secure ? "; secure" : "");
4964
}
4965
 
4966
dojo.io.cookie.set = dojo.io.cookie.setCookie;
4967
 
4968
dojo.io.cookie.getCookie = function(/*String*/name){
4969
	//summary: Gets a cookie with the given name.
4970
 
4971
	// FIXME: Which cookie should we return?
4972
	//        If there are cookies set for different sub domains in the current
4973
	//        scope there could be more than one cookie with the same name.
4974
	//        I think taking the last one in the list takes the one from the
4975
	//        deepest subdomain, which is what we're doing here.
4976
	var idx = document.cookie.lastIndexOf(name+'=');
4977
	if(idx == -1) { return null; }
4978
	var value = document.cookie.substring(idx+name.length+1);
4979
	var end = value.indexOf(';');
4980
	if(end == -1) { end = value.length; }
4981
	value = value.substring(0, end);
4982
	value = unescape(value);
4983
	return value; //String
4984
}
4985
 
4986
dojo.io.cookie.get = dojo.io.cookie.getCookie;
4987
 
4988
dojo.io.cookie.deleteCookie = function(/*String*/name){
4989
	//summary: Deletes a cookie with the given name.
4990
	dojo.io.cookie.setCookie(name, "-", 0);
4991
}
4992
 
4993
dojo.io.cookie.setObjectCookie = function(	/*String*/name, /*Object*/obj,
4994
											/*Number?*/days, /*String?*/path,
4995
											/*String?*/domain, /*boolean?*/secure,
4996
											/*boolean?*/clearCurrent){
4997
	//summary: Takes an object, serializes it to a cookie value, and either
4998
	//sets a cookie with the serialized value.
4999
	//description: If clearCurrent is true, then any current cookie value
5000
	//for this object will be replaced with the the new serialized object value.
5001
	//If clearCurrent is false, then the existing cookie value will be modified
5002
	//with any changes from the new object value.
5003
	//Objects must be simple name/value pairs where the value is either a string
5004
	//or a number. Any other value will be ignored.
5005
	if(arguments.length == 5){ // for backwards compat
5006
		clearCurrent = domain;
5007
		domain = null;
5008
		secure = null;
5009
	}
5010
	var pairs = [], cookie, value = "";
5011
	if(!clearCurrent){
5012
		cookie = dojo.io.cookie.getObjectCookie(name);
5013
	}
5014
	if(days >= 0){
5015
		if(!cookie){ cookie = {}; }
5016
		for(var prop in obj){
5017
			if(obj[prop] == null){
5018
				delete cookie[prop];
5019
			}else if((typeof obj[prop] == "string")||(typeof obj[prop] == "number")){
5020
				cookie[prop] = obj[prop];
5021
			}
5022
		}
5023
		prop = null;
5024
		for(var prop in cookie){
5025
			pairs.push(escape(prop) + "=" + escape(cookie[prop]));
5026
		}
5027
		value = pairs.join("&");
5028
	}
5029
	dojo.io.cookie.setCookie(name, value, days, path, domain, secure);
5030
}
5031
 
5032
dojo.io.cookie.getObjectCookie = function(/*String*/name){
5033
	//summary: Gets an object value for the given cookie name. The complement of
5034
	//dojo.io.cookie.setObjectCookie().
5035
	var values = null, cookie = dojo.io.cookie.getCookie(name);
5036
	if(cookie){
5037
		values = {};
5038
		var pairs = cookie.split("&");
5039
		for(var i = 0; i < pairs.length; i++){
5040
			var pair = pairs[i].split("=");
5041
			var value = pair[1];
5042
			if( isNaN(value) ){ value = unescape(pair[1]); }
5043
			values[ unescape(pair[0]) ] = value;
5044
		}
5045
	}
5046
	return values;
5047
}
5048
 
5049
dojo.io.cookie.isSupported = function(){
5050
	//summary: Tests the browser to see if cookies are enabled.
5051
	if(typeof navigator.cookieEnabled != "boolean"){
5052
		dojo.io.cookie.setCookie("__TestingYourBrowserForCookieSupport__",
5053
			"CookiesAllowed", 90, null);
5054
		var cookieVal = dojo.io.cookie.getCookie("__TestingYourBrowserForCookieSupport__");
5055
		navigator.cookieEnabled = (cookieVal == "CookiesAllowed");
5056
		if(navigator.cookieEnabled){
5057
			// FIXME: should we leave this around?
5058
			this.deleteCookie("__TestingYourBrowserForCookieSupport__");
5059
		}
5060
	}
5061
	return navigator.cookieEnabled; //boolean
5062
}
5063
 
5064
// need to leave this in for backwards-compat from 0.1 for when it gets pulled in by dojo.io.*
5065
if(!dojo.io.cookies){ dojo.io.cookies = dojo.io.cookie; }
5066
 
5067
dojo.kwCompoundRequire({
5068
	common: ["dojo.io.common"],
5069
	rhino: ["dojo.io.RhinoIO"],
5070
	browser: ["dojo.io.BrowserIO", "dojo.io.cookie"],
5071
	dashboard: ["dojo.io.BrowserIO", "dojo.io.cookie"]
5072
});
5073
dojo.provide("dojo.io.*");
5074
 
5075
dojo.provide("dojo.event.common");
5076
 
5077
 
5078
 
5079
 
5080
 
5081
// TODO: connection filter functions
5082
//			these are functions that accept a method invocation (like around
5083
//			advice) and return a boolean based on it. That value determines
5084
//			whether or not the connection proceeds. It could "feel" like around
5085
//			advice for those who know what it is (calling proceed() or not),
5086
//			but I think presenting it as a "filter" and/or calling it with the
5087
//			function args and not the MethodInvocation might make it more
5088
//			palletable to "normal" users than around-advice currently is
5089
// TODO: execution scope mangling
5090
//			YUI's event facility by default executes listeners in the context
5091
//			of the source object. This is very odd, but should probably be
5092
//			supported as an option (both for the source and for the dest). It
5093
//			can be thought of as a connection-specific hitch().
5094
// TODO: more resiliency for 4+ arguments to connect()
5095
 
5096
dojo.event = new function(){
5097
	this._canTimeout = dojo.lang.isFunction(dj_global["setTimeout"])||dojo.lang.isAlien(dj_global["setTimeout"]);
5098
 
5099
	// FIXME: where should we put this method (not here!)?
5100
	function interpolateArgs(args, searchForNames){
5101
		var dl = dojo.lang;
5102
		var ao = {
5103
			srcObj: dj_global,
5104
			srcFunc: null,
5105
			adviceObj: dj_global,
5106
			adviceFunc: null,
5107
			aroundObj: null,
5108
			aroundFunc: null,
5109
			adviceType: (args.length>2) ? args[0] : "after",
5110
			precedence: "last",
5111
			once: false,
5112
			delay: null,
5113
			rate: 0,
5114
			adviceMsg: false,
5115
			maxCalls: -1
5116
		};
5117
 
5118
		switch(args.length){
5119
			case 0: return;
5120
			case 1: return;
5121
			case 2:
5122
				ao.srcFunc = args[0];
5123
				ao.adviceFunc = args[1];
5124
				break;
5125
			case 3:
5126
				if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isString(args[2]))){
5127
					ao.adviceType = "after";
5128
					ao.srcObj = args[0];
5129
					ao.srcFunc = args[1];
5130
					ao.adviceFunc = args[2];
5131
				}else if((dl.isString(args[1]))&&(dl.isString(args[2]))){
5132
					ao.srcFunc = args[1];
5133
					ao.adviceFunc = args[2];
5134
				}else if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isFunction(args[2]))){
5135
					ao.adviceType = "after";
5136
					ao.srcObj = args[0];
5137
					ao.srcFunc = args[1];
5138
					var tmpName  = dl.nameAnonFunc(args[2], ao.adviceObj, searchForNames);
5139
					ao.adviceFunc = tmpName;
5140
				}else if((dl.isFunction(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))){
5141
					ao.adviceType = "after";
5142
					ao.srcObj = dj_global;
5143
					var tmpName  = dl.nameAnonFunc(args[0], ao.srcObj, searchForNames);
5144
					ao.srcFunc = tmpName;
5145
					ao.adviceObj = args[1];
5146
					ao.adviceFunc = args[2];
5147
				}
5148
				break;
5149
			case 4:
5150
				if((dl.isObject(args[0]))&&(dl.isObject(args[2]))){
5151
					// we can assume that we've got an old-style "connect" from
5152
					// the sigslot school of event attachment. We therefore
5153
					// assume after-advice.
5154
					ao.adviceType = "after";
5155
					ao.srcObj = args[0];
5156
					ao.srcFunc = args[1];
5157
					ao.adviceObj = args[2];
5158
					ao.adviceFunc = args[3];
5159
				}else if((dl.isString(args[0]))&&(dl.isString(args[1]))&&(dl.isObject(args[2]))){
5160
					ao.adviceType = args[0];
5161
					ao.srcObj = dj_global;
5162
					ao.srcFunc = args[1];
5163
					ao.adviceObj = args[2];
5164
					ao.adviceFunc = args[3];
5165
				}else if((dl.isString(args[0]))&&(dl.isFunction(args[1]))&&(dl.isObject(args[2]))){
5166
					ao.adviceType = args[0];
5167
					ao.srcObj = dj_global;
5168
					var tmpName  = dl.nameAnonFunc(args[1], dj_global, searchForNames);
5169
					ao.srcFunc = tmpName;
5170
					ao.adviceObj = args[2];
5171
					ao.adviceFunc = args[3];
5172
				}else if((dl.isString(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))&&(dl.isFunction(args[3]))){
5173
					ao.srcObj = args[1];
5174
					ao.srcFunc = args[2];
5175
					var tmpName  = dl.nameAnonFunc(args[3], dj_global, searchForNames);
5176
					ao.adviceObj = dj_global;
5177
					ao.adviceFunc = tmpName;
5178
				}else if(dl.isObject(args[1])){
5179
					ao.srcObj = args[1];
5180
					ao.srcFunc = args[2];
5181
					ao.adviceObj = dj_global;
5182
					ao.adviceFunc = args[3];
5183
				}else if(dl.isObject(args[2])){
5184
					ao.srcObj = dj_global;
5185
					ao.srcFunc = args[1];
5186
					ao.adviceObj = args[2];
5187
					ao.adviceFunc = args[3];
5188
				}else{
5189
					ao.srcObj = ao.adviceObj = ao.aroundObj = dj_global;
5190
					ao.srcFunc = args[1];
5191
					ao.adviceFunc = args[2];
5192
					ao.aroundFunc = args[3];
5193
				}
5194
				break;
5195
			case 6:
5196
				ao.srcObj = args[1];
5197
				ao.srcFunc = args[2];
5198
				ao.adviceObj = args[3]
5199
				ao.adviceFunc = args[4];
5200
				ao.aroundFunc = args[5];
5201
				ao.aroundObj = dj_global;
5202
				break;
5203
			default:
5204
				ao.srcObj = args[1];
5205
				ao.srcFunc = args[2];
5206
				ao.adviceObj = args[3]
5207
				ao.adviceFunc = args[4];
5208
				ao.aroundObj = args[5];
5209
				ao.aroundFunc = args[6];
5210
				ao.once = args[7];
5211
				ao.delay = args[8];
5212
				ao.rate = args[9];
5213
				ao.adviceMsg = args[10];
5214
				ao.maxCalls = (!isNaN(parseInt(args[11]))) ? args[11] : -1;
5215
				break;
5216
		}
5217
 
5218
		if(dl.isFunction(ao.aroundFunc)){
5219
			var tmpName  = dl.nameAnonFunc(ao.aroundFunc, ao.aroundObj, searchForNames);
5220
			ao.aroundFunc = tmpName;
5221
		}
5222
 
5223
		if(dl.isFunction(ao.srcFunc)){
5224
			ao.srcFunc = dl.getNameInObj(ao.srcObj, ao.srcFunc);
5225
		}
5226
 
5227
		if(dl.isFunction(ao.adviceFunc)){
5228
			ao.adviceFunc = dl.getNameInObj(ao.adviceObj, ao.adviceFunc);
5229
		}
5230
 
5231
		if((ao.aroundObj)&&(dl.isFunction(ao.aroundFunc))){
5232
			ao.aroundFunc = dl.getNameInObj(ao.aroundObj, ao.aroundFunc);
5233
		}
5234
 
5235
		if(!ao.srcObj){
5236
			dojo.raise("bad srcObj for srcFunc: "+ao.srcFunc);
5237
		}
5238
		if(!ao.adviceObj){
5239
			dojo.raise("bad adviceObj for adviceFunc: "+ao.adviceFunc);
5240
		}
5241
 
5242
		if(!ao.adviceFunc){
5243
			dojo.debug("bad adviceFunc for srcFunc: "+ao.srcFunc);
5244
			dojo.debugShallow(ao);
5245
		}
5246
 
5247
		return ao;
5248
	}
5249
 
5250
	this.connect = function(/*...*/){
5251
		// summary:
5252
		//		dojo.event.connect is the glue that holds most Dojo-based
5253
		//		applications together. Most combinations of arguments are
5254
		//		supported, with the connect() method attempting to disambiguate
5255
		//		the implied types of positional parameters. The following will
5256
		//		all work:
5257
		//			dojo.event.connect("globalFunctionName1", "globalFunctionName2");
5258
		//			dojo.event.connect(functionReference1, functionReference2);
5259
		//			dojo.event.connect("globalFunctionName1", functionReference2);
5260
		//			dojo.event.connect(functionReference1, "globalFunctionName2");
5261
		//			dojo.event.connect(scope1, "functionName1", "globalFunctionName2");
5262
		//			dojo.event.connect("globalFunctionName1", scope2, "functionName2");
5263
		//			dojo.event.connect(scope1, "functionName1", scope2, "functionName2");
5264
		//			dojo.event.connect("after", scope1, "functionName1", scope2, "functionName2");
5265
		//			dojo.event.connect("before", scope1, "functionName1", scope2, "functionName2");
5266
		//			dojo.event.connect("around", 	scope1, "functionName1",
5267
		//											scope2, "functionName2",
5268
		//											aroundFunctionReference);
5269
		//			dojo.event.connect("around", 	scope1, "functionName1",
5270
		//											scope2, "functionName2",
5271
		//											scope3, "aroundFunctionName");
5272
		//			dojo.event.connect("before-around", 	scope1, "functionName1",
5273
		//													scope2, "functionName2",
5274
		//													aroundFunctionReference);
5275
		//			dojo.event.connect("after-around", 		scope1, "functionName1",
5276
		//													scope2, "functionName2",
5277
		//													aroundFunctionReference);
5278
		//			dojo.event.connect("after-around", 		scope1, "functionName1",
5279
		//													scope2, "functionName2",
5280
		//													scope3, "aroundFunctionName");
5281
		//			dojo.event.connect("around", 	scope1, "functionName1",
5282
		//											scope2, "functionName2",
5283
		//											scope3, "aroundFunctionName", true, 30);
5284
		//			dojo.event.connect("around", 	scope1, "functionName1",
5285
		//											scope2, "functionName2",
5286
		//											scope3, "aroundFunctionName", null, null, 10);
5287
		// adviceType:
5288
		//		Optional. String. One of "before", "after", "around",
5289
		//		"before-around", or "after-around". FIXME
5290
		// srcObj:
5291
		//		the scope in which to locate/execute the named srcFunc. Along
5292
		//		with srcFunc, this creates a way to dereference the function to
5293
		//		call. So if the function in question is "foo.bar", the
5294
		//		srcObj/srcFunc pair would be foo and "bar", where "bar" is a
5295
		//		string and foo is an object reference.
5296
		// srcFunc:
5297
		//		the name of the function to connect to. When it is executed,
5298
		//		the listener being registered with this call will be called.
5299
		//		The adviceType defines the call order between the source and
5300
		//		the target functions.
5301
		// adviceObj:
5302
		//		the scope in which to locate/execute the named adviceFunc.
5303
		// adviceFunc:
5304
		//		the name of the function being conected to srcObj.srcFunc
5305
		// aroundObj:
5306
		//		the scope in which to locate/execute the named aroundFunc.
5307
		// aroundFunc:
5308
		//		the name of, or a reference to, the function that will be used
5309
		//		to mediate the advice call. Around advice requires a special
5310
		//		unary function that will be passed a "MethodInvocation" object.
5311
		//		These objects have several important properties, namely:
5312
		//			- args
5313
		//				a mutable array of arguments to be passed into the
5314
		//				wrapped function
5315
		//			- proceed
5316
		//				a function that "continues" the invocation. The result
5317
		//				of this function is the return of the wrapped function.
5318
		//				You can then manipulate this return before passing it
5319
		//				back out (or take further action based on it).
5320
		// once:
5321
		//		boolean that determines whether or not this connect() will
5322
		//		create a new connection if an identical connect() has already
5323
		//		been made. Defaults to "false".
5324
		// delay:
5325
		//		an optional delay (in ms), as an integer, for dispatch of a
5326
		//		listener after the source has been fired.
5327
		// rate:
5328
		//		an optional rate throttling parameter (integer, in ms). When
5329
		//		specified, this particular connection will not fire more than
5330
		//		once in the interval specified by the rate
5331
		// adviceMsg:
5332
		//		boolean. Should the listener have all the parameters passed in
5333
		//		as a single argument?
5334
 
5335
		/*
5336
				ao.adviceType = args[0];
5337
				ao.srcObj = args[1];
5338
				ao.srcFunc = args[2];
5339
				ao.adviceObj = args[3]
5340
				ao.adviceFunc = args[4];
5341
				ao.aroundObj = args[5];
5342
				ao.aroundFunc = args[6];
5343
				ao.once = args[7];
5344
				ao.delay = args[8];
5345
				ao.rate = args[9];
5346
				ao.adviceMsg = args[10];
5347
				ao.maxCalls = args[11];
5348
		*/
5349
		if(arguments.length == 1){
5350
			var ao = arguments[0];
5351
		}else{
5352
			var ao = interpolateArgs(arguments, true);
5353
		}
5354
		if(dojo.lang.isString(ao.srcFunc) && (ao.srcFunc.toLowerCase() == "onkey") ){
5355
			if(dojo.render.html.ie){
5356
				ao.srcFunc = "onkeydown";
5357
				this.connect(ao);
5358
			}
5359
			ao.srcFunc = "onkeypress";
5360
		}
5361
 
5362
		if(dojo.lang.isArray(ao.srcObj) && ao.srcObj!=""){
5363
			var tmpAO = {};
5364
			for(var x in ao){
5365
				tmpAO[x] = ao[x];
5366
			}
5367
			var mjps = [];
5368
			dojo.lang.forEach(ao.srcObj, function(src){
5369
				if((dojo.render.html.capable)&&(dojo.lang.isString(src))){
5370
					src = dojo.byId(src);
5371
					// dojo.debug(src);
5372
				}
5373
				tmpAO.srcObj = src;
5374
				// dojo.debug(tmpAO.srcObj, tmpAO.srcFunc);
5375
				// dojo.debug(tmpAO.adviceObj, tmpAO.adviceFunc);
5376
				mjps.push(dojo.event.connect.call(dojo.event, tmpAO));
5377
			});
5378
			return mjps;
5379
		}
5380
 
5381
		// FIXME: just doing a "getForMethod()" seems to be enough to put this into infinite recursion!!
5382
		var mjp = dojo.event.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc);
5383
		if(ao.adviceFunc){
5384
			var mjp2 = dojo.event.MethodJoinPoint.getForMethod(ao.adviceObj, ao.adviceFunc);
5385
		}
5386
 
5387
		mjp.kwAddAdvice(ao);
5388
 
5389
		// advanced users might want to fsck w/ the join point manually
5390
		return mjp; // a MethodJoinPoint object
5391
	}
5392
 
5393
	this.log = function(/*object or funcName*/ a1, /*funcName*/ a2){
5394
		// summary:
5395
		//		a function that will wrap and log all calls to the specified
5396
		//		a1.a2() function. If only a1 is passed, it'll be used as a
5397
		//		function or function name on the global context. Logging will
5398
		//		be sent to dojo.debug
5399
		// a1:
5400
		//		if a2 is passed, this should be an object. If not, it can be a
5401
		//		function or function name.
5402
		// a2:
5403
		//		a function name
5404
		var kwArgs;
5405
		if((arguments.length == 1)&&(typeof a1 == "object")){
5406
			kwArgs = a1;
5407
		}else{
5408
			kwArgs = {
5409
				srcObj: a1,
5410
				srcFunc: a2
5411
			};
5412
		}
5413
		kwArgs.adviceFunc = function(){
5414
			var argsStr = [];
5415
			for(var x=0; x<arguments.length; x++){
5416
				argsStr.push(arguments[x]);
5417
			}
5418
			dojo.debug("("+kwArgs.srcObj+")."+kwArgs.srcFunc, ":", argsStr.join(", "));
5419
		};
5420
		this.kwConnect(kwArgs);
5421
	}
5422
 
5423
	this.connectBefore = function(){
5424
		// summary:
5425
		//	 	takes the same parameters as dojo.event.connect(), except that
5426
		//	 	the advice type will always be "before"
5427
		var args = ["before"];
5428
		for(var i = 0; i < arguments.length; i++){ args.push(arguments[i]); }
5429
		return this.connect.apply(this, args); // a MethodJoinPoint object
5430
	}
5431
 
5432
	this.connectAround = function(){
5433
		// summary:
5434
		//	 	takes the same parameters as dojo.event.connect(), except that
5435
		//	 	the advice type will always be "around"
5436
		var args = ["around"];
5437
		for(var i = 0; i < arguments.length; i++){ args.push(arguments[i]); }
5438
		return this.connect.apply(this, args); // a MethodJoinPoint object
5439
	}
5440
 
5441
	this.connectOnce = function(){
5442
		// summary:
5443
		//	 	takes the same parameters as dojo.event.connect(), except that
5444
		//	 	the "once" flag will always be set to "true"
5445
		var ao = interpolateArgs(arguments, true);
5446
		ao.once = true;
5447
		return this.connect(ao); // a MethodJoinPoint object
5448
	}
5449
 
5450
	this.connectRunOnce = function(){
5451
		// summary:
5452
		//	 	takes the same parameters as dojo.event.connect(), except that
5453
		//	 	the "maxCalls" flag will always be set to 1
5454
		var ao = interpolateArgs(arguments, true);
5455
		ao.maxCalls = 1;
5456
		return this.connect(ao); // a MethodJoinPoint object
5457
	}
5458
 
5459
	this._kwConnectImpl = function(kwArgs, disconnect){
5460
		var fn = (disconnect) ? "disconnect" : "connect";
5461
		if(typeof kwArgs["srcFunc"] == "function"){
5462
			kwArgs.srcObj = kwArgs["srcObj"]||dj_global;
5463
			var tmpName  = dojo.lang.nameAnonFunc(kwArgs.srcFunc, kwArgs.srcObj, true);
5464
			kwArgs.srcFunc = tmpName;
5465
		}
5466
		if(typeof kwArgs["adviceFunc"] == "function"){
5467
			kwArgs.adviceObj = kwArgs["adviceObj"]||dj_global;
5468
			var tmpName  = dojo.lang.nameAnonFunc(kwArgs.adviceFunc, kwArgs.adviceObj, true);
5469
			kwArgs.adviceFunc = tmpName;
5470
		}
5471
		kwArgs.srcObj = kwArgs["srcObj"]||dj_global;
5472
		kwArgs.adviceObj = kwArgs["adviceObj"]||kwArgs["targetObj"]||dj_global;
5473
		kwArgs.adviceFunc = kwArgs["adviceFunc"]||kwArgs["targetFunc"];
5474
		// pass kwargs to avoid unrolling/repacking
5475
		return dojo.event[fn](kwArgs);
5476
	}
5477
 
5478
	this.kwConnect = function(/*Object*/ kwArgs){
5479
		// summary:
5480
		//		A version of dojo.event.connect() that takes a map of named
5481
		//		parameters instead of the positional parameters that
5482
		//		dojo.event.connect() uses. For many advanced connection types,
5483
		//		this can be a much more readable (and potentially faster)
5484
		//		alternative.
5485
		// kwArgs:
5486
		// 		An object that can have the following properties:
5487
		//			- adviceType
5488
		//			- srcObj
5489
		//			- srcFunc
5490
		//			- adviceObj
5491
		//			- adviceFunc
5492
		//			- aroundObj
5493
		//			- aroundFunc
5494
		//			- once
5495
		//			- delay
5496
		//			- rate
5497
		//			- adviceMsg
5498
		//		As with connect, only srcFunc and adviceFunc are generally
5499
		//		required
5500
 
5501
		return this._kwConnectImpl(kwArgs, false); // a MethodJoinPoint object
5502
 
5503
	}
5504
 
5505
	this.disconnect = function(){
5506
		// summary:
5507
		//		Takes the same parameters as dojo.event.connect() but destroys
5508
		//		an existing connection instead of building a new one. For
5509
		//		multiple identical connections, multiple disconnect() calls
5510
		//		will unroll one each time it's called.
5511
		if(arguments.length == 1){
5512
			var ao = arguments[0];
5513
		}else{
5514
			var ao = interpolateArgs(arguments, true);
5515
		}
5516
		if(!ao.adviceFunc){ return; } // nothing to disconnect
5517
		if(dojo.lang.isString(ao.srcFunc) && (ao.srcFunc.toLowerCase() == "onkey") ){
5518
			if(dojo.render.html.ie){
5519
				ao.srcFunc = "onkeydown";
5520
				this.disconnect(ao);
5521
			}
5522
			ao.srcFunc = "onkeypress";
5523
		}
5524
		if(!ao.srcObj[ao.srcFunc]){ return null; } // prevent un-necessaray joinpoint creation
5525
		var mjp = dojo.event.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc, true);
5526
		mjp.removeAdvice(ao.adviceObj, ao.adviceFunc, ao.adviceType, ao.once); // a MethodJoinPoint object
5527
		return mjp;
5528
	}
5529
 
5530
	this.kwDisconnect = function(kwArgs){
5531
		// summary:
5532
		//		Takes the same parameters as dojo.event.kwConnect() but
5533
		//		destroys an existing connection instead of building a new one.
5534
		return this._kwConnectImpl(kwArgs, true);
5535
	}
5536
}
5537
 
5538
// exactly one of these is created whenever a method with a joint point is run,
5539
// if there is at least one 'around' advice.
5540
dojo.event.MethodInvocation = function(/*dojo.event.MethodJoinPoint*/join_point, /*Object*/obj, /*Array*/args){
5541
	// summary:
5542
	//		a class the models the call into a function. This is used under the
5543
	//		covers for all method invocations on both ends of a
5544
	//		connect()-wrapped function dispatch. This allows us to "pickle"
5545
	//		calls, such as in the case of around advice.
5546
	// join_point:
5547
	//		a dojo.event.MethodJoinPoint object that represents a connection
5548
	// obj:
5549
	//		the scope the call will execute in
5550
	// args:
5551
	//		an array of parameters that will get passed to the callee
5552
	this.jp_ = join_point;
5553
	this.object = obj;
5554
	this.args = [];
5555
	// make sure we don't lock into a mutable object which can change under us.
5556
	// It's ok if the individual items change, though.
5557
	for(var x=0; x<args.length; x++){
5558
		this.args[x] = args[x];
5559
	}
5560
	// the index of the 'around' that is currently being executed.
5561
	this.around_index = -1;
5562
}
5563
 
5564
dojo.event.MethodInvocation.prototype.proceed = function(){
5565
	// summary:
5566
	//		proceed with the method call that's represented by this invocation
5567
	//		object
5568
	this.around_index++;
5569
	if(this.around_index >= this.jp_.around.length){
5570
		return this.jp_.object[this.jp_.methodname].apply(this.jp_.object, this.args);
5571
		// return this.jp_.run_before_after(this.object, this.args);
5572
	}else{
5573
		var ti = this.jp_.around[this.around_index];
5574
		var mobj = ti[0]||dj_global;
5575
		var meth = ti[1];
5576
		return mobj[meth].call(mobj, this);
5577
	}
5578
}
5579
 
5580
 
5581
dojo.event.MethodJoinPoint = function(/*Object*/obj, /*String*/funcName){
5582
	this.object = obj||dj_global;
5583
	this.methodname = funcName;
5584
	this.methodfunc = this.object[funcName];
5585
	this.squelch = false;
5586
	// this.before = [];
5587
	// this.after = [];
5588
	// this.around = [];
5589
}
5590
 
5591
dojo.event.MethodJoinPoint.getForMethod = function(/*Object*/obj, /*String*/funcName){
5592
	// summary:
5593
	//		"static" class function for returning a MethodJoinPoint from a
5594
	//		scoped function. If one doesn't exist, one is created.
5595
	// obj:
5596
	//		the scope to search for the function in
5597
	// funcName:
5598
	//		the name of the function to return a MethodJoinPoint for
5599
	if(!obj){ obj = dj_global; }
5600
	var ofn = obj[funcName];
5601
	if(!ofn){
5602
		// supply a do-nothing method implementation
5603
		ofn = obj[funcName] = function(){};
5604
		if(!obj[funcName]){
5605
			// e.g. cannot add to inbuilt objects in IE6
5606
			dojo.raise("Cannot set do-nothing method on that object "+funcName);
5607
		}
5608
	}else if((typeof ofn != "function")&&(!dojo.lang.isFunction(ofn))&&(!dojo.lang.isAlien(ofn))){
5609
		// FIXME: should we throw an exception here instead?
5610
		return null;
5611
	}
5612
	// we hide our joinpoint instance in obj[funcName + '$joinpoint']
5613
	var jpname = funcName + "$joinpoint";
5614
	var jpfuncname = funcName + "$joinpoint$method";
5615
	var joinpoint = obj[jpname];
5616
	if(!joinpoint){
5617
		var isNode = false;
5618
		if(dojo.event["browser"]){
5619
			if( (obj["attachEvent"])||
5620
				(obj["nodeType"])||
5621
				(obj["addEventListener"]) ){
5622
				isNode = true;
5623
				dojo.event.browser.addClobberNodeAttrs(obj, [jpname, jpfuncname, funcName]);
5624
			}
5625
		}
5626
		var origArity = ofn.length;
5627
		obj[jpfuncname] = ofn;
5628
		// joinpoint = obj[jpname] = new dojo.event.MethodJoinPoint(obj, funcName);
5629
		joinpoint = obj[jpname] = new dojo.event.MethodJoinPoint(obj, jpfuncname);
5630
 
5631
		if(!isNode){
5632
			obj[funcName] = function(){
5633
				// var args = [];
5634
				// for(var x=0; x<arguments.length; x++){
5635
					// args.push(arguments[x]);
5636
				// }
5637
				// return joinpoint.run.apply(joinpoint, args);
5638
				return joinpoint.run.apply(joinpoint, arguments);
5639
			}
5640
		}else{
5641
			obj[funcName] = function(){
5642
				var args = [];
5643
 
5644
				if(!arguments.length){
5645
					var evt = null;
5646
					try{
5647
						if(obj.ownerDocument){
5648
							evt = obj.ownerDocument.parentWindow.event;
5649
						}else if(obj.documentElement){
5650
							evt = obj.documentElement.ownerDocument.parentWindow.event;
5651
						}else if(obj.event){ //obj is a window
5652
							evt = obj.event;
5653
						}else{
5654
							evt = window.event;
5655
						}
5656
					}catch(e){
5657
						evt = window.event;
5658
					}
5659
 
5660
					if(evt){
5661
						args.push(dojo.event.browser.fixEvent(evt, this));
5662
					}
5663
				}else{
5664
					for(var x=0; x<arguments.length; x++){
5665
						if((x==0)&&(dojo.event.browser.isEvent(arguments[x]))){
5666
							args.push(dojo.event.browser.fixEvent(arguments[x], this));
5667
						}else{
5668
							args.push(arguments[x]);
5669
						}
5670
					}
5671
				}
5672
				// return joinpoint.run.apply(joinpoint, arguments);
5673
				return joinpoint.run.apply(joinpoint, args);
5674
			}
5675
		}
5676
		obj[funcName].__preJoinArity = origArity;
5677
	}
5678
	return joinpoint; // dojo.event.MethodJoinPoint
5679
}
5680
 
5681
dojo.lang.extend(dojo.event.MethodJoinPoint, {
5682
	squelch: false,
5683
 
5684
	unintercept: function(){
5685
		// summary:
5686
		//		destroy the connection to all listeners that may have been
5687
		//		registered on this joinpoint
5688
		this.object[this.methodname] = this.methodfunc;
5689
		this.before = [];
5690
		this.after = [];
5691
		this.around = [];
5692
	},
5693
 
5694
	disconnect: dojo.lang.forward("unintercept"),
5695
 
5696
	run: function(){
5697
		// summary:
5698
		//		execute the connection represented by this join point. The
5699
		//		arguments passed to run() will be passed to the function and
5700
		//		its listeners.
5701
		var obj = this.object||dj_global;
5702
		var args = arguments;
5703
 
5704
		// optimization. We only compute once the array version of the arguments
5705
		// pseudo-arr in order to prevent building it each time advice is unrolled.
5706
		var aargs = [];
5707
		for(var x=0; x<args.length; x++){
5708
			aargs[x] = args[x];
5709
		}
5710
 
5711
		var unrollAdvice  = function(marr){
5712
			if(!marr){
5713
				dojo.debug("Null argument to unrollAdvice()");
5714
				return;
5715
			}
5716
 
5717
			var callObj = marr[0]||dj_global;
5718
			var callFunc = marr[1];
5719
 
5720
			if(!callObj[callFunc]){
5721
				dojo.raise("function \"" + callFunc + "\" does not exist on \"" + callObj + "\"");
5722
			}
5723
 
5724
			var aroundObj = marr[2]||dj_global;
5725
			var aroundFunc = marr[3];
5726
			var msg = marr[6];
5727
			var maxCount = marr[7];
5728
			if(maxCount > -1){
5729
				if(maxCount == 0){
5730
					return;
5731
				}
5732
				marr[7]--;
5733
			}
5734
			var undef;
5735
 
5736
			var to = {
5737
				args: [],
5738
				jp_: this,
5739
				object: obj,
5740
				proceed: function(){
5741
					return callObj[callFunc].apply(callObj, to.args);
5742
				}
5743
			};
5744
			to.args = aargs;
5745
 
5746
			var delay = parseInt(marr[4]);
5747
			var hasDelay = ((!isNaN(delay))&&(marr[4]!==null)&&(typeof marr[4] != "undefined"));
5748
			if(marr[5]){
5749
				var rate = parseInt(marr[5]);
5750
				var cur = new Date();
5751
				var timerSet = false;
5752
				if((marr["last"])&&((cur-marr.last)<=rate)){
5753
					if(dojo.event._canTimeout){
5754
						if(marr["delayTimer"]){
5755
							clearTimeout(marr.delayTimer);
5756
						}
5757
						var tod = parseInt(rate*2); // is rate*2 naive?
5758
						var mcpy = dojo.lang.shallowCopy(marr);
5759
						marr.delayTimer = setTimeout(function(){
5760
							// FIXME: on IE at least, event objects from the
5761
							// browser can go out of scope. How (or should?) we
5762
							// deal with it?
5763
							mcpy[5] = 0;
5764
							unrollAdvice(mcpy);
5765
						}, tod);
5766
					}
5767
					return;
5768
				}else{
5769
					marr.last = cur;
5770
				}
5771
			}
5772
 
5773
			// FIXME: need to enforce rates for a connection here!
5774
 
5775
			if(aroundFunc){
5776
				// NOTE: around advice can't delay since we might otherwise depend
5777
				// on execution order!
5778
				aroundObj[aroundFunc].call(aroundObj, to);
5779
			}else{
5780
				// var tmjp = dojo.event.MethodJoinPoint.getForMethod(obj, methname);
5781
				if((hasDelay)&&((dojo.render.html)||(dojo.render.svg))){  // FIXME: the render checks are grotty!
5782
					dj_global["setTimeout"](function(){
5783
						if(msg){
5784
							callObj[callFunc].call(callObj, to);
5785
						}else{
5786
							callObj[callFunc].apply(callObj, args);
5787
						}
5788
					}, delay);
5789
				}else{ // many environments can't support delay!
5790
					if(msg){
5791
						callObj[callFunc].call(callObj, to);
5792
					}else{
5793
						callObj[callFunc].apply(callObj, args);
5794
					}
5795
				}
5796
			}
5797
		};
5798
 
5799
		var unRollSquelch = function(){
5800
			if(this.squelch){
5801
				try{
5802
					return unrollAdvice.apply(this, arguments);
5803
				}catch(e){
5804
					dojo.debug(e);
5805
				}
5806
			}else{
5807
				return unrollAdvice.apply(this, arguments);
5808
			}
5809
		};
5810
 
5811
		if((this["before"])&&(this.before.length>0)){
5812
			// pass a cloned array, if this event disconnects this event forEach on this.before wont work
5813
			dojo.lang.forEach(this.before.concat(new Array()), unRollSquelch);
5814
		}
5815
 
5816
		var result;
5817
		try{
5818
			if((this["around"])&&(this.around.length>0)){
5819
				var mi = new dojo.event.MethodInvocation(this, obj, args);
5820
				result = mi.proceed();
5821
			}else if(this.methodfunc){
5822
				result = this.object[this.methodname].apply(this.object, args);
5823
			}
5824
		}catch(e){
5825
			if(!this.squelch){
5826
				dojo.debug(e,"when calling",this.methodname,"on",this.object,"with arguments",args);
5827
				dojo.raise(e);
5828
			}
5829
		}
5830
 
5831
		if((this["after"])&&(this.after.length>0)){
5832
			// see comment on this.before above
5833
			dojo.lang.forEach(this.after.concat(new Array()), unRollSquelch);
5834
		}
5835
 
5836
		return (this.methodfunc) ? result : null;
5837
	},
5838
 
5839
	getArr: function(/*String*/kind){
5840
		// summary: return a list of listeners of the past "kind"
5841
		// kind:
5842
		//		can be one of: "before", "after", "around", "before-around", or
5843
		//		"after-around"
5844
		var type = "after";
5845
		// FIXME: we should be able to do this through props or Array.in()
5846
		if((typeof kind == "string")&&(kind.indexOf("before")!=-1)){
5847
			type = "before";
5848
		}else if(kind=="around"){
5849
			type = "around";
5850
		}
5851
		if(!this[type]){ this[type] = []; }
5852
		return this[type]; // Array
5853
	},
5854
 
5855
	kwAddAdvice: function(/*Object*/args){
5856
		// summary:
5857
		//		adds advice to the joinpoint with arguments in a map
5858
		// args:
5859
		// 		An object that can have the following properties:
5860
		//			- adviceType
5861
		//			- adviceObj
5862
		//			- adviceFunc
5863
		//			- aroundObj
5864
		//			- aroundFunc
5865
		//			- once
5866
		//			- delay
5867
		//			- rate
5868
		//			- adviceMsg
5869
		//			- maxCalls
5870
		this.addAdvice(	args["adviceObj"], args["adviceFunc"],
5871
						args["aroundObj"], args["aroundFunc"],
5872
						args["adviceType"], args["precedence"],
5873
						args["once"], args["delay"], args["rate"],
5874
						args["adviceMsg"], args["maxCalls"]);
5875
	},
5876
 
5877
	addAdvice: function(	thisAdviceObj, thisAdvice,
5878
							thisAroundObj, thisAround,
5879
							adviceType, precedence,
5880
							once, delay, rate, asMessage,
5881
							maxCalls){
5882
		// summary:
5883
		//		add advice to this joinpoint using positional parameters
5884
		// thisAdviceObj:
5885
		//		the scope in which to locate/execute the named adviceFunc.
5886
		// thisAdviceFunc:
5887
		//		the name of the function being conected
5888
		// thisAroundObj:
5889
		//		the scope in which to locate/execute the named aroundFunc.
5890
		// thisAroundFunc:
5891
		//		the name of the function that will be used to mediate the
5892
		//		advice call.
5893
		// adviceType:
5894
		//		Optional. String. One of "before", "after", "around",
5895
		//		"before-around", or "after-around". FIXME
5896
		// once:
5897
		//		boolean that determines whether or not this advice will create
5898
		//		a new connection if an identical advice set has already been
5899
		//		provided. Defaults to "false".
5900
		// delay:
5901
		//		an optional delay (in ms), as an integer, for dispatch of a
5902
		//		listener after the source has been fired.
5903
		// rate:
5904
		//		an optional rate throttling parameter (integer, in ms). When
5905
		//		specified, this particular connection will not fire more than
5906
		//		once in the interval specified by the rate
5907
		// adviceMsg:
5908
		//		boolean. Should the listener have all the parameters passed in
5909
		//		as a single argument?
5910
		// maxCalls:
5911
		//		Integer. The maximum number of times this connection can be
5912
		//		used before being auto-disconnected. -1 signals that the
5913
		//		connection should never be disconnected.
5914
		var arr = this.getArr(adviceType);
5915
		if(!arr){
5916
			dojo.raise("bad this: " + this);
5917
		}
5918
 
5919
		var ao = [thisAdviceObj, thisAdvice, thisAroundObj, thisAround, delay, rate, asMessage, maxCalls];
5920
 
5921
		if(once){
5922
			if(this.hasAdvice(thisAdviceObj, thisAdvice, adviceType, arr) >= 0){
5923
				return;
5924
			}
5925
		}
5926
 
5927
		if(precedence == "first"){
5928
			arr.unshift(ao);
5929
		}else{
5930
			arr.push(ao);
5931
		}
5932
	},
5933
 
5934
	hasAdvice: function(thisAdviceObj, thisAdvice, adviceType, arr){
5935
		// summary:
5936
		//		returns the array index of the first existing connection
5937
		//		betweened the passed advice and this joinpoint. Will be -1 if
5938
		//		none exists.
5939
		// thisAdviceObj:
5940
		//		the scope in which to locate/execute the named adviceFunc.
5941
		// thisAdviceFunc:
5942
		//		the name of the function being conected
5943
		// adviceType:
5944
		//		Optional. String. One of "before", "after", "around",
5945
		//		"before-around", or "after-around". FIXME
5946
		// arr:
5947
		//		Optional. The list of advices to search. Will be found via
5948
		//		adviceType if not passed
5949
		if(!arr){ arr = this.getArr(adviceType); }
5950
		var ind = -1;
5951
		for(var x=0; x<arr.length; x++){
5952
			var aao = (typeof thisAdvice == "object") ? (new String(thisAdvice)).toString() : thisAdvice;
5953
			var a1o = (typeof arr[x][1] == "object") ? (new String(arr[x][1])).toString() : arr[x][1];
5954
			if((arr[x][0] == thisAdviceObj)&&(a1o == aao)){
5955
				ind = x;
5956
			}
5957
		}
5958
		return ind; // Integer
5959
	},
5960
 
5961
	removeAdvice: function(thisAdviceObj, thisAdvice, adviceType, once){
5962
		// summary:
5963
		//		returns the array index of the first existing connection
5964
		//		betweened the passed advice and this joinpoint. Will be -1 if
5965
		//		none exists.
5966
		// thisAdviceObj:
5967
		//		the scope in which to locate/execute the named adviceFunc.
5968
		// thisAdviceFunc:
5969
		//		the name of the function being conected
5970
		// adviceType:
5971
		//		Optional. String. One of "before", "after", "around",
5972
		//		"before-around", or "after-around". FIXME
5973
		// once:
5974
		//		Optional. Should this only remove the first occurance of the
5975
		//		connection?
5976
		var arr = this.getArr(adviceType);
5977
		var ind = this.hasAdvice(thisAdviceObj, thisAdvice, adviceType, arr);
5978
		if(ind == -1){
5979
			return false;
5980
		}
5981
		while(ind != -1){
5982
			arr.splice(ind, 1);
5983
			if(once){ break; }
5984
			ind = this.hasAdvice(thisAdviceObj, thisAdvice, adviceType, arr);
5985
		}
5986
		return true;
5987
	}
5988
});
5989
 
5990
 
5991
dojo.provide("dojo.event.topic");
5992
 
5993
dojo.event.topic = new function(){
5994
	this.topics = {};
5995
 
5996
	this.getTopic = function(/*String*/topic){
5997
		// summary:
5998
		//		returns a topic implementation object of type
5999
		//		dojo.event.topic.TopicImpl
6000
		// topic:
6001
		//		a unique, opaque string that names the topic
6002
		if(!this.topics[topic]){
6003
			this.topics[topic] = new this.TopicImpl(topic);
6004
		}
6005
		return this.topics[topic]; // a dojo.event.topic.TopicImpl object
6006
	}
6007
 
6008
	this.registerPublisher = function(/*String*/topic, /*Object*/obj, /*String*/funcName){
6009
		// summary:
6010
		//		registers a function as a publisher on a topic. Subsequent
6011
		//		calls to the function will cause a publish event on the topic
6012
		//		with the arguments passed to the function passed to registered
6013
		//		listeners.
6014
		// topic:
6015
		//		a unique, opaque string that names the topic
6016
		// obj:
6017
		//		the scope to locate the function in
6018
		// funcName:
6019
		//		the name of the function to register
6020
		var topic = this.getTopic(topic);
6021
		topic.registerPublisher(obj, funcName);
6022
	}
6023
 
6024
	this.subscribe = function(/*String*/topic, /*Object*/obj, /*String*/funcName){
6025
		// summary:
6026
		//		susbscribes the function to the topic. Subsequent events
6027
		//		dispached to the topic will create a function call for the
6028
		//		obj.funcName() function.
6029
		// topic:
6030
		//		a unique, opaque string that names the topic
6031
		// obj:
6032
		//		the scope to locate the function in
6033
		// funcName:
6034
		//		the name of the function to being registered as a listener
6035
		var topic = this.getTopic(topic);
6036
		topic.subscribe(obj, funcName);
6037
	}
6038
 
6039
	this.unsubscribe = function(/*String*/topic, /*Object*/obj, /*String*/funcName){
6040
		// summary:
6041
		//		unsubscribes the obj.funcName() from the topic
6042
		// topic:
6043
		//		a unique, opaque string that names the topic
6044
		// obj:
6045
		//		the scope to locate the function in
6046
		// funcName:
6047
		//		the name of the function to being unregistered as a listener
6048
		var topic = this.getTopic(topic);
6049
		topic.unsubscribe(obj, funcName);
6050
	}
6051
 
6052
	this.destroy = function(/*String*/topic){
6053
		// summary:
6054
		//		destroys the topic and unregisters all listeners
6055
		// topic:
6056
		//		a unique, opaque string that names the topic
6057
		this.getTopic(topic).destroy();
6058
		delete this.topics[topic];
6059
	}
6060
 
6061
	this.publishApply = function(/*String*/topic, /*Array*/args){
6062
		// summary:
6063
		//		dispatches an event to the topic using the args array as the
6064
		//		source for the call arguments to each listener. This is similar
6065
		//		to JavaScript's built-in Function.apply()
6066
		// topic:
6067
		//		a unique, opaque string that names the topic
6068
		// args:
6069
		//		the arguments to be passed into listeners of the topic
6070
		var topic = this.getTopic(topic);
6071
		topic.sendMessage.apply(topic, args);
6072
	}
6073
 
6074
	this.publish = function(/*String*/topic, /*Object*/message){
6075
		// summary:
6076
		//		manually "publish" to the passed topic
6077
		// topic:
6078
		//		a unique, opaque string that names the topic
6079
		// message:
6080
		//		can be an array of parameters (similar to publishApply), or
6081
		//		will be treated as one of many arguments to be passed along in
6082
		//		a "flat" unrolling
6083
		var topic = this.getTopic(topic);
6084
		// if message is an array, we treat it as a set of arguments,
6085
		// otherwise, we just pass on the arguments passed in as-is
6086
		var args = [];
6087
		// could we use concat instead here?
6088
		for(var x=1; x<arguments.length; x++){
6089
			args.push(arguments[x]);
6090
		}
6091
		topic.sendMessage.apply(topic, args);
6092
	}
6093
}
6094
 
6095
dojo.event.topic.TopicImpl = function(topicName){
6096
	// summary: a class to represent topics
6097
 
6098
	this.topicName = topicName;
6099
 
6100
	this.subscribe = function(/*Object*/listenerObject, /*Function or String*/listenerMethod){
6101
		// summary:
6102
		//		use dojo.event.connect() to attach the passed listener to the
6103
		//		topic represented by this object
6104
		// listenerObject:
6105
		//		if a string and listenerMethod is ommitted, this is treated as
6106
		//		the name of a function in the global namespace. If
6107
		//		listenerMethod is provided, this is the scope to find/execute
6108
		//		the function in.
6109
		// listenerMethod:
6110
		//		Optional. The function to register.
6111
		var tf = listenerMethod||listenerObject;
6112
		var to = (!listenerMethod) ? dj_global : listenerObject;
6113
		return dojo.event.kwConnect({ // dojo.event.MethodJoinPoint
6114
			srcObj:		this,
6115
			srcFunc:	"sendMessage",
6116
			adviceObj:	to,
6117
			adviceFunc: tf
6118
		});
6119
	}
6120
 
6121
	this.unsubscribe = function(/*Object*/listenerObject, /*Function or String*/listenerMethod){
6122
		// summary:
6123
		//		use dojo.event.disconnect() to attach the passed listener to the
6124
		//		topic represented by this object
6125
		// listenerObject:
6126
		//		if a string and listenerMethod is ommitted, this is treated as
6127
		//		the name of a function in the global namespace. If
6128
		//		listenerMethod is provided, this is the scope to find the
6129
		//		function in.
6130
		// listenerMethod:
6131
		//		Optional. The function to unregister.
6132
		var tf = (!listenerMethod) ? listenerObject : listenerMethod;
6133
		var to = (!listenerMethod) ? null : listenerObject;
6134
		return dojo.event.kwDisconnect({ // dojo.event.MethodJoinPoint
6135
			srcObj:		this,
6136
			srcFunc:	"sendMessage",
6137
			adviceObj:	to,
6138
			adviceFunc: tf
6139
		});
6140
	}
6141
 
6142
	this._getJoinPoint = function(){
6143
		return dojo.event.MethodJoinPoint.getForMethod(this, "sendMessage");
6144
	}
6145
 
6146
	this.setSquelch = function(/*Boolean*/shouldSquelch){
6147
		// summary:
6148
		//		determine whether or not exceptions in the calling of a
6149
		//		listener in the chain should stop execution of the chain.
6150
		this._getJoinPoint().squelch = shouldSquelch;
6151
	}
6152
 
6153
	this.destroy = function(){
6154
		// summary: disconnects all listeners from this topic
6155
		this._getJoinPoint().disconnect();
6156
	}
6157
 
6158
	this.registerPublisher = function(	/*Object*/publisherObject,
6159
										/*Function or String*/publisherMethod){
6160
		// summary:
6161
		//		registers the passed function as a publisher on this topic.
6162
		//		Each time the function is called, an event will be published on
6163
		//		this topic.
6164
		// publisherObject:
6165
		//		if a string and listenerMethod is ommitted, this is treated as
6166
		//		the name of a function in the global namespace. If
6167
		//		listenerMethod is provided, this is the scope to find the
6168
		//		function in.
6169
		// publisherMethod:
6170
		//		Optional. The function to register.
6171
		dojo.event.connect(publisherObject, publisherMethod, this, "sendMessage");
6172
	}
6173
 
6174
	this.sendMessage = function(message){
6175
		// summary: a stub to be called when a message is sent to the topic.
6176
 
6177
		// The message has been propagated
6178
	}
6179
}
6180
 
6181
 
6182
dojo.provide("dojo.event.browser");
6183
 
6184
 
6185
// FIXME: any particular reason this is in the global scope?
6186
dojo._ie_clobber = new function(){
6187
	this.clobberNodes = [];
6188
 
6189
	function nukeProp(node, prop){
6190
		// try{ node.removeAttribute(prop); 	}catch(e){ /* squelch */ }
6191
		try{ node[prop] = null; 			}catch(e){ /* squelch */ }
6192
		try{ delete node[prop]; 			}catch(e){ /* squelch */ }
6193
		// FIXME: JotLive needs this, but I'm not sure if it's too slow or not
6194
		try{ node.removeAttribute(prop);	}catch(e){ /* squelch */ }
6195
	}
6196
 
6197
	this.clobber = function(nodeRef){
6198
		var na;
6199
		var tna;
6200
		if(nodeRef){
6201
			tna = nodeRef.all || nodeRef.getElementsByTagName("*");
6202
			na = [nodeRef];
6203
			for(var x=0; x<tna.length; x++){
6204
				// if we're gonna be clobbering the thing, at least make sure
6205
				// we aren't trying to do it twice
6206
				if(tna[x]["__doClobber__"]){
6207
					na.push(tna[x]);
6208
				}
6209
			}
6210
		}else{
6211
			try{ window.onload = null; }catch(e){}
6212
			na = (this.clobberNodes.length) ? this.clobberNodes : document.all;
6213
		}
6214
		tna = null;
6215
		var basis = {};
6216
		for(var i = na.length-1; i>=0; i=i-1){
6217
			var el = na[i];
6218
			try{
6219
				if(el && el["__clobberAttrs__"]){
6220
					for(var j=0; j<el.__clobberAttrs__.length; j++){
6221
						nukeProp(el, el.__clobberAttrs__[j]);
6222
					}
6223
					nukeProp(el, "__clobberAttrs__");
6224
					nukeProp(el, "__doClobber__");
6225
				}
6226
			}catch(e){ /* squelch! */};
6227
		}
6228
		na = null;
6229
	}
6230
}
6231
 
6232
if(dojo.render.html.ie){
6233
	dojo.addOnUnload(function(){
6234
		dojo._ie_clobber.clobber();
6235
		try{
6236
			if((dojo["widget"])&&(dojo.widget["manager"])){
6237
				dojo.widget.manager.destroyAll();
6238
			}
6239
		}catch(e){}
6240
 
6241
		// Workaround for IE leak recommended in ticket #1727 by schallm
6242
		if(dojo.widget){
6243
			for(var name in dojo.widget._templateCache){
6244
				if(dojo.widget._templateCache[name].node){
6245
					dojo.dom.destroyNode(dojo.widget._templateCache[name].node);
6246
					dojo.widget._templateCache[name].node = null;
6247
					delete dojo.widget._templateCache[name].node;
6248
				}
6249
			}
6250
		}
6251
 
6252
		try{ window.onload = null; }catch(e){}
6253
		try{ window.onunload = null; }catch(e){}
6254
		dojo._ie_clobber.clobberNodes = [];
6255
		// CollectGarbage();
6256
	});
6257
}
6258
 
6259
dojo.event.browser = new function(){
6260
 
6261
	var clobberIdx = 0;
6262
 
6263
	this.normalizedEventName = function(/*String*/eventName){
6264
		switch(eventName){
6265
			case "CheckboxStateChange":
6266
			case "DOMAttrModified":
6267
			case "DOMMenuItemActive":
6268
			case "DOMMenuItemInactive":
6269
			case "DOMMouseScroll":
6270
			case "DOMNodeInserted":
6271
			case "DOMNodeRemoved":
6272
			case "RadioStateChange":
6273
				return eventName;
6274
				break;
6275
			default:
6276
				var lcn = eventName.toLowerCase();
6277
				return (lcn.indexOf("on") == 0) ? lcn.substr(2) : lcn;
6278
				break;
6279
		}
6280
	}
6281
 
6282
	this.clean = function(/*DOMNode*/node){
6283
		// summary:
6284
		//		removes native event handlers so that destruction of the node
6285
		//		will not leak memory. On most browsers this is a no-op, but
6286
		//		it's critical for manual node removal on IE.
6287
		// node:
6288
		//		A DOM node. All of it's children will also be cleaned.
6289
		if(dojo.render.html.ie){
6290
			dojo._ie_clobber.clobber(node);
6291
		}
6292
	}
6293
 
6294
	this.addClobberNode = function(/*DOMNode*/node){
6295
		// summary:
6296
		//		register the passed node to support event stripping
6297
		// node:
6298
		//		A DOM node
6299
		if(!dojo.render.html.ie){ return; }
6300
		if(!node["__doClobber__"]){
6301
			node.__doClobber__ = true;
6302
			dojo._ie_clobber.clobberNodes.push(node);
6303
			// this might not be the most efficient thing to do, but it's
6304
			// much less error prone than other approaches which were
6305
			// previously tried and failed
6306
			node.__clobberAttrs__ = [];
6307
		}
6308
	}
6309
 
6310
	this.addClobberNodeAttrs = function(/*DOMNode*/node, /*Array*/props){
6311
		// summary:
6312
		//		register the passed node to support event stripping
6313
		// node:
6314
		//		A DOM node to stip properties from later
6315
		// props:
6316
		//		A list of propeties to strip from the node
6317
		if(!dojo.render.html.ie){ return; }
6318
		this.addClobberNode(node);
6319
		for(var x=0; x<props.length; x++){
6320
			node.__clobberAttrs__.push(props[x]);
6321
		}
6322
	}
6323
 
6324
	this.removeListener = function(	/*DOMNode*/ node,
6325
									/*String*/	evtName,
6326
									/*Function*/fp,
6327
									/*Boolean*/	capture){
6328
		// summary:
6329
		//		clobbers the listener from the node
6330
		// evtName:
6331
		//		the name of the handler to remove the function from
6332
		// node:
6333
		//		DOM node to attach the event to
6334
		// fp:
6335
		//		the function to register
6336
		// capture:
6337
		//		Optional. should this listener prevent propigation?
6338
		if(!capture){ var capture = false; }
6339
		evtName = dojo.event.browser.normalizedEventName(evtName);
6340
		if(evtName == "key"){
6341
			if(dojo.render.html.ie){
6342
				this.removeListener(node, "onkeydown", fp, capture);
6343
			}
6344
			evtName = "keypress";
6345
		}
6346
		// FIXME: this is mostly a punt, we aren't actually doing anything on IE
6347
		if(node.removeEventListener){
6348
			node.removeEventListener(evtName, fp, capture);
6349
		}
6350
	}
6351
 
6352
	this.addListener = function(/*DOMNode*/node, /*String*/evtName, /*Function*/fp, /*Boolean*/capture, /*Boolean*/dontFix){
6353
		// summary:
6354
		//		adds a listener to the node
6355
		// evtName:
6356
		//		the name of the handler to add the listener to can be either of
6357
		//		the form "onclick" or "click"
6358
		// node:
6359
		//		DOM node to attach the event to
6360
		// fp:
6361
		//		the function to register
6362
		// capture:
6363
		//		Optional. Should this listener prevent propigation?
6364
		// dontFix:
6365
		//		Optional. Should we avoid registering a new closure around the
6366
		//		listener to enable fixEvent for dispatch of the registered
6367
		//		function?
6368
		if(!node){ return; } // FIXME: log and/or bail?
6369
		if(!capture){ var capture = false; }
6370
		evtName = dojo.event.browser.normalizedEventName(evtName);
6371
		if(evtName == "key"){
6372
			if(dojo.render.html.ie){
6373
				this.addListener(node, "onkeydown", fp, capture, dontFix);
6374
			}
6375
			evtName = "keypress";
6376
		}
6377
 
6378
		if(!dontFix){
6379
			// build yet another closure around fp in order to inject fixEvent
6380
			// around the resulting event
6381
			var newfp = function(evt){
6382
				if(!evt){ evt = window.event; }
6383
				var ret = fp(dojo.event.browser.fixEvent(evt, this));
6384
				if(capture){
6385
					dojo.event.browser.stopEvent(evt);
6386
				}
6387
				return ret;
6388
			}
6389
		}else{
6390
			newfp = fp;
6391
		}
6392
 
6393
		if(node.addEventListener){
6394
			node.addEventListener(evtName, newfp, capture);
6395
			return newfp;
6396
		}else{
6397
			evtName = "on"+evtName;
6398
			if(typeof node[evtName] == "function" ){
6399
				var oldEvt = node[evtName];
6400
				node[evtName] = function(e){
6401
					oldEvt(e);
6402
					return newfp(e);
6403
				}
6404
			}else{
6405
				node[evtName]=newfp;
6406
			}
6407
			if(dojo.render.html.ie){
6408
				this.addClobberNodeAttrs(node, [evtName]);
6409
			}
6410
			return newfp;
6411
		}
6412
	}
6413
 
6414
	this.isEvent = function(/*Object*/obj){
6415
		// summary:
6416
		//		Tries to determine whether or not the object is a DOM event.
6417
 
6418
		// FIXME: event detection hack ... could test for additional attributes
6419
		// if necessary
6420
		return (typeof obj != "undefined")&&(obj)&&(typeof Event != "undefined")&&(obj.eventPhase); // Boolean
6421
		// Event does not support instanceof in Opera, otherwise:
6422
		//return (typeof Event != "undefined")&&(obj instanceof Event);
6423
	}
6424
 
6425
	this.currentEvent = null;
6426
 
6427
	this.callListener = function(/*Function*/listener, /*DOMNode*/curTarget){
6428
		// summary:
6429
		//		calls the specified listener in the context of the passed node
6430
		//		with the current DOM event object as the only parameter
6431
		// listener:
6432
		//		the function to call
6433
		// curTarget:
6434
		//		the Node to call the function in the scope of
6435
		if(typeof listener != 'function'){
6436
			dojo.raise("listener not a function: " + listener);
6437
		}
6438
		dojo.event.browser.currentEvent.currentTarget = curTarget;
6439
		return listener.call(curTarget, dojo.event.browser.currentEvent);
6440
	}
6441
 
6442
	this._stopPropagation = function(){
6443
		dojo.event.browser.currentEvent.cancelBubble = true;
6444
	}
6445
 
6446
	this._preventDefault = function(){
6447
		dojo.event.browser.currentEvent.returnValue = false;
6448
	}
6449
 
6450
	this.keys = {
6451
		KEY_BACKSPACE: 8,
6452
		KEY_TAB: 9,
6453
		KEY_CLEAR: 12,
6454
		KEY_ENTER: 13,
6455
		KEY_SHIFT: 16,
6456
		KEY_CTRL: 17,
6457
		KEY_ALT: 18,
6458
		KEY_PAUSE: 19,
6459
		KEY_CAPS_LOCK: 20,
6460
		KEY_ESCAPE: 27,
6461
		KEY_SPACE: 32,
6462
		KEY_PAGE_UP: 33,
6463
		KEY_PAGE_DOWN: 34,
6464
		KEY_END: 35,
6465
		KEY_HOME: 36,
6466
		KEY_LEFT_ARROW: 37,
6467
		KEY_UP_ARROW: 38,
6468
		KEY_RIGHT_ARROW: 39,
6469
		KEY_DOWN_ARROW: 40,
6470
		KEY_INSERT: 45,
6471
		KEY_DELETE: 46,
6472
		KEY_HELP: 47,
6473
		KEY_LEFT_WINDOW: 91,
6474
		KEY_RIGHT_WINDOW: 92,
6475
		KEY_SELECT: 93,
6476
		KEY_NUMPAD_0: 96,
6477
		KEY_NUMPAD_1: 97,
6478
		KEY_NUMPAD_2: 98,
6479
		KEY_NUMPAD_3: 99,
6480
		KEY_NUMPAD_4: 100,
6481
		KEY_NUMPAD_5: 101,
6482
		KEY_NUMPAD_6: 102,
6483
		KEY_NUMPAD_7: 103,
6484
		KEY_NUMPAD_8: 104,
6485
		KEY_NUMPAD_9: 105,
6486
		KEY_NUMPAD_MULTIPLY: 106,
6487
		KEY_NUMPAD_PLUS: 107,
6488
		KEY_NUMPAD_ENTER: 108,
6489
		KEY_NUMPAD_MINUS: 109,
6490
		KEY_NUMPAD_PERIOD: 110,
6491
		KEY_NUMPAD_DIVIDE: 111,
6492
		KEY_F1: 112,
6493
		KEY_F2: 113,
6494
		KEY_F3: 114,
6495
		KEY_F4: 115,
6496
		KEY_F5: 116,
6497
		KEY_F6: 117,
6498
		KEY_F7: 118,
6499
		KEY_F8: 119,
6500
		KEY_F9: 120,
6501
		KEY_F10: 121,
6502
		KEY_F11: 122,
6503
		KEY_F12: 123,
6504
		KEY_F13: 124,
6505
		KEY_F14: 125,
6506
		KEY_F15: 126,
6507
		KEY_NUM_LOCK: 144,
6508
		KEY_SCROLL_LOCK: 145
6509
	};
6510
 
6511
	// reverse lookup
6512
	this.revKeys = [];
6513
	for(var key in this.keys){
6514
		this.revKeys[this.keys[key]] = key;
6515
	}
6516
 
6517
	this.fixEvent = function(/*Event*/evt, /*DOMNode*/sender){
6518
		// summary:
6519
		//		normalizes properties on the event object including event
6520
		//		bubbling methods, keystroke normalization, and x/y positions
6521
		// evt: the native event object
6522
		// sender: the node to treat as "currentTarget"
6523
		if(!evt){
6524
			if(window["event"]){
6525
				evt = window.event;
6526
			}
6527
		}
6528
 
6529
		if((evt["type"])&&(evt["type"].indexOf("key") == 0)){ // key events
6530
			evt.keys = this.revKeys;
6531
			// FIXME: how can we eliminate this iteration?
6532
			for(var key in this.keys){
6533
				evt[key] = this.keys[key];
6534
			}
6535
			if(evt["type"] == "keydown" && dojo.render.html.ie){
6536
				switch(evt.keyCode){
6537
					case evt.KEY_SHIFT:
6538
					case evt.KEY_CTRL:
6539
					case evt.KEY_ALT:
6540
					case evt.KEY_CAPS_LOCK:
6541
					case evt.KEY_LEFT_WINDOW:
6542
					case evt.KEY_RIGHT_WINDOW:
6543
					case evt.KEY_SELECT:
6544
					case evt.KEY_NUM_LOCK:
6545
					case evt.KEY_SCROLL_LOCK:
6546
					// I'll get these in keypress after the OS munges them based on numlock
6547
					case evt.KEY_NUMPAD_0:
6548
					case evt.KEY_NUMPAD_1:
6549
					case evt.KEY_NUMPAD_2:
6550
					case evt.KEY_NUMPAD_3:
6551
					case evt.KEY_NUMPAD_4:
6552
					case evt.KEY_NUMPAD_5:
6553
					case evt.KEY_NUMPAD_6:
6554
					case evt.KEY_NUMPAD_7:
6555
					case evt.KEY_NUMPAD_8:
6556
					case evt.KEY_NUMPAD_9:
6557
					case evt.KEY_NUMPAD_PERIOD:
6558
						break; // just ignore the keys that can morph
6559
					case evt.KEY_NUMPAD_MULTIPLY:
6560
					case evt.KEY_NUMPAD_PLUS:
6561
					case evt.KEY_NUMPAD_ENTER:
6562
					case evt.KEY_NUMPAD_MINUS:
6563
					case evt.KEY_NUMPAD_DIVIDE:
6564
						break; // I could handle these but just pick them up in keypress
6565
					case evt.KEY_PAUSE:
6566
					case evt.KEY_TAB:
6567
					case evt.KEY_BACKSPACE:
6568
					case evt.KEY_ENTER:
6569
					case evt.KEY_ESCAPE:
6570
					case evt.KEY_PAGE_UP:
6571
					case evt.KEY_PAGE_DOWN:
6572
					case evt.KEY_END:
6573
					case evt.KEY_HOME:
6574
					case evt.KEY_LEFT_ARROW:
6575
					case evt.KEY_UP_ARROW:
6576
					case evt.KEY_RIGHT_ARROW:
6577
					case evt.KEY_DOWN_ARROW:
6578
					case evt.KEY_INSERT:
6579
					case evt.KEY_DELETE:
6580
					case evt.KEY_F1:
6581
					case evt.KEY_F2:
6582
					case evt.KEY_F3:
6583
					case evt.KEY_F4:
6584
					case evt.KEY_F5:
6585
					case evt.KEY_F6:
6586
					case evt.KEY_F7:
6587
					case evt.KEY_F8:
6588
					case evt.KEY_F9:
6589
					case evt.KEY_F10:
6590
					case evt.KEY_F11:
6591
					case evt.KEY_F12:
6592
					case evt.KEY_F12:
6593
					case evt.KEY_F13:
6594
					case evt.KEY_F14:
6595
					case evt.KEY_F15:
6596
					case evt.KEY_CLEAR:
6597
					case evt.KEY_HELP:
6598
						evt.key = evt.keyCode;
6599
						break;
6600
					default:
6601
						if(evt.ctrlKey || evt.altKey){
6602
							var unifiedCharCode = evt.keyCode;
6603
							// if lower case but keycode is uppercase, convert it
6604
							if(unifiedCharCode >= 65 && unifiedCharCode <= 90 && evt.shiftKey == false){
6605
								unifiedCharCode += 32;
6606
							}
6607
							if(unifiedCharCode >= 1 && unifiedCharCode <= 26 && evt.ctrlKey){
6608
								unifiedCharCode += 96; // 001-032 = ctrl+[a-z]
6609
							}
6610
							evt.key = String.fromCharCode(unifiedCharCode);
6611
						}
6612
				}
6613
			} else if(evt["type"] == "keypress"){
6614
				if(dojo.render.html.opera){
6615
					if(evt.which == 0){
6616
						evt.key = evt.keyCode;
6617
					}else if(evt.which > 0){
6618
						switch(evt.which){
6619
							case evt.KEY_SHIFT:
6620
							case evt.KEY_CTRL:
6621
							case evt.KEY_ALT:
6622
							case evt.KEY_CAPS_LOCK:
6623
							case evt.KEY_NUM_LOCK:
6624
							case evt.KEY_SCROLL_LOCK:
6625
								break;
6626
							case evt.KEY_PAUSE:
6627
							case evt.KEY_TAB:
6628
							case evt.KEY_BACKSPACE:
6629
							case evt.KEY_ENTER:
6630
							case evt.KEY_ESCAPE:
6631
								evt.key = evt.which;
6632
								break;
6633
							default:
6634
								var unifiedCharCode = evt.which;
6635
								if((evt.ctrlKey || evt.altKey || evt.metaKey) && (evt.which >= 65 && evt.which <= 90 && evt.shiftKey == false)){
6636
									unifiedCharCode += 32;
6637
								}
6638
								evt.key = String.fromCharCode(unifiedCharCode);
6639
						}
6640
					}
6641
				}else if(dojo.render.html.ie){ // catch some IE keys that are hard to get in keyDown
6642
					// key combinations were handled in onKeyDown
6643
					if(!evt.ctrlKey && !evt.altKey && evt.keyCode >= evt.KEY_SPACE){
6644
						evt.key = String.fromCharCode(evt.keyCode);
6645
					}
6646
				}else if(dojo.render.html.safari){
6647
					switch(evt.keyCode){
6648
						case 25: evt.key = evt.KEY_TAB; evt.shift = true;break;
6649
						case 63232: evt.key = evt.KEY_UP_ARROW; break;
6650
						case 63233: evt.key = evt.KEY_DOWN_ARROW; break;
6651
						case 63234: evt.key = evt.KEY_LEFT_ARROW; break;
6652
						case 63235: evt.key = evt.KEY_RIGHT_ARROW; break;
6653
						case 63236: evt.key = evt.KEY_F1; break;
6654
						case 63237: evt.key = evt.KEY_F2; break;
6655
						case 63238: evt.key = evt.KEY_F3; break;
6656
						case 63239: evt.key = evt.KEY_F4; break;
6657
						case 63240: evt.key = evt.KEY_F5; break;
6658
						case 63241: evt.key = evt.KEY_F6; break;
6659
						case 63242: evt.key = evt.KEY_F7; break;
6660
						case 63243: evt.key = evt.KEY_F8; break;
6661
						case 63244: evt.key = evt.KEY_F9; break;
6662
						case 63245: evt.key = evt.KEY_F10; break;
6663
						case 63246: evt.key = evt.KEY_F11; break;
6664
						case 63247: evt.key = evt.KEY_F12; break;
6665
						case 63250: evt.key = evt.KEY_PAUSE; break;
6666
						case 63272: evt.key = evt.KEY_DELETE; break;
6667
						case 63273: evt.key = evt.KEY_HOME; break;
6668
						case 63275: evt.key = evt.KEY_END; break;
6669
						case 63276: evt.key = evt.KEY_PAGE_UP; break;
6670
						case 63277: evt.key = evt.KEY_PAGE_DOWN; break;
6671
						case 63302: evt.key = evt.KEY_INSERT; break;
6672
						case 63248://prtscr
6673
						case 63249://scrolllock
6674
						case 63289://numlock
6675
							break;
6676
						default:
6677
							evt.key = evt.charCode >= evt.KEY_SPACE ? String.fromCharCode(evt.charCode) : evt.keyCode;
6678
					}
6679
				}else{
6680
					evt.key = evt.charCode > 0 ? String.fromCharCode(evt.charCode) : evt.keyCode;
6681
				}
6682
			}
6683
		}
6684
		if(dojo.render.html.ie){
6685
			if(!evt.target){ evt.target = evt.srcElement; }
6686
			if(!evt.currentTarget){ evt.currentTarget = (sender ? sender : evt.srcElement); }
6687
			if(!evt.layerX){ evt.layerX = evt.offsetX; }
6688
			if(!evt.layerY){ evt.layerY = evt.offsetY; }
6689
			// FIXME: scroll position query is duped from dojo.html to avoid dependency on that entire module
6690
			// DONOT replace the following to use dojo.body(), in IE, document.documentElement should be used
6691
			// here rather than document.body
6692
			var doc = (evt.srcElement && evt.srcElement.ownerDocument) ? evt.srcElement.ownerDocument : document;
6693
			var docBody = ((dojo.render.html.ie55)||(doc["compatMode"] == "BackCompat")) ? doc.body : doc.documentElement;
6694
			if(!evt.pageX){ evt.pageX = evt.clientX + (docBody.scrollLeft || 0) }
6695
			if(!evt.pageY){ evt.pageY = evt.clientY + (docBody.scrollTop || 0) }
6696
			// mouseover
6697
			if(evt.type == "mouseover"){ evt.relatedTarget = evt.fromElement; }
6698
			// mouseout
6699
			if(evt.type == "mouseout"){ evt.relatedTarget = evt.toElement; }
6700
			this.currentEvent = evt;
6701
			evt.callListener = this.callListener;
6702
			evt.stopPropagation = this._stopPropagation;
6703
			evt.preventDefault = this._preventDefault;
6704
		}
6705
		return evt; // Event
6706
	}
6707
 
6708
	this.stopEvent = function(/*Event*/evt){
6709
		// summary:
6710
		//		prevents propigation and clobbers the default action of the
6711
		//		passed event
6712
		// evt: Optional for IE. The native event object.
6713
		if(window.event){
6714
			evt.cancelBubble = true;
6715
			evt.returnValue = false;
6716
		}else{
6717
			evt.preventDefault();
6718
			evt.stopPropagation();
6719
		}
6720
	}
6721
}
6722
 
6723
dojo.kwCompoundRequire({
6724
	common: ["dojo.event.common", "dojo.event.topic"],
6725
	browser: ["dojo.event.browser"],
6726
	dashboard: ["dojo.event.browser"]
6727
});
6728
dojo.provide("dojo.event.*");
6729
 
6730
dojo.provide("dojo.gfx.color");
6731
 
6732
 
6733
 
6734
// TODO: rewrite the "x2y" methods to take advantage of the parsing
6735
//       abilities of the Color object. Also, beef up the Color
6736
//       object (as possible) to parse most common formats
6737
 
6738
// takes an r, g, b, a(lpha) value, [r, g, b, a] array, "rgb(...)" string, hex string (#aaa, #aaaaaa, aaaaaaa)
6739
dojo.gfx.color.Color = function(r, g, b, a) {
6740
	// dojo.debug("r:", r[0], "g:", r[1], "b:", r[2]);
6741
	if(dojo.lang.isArray(r)){
6742
		this.r = r[0];
6743
		this.g = r[1];
6744
		this.b = r[2];
6745
		this.a = r[3]||1.0;
6746
	}else if(dojo.lang.isString(r)){
6747
		var rgb = dojo.gfx.color.extractRGB(r);
6748
		this.r = rgb[0];
6749
		this.g = rgb[1];
6750
		this.b = rgb[2];
6751
		this.a = g||1.0;
6752
	}else if(r instanceof dojo.gfx.color.Color){
6753
		// why does this create a new instance if we were passed one?
6754
		this.r = r.r;
6755
		this.b = r.b;
6756
		this.g = r.g;
6757
		this.a = r.a;
6758
	}else{
6759
		this.r = r;
6760
		this.g = g;
6761
		this.b = b;
6762
		this.a = a;
6763
	}
6764
}
6765
 
6766
dojo.gfx.color.Color.fromArray = function(arr) {
6767
	return new dojo.gfx.color.Color(arr[0], arr[1], arr[2], arr[3]);
6768
}
6769
 
6770
dojo.extend(dojo.gfx.color.Color, {
6771
	toRgb: function(includeAlpha) {
6772
		if(includeAlpha) {
6773
			return this.toRgba();
6774
		} else {
6775
			return [this.r, this.g, this.b];
6776
		}
6777
	},
6778
	toRgba: function() {
6779
		return [this.r, this.g, this.b, this.a];
6780
	},
6781
	toHex: function() {
6782
		return dojo.gfx.color.rgb2hex(this.toRgb());
6783
	},
6784
	toCss: function() {
6785
		return "rgb(" + this.toRgb().join() + ")";
6786
	},
6787
	toString: function() {
6788
		return this.toHex(); // decent default?
6789
	},
6790
	blend: function(color, weight){
6791
		var rgb = null;
6792
		if(dojo.lang.isArray(color)){
6793
			rgb = color;
6794
		}else if(color instanceof dojo.gfx.color.Color){
6795
			rgb = color.toRgb();
6796
		}else{
6797
			rgb = new dojo.gfx.color.Color(color).toRgb();
6798
		}
6799
		return dojo.gfx.color.blend(this.toRgb(), rgb, weight);
6800
	}
6801
});
6802
 
6803
dojo.gfx.color.named = {
6804
	white:      [255,255,255],
6805
	black:      [0,0,0],
6806
	red:        [255,0,0],
6807
	green:	    [0,255,0],
6808
	lime:	    [0,255,0],
6809
	blue:       [0,0,255],
6810
	navy:       [0,0,128],
6811
	gray:       [128,128,128],
6812
	silver:     [192,192,192]
6813
};
6814
 
6815
dojo.gfx.color.blend = function(a, b, weight){
6816
	// summary:
6817
	//		blend colors a and b (both as RGB array or hex strings) with weight
6818
	//		from -1 to +1, 0 being a 50/50 blend
6819
	if(typeof a == "string"){
6820
		return dojo.gfx.color.blendHex(a, b, weight);
6821
	}
6822
	if(!weight){
6823
		weight = 0;
6824
	}
6825
	weight = Math.min(Math.max(-1, weight), 1);
6826
 
6827
	// alex: this interface blows.
6828
	// map -1 to 1 to the range 0 to 1
6829
	weight = ((weight + 1)/2);
6830
 
6831
	var c = [];
6832
 
6833
	// var stop = (1000*weight);
6834
	for(var x = 0; x < 3; x++){
6835
		c[x] = parseInt( b[x] + ( (a[x] - b[x]) * weight) );
6836
	}
6837
	return c;
6838
}
6839
 
6840
// very convenient blend that takes and returns hex values
6841
// (will get called automatically by blend when blend gets strings)
6842
dojo.gfx.color.blendHex = function(a, b, weight) {
6843
	return dojo.gfx.color.rgb2hex(dojo.gfx.color.blend(dojo.gfx.color.hex2rgb(a), dojo.gfx.color.hex2rgb(b), weight));
6844
}
6845
 
6846
// get RGB array from css-style color declarations
6847
dojo.gfx.color.extractRGB = function(color) {
6848
	var hex = "0123456789abcdef";
6849
	color = color.toLowerCase();
6850
	if( color.indexOf("rgb") == 0 ) {
6851
		var matches = color.match(/rgba*\((\d+), *(\d+), *(\d+)/i);
6852
		var ret = matches.splice(1, 3);
6853
		return ret;
6854
	} else {
6855
		var colors = dojo.gfx.color.hex2rgb(color);
6856
		if(colors) {
6857
			return colors;
6858
		} else {
6859
			// named color (how many do we support?)
6860
			return dojo.gfx.color.named[color] || [255, 255, 255];
6861
		}
6862
	}
6863
}
6864
 
6865
dojo.gfx.color.hex2rgb = function(hex) {
6866
	var hexNum = "0123456789ABCDEF";
6867
	var rgb = new Array(3);
6868
	if( hex.indexOf("#") == 0 ) { hex = hex.substring(1); }
6869
	hex = hex.toUpperCase();
6870
	if(hex.replace(new RegExp("["+hexNum+"]", "g"), "") != "") {
6871
		return null;
6872
	}
6873
	if( hex.length == 3 ) {
6874
		rgb[0] = hex.charAt(0) + hex.charAt(0)
6875
		rgb[1] = hex.charAt(1) + hex.charAt(1)
6876
		rgb[2] = hex.charAt(2) + hex.charAt(2);
6877
	} else {
6878
		rgb[0] = hex.substring(0, 2);
6879
		rgb[1] = hex.substring(2, 4);
6880
		rgb[2] = hex.substring(4);
6881
	}
6882
	for(var i = 0; i < rgb.length; i++) {
6883
		rgb[i] = hexNum.indexOf(rgb[i].charAt(0)) * 16 + hexNum.indexOf(rgb[i].charAt(1));
6884
	}
6885
	return rgb;
6886
}
6887
 
6888
dojo.gfx.color.rgb2hex = function(r, g, b) {
6889
	if(dojo.lang.isArray(r)) {
6890
		g = r[1] || 0;
6891
		b = r[2] || 0;
6892
		r = r[0] || 0;
6893
	}
6894
	var ret = dojo.lang.map([r, g, b], function(x) {
6895
		x = new Number(x);
6896
		var s = x.toString(16);
6897
		while(s.length < 2) { s = "0" + s; }
6898
		return s;
6899
	});
6900
	ret.unshift("#");
6901
	return ret.join("");
6902
}
6903
 
6904
dojo.provide("dojo.lfx.Animation");
6905
 
6906
 
6907
 
6908
/*
6909
	Animation package based on Dan Pupius' work: http://pupius.co.uk/js/Toolkit.Drawing.js
6910
*/
6911
dojo.lfx.Line = function(/*int*/ start, /*int*/ end){
6912
	// summary: dojo.lfx.Line is the object used to generate values
6913
	//			from a start value to an end value
6914
	this.start = start;
6915
	this.end = end;
6916
	if(dojo.lang.isArray(start)){
6917
		/* start: Array
6918
		   end: Array
6919
		   pId: a */
6920
		var diff = [];
6921
		dojo.lang.forEach(this.start, function(s,i){
6922
			diff[i] = this.end[i] - s;
6923
		}, this);
6924
 
6925
		this.getValue = function(/*float*/ n){
6926
			var res = [];
6927
			dojo.lang.forEach(this.start, function(s, i){
6928
				res[i] = (diff[i] * n) + s;
6929
			}, this);
6930
			return res; // Array
6931
		}
6932
	}else{
6933
		var diff = end - start;
6934
 
6935
		this.getValue = function(/*float*/ n){
6936
			//	summary: returns the point on the line
6937
			//	n: a floating point number greater than 0 and less than 1
6938
			return (diff * n) + this.start; // Decimal
6939
		}
6940
	}
6941
}
6942
 
6943
if((dojo.render.html.khtml)&&(!dojo.render.html.safari)){
6944
	// the cool kids are obviously not using konqueror...
6945
	// found a very wierd bug in floats constants, 1.5 evals as 1
6946
	// seems somebody mixed up ints and floats in 3.5.4 ??
6947
	// FIXME: investigate more and post a KDE bug (Fredrik)
6948
	dojo.lfx.easeDefault = function(/*Decimal?*/ n){
6949
		//	summary: Returns the point for point n on a sin wave.
6950
		return (parseFloat("0.5")+((Math.sin( (n+parseFloat("1.5")) * Math.PI))/2));
6951
	}
6952
}else{
6953
	dojo.lfx.easeDefault = function(/*Decimal?*/ n){
6954
		return (0.5+((Math.sin( (n+1.5) * Math.PI))/2));
6955
	}
6956
}
6957
 
6958
dojo.lfx.easeIn = function(/*Decimal?*/ n){
6959
	//	summary: returns the point on an easing curve
6960
	//	n: a floating point number greater than 0 and less than 1
6961
	return Math.pow(n, 3);
6962
}
6963
 
6964
dojo.lfx.easeOut = function(/*Decimal?*/ n){
6965
	//	summary: returns the point on the line
6966
	//	n: a floating point number greater than 0 and less than 1
6967
	return ( 1 - Math.pow(1 - n, 3) );
6968
}
6969
 
6970
dojo.lfx.easeInOut = function(/*Decimal?*/ n){
6971
	//	summary: returns the point on the line
6972
	//	n: a floating point number greater than 0 and less than 1
6973
	return ( (3 * Math.pow(n, 2)) - (2 * Math.pow(n, 3)) );
6974
}
6975
 
6976
dojo.lfx.IAnimation = function(){
6977
	// summary: dojo.lfx.IAnimation is an interface that implements
6978
	//			commonly used functions of animation objects
6979
}
6980
dojo.lang.extend(dojo.lfx.IAnimation, {
6981
	// public properties
6982
	curve: null,
6983
	duration: 1000,
6984
	easing: null,
6985
	repeatCount: 0,
6986
	rate: 10,
6987
 
6988
	// events
6989
	handler: null,
6990
	beforeBegin: null,
6991
	onBegin: null,
6992
	onAnimate: null,
6993
	onEnd: null,
6994
	onPlay: null,
6995
	onPause: null,
6996
	onStop: null,
6997
 
6998
	// public methods
6999
	play: null,
7000
	pause: null,
7001
	stop: null,
7002
 
7003
	connect: function(/*Event*/ evt, /*Object*/ scope, /*Function*/ newFunc){
7004
		// summary: Convenience function.  Quickly connect to an event
7005
		//			of this object and save the old functions connected to it.
7006
		// evt: The name of the event to connect to.
7007
		// scope: the scope in which to run newFunc.
7008
		// newFunc: the function to run when evt is fired.
7009
		if(!newFunc){
7010
			/* scope: Function
7011
			   newFunc: null
7012
			   pId: f */
7013
			newFunc = scope;
7014
			scope = this;
7015
		}
7016
		newFunc = dojo.lang.hitch(scope, newFunc);
7017
		var oldFunc = this[evt]||function(){};
7018
		this[evt] = function(){
7019
			var ret = oldFunc.apply(this, arguments);
7020
			newFunc.apply(this, arguments);
7021
			return ret;
7022
		}
7023
		return this; // dojo.lfx.IAnimation
7024
	},
7025
 
7026
	fire: function(/*Event*/ evt, /*Array*/ args){
7027
		// summary: Convenience function.  Fire event "evt" and pass it
7028
		//			the arguments specified in "args".
7029
		// evt: The event to fire.
7030
		// args: The arguments to pass to the event.
7031
		if(this[evt]){
7032
			this[evt].apply(this, (args||[]));
7033
		}
7034
		return this; // dojo.lfx.IAnimation
7035
	},
7036
 
7037
	repeat: function(/*int*/ count){
7038
		// summary: Set the repeat count of this object.
7039
		// count: How many times to repeat the animation.
7040
		this.repeatCount = count;
7041
		return this; // dojo.lfx.IAnimation
7042
	},
7043
 
7044
	// private properties
7045
	_active: false,
7046
	_paused: false
7047
});
7048
 
7049
dojo.lfx.Animation = function(	/*Object*/ handlers,
7050
								/*int*/ duration,
7051
								/*dojo.lfx.Line*/ curve,
7052
								/*function*/ easing,
7053
								/*int*/ repeatCount,
7054
								/*int*/ rate){
7055
	//	summary
7056
	//		a generic animation object that fires callbacks into it's handlers
7057
	//		object at various states
7058
	//	handlers: { handler: Function?, onstart: Function?, onstop: Function?, onanimate: Function? }
7059
	dojo.lfx.IAnimation.call(this);
7060
	if(dojo.lang.isNumber(handlers)||(!handlers && duration.getValue)){
7061
		// no handlers argument:
7062
		rate = repeatCount;
7063
		repeatCount = easing;
7064
		easing = curve;
7065
		curve = duration;
7066
		duration = handlers;
7067
		handlers = null;
7068
	}else if(handlers.getValue||dojo.lang.isArray(handlers)){
7069
		// no handlers or duration:
7070
		rate = easing;
7071
		repeatCount = curve;
7072
		easing = duration;
7073
		curve = handlers;
7074
		duration = null;
7075
		handlers = null;
7076
	}
7077
	if(dojo.lang.isArray(curve)){
7078
		/* curve: Array
7079
		   pId: a */
7080
		this.curve = new dojo.lfx.Line(curve[0], curve[1]);
7081
	}else{
7082
		this.curve = curve;
7083
	}
7084
	if(duration != null && duration > 0){ this.duration = duration; }
7085
	if(repeatCount){ this.repeatCount = repeatCount; }
7086
	if(rate){ this.rate = rate; }
7087
	if(handlers){
7088
		dojo.lang.forEach([
7089
				"handler", "beforeBegin", "onBegin",
7090
				"onEnd", "onPlay", "onStop", "onAnimate"
7091
			], function(item){
7092
				if(handlers[item]){
7093
					this.connect(item, handlers[item]);
7094
				}
7095
			}, this);
7096
	}
7097
	if(easing && dojo.lang.isFunction(easing)){
7098
		this.easing=easing;
7099
	}
7100
}
7101
dojo.inherits(dojo.lfx.Animation, dojo.lfx.IAnimation);
7102
dojo.lang.extend(dojo.lfx.Animation, {
7103
	// "private" properties
7104
	_startTime: null,
7105
	_endTime: null,
7106
	_timer: null,
7107
	_percent: 0,
7108
	_startRepeatCount: 0,
7109
 
7110
	// public methods
7111
	play: function(/*int?*/ delay, /*bool?*/ gotoStart){
7112
		// summary: Start the animation.
7113
		// delay: How many milliseconds to delay before starting.
7114
		// gotoStart: If true, starts the animation from the beginning; otherwise,
7115
		//            starts it from its current position.
7116
		if(gotoStart){
7117
			clearTimeout(this._timer);
7118
			this._active = false;
7119
			this._paused = false;
7120
			this._percent = 0;
7121
		}else if(this._active && !this._paused){
7122
			return this; // dojo.lfx.Animation
7123
		}
7124
 
7125
		this.fire("handler", ["beforeBegin"]);
7126
		this.fire("beforeBegin");
7127
 
7128
		if(delay > 0){
7129
			setTimeout(dojo.lang.hitch(this, function(){ this.play(null, gotoStart); }), delay);
7130
			return this; // dojo.lfx.Animation
7131
		}
7132
 
7133
		this._startTime = new Date().valueOf();
7134
		if(this._paused){
7135
			this._startTime -= (this.duration * this._percent / 100);
7136
		}
7137
		this._endTime = this._startTime + this.duration;
7138
 
7139
		this._active = true;
7140
		this._paused = false;
7141
 
7142
		var step = this._percent / 100;
7143
		var value = this.curve.getValue(step);
7144
		if(this._percent == 0 ){
7145
			if(!this._startRepeatCount){
7146
				this._startRepeatCount = this.repeatCount;
7147
			}
7148
			this.fire("handler", ["begin", value]);
7149
			this.fire("onBegin", [value]);
7150
		}
7151
 
7152
		this.fire("handler", ["play", value]);
7153
		this.fire("onPlay", [value]);
7154
 
7155
		this._cycle();
7156
		return this; // dojo.lfx.Animation
7157
	},
7158
 
7159
	pause: function(){
7160
		// summary: Pauses a running animation.
7161
		clearTimeout(this._timer);
7162
		if(!this._active){ return this; /*dojo.lfx.Animation*/}
7163
		this._paused = true;
7164
		var value = this.curve.getValue(this._percent / 100);
7165
		this.fire("handler", ["pause", value]);
7166
		this.fire("onPause", [value]);
7167
		return this; // dojo.lfx.Animation
7168
	},
7169
 
7170
	gotoPercent: function(/*Decimal*/ pct, /*bool?*/ andPlay){
7171
		// summary: Sets the progress of the animation.
7172
		// pct: A percentage in decimal notation (between and including 0.0 and 1.0).
7173
		// andPlay: If true, play the animation after setting the progress.
7174
		clearTimeout(this._timer);
7175
		this._active = true;
7176
		this._paused = true;
7177
		this._percent = pct;
7178
		if(andPlay){ this.play(); }
7179
		return this; // dojo.lfx.Animation
7180
	},
7181
 
7182
	stop: function(/*bool?*/ gotoEnd){
7183
		// summary: Stops a running animation.
7184
		// gotoEnd: If true, the animation will end.
7185
		clearTimeout(this._timer);
7186
		var step = this._percent / 100;
7187
		if(gotoEnd){
7188
			step = 1;
7189
		}
7190
		var value = this.curve.getValue(step);
7191
		this.fire("handler", ["stop", value]);
7192
		this.fire("onStop", [value]);
7193
		this._active = false;
7194
		this._paused = false;
7195
		return this; // dojo.lfx.Animation
7196
	},
7197
 
7198
	status: function(){
7199
		// summary: Returns a string representation of the status of
7200
		//			the animation.
7201
		if(this._active){
7202
			return this._paused ? "paused" : "playing"; // String
7203
		}else{
7204
			return "stopped"; // String
7205
		}
7206
		return this;
7207
	},
7208
 
7209
	// "private" methods
7210
	_cycle: function(){
7211
		clearTimeout(this._timer);
7212
		if(this._active){
7213
			var curr = new Date().valueOf();
7214
			var step = (curr - this._startTime) / (this._endTime - this._startTime);
7215
 
7216
			if(step >= 1){
7217
				step = 1;
7218
				this._percent = 100;
7219
			}else{
7220
				this._percent = step * 100;
7221
			}
7222
 
7223
			// Perform easing
7224
			if((this.easing)&&(dojo.lang.isFunction(this.easing))){
7225
				step = this.easing(step);
7226
			}
7227
 
7228
			var value = this.curve.getValue(step);
7229
			this.fire("handler", ["animate", value]);
7230
			this.fire("onAnimate", [value]);
7231
 
7232
			if( step < 1 ){
7233
				this._timer = setTimeout(dojo.lang.hitch(this, "_cycle"), this.rate);
7234
			}else{
7235
				this._active = false;
7236
				this.fire("handler", ["end"]);
7237
				this.fire("onEnd");
7238
 
7239
				if(this.repeatCount > 0){
7240
					this.repeatCount--;
7241
					this.play(null, true);
7242
				}else if(this.repeatCount == -1){
7243
					this.play(null, true);
7244
				}else{
7245
					if(this._startRepeatCount){
7246
						this.repeatCount = this._startRepeatCount;
7247
						this._startRepeatCount = 0;
7248
					}
7249
				}
7250
			}
7251
		}
7252
		return this; // dojo.lfx.Animation
7253
	}
7254
});
7255
 
7256
dojo.lfx.Combine = function(/*dojo.lfx.IAnimation...*/ animations){
7257
	// summary: An animation object to play animations passed to it at the same time.
7258
	dojo.lfx.IAnimation.call(this);
7259
	this._anims = [];
7260
	this._animsEnded = 0;
7261
 
7262
	var anims = arguments;
7263
	if(anims.length == 1 && (dojo.lang.isArray(anims[0]) || dojo.lang.isArrayLike(anims[0]))){
7264
		/* animations: dojo.lfx.IAnimation[]
7265
		   pId: a */
7266
		anims = anims[0];
7267
	}
7268
 
7269
	dojo.lang.forEach(anims, function(anim){
7270
		this._anims.push(anim);
7271
		anim.connect("onEnd", dojo.lang.hitch(this, "_onAnimsEnded"));
7272
	}, this);
7273
}
7274
dojo.inherits(dojo.lfx.Combine, dojo.lfx.IAnimation);
7275
dojo.lang.extend(dojo.lfx.Combine, {
7276
	// private members
7277
	_animsEnded: 0,
7278
 
7279
	// public methods
7280
	play: function(/*int?*/ delay, /*bool?*/ gotoStart){
7281
		// summary: Start the animations.
7282
		// delay: How many milliseconds to delay before starting.
7283
		// gotoStart: If true, starts the animations from the beginning; otherwise,
7284
		//            starts them from their current position.
7285
		if( !this._anims.length ){ return this; /*dojo.lfx.Combine*/}
7286
 
7287
		this.fire("beforeBegin");
7288
 
7289
		if(delay > 0){
7290
			setTimeout(dojo.lang.hitch(this, function(){ this.play(null, gotoStart); }), delay);
7291
			return this; // dojo.lfx.Combine
7292
		}
7293
 
7294
		if(gotoStart || this._anims[0].percent == 0){
7295
			this.fire("onBegin");
7296
		}
7297
		this.fire("onPlay");
7298
		this._animsCall("play", null, gotoStart);
7299
		return this; // dojo.lfx.Combine
7300
	},
7301
 
7302
	pause: function(){
7303
		// summary: Pauses the running animations.
7304
		this.fire("onPause");
7305
		this._animsCall("pause");
7306
		return this; // dojo.lfx.Combine
7307
	},
7308
 
7309
	stop: function(/*bool?*/ gotoEnd){
7310
		// summary: Stops the running animations.
7311
		// gotoEnd: If true, the animations will end.
7312
		this.fire("onStop");
7313
		this._animsCall("stop", gotoEnd);
7314
		return this; // dojo.lfx.Combine
7315
	},
7316
 
7317
	// private methods
7318
	_onAnimsEnded: function(){
7319
		this._animsEnded++;
7320
		if(this._animsEnded >= this._anims.length){
7321
			this.fire("onEnd");
7322
		}
7323
		return this; // dojo.lfx.Combine
7324
	},
7325
 
7326
	_animsCall: function(/*String*/ funcName){
7327
		var args = [];
7328
		if(arguments.length > 1){
7329
			for(var i = 1 ; i < arguments.length ; i++){
7330
				args.push(arguments[i]);
7331
			}
7332
		}
7333
		var _this = this;
7334
		dojo.lang.forEach(this._anims, function(anim){
7335
			anim[funcName](args);
7336
		}, _this);
7337
		return this; // dojo.lfx.Combine
7338
	}
7339
});
7340
 
7341
dojo.lfx.Chain = function(/*dojo.lfx.IAnimation...*/ animations) {
7342
	// summary: An animation object to play animations passed to it
7343
	//			one after another.
7344
	dojo.lfx.IAnimation.call(this);
7345
	this._anims = [];
7346
	this._currAnim = -1;
7347
 
7348
	var anims = arguments;
7349
	if(anims.length == 1 && (dojo.lang.isArray(anims[0]) || dojo.lang.isArrayLike(anims[0]))){
7350
		/* animations: dojo.lfx.IAnimation[]
7351
		   pId: a */
7352
		anims = anims[0];
7353
	}
7354
 
7355
	var _this = this;
7356
	dojo.lang.forEach(anims, function(anim, i, anims_arr){
7357
		this._anims.push(anim);
7358
		if(i < anims_arr.length - 1){
7359
			anim.connect("onEnd", dojo.lang.hitch(this, "_playNext") );
7360
		}else{
7361
			anim.connect("onEnd", dojo.lang.hitch(this, function(){ this.fire("onEnd"); }) );
7362
		}
7363
	}, this);
7364
}
7365
dojo.inherits(dojo.lfx.Chain, dojo.lfx.IAnimation);
7366
dojo.lang.extend(dojo.lfx.Chain, {
7367
	// private members
7368
	_currAnim: -1,
7369
 
7370
	// public methods
7371
	play: function(/*int?*/ delay, /*bool?*/ gotoStart){
7372
		// summary: Start the animation sequence.
7373
		// delay: How many milliseconds to delay before starting.
7374
		// gotoStart: If true, starts the sequence from the beginning; otherwise,
7375
		//            starts it from its current position.
7376
		if( !this._anims.length ) { return this; /*dojo.lfx.Chain*/}
7377
		if( gotoStart || !this._anims[this._currAnim] ) {
7378
			this._currAnim = 0;
7379
		}
7380
 
7381
		var currentAnimation = this._anims[this._currAnim];
7382
 
7383
		this.fire("beforeBegin");
7384
		if(delay > 0){
7385
			setTimeout(dojo.lang.hitch(this, function(){ this.play(null, gotoStart); }), delay);
7386
			return this; // dojo.lfx.Chain
7387
		}
7388
 
7389
		if(currentAnimation){
7390
			if(this._currAnim == 0){
7391
				this.fire("handler", ["begin", this._currAnim]);
7392
				this.fire("onBegin", [this._currAnim]);
7393
			}
7394
			this.fire("onPlay", [this._currAnim]);
7395
			currentAnimation.play(null, gotoStart);
7396
		}
7397
		return this; // dojo.lfx.Chain
7398
	},
7399
 
7400
	pause: function(){
7401
		// summary: Pauses the running animation sequence.
7402
		if( this._anims[this._currAnim] ) {
7403
			this._anims[this._currAnim].pause();
7404
			this.fire("onPause", [this._currAnim]);
7405
		}
7406
		return this; // dojo.lfx.Chain
7407
	},
7408
 
7409
	playPause: function(){
7410
		// summary: If the animation sequence is playing, pause it; otherwise,
7411
		//			play it.
7412
		if(this._anims.length == 0){ return this; }
7413
		if(this._currAnim == -1){ this._currAnim = 0; }
7414
		var currAnim = this._anims[this._currAnim];
7415
		if( currAnim ) {
7416
			if( !currAnim._active || currAnim._paused ) {
7417
				this.play();
7418
			} else {
7419
				this.pause();
7420
			}
7421
		}
7422
		return this; // dojo.lfx.Chain
7423
	},
7424
 
7425
	stop: function(){
7426
		// summary: Stops the running animations.
7427
		var currAnim = this._anims[this._currAnim];
7428
		if(currAnim){
7429
			currAnim.stop();
7430
			this.fire("onStop", [this._currAnim]);
7431
		}
7432
		return currAnim; // dojo.lfx.IAnimation
7433
	},
7434
 
7435
	// private methods
7436
	_playNext: function(){
7437
		if( this._currAnim == -1 || this._anims.length == 0 ) { return this; }
7438
		this._currAnim++;
7439
		if( this._anims[this._currAnim] ){
7440
			this._anims[this._currAnim].play(null, true);
7441
		}
7442
		return this; // dojo.lfx.Chain
7443
	}
7444
});
7445
 
7446
dojo.lfx.combine = function(/*dojo.lfx.IAnimation...*/ animations){
7447
	// summary: Convenience function.  Returns a dojo.lfx.Combine created
7448
	//			using the animations passed in.
7449
	var anims = arguments;
7450
	if(dojo.lang.isArray(arguments[0])){
7451
		/* animations: dojo.lfx.IAnimation[]
7452
		   pId: a */
7453
		anims = arguments[0];
7454
	}
7455
	if(anims.length == 1){ return anims[0]; }
7456
	return new dojo.lfx.Combine(anims); // dojo.lfx.Combine
7457
}
7458
 
7459
dojo.lfx.chain = function(/*dojo.lfx.IAnimation...*/ animations){
7460
	// summary: Convenience function.  Returns a dojo.lfx.Chain created
7461
	//			using the animations passed in.
7462
	var anims = arguments;
7463
	if(dojo.lang.isArray(arguments[0])){
7464
		/* animations: dojo.lfx.IAnimation[]
7465
		   pId: a */
7466
		anims = arguments[0];
7467
	}
7468
	if(anims.length == 1){ return anims[0]; }
7469
	return new dojo.lfx.Chain(anims); // dojo.lfx.Combine
7470
}
7471
 
7472
dojo.provide("dojo.html.common");
7473
 
7474
 
7475
 
7476
dojo.lang.mixin(dojo.html, dojo.dom);
7477
 
7478
dojo.html.body = function(){
7479
	dojo.deprecated("dojo.html.body() moved to dojo.body()", "0.5");
7480
	return dojo.body();
7481
}
7482
 
7483
// FIXME: we are going to assume that we can throw any and every rendering
7484
// engine into the IE 5.x box model. In Mozilla, we do this w/ CSS.
7485
// Need to investigate for KHTML and Opera
7486
 
7487
dojo.html.getEventTarget = function(/* DOMEvent */evt){
7488
	//	summary
7489
	//	Returns the target of an event
7490
	if(!evt) { evt = dojo.global().event || {} };
7491
	var t = (evt.srcElement ? evt.srcElement : (evt.target ? evt.target : null));
7492
	while((t)&&(t.nodeType!=1)){ t = t.parentNode; }
7493
	return t;	//	HTMLElement
7494
}
7495
 
7496
dojo.html.getViewport = function(){
7497
	//	summary
7498
	//	Returns the dimensions of the viewable area of a browser window
7499
	var _window = dojo.global();
7500
	var _document = dojo.doc();
7501
	var w = 0;
7502
	var h = 0;
7503
 
7504
	if(dojo.render.html.mozilla){
7505
		// mozilla
7506
		w = _document.documentElement.clientWidth;
7507
		h = _window.innerHeight;
7508
	}else if(!dojo.render.html.opera && _window.innerWidth){
7509
		//in opera9, dojo.body().clientWidth should be used, instead
7510
		//of window.innerWidth/document.documentElement.clientWidth
7511
		//so we have to check whether it is opera
7512
		w = _window.innerWidth;
7513
		h = _window.innerHeight;
7514
	} else if (!dojo.render.html.opera && dojo.exists(_document, "documentElement.clientWidth")){
7515
		// IE6 Strict
7516
		var w2 = _document.documentElement.clientWidth;
7517
		// this lets us account for scrollbars
7518
		if(!w || w2 && w2 < w) {
7519
			w = w2;
7520
		}
7521
		h = _document.documentElement.clientHeight;
7522
	} else if (dojo.body().clientWidth){
7523
		// IE, Opera
7524
		w = dojo.body().clientWidth;
7525
		h = dojo.body().clientHeight;
7526
	}
7527
	return { width: w, height: h };	//	object
7528
}
7529
 
7530
dojo.html.getScroll = function(){
7531
	//	summary
7532
	//	Returns the scroll position of the document
7533
	var _window = dojo.global();
7534
	var _document = dojo.doc();
7535
	var top = _window.pageYOffset || _document.documentElement.scrollTop || dojo.body().scrollTop || 0;
7536
	var left = _window.pageXOffset || _document.documentElement.scrollLeft || dojo.body().scrollLeft || 0;
7537
	return {
7538
		top: top,
7539
		left: left,
7540
		offset:{ x: left, y: top }	//	note the change, NOT an Array with added properties.
7541
	};	//	object
7542
}
7543
 
7544
dojo.html.getParentByType = function(/* HTMLElement */node, /* string */type) {
7545
	//	summary
7546
	//	Returns the first ancestor of node with tagName type.
7547
	var _document = dojo.doc();
7548
	var parent = dojo.byId(node);
7549
	type = type.toLowerCase();
7550
	while((parent)&&(parent.nodeName.toLowerCase()!=type)){
7551
		if(parent==(_document["body"]||_document["documentElement"])){
7552
			return null;
7553
		}
7554
		parent = parent.parentNode;
7555
	}
7556
	return parent;	//	HTMLElement
7557
}
7558
 
7559
dojo.html.getAttribute = function(/* HTMLElement */node, /* string */attr){
7560
	//	summary
7561
	//	Returns the value of attribute attr from node.
7562
	node = dojo.byId(node);
7563
	// FIXME: need to add support for attr-specific accessors
7564
	if((!node)||(!node.getAttribute)){
7565
		// if(attr !== 'nwType'){
7566
		//	alert("getAttr of '" + attr + "' with bad node");
7567
		// }
7568
		return null;
7569
	}
7570
	var ta = typeof attr == 'string' ? attr : new String(attr);
7571
 
7572
	// first try the approach most likely to succeed
7573
	var v = node.getAttribute(ta.toUpperCase());
7574
	if((v)&&(typeof v == 'string')&&(v!="")){
7575
		return v;	//	string
7576
	}
7577
 
7578
	// try returning the attributes value, if we couldn't get it as a string
7579
	if(v && v.value){
7580
		return v.value;	//	string
7581
	}
7582
 
7583
	// this should work on Opera 7, but it's a little on the crashy side
7584
	if((node.getAttributeNode)&&(node.getAttributeNode(ta))){
7585
		return (node.getAttributeNode(ta)).value;	//	string
7586
	}else if(node.getAttribute(ta)){
7587
		return node.getAttribute(ta);	//	string
7588
	}else if(node.getAttribute(ta.toLowerCase())){
7589
		return node.getAttribute(ta.toLowerCase());	//	string
7590
	}
7591
	return null;	//	string
7592
}
7593
 
7594
dojo.html.hasAttribute = function(/* HTMLElement */node, /* string */attr){
7595
	//	summary
7596
	//	Determines whether or not the specified node carries a value for the attribute in question.
7597
	return dojo.html.getAttribute(dojo.byId(node), attr) ? true : false;	//	boolean
7598
}
7599
 
7600
dojo.html.getCursorPosition = function(/* DOMEvent */e){
7601
	//	summary
7602
	//	Returns the mouse position relative to the document (not the viewport).
7603
	//	For example, if you have a document that is 10000px tall,
7604
	//	but your browser window is only 100px tall,
7605
	//	if you scroll to the bottom of the document and call this function it
7606
	//	will return {x: 0, y: 10000}
7607
	//	NOTE: for events delivered via dojo.event.connect() and/or dojoAttachEvent (for widgets),
7608
	//	you can just access evt.pageX and evt.pageY, rather than calling this function.
7609
	e = e || dojo.global().event;
7610
	var cursor = {x:0, y:0};
7611
	if(e.pageX || e.pageY){
7612
		cursor.x = e.pageX;
7613
		cursor.y = e.pageY;
7614
	}else{
7615
		var de = dojo.doc().documentElement;
7616
		var db = dojo.body();
7617
		cursor.x = e.clientX + ((de||db)["scrollLeft"]) - ((de||db)["clientLeft"]);
7618
		cursor.y = e.clientY + ((de||db)["scrollTop"]) - ((de||db)["clientTop"]);
7619
	}
7620
	return cursor;	//	object
7621
}
7622
 
7623
dojo.html.isTag = function(/* HTMLElement */node) {
7624
	//	summary
7625
	//	Like dojo.dom.isTag, except case-insensitive
7626
	node = dojo.byId(node);
7627
	if(node && node.tagName) {
7628
		for (var i=1; i<arguments.length; i++){
7629
			if (node.tagName.toLowerCase()==String(arguments[i]).toLowerCase()){
7630
				return String(arguments[i]).toLowerCase();	//	string
7631
			}
7632
		}
7633
	}
7634
	return "";	//	string
7635
}
7636
 
7637
//define dojo.html.createExternalElement for IE to workaround the annoying activation "feature" in new IE
7638
//details: http://msdn.microsoft.com/library/default.asp?url=/workshop/author/dhtml/overview/activating_activex.asp
7639
if(dojo.render.html.ie && !dojo.render.html.ie70){
7640
	//only define createExternalElement for IE in none https to avoid "mixed content" warning dialog
7641
	if(window.location.href.substr(0,6).toLowerCase() != "https:"){
7642
		(function(){
7643
			// FIXME: this seems not to work correctly on IE 7!!
7644
 
7645
			//The trick is to define a function in a script.src property:
7646
			// <script src="javascript:'function createExternalElement(){...}'"></script>,
7647
			//which will be treated as an external javascript file in IE
7648
			var xscript = dojo.doc().createElement('script');
7649
			xscript.src = "javascript:'dojo.html.createExternalElement=function(doc, tag){ return doc.createElement(tag); }'";
7650
			dojo.doc().getElementsByTagName("head")[0].appendChild(xscript);
7651
		})();
7652
	}
7653
}else{
7654
	//for other browsers, simply use document.createElement
7655
	//is enough
7656
	dojo.html.createExternalElement = function(/* HTMLDocument */doc, /* string */tag){
7657
		//	summary
7658
		//	Creates an element in the HTML document, here for ActiveX activation workaround.
7659
		return doc.createElement(tag);	//	HTMLElement
7660
	}
7661
}
7662
 
7663
dojo.html._callDeprecated = function(inFunc, replFunc, args, argName, retValue){
7664
	dojo.deprecated("dojo.html." + inFunc,
7665
					"replaced by dojo.html." + replFunc + "(" + (argName ? "node, {"+ argName + ": " + argName + "}" : "" ) + ")" + (retValue ? "." + retValue : ""), "0.5");
7666
	var newArgs = [];
7667
	if(argName){ var argsIn = {}; argsIn[argName] = args[1]; newArgs.push(args[0]); newArgs.push(argsIn); }
7668
	else { newArgs = args }
7669
	var ret = dojo.html[replFunc].apply(dojo.html, args);
7670
	if(retValue){ return ret[retValue]; }
7671
	else { return ret; }
7672
}
7673
 
7674
dojo.html.getViewportWidth = function(){
7675
	return dojo.html._callDeprecated("getViewportWidth", "getViewport", arguments, null, "width");
7676
}
7677
dojo.html.getViewportHeight = function(){
7678
	return dojo.html._callDeprecated("getViewportHeight", "getViewport", arguments, null, "height");
7679
}
7680
dojo.html.getViewportSize = function(){
7681
	return dojo.html._callDeprecated("getViewportSize", "getViewport", arguments);
7682
}
7683
dojo.html.getScrollTop = function(){
7684
	return dojo.html._callDeprecated("getScrollTop", "getScroll", arguments, null, "top");
7685
}
7686
dojo.html.getScrollLeft = function(){
7687
	return dojo.html._callDeprecated("getScrollLeft", "getScroll", arguments, null, "left");
7688
}
7689
dojo.html.getScrollOffset = function(){
7690
	return dojo.html._callDeprecated("getScrollOffset", "getScroll", arguments, null, "offset");
7691
}
7692
 
7693
dojo.provide("dojo.uri.Uri");
7694
 
7695
dojo.uri = new function() {
7696
	this.dojoUri = function (/*dojo.uri.Uri||String*/uri) {
7697
		// summary: returns a Uri object resolved relative to the dojo root
7698
		return new dojo.uri.Uri(dojo.hostenv.getBaseScriptUri(), uri);
7699
	}
7700
 
7701
	this.moduleUri = function(/*String*/module, /*dojo.uri.Uri||String*/uri){
7702
		// summary: returns a Uri object relative to a module
7703
		// description: Examples: dojo.uri.moduleUri("dojo.widget","templates/template.html"), or dojo.uri.moduleUri("acme","images/small.png")
7704
		var loc = dojo.hostenv.getModuleSymbols(module).join('/');
7705
		if(!loc){
7706
			return null;
7707
		}
7708
		if(loc.lastIndexOf("/") != loc.length-1){
7709
			loc += "/";
7710
		}
7711
 
7712
		//If the path is an absolute path (starts with a / or is on another domain/xdomain)
7713
		//then don't add the baseScriptUri.
7714
		var colonIndex = loc.indexOf(":");
7715
		var slashIndex = loc.indexOf("/");
7716
		if(loc.charAt(0) != "/" && (colonIndex == -1 || colonIndex > slashIndex)){
7717
			loc = dojo.hostenv.getBaseScriptUri() + loc;
7718
		}
7719
 
7720
		return new dojo.uri.Uri(loc,uri);
7721
	}
7722
 
7723
	this.Uri = function (/*dojo.uri.Uri||String...*/) {
7724
		// summary: Constructor to create an object representing a URI.
7725
		// description:
7726
		//  Each argument is evaluated in order relative to the next until
7727
		//  a canonical uri is produced. To get an absolute Uri relative
7728
		//  to the current document use
7729
		//      new dojo.uri.Uri(document.baseURI, uri)
7730
 
7731
		// TODO: support for IPv6, see RFC 2732
7732
 
7733
		// resolve uri components relative to each other
7734
		var uri = arguments[0];
7735
		for (var i = 1; i < arguments.length; i++) {
7736
			if(!arguments[i]) { continue; }
7737
 
7738
			// Safari doesn't support this.constructor so we have to be explicit
7739
			var relobj = new dojo.uri.Uri(arguments[i].toString());
7740
			var uriobj = new dojo.uri.Uri(uri.toString());
7741
 
7742
			if ((relobj.path=="")&&(relobj.scheme==null)&&(relobj.authority==null)&&(relobj.query==null)) {
7743
				if (relobj.fragment != null) { uriobj.fragment = relobj.fragment; }
7744
				relobj = uriobj;
7745
			} else if (relobj.scheme == null) {
7746
				relobj.scheme = uriobj.scheme;
7747
 
7748
				if (relobj.authority == null) {
7749
					relobj.authority = uriobj.authority;
7750
 
7751
					if (relobj.path.charAt(0) != "/") {
7752
						var path = uriobj.path.substring(0,
7753
							uriobj.path.lastIndexOf("/") + 1) + relobj.path;
7754
 
7755
						var segs = path.split("/");
7756
						for (var j = 0; j < segs.length; j++) {
7757
							if (segs[j] == ".") {
7758
								if (j == segs.length - 1) { segs[j] = ""; }
7759
								else { segs.splice(j, 1); j--; }
7760
							} else if (j > 0 && !(j == 1 && segs[0] == "") &&
7761
								segs[j] == ".." && segs[j-1] != "..") {
7762
 
7763
								if (j == segs.length - 1) { segs.splice(j, 1); segs[j - 1] = ""; }
7764
								else { segs.splice(j - 1, 2); j -= 2; }
7765
							}
7766
						}
7767
						relobj.path = segs.join("/");
7768
					}
7769
				}
7770
			}
7771
 
7772
			uri = "";
7773
			if (relobj.scheme != null) { uri += relobj.scheme + ":"; }
7774
			if (relobj.authority != null) { uri += "//" + relobj.authority; }
7775
			uri += relobj.path;
7776
			if (relobj.query != null) { uri += "?" + relobj.query; }
7777
			if (relobj.fragment != null) { uri += "#" + relobj.fragment; }
7778
		}
7779
 
7780
		this.uri = uri.toString();
7781
 
7782
		// break the uri into its main components
7783
		var regexp = "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$";
7784
		var r = this.uri.match(new RegExp(regexp));
7785
 
7786
		this.scheme = r[2] || (r[1] ? "" : null);
7787
		this.authority = r[4] || (r[3] ? "" : null);
7788
		this.path = r[5]; // can never be undefined
7789
		this.query = r[7] || (r[6] ? "" : null);
7790
		this.fragment  = r[9] || (r[8] ? "" : null);
7791
 
7792
		if (this.authority != null) {
7793
			// server based naming authority
7794
			regexp = "^((([^:]+:)?([^@]+))@)?([^:]*)(:([0-9]+))?$";
7795
			r = this.authority.match(new RegExp(regexp));
7796
 
7797
			this.user = r[3] || null;
7798
			this.password = r[4] || null;
7799
			this.host = r[5];
7800
			this.port = r[7] || null;
7801
		}
7802
 
7803
		this.toString = function(){ return this.uri; }
7804
	}
7805
};
7806
 
7807
dojo.provide("dojo.html.style");
7808
 
7809
 
7810
 
7811
dojo.html.getClass = function(/* HTMLElement */node){
7812
	//	summary
7813
	//	Returns the string value of the list of CSS classes currently assigned directly
7814
	//	to the node in question. Returns an empty string if no class attribute is found;
7815
	node = dojo.byId(node);
7816
	if(!node){ return ""; }
7817
	var cs = "";
7818
	if(node.className){
7819
		cs = node.className;
7820
	}else if(dojo.html.hasAttribute(node, "class")){
7821
		cs = dojo.html.getAttribute(node, "class");
7822
	}
7823
	return cs.replace(/^\s+|\s+$/g, "");	//	string
7824
}
7825
 
7826
dojo.html.getClasses = function(/* HTMLElement */node) {
7827
	//	summary
7828
	//	Returns an array of CSS classes currently assigned directly to the node in question.
7829
	//	Returns an empty array if no classes are found;
7830
	var c = dojo.html.getClass(node);
7831
	return (c == "") ? [] : c.split(/\s+/g);	//	array
7832
}
7833
 
7834
dojo.html.hasClass = function(/* HTMLElement */node, /* string */classname){
7835
	//	summary
7836
	//	Returns whether or not the specified classname is a portion of the
7837
	//	class list currently applied to the node. Does not cover cascaded
7838
	//	styles, only classes directly applied to the node.
7839
	return (new RegExp('(^|\\s+)'+classname+'(\\s+|$)')).test(dojo.html.getClass(node))	//	boolean
7840
}
7841
 
7842
dojo.html.prependClass = function(/* HTMLElement */node, /* string */classStr){
7843
	//	summary
7844
	//	Adds the specified class to the beginning of the class list on the
7845
	//	passed node. This gives the specified class the highest precidence
7846
	//	when style cascading is calculated for the node. Returns true or
7847
	//	false; indicating success or failure of the operation, respectively.
7848
	classStr += " " + dojo.html.getClass(node);
7849
	return dojo.html.setClass(node, classStr);	//	boolean
7850
}
7851
 
7852
dojo.html.addClass = function(/* HTMLElement */node, /* string */classStr){
7853
	//	summary
7854
	//	Adds the specified class to the end of the class list on the
7855
	//	passed &node;. Returns &true; or &false; indicating success or failure.
7856
	if (dojo.html.hasClass(node, classStr)) {
7857
	  return false;
7858
	}
7859
	classStr = (dojo.html.getClass(node) + " " + classStr).replace(/^\s+|\s+$/g,"");
7860
	return dojo.html.setClass(node, classStr);	//	boolean
7861
}
7862
 
7863
dojo.html.setClass = function(/* HTMLElement */node, /* string */classStr){
7864
	//	summary
7865
	//	Clobbers the existing list of classes for the node, replacing it with
7866
	//	the list given in the 2nd argument. Returns true or false
7867
	//	indicating success or failure.
7868
	node = dojo.byId(node);
7869
	var cs = new String(classStr);
7870
	try{
7871
		if(typeof node.className == "string"){
7872
			node.className = cs;
7873
		}else if(node.setAttribute){
7874
			node.setAttribute("class", classStr);
7875
			node.className = cs;
7876
		}else{
7877
			return false;
7878
		}
7879
	}catch(e){
7880
		dojo.debug("dojo.html.setClass() failed", e);
7881
	}
7882
	return true;
7883
}
7884
 
7885
dojo.html.removeClass = function(/* HTMLElement */node, /* string */classStr, /* boolean? */allowPartialMatches){
7886
	//	summary
7887
	//	Removes the className from the node;. Returns true or false indicating success or failure.
7888
	try{
7889
		if (!allowPartialMatches) {
7890
			var newcs = dojo.html.getClass(node).replace(new RegExp('(^|\\s+)'+classStr+'(\\s+|$)'), "$1$2");
7891
		} else {
7892
			var newcs = dojo.html.getClass(node).replace(classStr,'');
7893
		}
7894
		dojo.html.setClass(node, newcs);
7895
	}catch(e){
7896
		dojo.debug("dojo.html.removeClass() failed", e);
7897
	}
7898
	return true;	//	boolean
7899
}
7900
 
7901
dojo.html.replaceClass = function(/* HTMLElement */node, /* string */newClass, /* string */oldClass) {
7902
	//	summary
7903
	//	Replaces 'oldClass' and adds 'newClass' to node
7904
	dojo.html.removeClass(node, oldClass);
7905
	dojo.html.addClass(node, newClass);
7906
}
7907
 
7908
// Enum type for getElementsByClass classMatchType arg:
7909
dojo.html.classMatchType = {
7910
	ContainsAll : 0, // all of the classes are part of the node's class (default)
7911
	ContainsAny : 1, // any of the classes are part of the node's class
7912
	IsOnly : 2 // only all of the classes are part of the node's class
7913
}
7914
 
7915
 
7916
dojo.html.getElementsByClass = function(
7917
	/* string */classStr,
7918
	/* HTMLElement? */parent,
7919
	/* string? */nodeType,
7920
	/* integer? */classMatchType,
7921
	/* boolean? */useNonXpath
7922
){
7923
	//	summary
7924
	//	Returns an array of nodes for the given classStr, children of a
7925
	//	parent, and optionally of a certain nodeType
7926
	// FIXME: temporarily set to false because of several dojo tickets related
7927
	// to the xpath version not working consistently in firefox.
7928
	useNonXpath = false;
7929
	var _document = dojo.doc();
7930
	parent = dojo.byId(parent) || _document;
7931
	var classes = classStr.split(/\s+/g);
7932
	var nodes = [];
7933
	if( classMatchType != 1 && classMatchType != 2 ) classMatchType = 0; // make it enum
7934
	var reClass = new RegExp("(\\s|^)((" + classes.join(")|(") + "))(\\s|$)");
7935
	var srtLength = classes.join(" ").length;
7936
	var candidateNodes = [];
7937
 
7938
	if(!useNonXpath && _document.evaluate) { // supports dom 3 xpath
7939
		var xpath = ".//" + (nodeType || "*") + "[contains(";
7940
		if(classMatchType != dojo.html.classMatchType.ContainsAny){
7941
			xpath += "concat(' ',@class,' '), ' " +
7942
			classes.join(" ') and contains(concat(' ',@class,' '), ' ") +
7943
			" ')";
7944
			if (classMatchType == 2) {
7945
				xpath += " and string-length(@class)="+srtLength+"]";
7946
			}else{
7947
				xpath += "]";
7948
			}
7949
		}else{
7950
			xpath += "concat(' ',@class,' '), ' " +
7951
			classes.join(" ') or contains(concat(' ',@class,' '), ' ") +
7952
			" ')]";
7953
		}
7954
		var xpathResult = _document.evaluate(xpath, parent, null, XPathResult.ANY_TYPE, null);
7955
		var result = xpathResult.iterateNext();
7956
		while(result){
7957
			try{
7958
				candidateNodes.push(result);
7959
				result = xpathResult.iterateNext();
7960
			}catch(e){ break; }
7961
		}
7962
		return candidateNodes;	//	NodeList
7963
	}else{
7964
		if(!nodeType){
7965
			nodeType = "*";
7966
		}
7967
		candidateNodes = parent.getElementsByTagName(nodeType);
7968
 
7969
		var node, i = 0;
7970
		outer:
7971
		while(node = candidateNodes[i++]){
7972
			var nodeClasses = dojo.html.getClasses(node);
7973
			if(nodeClasses.length == 0){ continue outer; }
7974
			var matches = 0;
7975
 
7976
			for(var j = 0; j < nodeClasses.length; j++){
7977
				if(reClass.test(nodeClasses[j])){
7978
					if(classMatchType == dojo.html.classMatchType.ContainsAny){
7979
						nodes.push(node);
7980
						continue outer;
7981
					}else{
7982
						matches++;
7983
					}
7984
				}else{
7985
					if(classMatchType == dojo.html.classMatchType.IsOnly){
7986
						continue outer;
7987
					}
7988
				}
7989
			}
7990
 
7991
			if(matches == classes.length){
7992
				if(	(classMatchType == dojo.html.classMatchType.IsOnly)&&
7993
					(matches == nodeClasses.length)){
7994
					nodes.push(node);
7995
				}else if(classMatchType == dojo.html.classMatchType.ContainsAll){
7996
					nodes.push(node);
7997
				}
7998
			}
7999
		}
8000
		return nodes;	//	NodeList
8001
	}
8002
}
8003
dojo.html.getElementsByClassName = dojo.html.getElementsByClass;
8004
 
8005
dojo.html.toCamelCase = function(/* string */selector){
8006
	//	summary
8007
	//	Translates a CSS selector string to a camel-cased one.
8008
	var arr = selector.split('-'), cc = arr[0];
8009
	for(var i = 1; i < arr.length; i++) {
8010
		cc += arr[i].charAt(0).toUpperCase() + arr[i].substring(1);
8011
	}
8012
	return cc;	//	string
8013
}
8014
 
8015
dojo.html.toSelectorCase = function(/* string */selector){
8016
	//	summary
8017
	//	Translates a camel cased string to a selector cased one.
8018
	return selector.replace(/([A-Z])/g, "-$1" ).toLowerCase();	//	string
8019
}
8020
 
8021
if (dojo.render.html.ie) {
8022
	// IE branch
8023
	dojo.html.getComputedStyle = function(/*HTMLElement|String*/node, /*String*/property, /*String*/value) {
8024
		// summary
8025
		// Get the computed style value for style "property" on "node" (IE).
1422 alexandre_ 8026
		node = dojo.byId(node); // FIXME: remove ability to access nodes by id for this time-critical function
8027
		if(!node || !node.currentStyle){return value;}
1318 alexandre_ 8028
		// FIXME: standardize on camel-case input to improve speed
8029
		return node.currentStyle[dojo.html.toCamelCase(property)]; // String
8030
	}
8031
	// SJM: getComputedStyle should be abandoned and replaced with the below function.
8032
	// All our supported browsers can return CSS2 compliant CssStyleDeclaration objects
8033
	// which can be queried directly for multiple styles.
8034
	dojo.html.getComputedStyles = function(/*HTMLElement*/node) {
8035
		// summary
8036
		// Get a style object containing computed styles for HTML Element node (IE).
8037
		return node.currentStyle; // CSSStyleDeclaration
8038
	}
8039
} else {
8040
	// non-IE branch
8041
	dojo.html.getComputedStyle = function(/*HTMLElement|String*/node, /*String*/property, /*Any*/value) {
8042
		// summary
8043
		// Get the computed style value for style "property" on "node" (non-IE).
8044
		node = dojo.byId(node);
8045
		if(!node || !node.style){return value;}
8046
		var s = document.defaultView.getComputedStyle(node, null);
8047
		// s may be null on Safari
8048
		return (s&&s[dojo.html.toCamelCase(property)])||''; // String
8049
	}
8050
	// SJM: getComputedStyle should be abandoned and replaced with the below function.
8051
	// All our supported browsers can return CSS2 compliant CssStyleDeclaration objects
8052
	// which can be queried directly for multiple styles.
8053
	dojo.html.getComputedStyles = function(node) {
8054
		// summary
8055
		// Get a style object containing computed styles for HTML Element node (non-IE).
8056
		return document.defaultView.getComputedStyle(node, null); // CSSStyleDeclaration
8057
	}
8058
}
8059
 
8060
dojo.html.getStyleProperty = function(/* HTMLElement */node, /* string */cssSelector){
8061
	//	summary
8062
	//	Returns the value of the passed style
8063
	node = dojo.byId(node);
8064
	return (node && node.style ? node.style[dojo.html.toCamelCase(cssSelector)] : undefined);	//	string
8065
}
8066
 
8067
dojo.html.getStyle = function(/* HTMLElement */node, /* string */cssSelector){
8068
	//	summary
8069
	//	Returns the computed value of the passed style
8070
	var value = dojo.html.getStyleProperty(node, cssSelector);
8071
	return (value ? value : dojo.html.getComputedStyle(node, cssSelector));	//	string || integer
8072
}
8073
 
8074
dojo.html.setStyle = function(/* HTMLElement */node, /* string */cssSelector, /* string */value){
8075
	//	summary
8076
	//	Set the value of passed style on node
8077
	node = dojo.byId(node);
8078
	if(node && node.style){
8079
		var camelCased = dojo.html.toCamelCase(cssSelector);
8080
		node.style[camelCased] = value;
8081
	}
8082
}
8083
 
8084
dojo.html.setStyleText = function (/* HTMLElement */target, /* string */text) {
8085
	//	summary
8086
	//	Try to set the entire cssText property of the passed target; equiv of setting style attribute.
8087
	try {
8088
	 	target.style.cssText = text;
8089
	} catch (e) {
8090
		target.setAttribute("style", text);
8091
	}
8092
}
8093
 
8094
dojo.html.copyStyle = function(/* HTMLElement */target, /* HTMLElement */source){
8095
	//	summary
8096
	// work around for opera which doesn't have cssText, and for IE which fails on setAttribute
8097
	if(!source.style.cssText){
8098
		target.setAttribute("style", source.getAttribute("style"));
8099
	}else{
8100
		target.style.cssText = source.style.cssText;
8101
	}
8102
	dojo.html.addClass(target, dojo.html.getClass(source));
8103
}
8104
 
8105
dojo.html.getUnitValue = function(/* HTMLElement */node, /* string */cssSelector, /* boolean? */autoIsZero){
8106
	//	summary
8107
	//	Get the value of passed selector, with the specific units used
8108
	var s = dojo.html.getComputedStyle(node, cssSelector);
8109
	if((!s)||((s == 'auto')&&(autoIsZero))){
8110
		return { value: 0, units: 'px' };	//	object
8111
	}
8112
	// FIXME: is regex inefficient vs. parseInt or some manual test?
8113
	var match = s.match(/(\-?[\d.]+)([a-z%]*)/i);
8114
	if (!match){return dojo.html.getUnitValue.bad;}
8115
	return { value: Number(match[1]), units: match[2].toLowerCase() };	//	object
8116
}
8117
dojo.html.getUnitValue.bad = { value: NaN, units: '' };
8118
 
8119
if (dojo.render.html.ie) {
8120
	// IE branch
8121
	dojo.html.toPixelValue = function(/* HTMLElement */element, /* String */styleValue){
8122
		// summary
8123
		//  Extract value in pixels from styleValue (IE version).
8124
		//  If a value cannot be extracted, zero is returned.
8125
		if(!styleValue){return 0;}
8126
		if(styleValue.slice(-2) == 'px'){return parseFloat(styleValue);}
8127
		var pixelValue = 0;
8128
		with(element){
8129
			var sLeft = style.left;
8130
			var rsLeft = runtimeStyle.left;
8131
			runtimeStyle.left = currentStyle.left;
8132
			try {
8133
				style.left = styleValue || 0;
8134
				pixelValue = style.pixelLeft;
8135
				style.left = sLeft;
8136
				runtimeStyle.left = rsLeft;
8137
			}catch(e){
8138
				// FIXME: it's possible for styleValue to be incompatible with
8139
				// style.left. In particular, border width values of
8140
				// "thick", "medium", or "thin" will provoke an exception.
8141
			}
8142
		}
8143
		return pixelValue; // Number
8144
	}
8145
} else {
8146
	// non-IE branch
8147
	dojo.html.toPixelValue = function(/* HTMLElement */element, /* String */styleValue){
8148
		// summary
8149
		//  Extract value in pixels from styleValue (non-IE version).
8150
		//  If a value cannot be extracted, zero is returned.
8151
		return (styleValue && (styleValue.slice(-2)=='px') ? parseFloat(styleValue) : 0); // Number
8152
	}
8153
}
8154
 
8155
dojo.html.getPixelValue = function(/* HTMLElement */node, /* string */styleProperty, /* boolean? */autoIsZero){
8156
	// summary
8157
	//  Get a computed style value, in pixels.
8158
	// node: HTMLElement
8159
	//  Node to interrogate
8160
	// styleProperty: String
8161
	//  Style property to query, in either css-selector or camelCase (property) format.
8162
	// autoIsZero: Boolean
8163
	//  Deprecated. Any value that cannot be converted to pixels is returned as zero.
8164
	//
8165
	//  summary
8166
	//  Get the value of passed selector in pixels.
8167
	//
8168
	return dojo.html.toPixelValue(node, dojo.html.getComputedStyle(node, styleProperty));
8169
}
8170
 
8171
dojo.html.setPositivePixelValue = function(/* HTMLElement */node, /* string */selector, /* integer */value){
8172
	//	summary
8173
	//	Attempt to set the value of selector on node as a positive pixel value.
8174
	if(isNaN(value)){return false;}
8175
	node.style[selector] = Math.max(0, value) + 'px';
8176
	return true;	//	boolean
8177
}
8178
 
8179
dojo.html.styleSheet = null;
8180
 
8181
// FIXME: this is a really basic stub for adding and removing cssRules, but
8182
// it assumes that you know the index of the cssRule that you want to add
8183
// or remove, making it less than useful.  So we need something that can
8184
// search for the selector that you you want to remove.
8185
dojo.html.insertCssRule = function(/* string */selector, /* string */declaration, /* integer? */index) {
8186
	//	summary
8187
	//	Attempt to insert declaration as selector on the internal stylesheet; if index try to set it there.
8188
	if (!dojo.html.styleSheet) {
8189
		if (document.createStyleSheet) { // IE
8190
			dojo.html.styleSheet = document.createStyleSheet();
8191
		} else if (document.styleSheets[0]) { // rest
8192
			// FIXME: should create a new style sheet here
8193
			// fall back on an exsiting style sheet
8194
			dojo.html.styleSheet = document.styleSheets[0];
8195
		} else {
8196
			return null;	//	integer
8197
		} // fail
8198
	}
8199
 
8200
	if (arguments.length < 3) { // index may == 0
8201
		if (dojo.html.styleSheet.cssRules) { // W3
8202
			index = dojo.html.styleSheet.cssRules.length;
8203
		} else if (dojo.html.styleSheet.rules) { // IE
8204
			index = dojo.html.styleSheet.rules.length;
8205
		} else {
8206
			return null;	//	integer
8207
		} // fail
8208
	}
8209
 
8210
	if (dojo.html.styleSheet.insertRule) { // W3
8211
		var rule = selector + " { " + declaration + " }";
8212
		return dojo.html.styleSheet.insertRule(rule, index);	//	integer
8213
	} else if (dojo.html.styleSheet.addRule) { // IE
8214
		return dojo.html.styleSheet.addRule(selector, declaration, index);	//	integer
8215
	} else {
8216
		return null; // integer
8217
	} // fail
8218
}
8219
 
8220
dojo.html.removeCssRule = function(/* integer? */index){
8221
	//	summary
8222
	//	Attempt to remove the rule at index.
8223
	if(!dojo.html.styleSheet){
8224
		dojo.debug("no stylesheet defined for removing rules");
8225
		return false;
8226
	}
8227
	if(dojo.render.html.ie){
8228
		if(!index){
8229
			index = dojo.html.styleSheet.rules.length;
8230
			dojo.html.styleSheet.removeRule(index);
8231
		}
8232
	}else if(document.styleSheets[0]){
8233
		if(!index){
8234
			index = dojo.html.styleSheet.cssRules.length;
8235
		}
8236
		dojo.html.styleSheet.deleteRule(index);
8237
	}
8238
	return true;	//	boolean
8239
}
8240
 
8241
dojo.html._insertedCssFiles = []; // cache container needed because IE reformats cssText when added to DOM
8242
dojo.html.insertCssFile = function(/* string */URI, /* HTMLDocument? */doc, /* boolean? */checkDuplicates, /* boolean */fail_ok){
8243
	//	summary
8244
	// calls css by XmlHTTP and inserts it into DOM as <style [widgetType="widgetType"]> *downloaded cssText*</style>
8245
	if(!URI){ return; }
8246
	if(!doc){ doc = document; }
8247
	var cssStr = dojo.hostenv.getText(URI, false, fail_ok);
8248
	if(cssStr===null){ return; }
8249
	cssStr = dojo.html.fixPathsInCssText(cssStr, URI);
8250
 
8251
	if(checkDuplicates){
8252
		var idx = -1, node, ent = dojo.html._insertedCssFiles;
8253
		for(var i = 0; i < ent.length; i++){
8254
			if((ent[i].doc == doc) && (ent[i].cssText == cssStr)){
8255
				idx = i; node = ent[i].nodeRef;
8256
				break;
8257
			}
8258
		}
8259
		// make sure we havent deleted our node
8260
		if(node){
8261
			var styles = doc.getElementsByTagName("style");
8262
			for(var i = 0; i < styles.length; i++){
8263
				if(styles[i] == node){
8264
					return;
8265
				}
8266
			}
8267
			// delete this entry
8268
			dojo.html._insertedCssFiles.shift(idx, 1);
8269
		}
8270
	}
8271
 
8272
	var style = dojo.html.insertCssText(cssStr, doc);
8273
	dojo.html._insertedCssFiles.push({'doc': doc, 'cssText': cssStr, 'nodeRef': style});
8274
 
8275
	// insert custom attribute ex dbgHref="../foo.css" usefull when debugging in DOM inspectors, no?
8276
	if(style && djConfig.isDebug){
8277
		style.setAttribute("dbgHref", URI);
8278
	}
8279
	return style;	//	HTMLStyleElement
8280
}
8281
 
8282
dojo.html.insertCssText = function(/* string */cssStr, /* HTMLDocument? */doc, /* string? */URI){
8283
	//	summary
8284
	//	Attempt to insert CSS rules into the document through inserting a style element
8285
	// DomNode Style  = insertCssText(String ".dojoMenu {color: green;}"[, DomDoc document, dojo.uri.Uri Url ])
8286
	if(!cssStr){
8287
		return; //	HTMLStyleElement
8288
	}
8289
	if(!doc){ doc = document; }
8290
	if(URI){// fix paths in cssStr
8291
		cssStr = dojo.html.fixPathsInCssText(cssStr, URI);
8292
	}
8293
	var style = doc.createElement("style");
8294
	style.setAttribute("type", "text/css");
8295
	// IE is b0rken enough to require that we add the element to the doc
8296
	// before changing it's properties
8297
	var head = doc.getElementsByTagName("head")[0];
8298
	if(!head){ // must have a head tag
8299
		dojo.debug("No head tag in document, aborting styles");
8300
		return;	//	HTMLStyleElement
8301
	}else{
8302
		head.appendChild(style);
8303
	}
8304
	if(style.styleSheet){// IE
8305
		var setFunc = function(){
8306
			try{
8307
				style.styleSheet.cssText = cssStr;
8308
			}catch(e){ dojo.debug(e); }
8309
		};
8310
		if(style.styleSheet.disabled){
8311
			setTimeout(setFunc, 10);
8312
		}else{
8313
			setFunc();
8314
		}
8315
	}else{ // w3c
8316
		var cssText = doc.createTextNode(cssStr);
8317
		style.appendChild(cssText);
8318
	}
8319
	return style;	//	HTMLStyleElement
8320
}
8321
 
8322
dojo.html.fixPathsInCssText = function(/* string */cssStr, /* string */URI){
8323
	//	summary
8324
	// usage: cssText comes from dojoroot/src/widget/templates/Foobar.css
8325
	// 	it has .dojoFoo { background-image: url(images/bar.png);} then uri should point to dojoroot/src/widget/templates/
8326
	if(!cssStr || !URI){ return; }
8327
	var match, str = "", url = "", urlChrs = "[\\t\\s\\w\\(\\)\\/\\.\\\\'\"-:#=&?~]+";
8328
	var regex = new RegExp('url\\(\\s*('+urlChrs+')\\s*\\)');
8329
	var regexProtocol = /(file|https?|ftps?):\/\//;
8330
	regexTrim = new RegExp("^[\\s]*(['\"]?)("+urlChrs+")\\1[\\s]*?$");
8331
	if(dojo.render.html.ie55 || dojo.render.html.ie60){
8332
		var regexIe = new RegExp("AlphaImageLoader\\((.*)src\=['\"]("+urlChrs+")['\"]");
8333
		// TODO: need to decide how to handle relative paths and AlphaImageLoader see #1441
8334
		// current implementation breaks on build with intern_strings
8335
		while(match = regexIe.exec(cssStr)){
8336
			url = match[2].replace(regexTrim, "$2");
8337
			if(!regexProtocol.exec(url)){
8338
				url = (new dojo.uri.Uri(URI, url).toString());
8339
			}
8340
			str += cssStr.substring(0, match.index) + "AlphaImageLoader(" + match[1] + "src='" + url + "'";
8341
			cssStr = cssStr.substr(match.index + match[0].length);
8342
		}
8343
		cssStr = str + cssStr;
8344
		str = "";
8345
	}
8346
 
8347
	while(match = regex.exec(cssStr)){
8348
		url = match[1].replace(regexTrim, "$2");
8349
		if(!regexProtocol.exec(url)){
8350
			url = (new dojo.uri.Uri(URI, url).toString());
8351
		}
8352
		str += cssStr.substring(0, match.index) + "url(" + url + ")";
8353
		cssStr = cssStr.substr(match.index + match[0].length);
8354
	}
8355
	return str + cssStr;	//	string
8356
}
8357
 
8358
dojo.html.setActiveStyleSheet = function(/* string */title){
8359
	//	summary
8360
	//	Activate style sheet with specified title.
8361
	var i = 0, a, els = dojo.doc().getElementsByTagName("link");
8362
	while (a = els[i++]) {
8363
		if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title")){
8364
			a.disabled = true;
8365
			if (a.getAttribute("title") == title) { a.disabled = false; }
8366
		}
8367
	}
8368
}
8369
 
8370
dojo.html.getActiveStyleSheet = function(){
8371
	//	summary
8372
	//	return the title of the currently active stylesheet
8373
	var i = 0, a, els = dojo.doc().getElementsByTagName("link");
8374
	while (a = els[i++]) {
8375
		if (a.getAttribute("rel").indexOf("style") != -1
8376
			&& a.getAttribute("title")
8377
			&& !a.disabled
8378
		){
8379
			return a.getAttribute("title");	//	string
8380
		}
8381
	}
8382
	return null;	//	string
8383
}
8384
 
8385
dojo.html.getPreferredStyleSheet = function(){
8386
	//	summary
8387
	//	Return the preferred stylesheet title (i.e. link without alt attribute)
8388
	var i = 0, a, els = dojo.doc().getElementsByTagName("link");
8389
	while (a = els[i++]) {
8390
		if(a.getAttribute("rel").indexOf("style") != -1
8391
			&& a.getAttribute("rel").indexOf("alt") == -1
8392
			&& a.getAttribute("title")
8393
		){
8394
			return a.getAttribute("title"); 	//	string
8395
		}
8396
	}
8397
	return null;	//	string
8398
}
8399
 
8400
dojo.html.applyBrowserClass = function(/* HTMLElement */node){
8401
	//	summary
8402
	//	Applies pre-set class names based on browser & version to the passed node.
8403
	//	Modified version of Morris' CSS hack.
8404
	var drh=dojo.render.html;
8405
	var classes = {
8406
		dj_ie: drh.ie,
8407
		dj_ie55: drh.ie55,
8408
		dj_ie6: drh.ie60,
8409
		dj_ie7: drh.ie70,
8410
		dj_iequirks: drh.ie && drh.quirks,
8411
		dj_opera: drh.opera,
8412
		dj_opera8: drh.opera && (Math.floor(dojo.render.version)==8),
8413
		dj_opera9: drh.opera && (Math.floor(dojo.render.version)==9),
8414
		dj_khtml: drh.khtml,
8415
		dj_safari: drh.safari,
8416
		dj_gecko: drh.mozilla
8417
	}; // no dojo unsupported browsers
8418
	for(var p in classes){
8419
		if(classes[p]){
8420
			dojo.html.addClass(node, p);
8421
		}
8422
	}
8423
};
8424
 
8425
dojo.provide("dojo.html.display");
8426
 
8427
 
8428
dojo.html._toggle = function(node, tester, setter){
8429
	node = dojo.byId(node);
8430
	setter(node, !tester(node));
8431
	return tester(node);
8432
}
8433
 
8434
dojo.html.show = function(/* HTMLElement */node){
8435
	//	summary
8436
	//	Show the passed element by reverting display property set by dojo.html.hide
8437
	node = dojo.byId(node);
8438
	if(dojo.html.getStyleProperty(node, 'display')=='none'){
8439
		dojo.html.setStyle(node, 'display', (node.dojoDisplayCache||''));
8440
		node.dojoDisplayCache = undefined;	// cannot use delete on a node in IE6
8441
	}
8442
}
8443
 
8444
dojo.html.hide = function(/* HTMLElement */node){
8445
	//	summary
8446
	//	Hide the passed element by setting display:none
8447
	node = dojo.byId(node);
8448
	if(typeof node["dojoDisplayCache"] == "undefined"){ // it could == '', so we cannot say !node.dojoDisplayCount
8449
		var d = dojo.html.getStyleProperty(node, 'display')
8450
		if(d!='none'){
8451
			node.dojoDisplayCache = d;
8452
		}
8453
	}
8454
	dojo.html.setStyle(node, 'display', 'none');
8455
}
8456
 
8457
dojo.html.setShowing = function(/* HTMLElement */node, /* boolean? */showing){
8458
	//	summary
8459
	// Calls show() if showing is true, hide() otherwise
8460
	dojo.html[(showing ? 'show' : 'hide')](node);
8461
}
8462
 
8463
dojo.html.isShowing = function(/* HTMLElement */node){
8464
	//	summary
8465
	//	Returns whether the element is displayed or not.
8466
	// FIXME: returns true if node is bad, isHidden would be easier to make correct
8467
	return (dojo.html.getStyleProperty(node, 'display') != 'none');	//	boolean
8468
}
8469
 
8470
dojo.html.toggleShowing = function(/* HTMLElement */node){
8471
	//	summary
8472
	// Call setShowing() on node with the complement of isShowing(), then return the new value of isShowing()
8473
	return dojo.html._toggle(node, dojo.html.isShowing, dojo.html.setShowing);	//	boolean
8474
}
8475
 
8476
// Simple mapping of tag names to display values
8477
// FIXME: simplistic
8478
dojo.html.displayMap = { tr: '', td: '', th: '', img: 'inline', span: 'inline', input: 'inline', button: 'inline' };
8479
 
8480
dojo.html.suggestDisplayByTagName = function(/* HTMLElement */node){
8481
	//	summary
8482
	// Suggest a value for the display property that will show 'node' based on it's tag
8483
	node = dojo.byId(node);
8484
	if(node && node.tagName){
8485
		var tag = node.tagName.toLowerCase();
8486
		return (tag in dojo.html.displayMap ? dojo.html.displayMap[tag] : 'block');	//	string
8487
	}
8488
}
8489
 
8490
dojo.html.setDisplay = function(/* HTMLElement */node, /* string */display){
8491
	//	summary
8492
	// 	Sets the value of style.display to value of 'display' parameter if it is a string.
8493
	// 	Otherwise, if 'display' is false, set style.display to 'none'.
8494
	// 	Finally, set 'display' to a suggested display value based on the node's tag
8495
	dojo.html.setStyle(node, 'display', ((display instanceof String || typeof display == "string") ? display : (display ? dojo.html.suggestDisplayByTagName(node) : 'none')));
8496
}
8497
 
8498
dojo.html.isDisplayed = function(/* HTMLElement */node){
8499
	//	summary
8500
	// 	Is true if the the computed display style for node is not 'none'
8501
	// 	FIXME: returns true if node is bad, isNotDisplayed would be easier to make correct
8502
	return (dojo.html.getComputedStyle(node, 'display') != 'none');	//	boolean
8503
}
8504
 
8505
dojo.html.toggleDisplay = function(/* HTMLElement */node){
8506
	//	summary
8507
	// 	Call setDisplay() on node with the complement of isDisplayed(), then
8508
	// 	return the new value of isDisplayed()
8509
	return dojo.html._toggle(node, dojo.html.isDisplayed, dojo.html.setDisplay);	//	boolean
8510
}
8511
 
8512
dojo.html.setVisibility = function(/* HTMLElement */node, /* string */visibility){
8513
	//	summary
8514
	// 	Sets the value of style.visibility to value of 'visibility' parameter if it is a string.
8515
	// 	Otherwise, if 'visibility' is false, set style.visibility to 'hidden'. Finally, set style.visibility to 'visible'.
8516
	dojo.html.setStyle(node, 'visibility', ((visibility instanceof String || typeof visibility == "string") ? visibility : (visibility ? 'visible' : 'hidden')));
8517
}
8518
 
8519
dojo.html.isVisible = function(/* HTMLElement */node){
8520
	//	summary
8521
	// 	Returns true if the the computed visibility style for node is not 'hidden'
8522
	// 	FIXME: returns true if node is bad, isInvisible would be easier to make correct
8523
	return (dojo.html.getComputedStyle(node, 'visibility') != 'hidden');	//	boolean
8524
}
8525
 
8526
dojo.html.toggleVisibility = function(node){
8527
	//	summary
8528
	// Call setVisibility() on node with the complement of isVisible(), then return the new value of isVisible()
8529
	return dojo.html._toggle(node, dojo.html.isVisible, dojo.html.setVisibility);	//	boolean
8530
}
8531
 
8532
dojo.html.setOpacity = function(/* HTMLElement */node, /* float */opacity, /* boolean? */dontFixOpacity){
8533
	//	summary
8534
	//	Sets the opacity of node in a cross-browser way.
8535
	//	float between 0.0 (transparent) and 1.0 (opaque)
8536
	node = dojo.byId(node);
8537
	var h = dojo.render.html;
8538
	if(!dontFixOpacity){
8539
		if( opacity >= 1.0){
8540
			if(h.ie){
8541
				dojo.html.clearOpacity(node);
8542
				return;
8543
			}else{
8544
				opacity = 0.999999;
8545
			}
8546
		}else if( opacity < 0.0){ opacity = 0; }
8547
	}
8548
	if(h.ie){
8549
		if(node.nodeName.toLowerCase() == "tr"){
8550
			// FIXME: is this too naive? will we get more than we want?
8551
			var tds = node.getElementsByTagName("td");
8552
			for(var x=0; x<tds.length; x++){
8553
				tds[x].style.filter = "Alpha(Opacity="+opacity*100+")";
8554
			}
8555
		}
8556
		node.style.filter = "Alpha(Opacity="+opacity*100+")";
8557
	}else if(h.moz){
8558
		node.style.opacity = opacity; // ffox 1.0 directly supports "opacity"
8559
		node.style.MozOpacity = opacity;
8560
	}else if(h.safari){
8561
		node.style.opacity = opacity; // 1.3 directly supports "opacity"
8562
		node.style.KhtmlOpacity = opacity;
8563
	}else{
8564
		node.style.opacity = opacity;
8565
	}
8566
}
8567
 
8568
dojo.html.clearOpacity = function(/* HTMLElement */node){
8569
	//	summary
8570
	//	Clears any opacity setting on the passed element.
8571
	node = dojo.byId(node);
8572
	var ns = node.style;
8573
	var h = dojo.render.html;
8574
	if(h.ie){
8575
		try {
8576
			if( node.filters && node.filters.alpha ){
8577
				ns.filter = ""; // FIXME: may get rid of other filter effects
8578
			}
8579
		} catch(e) {
8580
			/*
8581
			 * IE7 gives error if node.filters not set;
8582
			 * don't know why or how to workaround (other than this)
8583
			 */
8584
		}
8585
	}else if(h.moz){
8586
		ns.opacity = 1;
8587
		ns.MozOpacity = 1;
8588
	}else if(h.safari){
8589
		ns.opacity = 1;
8590
		ns.KhtmlOpacity = 1;
8591
	}else{
8592
		ns.opacity = 1;
8593
	}
8594
}
8595
 
8596
dojo.html.getOpacity = function(/* HTMLElement */node){
8597
	//	summary
8598
	//	Returns the opacity of the passed element
8599
	node = dojo.byId(node);
8600
	var h = dojo.render.html;
8601
	if(h.ie){
8602
		var opac = (node.filters && node.filters.alpha &&
8603
			typeof node.filters.alpha.opacity == "number"
8604
			? node.filters.alpha.opacity : 100) / 100;
8605
	}else{
8606
		var opac = node.style.opacity || node.style.MozOpacity ||
8607
			node.style.KhtmlOpacity || 1;
8608
	}
8609
	return opac >= 0.999999 ? 1.0 : Number(opac);	//	float
8610
}
8611
 
8612
 
8613
dojo.provide("dojo.html.color");
8614
 
8615
 
8616
 
8617
 
8618
dojo.html.getBackgroundColor = function(/* HTMLElement */node){
8619
	//	summary
8620
	//	returns the background color of the passed node as a 32-bit color (RGBA)
8621
	node = dojo.byId(node);
8622
	var color;
8623
	do{
8624
		color = dojo.html.getStyle(node, "background-color");
8625
		// Safari doesn't say "transparent"
8626
		if(color.toLowerCase() == "rgba(0, 0, 0, 0)") { color = "transparent"; }
8627
		if(node == document.getElementsByTagName("body")[0]) { node = null; break; }
8628
		node = node.parentNode;
8629
	}while(node && dojo.lang.inArray(["transparent", ""], color));
8630
	if(color == "transparent"){
8631
		color = [255, 255, 255, 0];
8632
	}else{
8633
		color = dojo.gfx.color.extractRGB(color);
8634
	}
8635
	return color;	//	array
8636
}
8637
 
8638
dojo.provide("dojo.html.layout");
8639
 
8640
 
8641
 
8642
 
8643
 
8644
dojo.html.sumAncestorProperties = function(/* HTMLElement */node, /* string */prop){
8645
	//	summary
8646
	//	Returns the sum of the passed property on all ancestors of node.
8647
	node = dojo.byId(node);
8648
	if(!node){ return 0; } // FIXME: throw an error?
8649
 
8650
	var retVal = 0;
8651
	while(node){
8652
		if(dojo.html.getComputedStyle(node, 'position') == 'fixed'){
8653
			return 0;
8654
		}
8655
		var val = node[prop];
8656
		if(val){
8657
			retVal += val - 0;
8658
			if(node==dojo.body()){ break; }// opera and khtml #body & #html has the same values, we only need one value
8659
		}
8660
		node = node.parentNode;
8661
	}
8662
	return retVal;	//	integer
8663
}
8664
 
8665
dojo.html.setStyleAttributes = function(/* HTMLElement */node, /* string */attributes) {
8666
	//	summary
8667
	//	allows a dev to pass a string similar to what you'd pass in style="", and apply it to a node.
8668
	node = dojo.byId(node);
8669
	var splittedAttribs=attributes.replace(/(;)?\s*$/, "").split(";");
8670
	for(var i=0; i<splittedAttribs.length; i++){
8671
		var nameValue=splittedAttribs[i].split(":");
8672
		var name=nameValue[0].replace(/\s*$/, "").replace(/^\s*/, "").toLowerCase();
8673
		var value=nameValue[1].replace(/\s*$/, "").replace(/^\s*/, "");
8674
		switch(name){
8675
			case "opacity":
8676
				dojo.html.setOpacity(node, value);
8677
				break;
8678
			case "content-height":
8679
				dojo.html.setContentBox(node, {height: value});
8680
				break;
8681
			case "content-width":
8682
				dojo.html.setContentBox(node, {width: value});
8683
				break;
8684
			case "outer-height":
8685
				dojo.html.setMarginBox(node, {height: value});
8686
				break;
8687
			case "outer-width":
8688
				dojo.html.setMarginBox(node, {width: value});
8689
				break;
8690
			default:
8691
				node.style[dojo.html.toCamelCase(name)]=value;
8692
		}
8693
	}
8694
}
8695
 
8696
dojo.html.boxSizing = {
8697
	MARGIN_BOX: "margin-box",
8698
	BORDER_BOX: "border-box",
8699
	PADDING_BOX: "padding-box",
8700
	CONTENT_BOX: "content-box"
8701
};
8702
 
8703
dojo.html.getAbsolutePosition = dojo.html.abs = function(/* HTMLElement */node, /* boolean? */includeScroll, /* string? */boxType){
8704
	//	summary
8705
	//	Gets the absolute position of the passed element based on the document itself.
8706
	node = dojo.byId(node, node.ownerDocument);
8707
	var ret = {
8708
		x: 0,
8709
		y: 0
8710
	};
8711
 
8712
	var bs = dojo.html.boxSizing;
8713
	if(!boxType) { boxType = bs.CONTENT_BOX; }
8714
	var nativeBoxType = 2; //BORDER box
8715
	var targetBoxType;
8716
	switch(boxType){
8717
		case bs.MARGIN_BOX:
8718
			targetBoxType = 3;
8719
			break;
8720
		case bs.BORDER_BOX:
8721
			targetBoxType = 2;
8722
			break;
8723
		case bs.PADDING_BOX:
8724
		default:
8725
			targetBoxType = 1;
8726
			break;
8727
		case bs.CONTENT_BOX:
8728
			targetBoxType = 0;
8729
			break;
8730
	}
8731
 
8732
	var h = dojo.render.html;
8733
	var db = document["body"]||document["documentElement"];
8734
 
8735
	if(h.ie){
8736
		with(node.getBoundingClientRect()){
8737
			ret.x = left-2;
8738
			ret.y = top-2;
8739
		}
8740
	}else if(document.getBoxObjectFor){
8741
		// mozilla
8742
		nativeBoxType = 1; //getBoxObjectFor return padding box coordinate
8743
		try{
8744
			var bo = document.getBoxObjectFor(node);
8745
			ret.x = bo.x - dojo.html.sumAncestorProperties(node, "scrollLeft");
8746
			ret.y = bo.y - dojo.html.sumAncestorProperties(node, "scrollTop");
8747
		}catch(e){
8748
			// squelch
8749
		}
8750
	}else{
8751
		if(node["offsetParent"]){
8752
			var endNode;
8753
			// in Safari, if the node is an absolutely positioned child of
8754
			// the body and the body has a margin the offset of the child
8755
			// and the body contain the body's margins, so we need to end
8756
			// at the body
8757
			if(	(h.safari)&&
8758
				(node.style.getPropertyValue("position") == "absolute")&&
8759
				(node.parentNode == db)){
8760
				endNode = db;
8761
			}else{
8762
				endNode = db.parentNode;
8763
			}
8764
 
8765
			//TODO: set correct nativeBoxType for safari/konqueror
8766
 
8767
			if(node.parentNode != db){
8768
				var nd = node;
8769
				if(dojo.render.html.opera){ nd = db; }
8770
				ret.x -= dojo.html.sumAncestorProperties(nd, "scrollLeft");
8771
				ret.y -= dojo.html.sumAncestorProperties(nd, "scrollTop");
8772
			}
8773
			var curnode = node;
8774
			do{
8775
				var n = curnode["offsetLeft"];
8776
				//FIXME: ugly hack to workaround the submenu in
8777
				//popupmenu2 does not shown up correctly in opera.
8778
				//Someone have a better workaround?
8779
				if(!h.opera || n>0){
8780
					ret.x += isNaN(n) ? 0 : n;
8781
				}
8782
				var m = curnode["offsetTop"];
8783
				ret.y += isNaN(m) ? 0 : m;
8784
				curnode = curnode.offsetParent;
8785
			}while((curnode != endNode)&&(curnode != null));
8786
		}else if(node["x"]&&node["y"]){
8787
			ret.x += isNaN(node.x) ? 0 : node.x;
8788
			ret.y += isNaN(node.y) ? 0 : node.y;
8789
		}
8790
	}
8791
 
8792
	// account for document scrolling!
8793
	if(includeScroll){
8794
		var scroll = dojo.html.getScroll();
8795
		ret.y += scroll.top;
8796
		ret.x += scroll.left;
8797
	}
8798
 
8799
	var extentFuncArray=[dojo.html.getPaddingExtent, dojo.html.getBorderExtent, dojo.html.getMarginExtent];
8800
	if(nativeBoxType > targetBoxType){
8801
		for(var i=targetBoxType;i<nativeBoxType;++i){
8802
			ret.y += extentFuncArray[i](node, 'top');
8803
			ret.x += extentFuncArray[i](node, 'left');
8804
		}
8805
	}else if(nativeBoxType < targetBoxType){
8806
		for(var i=targetBoxType;i>nativeBoxType;--i){
8807
			ret.y -= extentFuncArray[i-1](node, 'top');
8808
			ret.x -= extentFuncArray[i-1](node, 'left');
8809
		}
8810
	}
8811
	ret.top = ret.y;
8812
	ret.left = ret.x;
8813
	return ret;	//	object
8814
}
8815
 
8816
dojo.html.isPositionAbsolute = function(/* HTMLElement */node){
8817
	//	summary
8818
	//	Returns true if the element is absolutely positioned.
8819
	return (dojo.html.getComputedStyle(node, 'position') == 'absolute');	//	boolean
8820
}
8821
 
8822
dojo.html._sumPixelValues = function(/* HTMLElement */node, selectors, autoIsZero){
8823
	var total = 0;
8824
	for(var x=0; x<selectors.length; x++){
8825
		total += dojo.html.getPixelValue(node, selectors[x], autoIsZero);
8826
	}
8827
	return total;
8828
}
8829
 
8830
dojo.html.getMargin = function(/* HTMLElement */node){
8831
	//	summary
8832
	//	Returns the width and height of the passed node's margin
8833
	return {
8834
		width: dojo.html._sumPixelValues(node, ["margin-left", "margin-right"], (dojo.html.getComputedStyle(node, 'position') == 'absolute')),
8835
		height: dojo.html._sumPixelValues(node, ["margin-top", "margin-bottom"], (dojo.html.getComputedStyle(node, 'position') == 'absolute'))
8836
	};	//	object
8837
}
8838
 
8839
dojo.html.getBorder = function(/* HTMLElement */node){
8840
	//	summary
8841
	//	Returns the width and height of the passed node's border
8842
	return {
8843
		width: dojo.html.getBorderExtent(node, 'left') + dojo.html.getBorderExtent(node, 'right'),
8844
		height: dojo.html.getBorderExtent(node, 'top') + dojo.html.getBorderExtent(node, 'bottom')
8845
	};	//	object
8846
}
8847
 
8848
dojo.html.getBorderExtent = function(/* HTMLElement */node, /* string */side){
8849
	//	summary
8850
	//	returns the width of the requested border
8851
	return (dojo.html.getStyle(node, 'border-' + side + '-style') == 'none' ? 0 : dojo.html.getPixelValue(node, 'border-' + side + '-width'));	// integer
8852
}
8853
 
8854
dojo.html.getMarginExtent = function(/* HTMLElement */node, /* string */side){
8855
	//	summary
8856
	//	returns the width of the requested margin
8857
	return dojo.html._sumPixelValues(node, ["margin-" + side], dojo.html.isPositionAbsolute(node));	//	integer
8858
}
8859
 
8860
dojo.html.getPaddingExtent = function(/* HTMLElement */node, /* string */side){
8861
	//	summary
8862
	//	Returns the width of the requested padding
8863
	return dojo.html._sumPixelValues(node, ["padding-" + side], true);	//	integer
8864
}
8865
 
8866
dojo.html.getPadding = function(/* HTMLElement */node){
8867
	//	summary
8868
	//	Returns the width and height of the passed node's padding
8869
	return {
8870
		width: dojo.html._sumPixelValues(node, ["padding-left", "padding-right"], true),
8871
		height: dojo.html._sumPixelValues(node, ["padding-top", "padding-bottom"], true)
8872
	};	//	object
8873
}
8874
 
8875
dojo.html.getPadBorder = function(/* HTMLElement */node){
8876
	//	summary
8877
	//	Returns the width and height of the passed node's padding and border
8878
	var pad = dojo.html.getPadding(node);
8879
	var border = dojo.html.getBorder(node);
8880
	return { width: pad.width + border.width, height: pad.height + border.height };	//	object
8881
}
8882
 
8883
dojo.html.getBoxSizing = function(/* HTMLElement */node){
8884
	//	summary
8885
	//	Returns which box model the passed element is working with
8886
	var h = dojo.render.html;
8887
	var bs = dojo.html.boxSizing;
8888
	if(((h.ie)||(h.opera)) && node.nodeName.toLowerCase() != "img"){
8889
		var cm = document["compatMode"];
8890
		if((cm == "BackCompat")||(cm == "QuirksMode")){
8891
			return bs.BORDER_BOX; 	//	string
8892
		}else{
8893
			return bs.CONTENT_BOX; 	//	string
8894
		}
8895
	}else{
8896
		if(arguments.length == 0){ node = document.documentElement; }
8897
		var sizing;
8898
		if(!h.ie){
8899
			sizing = dojo.html.getStyle(node, "-moz-box-sizing");
8900
			if(!sizing){
8901
				sizing = dojo.html.getStyle(node, "box-sizing");
8902
			}
8903
		}
8904
		return (sizing ? sizing : bs.CONTENT_BOX);	//	string
8905
	}
8906
}
8907
 
8908
dojo.html.isBorderBox = function(/* HTMLElement */node){
8909
	//	summary
8910
	//	returns whether the passed element is using border box sizing or not.
8911
	return (dojo.html.getBoxSizing(node) == dojo.html.boxSizing.BORDER_BOX);	//	boolean
8912
}
8913
 
8914
dojo.html.getBorderBox = function(/* HTMLElement */node){
8915
	//	summary
8916
	//	Returns the dimensions of the passed element based on border-box sizing.
8917
	node = dojo.byId(node);
8918
	return { width: node.offsetWidth, height: node.offsetHeight };	//	object
8919
}
8920
 
8921
dojo.html.getPaddingBox = function(/* HTMLElement */node){
8922
	//	summary
8923
	//	Returns the dimensions of the padding box (see http://www.w3.org/TR/CSS21/box.html)
8924
	var box = dojo.html.getBorderBox(node);
8925
	var border = dojo.html.getBorder(node);
8926
	return {
8927
		width: box.width - border.width,
8928
		height:box.height - border.height
8929
	};	//	object
8930
}
8931
 
8932
dojo.html.getContentBox = function(/* HTMLElement */node){
8933
	//	summary
8934
	//	Returns the dimensions of the content box (see http://www.w3.org/TR/CSS21/box.html)
8935
	node = dojo.byId(node);
8936
	var padborder = dojo.html.getPadBorder(node);
8937
	return {
8938
		width: node.offsetWidth - padborder.width,
8939
		height: node.offsetHeight - padborder.height
8940
	};	//	object
8941
}
8942
 
8943
dojo.html.setContentBox = function(/* HTMLElement */node, /* object */args){
8944
	//	summary
8945
	//	Sets the dimensions of the passed node according to content sizing.
8946
	node = dojo.byId(node);
8947
	var width = 0; var height = 0;
8948
	var isbb = dojo.html.isBorderBox(node);
8949
	var padborder = (isbb ? dojo.html.getPadBorder(node) : { width: 0, height: 0});
8950
	var ret = {};
8951
	if(typeof args.width != "undefined"){
8952
		width = args.width + padborder.width;
8953
		ret.width = dojo.html.setPositivePixelValue(node, "width", width);
8954
	}
8955
	if(typeof args.height != "undefined"){
8956
		height = args.height + padborder.height;
8957
		ret.height = dojo.html.setPositivePixelValue(node, "height", height);
8958
	}
8959
	return ret;	//	object
8960
}
8961
 
8962
dojo.html.getMarginBox = function(/* HTMLElement */node){
8963
	//	summary
8964
	//	returns the dimensions of the passed node including any margins.
8965
	var borderbox = dojo.html.getBorderBox(node);
8966
	var margin = dojo.html.getMargin(node);
8967
	return { width: borderbox.width + margin.width, height: borderbox.height + margin.height };	//	object
8968
}
8969
 
8970
dojo.html.setMarginBox = function(/* HTMLElement */node, /* object */args){
8971
	//	summary
8972
	//	Sets the dimensions of the passed node using margin box calcs.
8973
	node = dojo.byId(node);
8974
	var width = 0; var height = 0;
8975
	var isbb = dojo.html.isBorderBox(node);
8976
	var padborder = (!isbb ? dojo.html.getPadBorder(node) : { width: 0, height: 0 });
8977
	var margin = dojo.html.getMargin(node);
8978
	var ret = {};
8979
	if(typeof args.width != "undefined"){
8980
		width = args.width - padborder.width;
8981
		width -= margin.width;
8982
		ret.width = dojo.html.setPositivePixelValue(node, "width", width);
8983
	}
8984
	if(typeof args.height != "undefined"){
8985
		height = args.height - padborder.height;
8986
		height -= margin.height;
8987
		ret.height = dojo.html.setPositivePixelValue(node, "height", height);
8988
	}
8989
	return ret;	//	object
8990
}
8991
 
8992
dojo.html.getElementBox = function(/* HTMLElement */node, /* string */type){
8993
	//	summary
8994
	//	return dimesions of a node based on the passed box model type.
8995
	var bs = dojo.html.boxSizing;
8996
	switch(type){
8997
		case bs.MARGIN_BOX:
8998
			return dojo.html.getMarginBox(node);	//	object
8999
		case bs.BORDER_BOX:
9000
			return dojo.html.getBorderBox(node);	//	object
9001
		case bs.PADDING_BOX:
9002
			return dojo.html.getPaddingBox(node);	//	object
9003
		case bs.CONTENT_BOX:
9004
		default:
9005
			return dojo.html.getContentBox(node);	//	object
9006
	}
9007
}
9008
// in: coordinate array [x,y,w,h] or dom node
9009
// return: coordinate object
9010
dojo.html.toCoordinateObject = dojo.html.toCoordinateArray = function(/* array */coords, /* boolean? */includeScroll, /* string? */boxtype) {
9011
	//	summary
9012
	//	Converts an array of coordinates into an object of named arguments.
9013
	if(coords instanceof Array || typeof coords == "array"){
9014
		dojo.deprecated("dojo.html.toCoordinateArray", "use dojo.html.toCoordinateObject({left: , top: , width: , height: }) instead", "0.5");
9015
		// coords is already an array (of format [x,y,w,h]), just return it
9016
		while ( coords.length < 4 ) { coords.push(0); }
9017
		while ( coords.length > 4 ) { coords.pop(); }
9018
		var ret = {
9019
			left: coords[0],
9020
			top: coords[1],
9021
			width: coords[2],
9022
			height: coords[3]
9023
		};
9024
	}else if(!coords.nodeType && !(coords instanceof String || typeof coords == "string") &&
9025
			 ('width' in coords || 'height' in coords || 'left' in coords ||
9026
			  'x' in coords || 'top' in coords || 'y' in coords)){
9027
		// coords is a coordinate object or at least part of one
9028
		var ret = {
9029
			left: coords.left||coords.x||0,
9030
			top: coords.top||coords.y||0,
9031
			width: coords.width||0,
9032
			height: coords.height||0
9033
		};
9034
	}else{
9035
		// coords is an dom object (or dom object id); return it's coordinates
9036
		var node = dojo.byId(coords);
9037
		var pos = dojo.html.abs(node, includeScroll, boxtype);
9038
		var marginbox = dojo.html.getMarginBox(node);
9039
		var ret = {
9040
			left: pos.left,
9041
			top: pos.top,
9042
			width: marginbox.width,
9043
			height: marginbox.height
9044
		};
9045
	}
9046
	ret.x = ret.left;
9047
	ret.y = ret.top;
9048
	return ret;	//	object
9049
}
9050
 
9051
dojo.html.setMarginBoxWidth = dojo.html.setOuterWidth = function(node, width){
9052
	return dojo.html._callDeprecated("setMarginBoxWidth", "setMarginBox", arguments, "width");
9053
}
9054
dojo.html.setMarginBoxHeight = dojo.html.setOuterHeight = function(){
9055
	return dojo.html._callDeprecated("setMarginBoxHeight", "setMarginBox", arguments, "height");
9056
}
9057
dojo.html.getMarginBoxWidth = dojo.html.getOuterWidth = function(){
9058
	return dojo.html._callDeprecated("getMarginBoxWidth", "getMarginBox", arguments, null, "width");
9059
}
9060
dojo.html.getMarginBoxHeight = dojo.html.getOuterHeight = function(){
9061
	return dojo.html._callDeprecated("getMarginBoxHeight", "getMarginBox", arguments, null, "height");
9062
}
9063
dojo.html.getTotalOffset = function(node, type, includeScroll){
9064
	return dojo.html._callDeprecated("getTotalOffset", "getAbsolutePosition", arguments, null, type);
9065
}
9066
dojo.html.getAbsoluteX = function(node, includeScroll){
9067
	return dojo.html._callDeprecated("getAbsoluteX", "getAbsolutePosition", arguments, null, "x");
9068
}
9069
dojo.html.getAbsoluteY = function(node, includeScroll){
9070
	return dojo.html._callDeprecated("getAbsoluteY", "getAbsolutePosition", arguments, null, "y");
9071
}
9072
dojo.html.totalOffsetLeft = function(node, includeScroll){
9073
	return dojo.html._callDeprecated("totalOffsetLeft", "getAbsolutePosition", arguments, null, "left");
9074
}
9075
dojo.html.totalOffsetTop = function(node, includeScroll){
9076
	return dojo.html._callDeprecated("totalOffsetTop", "getAbsolutePosition", arguments, null, "top");
9077
}
9078
dojo.html.getMarginWidth = function(node){
9079
	return dojo.html._callDeprecated("getMarginWidth", "getMargin", arguments, null, "width");
9080
}
9081
dojo.html.getMarginHeight = function(node){
9082
	return dojo.html._callDeprecated("getMarginHeight", "getMargin", arguments, null, "height");
9083
}
9084
dojo.html.getBorderWidth = function(node){
9085
	return dojo.html._callDeprecated("getBorderWidth", "getBorder", arguments, null, "width");
9086
}
9087
dojo.html.getBorderHeight = function(node){
9088
	return dojo.html._callDeprecated("getBorderHeight", "getBorder", arguments, null, "height");
9089
}
9090
dojo.html.getPaddingWidth = function(node){
9091
	return dojo.html._callDeprecated("getPaddingWidth", "getPadding", arguments, null, "width");
9092
}
9093
dojo.html.getPaddingHeight = function(node){
9094
	return dojo.html._callDeprecated("getPaddingHeight", "getPadding", arguments, null, "height");
9095
}
9096
dojo.html.getPadBorderWidth = function(node){
9097
	return dojo.html._callDeprecated("getPadBorderWidth", "getPadBorder", arguments, null, "width");
9098
}
9099
dojo.html.getPadBorderHeight = function(node){
9100
	return dojo.html._callDeprecated("getPadBorderHeight", "getPadBorder", arguments, null, "height");
9101
}
9102
dojo.html.getBorderBoxWidth = dojo.html.getInnerWidth = function(){
9103
	return dojo.html._callDeprecated("getBorderBoxWidth", "getBorderBox", arguments, null, "width");
9104
}
9105
dojo.html.getBorderBoxHeight = dojo.html.getInnerHeight = function(){
9106
	return dojo.html._callDeprecated("getBorderBoxHeight", "getBorderBox", arguments, null, "height");
9107
}
9108
dojo.html.getContentBoxWidth = dojo.html.getContentWidth = function(){
9109
	return dojo.html._callDeprecated("getContentBoxWidth", "getContentBox", arguments, null, "width");
9110
}
9111
dojo.html.getContentBoxHeight = dojo.html.getContentHeight = function(){
9112
	return dojo.html._callDeprecated("getContentBoxHeight", "getContentBox", arguments, null, "height");
9113
}
9114
dojo.html.setContentBoxWidth = dojo.html.setContentWidth = function(node, width){
9115
	return dojo.html._callDeprecated("setContentBoxWidth", "setContentBox", arguments, "width");
9116
}
9117
dojo.html.setContentBoxHeight = dojo.html.setContentHeight = function(node, height){
9118
	return dojo.html._callDeprecated("setContentBoxHeight", "setContentBox", arguments, "height");
9119
}
9120
 
9121
dojo.provide("dojo.lfx.html");
9122
 
9123
 
9124
 
9125
 
9126
 
9127
 
9128
 
9129
 
9130
dojo.lfx.html._byId = function(nodes){
9131
	if(!nodes){ return []; }
9132
	if(dojo.lang.isArrayLike(nodes)){
9133
		if(!nodes.alreadyChecked){
9134
			var n = [];
9135
			dojo.lang.forEach(nodes, function(node){
9136
				n.push(dojo.byId(node));
9137
			});
9138
			n.alreadyChecked = true;
9139
			return n;
9140
		}else{
9141
			return nodes;
9142
		}
9143
	}else{
9144
		var n = [];
9145
		n.push(dojo.byId(nodes));
9146
		n.alreadyChecked = true;
9147
		return n;
9148
	}
9149
}
9150
 
9151
dojo.lfx.html.propertyAnimation = function(	/*DOMNode[]*/ nodes,
9152
											/*Object[]*/ propertyMap,
9153
											/*int*/ duration,
9154
											/*function*/ easing,
9155
											/*Object*/ handlers){
9156
	// summary: Returns an animation that will transition the properties of "nodes"
9157
	//			depending how they are defined in "propertyMap".
9158
	// nodes: An array of DOMNodes or one DOMNode.
9159
	// propertyMap: { property: String, start: Decimal?, end: Decimal?, units: String? }
9160
	//				An array of objects defining properties to change.
9161
	// duration: Duration of the animation in milliseconds.
9162
	// easing: An easing function.
9163
	// handlers: { handler: Function?, onstart: Function?, onstop: Function?, onanimate: Function? }
9164
	nodes = dojo.lfx.html._byId(nodes);
9165
 
9166
	var targs = {
9167
		"propertyMap": propertyMap,
9168
		"nodes": nodes,
9169
		"duration": duration,
9170
		"easing": easing||dojo.lfx.easeDefault
9171
	};
9172
 
9173
	var setEmUp = function(args){
9174
		if(args.nodes.length==1){
9175
			// FIXME: we're only supporting start-value filling when one node is
9176
			// passed
9177
 
9178
			var pm = args.propertyMap;
9179
			if(!dojo.lang.isArray(args.propertyMap)){
9180
				// it's stupid to have to pack an array with a set of objects
9181
				// when you can just pass in an object list
9182
				var parr = [];
9183
				for(var pname in pm){
9184
					pm[pname].property = pname;
9185
					parr.push(pm[pname]);
9186
				}
9187
				pm = args.propertyMap = parr;
9188
			}
9189
			dojo.lang.forEach(pm, function(prop){
9190
				if(dj_undef("start", prop)){
9191
					if(prop.property != "opacity"){
9192
						prop.start = parseInt(dojo.html.getComputedStyle(args.nodes[0], prop.property));
9193
					}else{
9194
						prop.start = dojo.html.getOpacity(args.nodes[0]);
9195
					}
9196
				}
9197
			});
9198
		}
9199
	}
9200
 
9201
	var coordsAsInts = function(coords){
9202
		var cints = [];
9203
		dojo.lang.forEach(coords, function(c){
9204
			cints.push(Math.round(c));
9205
		});
9206
		return cints;
9207
	}
9208
 
9209
	var setStyle = function(n, style){
9210
		n = dojo.byId(n);
9211
		if(!n || !n.style){ return; }
9212
		for(var s in style){
9213
			try{
9214
				if(s == "opacity"){
9215
					dojo.html.setOpacity(n, style[s]);
9216
				}else{
9217
						n.style[s] = style[s];
9218
				}
9219
			}catch(e){ dojo.debug(e); }
9220
		}
9221
	}
9222
 
9223
	var propLine = function(properties){
9224
		this._properties = properties;
9225
		this.diffs = new Array(properties.length);
9226
		dojo.lang.forEach(properties, function(prop, i){
9227
			// calculate the end - start to optimize a bit
9228
			if(dojo.lang.isFunction(prop.start)){
9229
				prop.start = prop.start(prop, i);
9230
			}
9231
			if(dojo.lang.isFunction(prop.end)){
9232
				prop.end = prop.end(prop, i);
9233
			}
9234
			if(dojo.lang.isArray(prop.start)){
9235
				// don't loop through the arrays
9236
				this.diffs[i] = null;
9237
			}else if(prop.start instanceof dojo.gfx.color.Color){
9238
				// save these so we don't have to call toRgb() every getValue() call
9239
				prop.startRgb = prop.start.toRgb();
9240
				prop.endRgb = prop.end.toRgb();
9241
			}else{
9242
				this.diffs[i] = prop.end - prop.start;
9243
			}
9244
		}, this);
9245
 
9246
		this.getValue = function(n){
9247
			var ret = {};
9248
			dojo.lang.forEach(this._properties, function(prop, i){
9249
				var value = null;
9250
				if(dojo.lang.isArray(prop.start)){
9251
					// FIXME: what to do here?
9252
				}else if(prop.start instanceof dojo.gfx.color.Color){
9253
					value = (prop.units||"rgb") + "(";
9254
					for(var j = 0 ; j < prop.startRgb.length ; j++){
9255
						value += Math.round(((prop.endRgb[j] - prop.startRgb[j]) * n) + prop.startRgb[j]) + (j < prop.startRgb.length - 1 ? "," : "");
9256
					}
9257
					value += ")";
9258
				}else{
9259
					value = ((this.diffs[i]) * n) + prop.start + (prop.property != "opacity" ? prop.units||"px" : "");
9260
				}
9261
				ret[dojo.html.toCamelCase(prop.property)] = value;
9262
			}, this);
9263
			return ret;
9264
		}
9265
	}
9266
 
9267
	var anim = new dojo.lfx.Animation({
9268
			beforeBegin: function(){
9269
				setEmUp(targs);
9270
				anim.curve = new propLine(targs.propertyMap);
9271
			},
9272
			onAnimate: function(propValues){
9273
				dojo.lang.forEach(targs.nodes, function(node){
9274
					setStyle(node, propValues);
9275
				});
9276
			}
9277
		},
9278
		targs.duration,
9279
		null,
9280
		targs.easing
9281
	);
9282
	if(handlers){
9283
		for(var x in handlers){
9284
			if(dojo.lang.isFunction(handlers[x])){
9285
				anim.connect(x, anim, handlers[x]);
9286
			}
9287
		}
9288
	}
9289
 
9290
	return anim; // dojo.lfx.Animation
9291
}
9292
 
9293
dojo.lfx.html._makeFadeable = function(nodes){
9294
	var makeFade = function(node){
9295
		if(dojo.render.html.ie){
9296
			// only set the zoom if the "tickle" value would be the same as the
9297
			// default
9298
			if( (node.style.zoom.length == 0) &&
9299
				(dojo.html.getStyle(node, "zoom") == "normal") ){
9300
				// make sure the node "hasLayout"
9301
				// NOTE: this has been tested with larger and smaller user-set text
9302
				// sizes and works fine
9303
				node.style.zoom = "1";
9304
				// node.style.zoom = "normal";
9305
			}
9306
			// don't set the width to auto if it didn't already cascade that way.
9307
			// We don't want to f anyones designs
9308
			if(	(node.style.width.length == 0) &&
9309
				(dojo.html.getStyle(node, "width") == "auto") ){
9310
				node.style.width = "auto";
9311
			}
9312
		}
9313
	}
9314
	if(dojo.lang.isArrayLike(nodes)){
9315
		dojo.lang.forEach(nodes, makeFade);
9316
	}else{
9317
		makeFade(nodes);
9318
	}
9319
}
9320
 
9321
dojo.lfx.html.fade = function(/*DOMNode[]*/ nodes,
9322
							  /*Object*/values,
9323
							  /*int?*/ duration,
9324
							  /*Function?*/ easing,
9325
							  /*Function?*/ callback){
9326
	// summary:Returns an animation that will fade the "nodes" from the start to end values passed.
9327
	// nodes: An array of DOMNodes or one DOMNode.
9328
	// values: { start: Decimal?, end: Decimal? }
9329
	// duration: Duration of the animation in milliseconds.
9330
	// easing: An easing function.
9331
	// callback: Function to run at the end of the animation.
9332
	nodes = dojo.lfx.html._byId(nodes);
9333
	var props = { property: "opacity" };
9334
	if(!dj_undef("start", values)){
9335
		props.start = values.start;
9336
	}else{
9337
		props.start = function(){ return dojo.html.getOpacity(nodes[0]); };
9338
	}
9339
 
9340
	if(!dj_undef("end", values)){
9341
		props.end = values.end;
9342
	}else{
9343
		dojo.raise("dojo.lfx.html.fade needs an end value");
9344
	}
9345
 
9346
	var anim = dojo.lfx.propertyAnimation(nodes, [ props ], duration, easing);
9347
	anim.connect("beforeBegin", function(){
9348
		dojo.lfx.html._makeFadeable(nodes);
9349
	});
9350
	if(callback){
9351
		anim.connect("onEnd", function(){ callback(nodes, anim); });
9352
	}
9353
 
9354
	return anim; // dojo.lfx.Animation
9355
}
9356
 
9357
dojo.lfx.html.fadeIn = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
9358
	// summary: Returns an animation that will fade "nodes" from its current opacity to fully opaque.
9359
	// nodes: An array of DOMNodes or one DOMNode.
9360
	// duration: Duration of the animation in milliseconds.
9361
	// easing: An easing function.
9362
	// callback: Function to run at the end of the animation.
9363
	return dojo.lfx.html.fade(nodes, { end: 1 }, duration, easing, callback); // dojo.lfx.Animation
9364
}
9365
 
9366
dojo.lfx.html.fadeOut = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
9367
	// summary: Returns an animation that will fade "nodes" from its current opacity to fully transparent.
9368
	// nodes: An array of DOMNodes or one DOMNode.
9369
	// duration: Duration of the animation in milliseconds.
9370
	// easing: An easing function.
9371
	// callback: Function to run at the end of the animation.
9372
	return dojo.lfx.html.fade(nodes, { end: 0 }, duration, easing, callback); // dojo.lfx.Animation
9373
}
9374
 
9375
dojo.lfx.html.fadeShow = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
9376
	// summary: Returns an animation that will fade "nodes" from transparent to opaque and shows
9377
	//			"nodes" at the end if it is hidden.
9378
	// nodes: An array of DOMNodes or one DOMNode.
9379
	// duration: Duration of the animation in milliseconds.
9380
	// easing: An easing function.
9381
	// callback: Function to run at the end of the animation.
9382
	nodes=dojo.lfx.html._byId(nodes);
9383
	dojo.lang.forEach(nodes, function(node){
9384
		dojo.html.setOpacity(node, 0.0);
9385
	});
9386
 
9387
	var anim = dojo.lfx.html.fadeIn(nodes, duration, easing, callback);
9388
	anim.connect("beforeBegin", function(){
9389
		if(dojo.lang.isArrayLike(nodes)){
9390
			dojo.lang.forEach(nodes, dojo.html.show);
9391
		}else{
9392
			dojo.html.show(nodes);
9393
		}
9394
	});
9395
 
9396
	return anim; // dojo.lfx.Animation
9397
}
9398
 
9399
dojo.lfx.html.fadeHide = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
9400
	// summary: Returns an animation that will fade "nodes" from its current opacity to opaque and hides
9401
	//			"nodes" at the end.
9402
	// nodes: An array of DOMNodes or one DOMNode.
9403
	// duration: Duration of the animation in milliseconds.
9404
	// easing: An easing function.
9405
	// callback: Function to run at the end of the animation.
9406
	var anim = dojo.lfx.html.fadeOut(nodes, duration, easing, function(){
9407
		if(dojo.lang.isArrayLike(nodes)){
9408
			dojo.lang.forEach(nodes, dojo.html.hide);
9409
		}else{
9410
			dojo.html.hide(nodes);
9411
		}
9412
		if(callback){ callback(nodes, anim); }
9413
	});
9414
 
9415
	return anim; // dojo.lfx.Animation
9416
}
9417
 
9418
dojo.lfx.html.wipeIn = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
9419
	// summary: Returns an animation that will show and wipe in "nodes".
9420
	// nodes: An array of DOMNodes or one DOMNode.
9421
	// duration: Duration of the animation in milliseconds.
9422
	// easing: An easing function.
9423
	// callback: Function to run at the end of the animation.
9424
	nodes = dojo.lfx.html._byId(nodes);
9425
	var anims = [];
9426
 
9427
	dojo.lang.forEach(nodes, function(node){
9428
		var oprop = {  };	// old properties of node (before we mucked w/them)
9429
 
9430
		// get node height, either it's natural height or it's height specified via style or class attributes
9431
		// (for FF, the node has to be (temporarily) rendered to measure height)
9432
		// TODO: should this offscreen code be part of dojo.html, so that getBorderBox() works on hidden nodes?
9433
		var origTop, origLeft, origPosition;
9434
		with(node.style){
9435
			origTop=top; origLeft=left; origPosition=position;
9436
			top="-9999px"; left="-9999px"; position="absolute";
9437
			display="";
9438
		}
9439
		var nodeHeight = dojo.html.getBorderBox(node).height;
9440
		with(node.style){
9441
			top=origTop; left=origLeft; position=origPosition;
9442
			display="none";
9443
		}
9444
 
9445
		var anim = dojo.lfx.propertyAnimation(node,
9446
			{	"height": {
9447
					start: 1, // 0 causes IE to display the whole panel
9448
					end: function(){ return nodeHeight; }
9449
				}
9450
			},
9451
			duration,
9452
			easing);
9453
 
9454
		anim.connect("beforeBegin", function(){
9455
			oprop.overflow = node.style.overflow;
9456
			oprop.height = node.style.height;
9457
			with(node.style){
9458
				overflow = "hidden";
9459
				height = "1px"; // 0 causes IE to display the whole panel
9460
			}
9461
			dojo.html.show(node);
9462
		});
9463
 
9464
		anim.connect("onEnd", function(){
9465
			with(node.style){
9466
				overflow = oprop.overflow;
9467
				height = oprop.height;
9468
			}
9469
			if(callback){ callback(node, anim); }
9470
		});
9471
		anims.push(anim);
9472
	});
9473
 
9474
	return dojo.lfx.combine(anims); // dojo.lfx.Combine
9475
}
9476
 
9477
dojo.lfx.html.wipeOut = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
9478
	// summary: Returns an animation that will wipe out and hide "nodes".
9479
	// nodes: An array of DOMNodes or one DOMNode.
9480
	// duration: Duration of the animation in milliseconds.
9481
	// easing: An easing function.
9482
	// callback: Function to run at the end of the animation.
9483
	nodes = dojo.lfx.html._byId(nodes);
9484
	var anims = [];
9485
 
9486
	dojo.lang.forEach(nodes, function(node){
9487
		var oprop = {  };	// old properties of node (before we mucked w/them)
9488
		var anim = dojo.lfx.propertyAnimation(node,
9489
			{	"height": {
9490
					start: function(){ return dojo.html.getContentBox(node).height; },
9491
					end: 1 // 0 causes IE to display the whole panel
9492
				}
9493
			},
9494
			duration,
9495
			easing,
9496
			{
9497
				"beforeBegin": function(){
9498
					oprop.overflow = node.style.overflow;
9499
					oprop.height = node.style.height;
9500
					with(node.style){
9501
						overflow = "hidden";
9502
					}
9503
					dojo.html.show(node);
9504
				},
9505
 
9506
				"onEnd": function(){
9507
					dojo.html.hide(node);
9508
					with(node.style){
9509
						overflow = oprop.overflow;
9510
						height = oprop.height;
9511
					}
9512
					if(callback){ callback(node, anim); }
9513
				}
9514
			}
9515
		);
9516
		anims.push(anim);
9517
	});
9518
 
9519
	return dojo.lfx.combine(anims); // dojo.lfx.Combine
9520
}
9521
 
9522
dojo.lfx.html.slideTo = function(/*DOMNode*/ nodes,
9523
								 /*Object*/ coords,
9524
								 /*int?*/ duration,
9525
								 /*Function?*/ easing,
9526
								 /*Function?*/ callback){
9527
	// summary: Returns an animation that will slide "nodes" from its current position to
9528
	//			the position defined in "coords".
9529
	// nodes: An array of DOMNodes or one DOMNode.
9530
	// coords: { top: Decimal?, left: Decimal? }
9531
	// duration: Duration of the animation in milliseconds.
9532
	// easing: An easing function.
9533
	// callback: Function to run at the end of the animation.
9534
	nodes = dojo.lfx.html._byId(nodes);
9535
	var anims = [];
9536
	var compute = dojo.html.getComputedStyle;
9537
 
9538
	if(dojo.lang.isArray(coords)){
9539
		/* coords: Array
9540
		   pId: a */
9541
		dojo.deprecated('dojo.lfx.html.slideTo(node, array)', 'use dojo.lfx.html.slideTo(node, {top: value, left: value});', '0.5');
9542
		coords = { top: coords[0], left: coords[1] };
9543
	}
9544
	dojo.lang.forEach(nodes, function(node){
9545
		var top = null;
9546
		var left = null;
9547
 
9548
		var init = (function(){
9549
			var innerNode = node;
9550
			return function(){
9551
				var pos = compute(innerNode, 'position');
9552
				top = (pos == 'absolute' ? node.offsetTop : parseInt(compute(node, 'top')) || 0);
9553
				left = (pos == 'absolute' ? node.offsetLeft : parseInt(compute(node, 'left')) || 0);
9554
 
9555
				if (!dojo.lang.inArray(['absolute', 'relative'], pos)) {
9556
					var ret = dojo.html.abs(innerNode, true);
9557
					dojo.html.setStyleAttributes(innerNode, "position:absolute;top:"+ret.y+"px;left:"+ret.x+"px;");
9558
					top = ret.y;
9559
					left = ret.x;
9560
				}
9561
			}
9562
		})();
9563
		init();
9564
 
9565
		var anim = dojo.lfx.propertyAnimation(node,
9566
			{	"top": { start: top, end: (coords.top||0) },
9567
				"left": { start: left, end: (coords.left||0)  }
9568
			},
9569
			duration,
9570
			easing,
9571
			{ "beforeBegin": init }
9572
		);
9573
 
9574
		if(callback){
9575
			anim.connect("onEnd", function(){ callback(nodes, anim); });
9576
		}
9577
 
9578
		anims.push(anim);
9579
	});
9580
 
9581
	return dojo.lfx.combine(anims); // dojo.lfx.Combine
9582
}
9583
 
9584
dojo.lfx.html.slideBy = function(/*DOMNode*/ nodes, /*Object*/ coords, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
9585
	// summary: Returns an animation that will slide "nodes" from its current position
9586
	//			to its current position plus the numbers defined in "coords".
9587
	// nodes: An array of DOMNodes or one DOMNode.
9588
	// coords: { top: Decimal?, left: Decimal? }
9589
	// duration: Duration of the animation in milliseconds.
9590
	// easing: An easing function.
9591
	// callback: Function to run at the end of the animation.
9592
	nodes = dojo.lfx.html._byId(nodes);
9593
	var anims = [];
9594
	var compute = dojo.html.getComputedStyle;
9595
 
9596
	if(dojo.lang.isArray(coords)){
9597
		/* coords: Array
9598
		   pId: a */
9599
		dojo.deprecated('dojo.lfx.html.slideBy(node, array)', 'use dojo.lfx.html.slideBy(node, {top: value, left: value});', '0.5');
9600
		coords = { top: coords[0], left: coords[1] };
9601
	}
9602
 
9603
	dojo.lang.forEach(nodes, function(node){
9604
		var top = null;
9605
		var left = null;
9606
 
9607
		var init = (function(){
9608
			var innerNode = node;
9609
			return function(){
9610
				var pos = compute(innerNode, 'position');
9611
				top = (pos == 'absolute' ? node.offsetTop : parseInt(compute(node, 'top')) || 0);
9612
				left = (pos == 'absolute' ? node.offsetLeft : parseInt(compute(node, 'left')) || 0);
9613
 
9614
				if (!dojo.lang.inArray(['absolute', 'relative'], pos)) {
9615
					var ret = dojo.html.abs(innerNode, true);
9616
					dojo.html.setStyleAttributes(innerNode, "position:absolute;top:"+ret.y+"px;left:"+ret.x+"px;");
9617
					top = ret.y;
9618
					left = ret.x;
9619
				}
9620
			}
9621
		})();
9622
		init();
9623
 
9624
		var anim = dojo.lfx.propertyAnimation(node,
9625
			{
9626
				"top": { start: top, end: top+(coords.top||0) },
9627
				"left": { start: left, end: left+(coords.left||0) }
9628
			},
9629
			duration,
9630
			easing).connect("beforeBegin", init);
9631
 
9632
		if(callback){
9633
			anim.connect("onEnd", function(){ callback(nodes, anim); });
9634
		}
9635
 
9636
		anims.push(anim);
9637
	});
9638
 
9639
	return dojo.lfx.combine(anims); // dojo.lfx.Combine
9640
}
9641
 
9642
dojo.lfx.html.explode = function(/*DOMNode*/ start,
9643
								 /*DOMNode*/ endNode,
9644
								 /*int?*/ duration,
9645
								 /*Function?*/ easing,
9646
								 /*Function?*/ callback){
9647
	// summary: Returns an animation that will
9648
	// start:
9649
	// endNode:
9650
	// duration: Duration of the animation in milliseconds.
9651
	// easing: An easing function.
9652
	// callback: Function to run at the end of the animation.
9653
	var h = dojo.html;
9654
	start = dojo.byId(start);
9655
	endNode = dojo.byId(endNode);
9656
	var startCoords = h.toCoordinateObject(start, true);
9657
	var outline = document.createElement("div");
9658
	h.copyStyle(outline, endNode);
9659
	if(endNode.explodeClassName){ outline.className = endNode.explodeClassName; }
9660
	with(outline.style){
9661
		position = "absolute";
9662
		display = "none";
9663
		// border = "1px solid black";
9664
		var backgroundStyle = h.getStyle(start, "background-color");
9665
		backgroundColor = backgroundStyle ? backgroundStyle.toLowerCase() : "transparent";
9666
		backgroundColor = (backgroundColor == "transparent") ? "rgb(221, 221, 221)" : backgroundColor;
9667
	}
9668
	dojo.body().appendChild(outline);
9669
 
9670
	with(endNode.style){
9671
		visibility = "hidden";
9672
		display = "block";
9673
	}
9674
	var endCoords = h.toCoordinateObject(endNode, true);
9675
	with(endNode.style){
9676
		display = "none";
9677
		visibility = "visible";
9678
	}
9679
 
9680
	var props = { opacity: { start: 0.5, end: 1.0 } };
9681
	dojo.lang.forEach(["height", "width", "top", "left"], function(type){
9682
		props[type] = { start: startCoords[type], end: endCoords[type] }
9683
	});
9684
 
9685
	var anim = new dojo.lfx.propertyAnimation(outline,
9686
		props,
9687
		duration,
9688
		easing,
9689
		{
9690
			"beforeBegin": function(){
9691
				h.setDisplay(outline, "block");
9692
			},
9693
			"onEnd": function(){
9694
				h.setDisplay(endNode, "block");
9695
				outline.parentNode.removeChild(outline);
9696
			}
9697
		}
9698
	);
9699
 
9700
	if(callback){
9701
		anim.connect("onEnd", function(){ callback(endNode, anim); });
9702
	}
9703
	return anim; // dojo.lfx.Animation
9704
}
9705
 
9706
dojo.lfx.html.implode = function(/*DOMNode*/ startNode,
9707
								 /*DOMNode*/ end,
9708
								 /*int?*/ duration,
9709
								 /*Function?*/ easing,
9710
								 /*Function?*/ callback){
9711
	// summary: Returns an animation that will
9712
	// startNode:
9713
	// end:
9714
	// duration: Duration of the animation in milliseconds.
9715
	// easing: An easing function.
9716
	// callback: Function to run at the end of the animation.
9717
	var h = dojo.html;
9718
	startNode = dojo.byId(startNode);
9719
	end = dojo.byId(end);
9720
	var startCoords = dojo.html.toCoordinateObject(startNode, true);
9721
	var endCoords = dojo.html.toCoordinateObject(end, true);
9722
 
9723
	var outline = document.createElement("div");
9724
	dojo.html.copyStyle(outline, startNode);
9725
	if (startNode.explodeClassName) { outline.className = startNode.explodeClassName; }
9726
	dojo.html.setOpacity(outline, 0.3);
9727
	with(outline.style){
9728
		position = "absolute";
9729
		display = "none";
9730
		backgroundColor = h.getStyle(startNode, "background-color").toLowerCase();
9731
	}
9732
	dojo.body().appendChild(outline);
9733
 
9734
	var props = { opacity: { start: 1.0, end: 0.5 } };
9735
	dojo.lang.forEach(["height", "width", "top", "left"], function(type){
9736
		props[type] = { start: startCoords[type], end: endCoords[type] }
9737
	});
9738
 
9739
	var anim = new dojo.lfx.propertyAnimation(outline,
9740
		props,
9741
		duration,
9742
		easing,
9743
		{
9744
			"beforeBegin": function(){
9745
				dojo.html.hide(startNode);
9746
				dojo.html.show(outline);
9747
			},
9748
			"onEnd": function(){
9749
				outline.parentNode.removeChild(outline);
9750
			}
9751
		}
9752
	);
9753
 
9754
	if(callback){
9755
		anim.connect("onEnd", function(){ callback(startNode, anim); });
9756
	}
9757
	return anim; // dojo.lfx.Animation
9758
}
9759
 
9760
dojo.lfx.html.highlight = function(/*DOMNode[]*/ nodes,
9761
								   /*dojo.gfx.color.Color*/ startColor,
9762
								   /*int?*/ duration,
9763
								   /*Function?*/ easing,
9764
								   /*Function?*/ callback){
9765
	// summary: Returns an animation that will set the background color
9766
	//			of "nodes" to startColor and transition it to "nodes"
9767
	//			original color.
9768
	// startColor: Color to transition from.
9769
	// duration: Duration of the animation in milliseconds.
9770
	// easing: An easing function.
9771
	// callback: Function to run at the end of the animation.
9772
	nodes = dojo.lfx.html._byId(nodes);
9773
	var anims = [];
9774
 
9775
	dojo.lang.forEach(nodes, function(node){
9776
		var color = dojo.html.getBackgroundColor(node);
9777
		var bg = dojo.html.getStyle(node, "background-color").toLowerCase();
9778
		var bgImage = dojo.html.getStyle(node, "background-image");
9779
		var wasTransparent = (bg == "transparent" || bg == "rgba(0, 0, 0, 0)");
9780
		while(color.length > 3) { color.pop(); }
9781
 
9782
		var rgb = new dojo.gfx.color.Color(startColor);
9783
		var endRgb = new dojo.gfx.color.Color(color);
9784
 
9785
		var anim = dojo.lfx.propertyAnimation(node,
9786
			{ "background-color": { start: rgb, end: endRgb } },
9787
			duration,
9788
			easing,
9789
			{
9790
				"beforeBegin": function(){
9791
					if(bgImage){
9792
						node.style.backgroundImage = "none";
9793
					}
9794
					node.style.backgroundColor = "rgb(" + rgb.toRgb().join(",") + ")";
9795
				},
9796
				"onEnd": function(){
9797
					if(bgImage){
9798
						node.style.backgroundImage = bgImage;
9799
					}
9800
					if(wasTransparent){
9801
						node.style.backgroundColor = "transparent";
9802
					}
9803
					if(callback){
9804
						callback(node, anim);
9805
					}
9806
				}
9807
			}
9808
		);
9809
 
9810
		anims.push(anim);
9811
	});
9812
	return dojo.lfx.combine(anims); // dojo.lfx.Combine
9813
}
9814
 
9815
dojo.lfx.html.unhighlight = function(/*DOMNode[]*/ nodes,
9816
									 /*dojo.gfx.color.Color*/ endColor,
9817
									 /*int?*/ duration,
9818
									 /*Function?*/ easing,
9819
									 /*Function?*/ callback){
9820
	// summary: Returns an animation that will transition "nodes" background color
9821
	//			from its current color to "endColor".
9822
	// endColor: Color to transition to.
9823
	// duration: Duration of the animation in milliseconds.
9824
	// easing: An easing function.
9825
	// callback: Function to run at the end of the animation.
9826
	nodes = dojo.lfx.html._byId(nodes);
9827
	var anims = [];
9828
 
9829
	dojo.lang.forEach(nodes, function(node){
9830
		var color = new dojo.gfx.color.Color(dojo.html.getBackgroundColor(node));
9831
		var rgb = new dojo.gfx.color.Color(endColor);
9832
 
9833
		var bgImage = dojo.html.getStyle(node, "background-image");
9834
 
9835
		var anim = dojo.lfx.propertyAnimation(node,
9836
			{ "background-color": { start: color, end: rgb } },
9837
			duration,
9838
			easing,
9839
			{
9840
				"beforeBegin": function(){
9841
					if(bgImage){
9842
						node.style.backgroundImage = "none";
9843
					}
9844
					node.style.backgroundColor = "rgb(" + color.toRgb().join(",") + ")";
9845
				},
9846
				"onEnd": function(){
9847
					if(callback){
9848
						callback(node, anim);
9849
					}
9850
				}
9851
			}
9852
		);
9853
		anims.push(anim);
9854
	});
9855
	return dojo.lfx.combine(anims); // dojo.lfx.Combine
9856
}
9857
 
9858
dojo.lang.mixin(dojo.lfx, dojo.lfx.html);
9859
 
9860
dojo.kwCompoundRequire({
9861
	browser: ["dojo.lfx.html"],
9862
	dashboard: ["dojo.lfx.html"]
9863
});
9864
dojo.provide("dojo.lfx.*");
9865