Subversion Repositories Applications.papyrus

Rev

Rev 1371 | 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.
102
	major: 0, minor: 4, patch: 2, flag: "",
103
	revision: Number("$Rev: 7616 $".match(/[0-9]+/)[0]),
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
 
1393
	//	BEGIN DOMContentLoaded, from Dean Edwards (http://dean.edwards.name/weblog/2006/06/again/)
1394
	function dj_load_init(e){
1395
		// allow multiple calls, only first one will take effect
1396
		// A bug in khtml calls events callbacks for document for event which isnt supported
1397
		// for example a created contextmenu event calls DOMContentLoaded, workaround
1398
		var type = (e && e.type) ? e.type.toLowerCase() : "load";
1399
		if(arguments.callee.initialized || (type!="domcontentloaded" && type!="load")){ return; }
1400
		arguments.callee.initialized = true;
1401
		if(typeof(_timer) != 'undefined'){
1402
			clearInterval(_timer);
1403
			delete _timer;
1404
		}
1405
 
1406
		var initFunc = function(){
1407
			//perform initialization
1408
			if(dojo.render.html.ie){
1409
				dojo.hostenv.makeWidgets();
1410
			}
1411
		};
1412
 
1413
		if(dojo.hostenv.inFlightCount == 0){
1414
			initFunc();
1415
			dojo.hostenv.modulesLoaded();
1416
		}else{
1417
			//This else case should be xdomain loading.
1418
			//Make sure this is the first thing in the load listener array.
1419
			//Part of the dojo.addOnLoad guarantee is that when the listeners are notified,
1420
			//It means the DOM (or page) has loaded and that widgets have been parsed.
1421
			dojo.hostenv.modulesLoadedListeners.unshift(initFunc);
1422
		}
1423
	}
1424
 
1425
	//	START DOMContentLoaded
1426
	// Mozilla and Opera 9 expose the event we could use
1427
	if(document.addEventListener){
1428
		// NOTE:
1429
		//		due to a threading issue in Firefox 2.0, we can't enable
1430
		//		DOMContentLoaded on that platform. For more information, see:
1431
		//		http://trac.dojotoolkit.org/ticket/1704
1432
		if(dojo.render.html.opera || (dojo.render.html.moz && (djConfig["enableMozDomContentLoaded"] === true))){
1433
			document.addEventListener("DOMContentLoaded", dj_load_init, null);
1434
		}
1435
 
1436
		//	mainly for Opera 8.5, won't be fired if DOMContentLoaded fired already.
1437
		//  also used for Mozilla because of trac #1640
1438
		window.addEventListener("load", dj_load_init, null);
1439
	}
1440
 
1441
	// 	for Internet Explorer. readyState will not be achieved on init call, but dojo doesn't need it
1442
	//	however, we'll include it because we don't know if there are other functions added that might.
1443
	//	Note that this has changed because the build process strips all comments--including conditional
1444
	//		ones.
1445
	if(dojo.render.html.ie && dojo.render.os.win){
1446
		document.attachEvent("onreadystatechange", function(e){
1447
			if(document.readyState == "complete"){
1448
				dj_load_init();
1449
			}
1450
		});
1451
	}
1452
 
1453
	if (/(WebKit|khtml)/i.test(navigator.userAgent)) { // sniff
1454
		var _timer = setInterval(function() {
1455
			if (/loaded|complete/.test(document.readyState)) {
1456
				dj_load_init(); // call the onload handler
1457
			}
1458
		}, 10);
1459
	}
1460
	//	END DOMContentLoaded
1461
 
1462
	// IE WebControl hosted in an application can fire "beforeunload" and "unload"
1463
	// events when control visibility changes, causing Dojo to unload too soon. The
1464
	// following code fixes the problem
1465
	// Reference: http://support.microsoft.com/default.aspx?scid=kb;en-us;199155
1466
	if(dojo.render.html.ie){
1467
		dj_addNodeEvtHdlr(window, "beforeunload", function(){
1468
			dojo.hostenv._unloading = true;
1469
			window.setTimeout(function() {
1470
				dojo.hostenv._unloading = false;
1471
			}, 0);
1472
		});
1473
	}
1474
 
1475
	dj_addNodeEvtHdlr(window, "unload", function(){
1476
		dojo.hostenv.unloaded();
1477
		if((!dojo.render.html.ie)||(dojo.render.html.ie && dojo.hostenv._unloading)){
1478
			dojo.hostenv.unloaded();
1479
		}
1480
	});
1481
 
1482
	dojo.hostenv.makeWidgets = function(){
1483
		// you can put searchIds in djConfig and dojo.hostenv at the moment
1484
		// we should probably eventually move to one or the other
1485
		var sids = [];
1486
		if(djConfig.searchIds && djConfig.searchIds.length > 0) {
1487
			sids = sids.concat(djConfig.searchIds);
1488
		}
1489
		if(dojo.hostenv.searchIds && dojo.hostenv.searchIds.length > 0) {
1490
			sids = sids.concat(dojo.hostenv.searchIds);
1491
		}
1492
 
1493
		if((djConfig.parseWidgets)||(sids.length > 0)){
1494
			if(dojo.evalObjPath("dojo.widget.Parse")){
1495
				// we must do this on a delay to avoid:
1496
				//	http://www.shaftek.org/blog/archives/000212.html
1497
				// (IE bug)
1498
					var parser = new dojo.xml.Parse();
1499
					if(sids.length > 0){
1500
						for(var x=0; x<sids.length; x++){
1501
							var tmpNode = document.getElementById(sids[x]);
1502
							if(!tmpNode){ continue; }
1503
							var frag = parser.parseElement(tmpNode, null, true);
1504
							dojo.widget.getParser().createComponents(frag);
1505
						}
1506
					}else if(djConfig.parseWidgets){
1507
						var frag  = parser.parseElement(dojo.body(), null, true);
1508
						dojo.widget.getParser().createComponents(frag);
1509
					}
1510
			}
1511
		}
1512
	}
1513
 
1514
	dojo.addOnLoad(function(){
1515
		if(!dojo.render.html.ie) {
1516
			dojo.hostenv.makeWidgets();
1517
		}
1518
	});
1519
 
1520
	try{
1521
		if(dojo.render.html.ie){
1522
			document.namespaces.add("v","urn:schemas-microsoft-com:vml");
1523
			document.createStyleSheet().addRule("v\\:*", "behavior:url(#default#VML)");
1524
		}
1525
	}catch(e){ }
1526
 
1527
	// stub, over-ridden by debugging code. This will at least keep us from
1528
	// breaking when it's not included
1529
	dojo.hostenv.writeIncludes = function(){}
1530
 
1531
	//TODOC:  HOW TO DOC THIS?
1532
	// @global: dj_currentDocument
1533
	// summary:
1534
	//		Current document object. 'dj_currentDocument' can be modified for temporary context shifting.
1535
	// description:
1536
	//    dojo.doc() returns dojo.currentDocument.
1537
	//		Refer to dojo.doc() rather than referring to 'window.document' to ensure your
1538
	//		code runs correctly in managed contexts.
1539
	if(!dj_undef("document", this)){
1540
		dj_currentDocument = this.document;
1541
	}
1542
 
1543
	dojo.doc = function(){
1544
		// summary:
1545
		//		return the document object associated with the dojo.global()
1546
		return dj_currentDocument;
1547
	}
1548
 
1549
	dojo.body = function(){
1550
		// summary:
1551
		//		return the body object associated with dojo.doc()
1552
		// Note: document.body is not defined for a strict xhtml document
1553
		return dojo.doc().body || dojo.doc().getElementsByTagName("body")[0];
1554
	}
1555
 
1556
	dojo.byId = function(/*String*/id, /*DocumentElement*/doc){
1557
		// summary:
1558
		// 		similar to other library's "$" function, takes a string
1559
		// 		representing a DOM id or a DomNode and returns the
1560
		// 		corresponding DomNode. If a Node is passed, this function is a
1561
		// 		no-op. Returns a single DOM node or null, working around
1562
		// 		several browser-specific bugs to do so.
1563
		// id: DOM id or DOM Node
1564
		// doc:
1565
		//		optional, defaults to the current value of dj_currentDocument.
1566
		//		Can be used to retreive node references from other documents.
1567
		if((id)&&((typeof id == "string")||(id instanceof String))){
1568
			if(!doc){ doc = dj_currentDocument; }
1569
			var ele = doc.getElementById(id);
1570
			// workaround bug in IE and Opera 8.2 where getElementById returns wrong element
1571
			if(ele && (ele.id != id) && doc.all){
1572
				ele = null;
1573
				// get all matching elements with this id
1574
				eles = doc.all[id];
1575
				if(eles){
1576
					// if more than 1, choose first with the correct id
1577
					if(eles.length){
1578
						for(var i=0; i<eles.length; i++){
1579
							if(eles[i].id == id){
1580
								ele = eles[i];
1581
								break;
1582
							}
1583
						}
1584
					// return 1 and only element
1585
					}else{
1586
						ele = eles;
1587
					}
1588
				}
1589
			}
1590
			return ele; // DomNode
1591
		}
1592
		return id; // DomNode
1593
	}
1594
 
1595
	dojo.setContext = function(/*Object*/globalObject, /*DocumentElement*/globalDocument){
1596
		// summary:
1597
		//		changes the behavior of many core Dojo functions that deal with
1598
		//		namespace and DOM lookup, changing them to work in a new global
1599
		//		context. The varibles dj_currentContext and dj_currentDocument
1600
		//		are modified as a result of calling this function.
1601
		dj_currentContext = globalObject;
1602
		dj_currentDocument = globalDocument;
1603
	};
1604
 
1605
	dojo._fireCallback = function(callback, context, cbArguments){
1606
		if((context)&&((typeof callback == "string")||(callback instanceof String))){
1607
			callback=context[callback];
1608
		}
1609
		return (context ? callback.apply(context, cbArguments || [ ]) : callback());
1610
	}
1611
 
1612
	dojo.withGlobal = function(/*Object*/globalObject, /*Function*/callback, /*Object?*/thisObject, /*Array?*/cbArguments){
1613
		// summary:
1614
		//		Call callback with globalObject as dojo.global() and globalObject.document
1615
		//		as dojo.doc(). If provided, globalObject will be executed in the context of
1616
		//		object thisObject
1617
		// description:
1618
		//		When callback() returns or throws an error, the dojo.global() and dojo.doc() will
1619
		//		be restored to its previous state.
1620
		var rval;
1621
		var oldGlob = dj_currentContext;
1622
		var oldDoc = dj_currentDocument;
1623
		try{
1624
			dojo.setContext(globalObject, globalObject.document);
1625
			rval = dojo._fireCallback(callback, thisObject, cbArguments);
1626
		}finally{
1627
			dojo.setContext(oldGlob, oldDoc);
1628
		}
1629
		return rval;
1630
	}
1631
 
1632
	dojo.withDoc = function (/*Object*/documentObject, /*Function*/callback, /*Object?*/thisObject, /*Array?*/cbArguments) {
1633
		// summary:
1634
		//		Call callback with documentObject as dojo.doc(). If provided, callback will be executed
1635
		//		in the context of object thisObject
1636
		// description:
1637
		//		When callback() returns or throws an error, the dojo.doc() will
1638
		//		be restored to its previous state.
1639
		var rval;
1640
		var oldDoc = dj_currentDocument;
1641
		try{
1642
			dj_currentDocument = documentObject;
1643
			rval = dojo._fireCallback(callback, thisObject, cbArguments);
1644
		}finally{
1645
			dj_currentDocument = oldDoc;
1646
		}
1647
		return rval;
1648
	}
1649
 
1650
} //if (typeof window != 'undefined')
1651
 
1652
//Load debug code if necessary.
1653
dojo.requireIf((djConfig["isDebug"] || djConfig["debugAtAllCosts"]), "dojo.debug");
1654
 
1655
//window.widget is for Dashboard detection
1656
//The full conditionals are spelled out to avoid issues during builds.
1657
//Builds may be looking for require/requireIf statements and processing them.
1658
dojo.requireIf(djConfig["debugAtAllCosts"] && !window.widget && !djConfig["useXDomain"], "dojo.browser_debug");
1659
dojo.requireIf(djConfig["debugAtAllCosts"] && !window.widget && djConfig["useXDomain"], "dojo.browser_debug_xd");
1660
 
1661
dojo.provide("dojo.string.common");
1662
 
1663
dojo.string.trim = function(/* string */str, /* integer? */wh){
1664
	//	summary
1665
	//	Trim whitespace from str.  If wh > 0, trim from start, if wh < 0, trim from end, else both
1666
	if(!str.replace){ return str; }
1667
	if(!str.length){ return str; }
1668
	var re = (wh > 0) ? (/^\s+/) : (wh < 0) ? (/\s+$/) : (/^\s+|\s+$/g);
1669
	return str.replace(re, "");	//	string
1670
}
1671
 
1672
dojo.string.trimStart = function(/* string */str) {
1673
	//	summary
1674
	//	Trim whitespace at the beginning of 'str'
1675
	return dojo.string.trim(str, 1);	//	string
1676
}
1677
 
1678
dojo.string.trimEnd = function(/* string */str) {
1679
	//	summary
1680
	//	Trim whitespace at the end of 'str'
1681
	return dojo.string.trim(str, -1);
1682
}
1683
 
1684
dojo.string.repeat = function(/* string */str, /* integer */count, /* string? */separator) {
1685
	//	summary
1686
	//	Return 'str' repeated 'count' times, optionally placing 'separator' between each rep
1687
	var out = "";
1688
	for(var i = 0; i < count; i++) {
1689
		out += str;
1690
		if(separator && i < count - 1) {
1691
			out += separator;
1692
		}
1693
	}
1694
	return out;	//	string
1695
}
1696
 
1697
dojo.string.pad = function(/* string */str, /* integer */len/*=2*/, /* string */ c/*='0'*/, /* integer */dir/*=1*/) {
1698
	//	summary
1699
	//	Pad 'str' to guarantee that it is at least 'len' length with the character 'c' at either the
1700
	//	start (dir=1) or end (dir=-1) of the string
1701
	var out = String(str);
1702
	if(!c) {
1703
		c = '0';
1704
	}
1705
	if(!dir) {
1706
		dir = 1;
1707
	}
1708
	while(out.length < len) {
1709
		if(dir > 0) {
1710
			out = c + out;
1711
		} else {
1712
			out += c;
1713
		}
1714
	}
1715
	return out;	//	string
1716
}
1717
 
1718
dojo.string.padLeft = function(/* string */str, /* integer */len, /* string */c) {
1719
	//	summary
1720
	//	same as dojo.string.pad(str, len, c, 1)
1721
	return dojo.string.pad(str, len, c, 1);	//	string
1722
}
1723
 
1724
dojo.string.padRight = function(/* string */str, /* integer */len, /* string */c) {
1725
	//	summary
1726
	//	same as dojo.string.pad(str, len, c, -1)
1727
	return dojo.string.pad(str, len, c, -1);	//	string
1728
}
1729
 
1730
dojo.provide("dojo.string");
1731
 
1732
 
1733
dojo.provide("dojo.lang.common");
1734
 
1735
dojo.lang.inherits = function(/*Function*/subclass, /*Function*/superclass){
1736
	// summary: Set up inheritance between two classes.
1737
	if(!dojo.lang.isFunction(superclass)){
1738
		dojo.raise("dojo.inherits: superclass argument ["+superclass+"] must be a function (subclass: ["+subclass+"']");
1739
	}
1740
	subclass.prototype = new superclass();
1741
	subclass.prototype.constructor = subclass;
1742
	subclass.superclass = superclass.prototype;
1743
	// DEPRECATED: super is a reserved word, use 'superclass'
1744
	subclass['super'] = superclass.prototype;
1745
}
1746
 
1747
dojo.lang._mixin = function(/*Object*/ obj, /*Object*/ props){
1748
	// summary:
1749
	//		Adds all properties and methods of props to obj. This addition is
1750
	//		"prototype extension safe", so that instances of objects will not
1751
	//		pass along prototype defaults.
1752
	var tobj = {};
1753
	for(var x in props){
1754
		// the "tobj" condition avoid copying properties in "props"
1755
		// inherited from Object.prototype.  For example, if obj has a custom
1756
		// toString() method, don't overwrite it with the toString() method
1757
		// that props inherited from Object.protoype
1758
		if((typeof tobj[x] == "undefined") || (tobj[x] != props[x])){
1759
			obj[x] = props[x];
1760
		}
1761
	}
1762
	// IE doesn't recognize custom toStrings in for..in
1763
	if(dojo.render.html.ie
1764
		&& (typeof(props["toString"]) == "function")
1765
		&& (props["toString"] != obj["toString"])
1766
		&& (props["toString"] != tobj["toString"]))
1767
	{
1768
		obj.toString = props.toString;
1769
	}
1770
	return obj; // Object
1771
}
1772
 
1773
dojo.lang.mixin = function(/*Object*/obj, /*Object...*/props){
1774
	// summary:	Adds all properties and methods of props to obj.
1775
	for(var i=1, l=arguments.length; i<l; i++){
1776
		dojo.lang._mixin(obj, arguments[i]);
1777
	}
1778
	return obj; // Object
1779
}
1780
 
1781
dojo.lang.extend = function(/*Object*/ constructor, /*Object...*/ props){
1782
	// summary:
1783
	//		Adds all properties and methods of props to constructor's
1784
	//		prototype, making them available to all instances created with
1785
	//		constructor.
1786
	for(var i=1, l=arguments.length; i<l; i++){
1787
		dojo.lang._mixin(constructor.prototype, arguments[i]);
1788
	}
1789
	return constructor; // Object
1790
}
1791
 
1792
// Promote to dojo module
1793
dojo.inherits = dojo.lang.inherits;
1794
//dojo.lang._mixin = dojo.lang._mixin;
1795
dojo.mixin = dojo.lang.mixin;
1796
dojo.extend = dojo.lang.extend;
1797
 
1798
dojo.lang.find = function(	/*Array*/		array,
1799
							/*Object*/		value,
1800
							/*Boolean?*/	identity,
1801
							/*Boolean?*/	findLast){
1802
	// summary:
1803
	//		Return the index of value in array, returning -1 if not found.
1804
	// array: just what you think
1805
	// value: the value to locate
1806
	// identity:
1807
	//		If true, matches with identity comparison (===). If false, uses
1808
	//		normal comparison (==).
1809
	// findLast:
1810
	//		If true, returns index of last instance of value.
1811
	// examples:
1812
	//		find(array, value[, identity [findLast]]) // recommended
1813
 	//		find(value, array[, identity [findLast]]) // deprecated
1814
 
1815
	// support both (array, value) and (value, array)
1816
	if(!dojo.lang.isArrayLike(array) && dojo.lang.isArrayLike(value)) {
1817
		dojo.deprecated('dojo.lang.find(value, array)', 'use dojo.lang.find(array, value) instead', "0.5");
1818
		var temp = array;
1819
		array = value;
1820
		value = temp;
1821
	}
1822
	var isString = dojo.lang.isString(array);
1823
	if(isString) { array = array.split(""); }
1824
 
1825
	if(findLast) {
1826
		var step = -1;
1827
		var i = array.length - 1;
1828
		var end = -1;
1829
	} else {
1830
		var step = 1;
1831
		var i = 0;
1832
		var end = array.length;
1833
	}
1834
	if(identity){
1835
		while(i != end) {
1836
			if(array[i] === value){ return i; }
1837
			i += step;
1838
		}
1839
	}else{
1840
		while(i != end) {
1841
			if(array[i] == value){ return i; }
1842
			i += step;
1843
		}
1844
	}
1845
	return -1;	// number
1846
}
1847
 
1848
dojo.lang.indexOf = dojo.lang.find;
1849
 
1850
dojo.lang.findLast = function(/*Array*/array, /*Object*/value, /*boolean?*/identity){
1851
	// summary:
1852
	//		Return index of last occurance of value in array, returning -1 if
1853
	//		not found. This is a shortcut for dojo.lang.find() with a true
1854
	//		value for its "findLast" parameter.
1855
	// identity:
1856
	//		If true, matches with identity comparison (===). If false, uses
1857
	//		normal comparison (==).
1858
	return dojo.lang.find(array, value, identity, true); // number
1859
}
1860
 
1861
dojo.lang.lastIndexOf = dojo.lang.findLast;
1862
 
1863
dojo.lang.inArray = function(array /*Array*/, value /*Object*/){
1864
	// summary:	Return true if value is present in array.
1865
	return dojo.lang.find(array, value) > -1; // boolean
1866
}
1867
 
1868
/**
1869
 * Partial implmentation of is* functions from
1870
 * http://www.crockford.com/javascript/recommend.html
1871
 * NOTE: some of these may not be the best thing to use in all situations
1872
 * as they aren't part of core JS and therefore can't work in every case.
1873
 * See WARNING messages inline for tips.
1874
 *
1875
 * The following is* functions are fairly "safe"
1876
 */
1877
 
1878
dojo.lang.isObject = function(/*anything*/ it){
1879
	// summary:	Return true if it is an Object, Array or Function.
1880
	if(typeof it == "undefined"){ return false; }
1881
	return (typeof it == "object" || it === null || dojo.lang.isArray(it) || dojo.lang.isFunction(it)); // Boolean
1882
}
1883
 
1884
dojo.lang.isArray = function(/*anything*/ it){
1885
	// summary:	Return true if it is an Array.
1886
	return (it && it instanceof Array || typeof it == "array"); // Boolean
1887
}
1888
 
1889
dojo.lang.isArrayLike = function(/*anything*/ it){
1890
	// summary:
1891
	//		Return true if it can be used as an array (i.e. is an object with
1892
	//		an integer length property).
1893
	if((!it)||(dojo.lang.isUndefined(it))){ return false; }
1894
	if(dojo.lang.isString(it)){ return false; }
1895
	if(dojo.lang.isFunction(it)){ return false; } // keeps out built-in constructors (Number, String, ...) which have length properties
1896
	if(dojo.lang.isArray(it)){ return true; }
1897
	// form node itself is ArrayLike, but not always iterable. Use form.elements instead.
1898
	if((it.tagName)&&(it.tagName.toLowerCase()=='form')){ return false; }
1899
	if(dojo.lang.isNumber(it.length) && isFinite(it.length)){ return true; }
1900
	return false; // Boolean
1901
}
1902
 
1903
dojo.lang.isFunction = function(/*anything*/ it){
1904
	// summary:	Return true if it is a Function.
1905
	return (it instanceof Function || typeof it == "function"); // Boolean
1906
};
1907
 
1908
(function(){
1909
	// webkit treats NodeList as a function, which is bad
1910
	if((dojo.render.html.capable)&&(dojo.render.html["safari"])){
1911
		dojo.lang.isFunction = function(/*anything*/ it){
1912
			if((typeof(it) == "function") && (it == "[object NodeList]")) { return false; }
1913
			return (it instanceof Function || typeof it == "function"); // Boolean
1914
		}
1915
	}
1916
})();
1917
 
1918
dojo.lang.isString = function(/*anything*/ it){
1919
	// summary:	Return true if it is a String.
1920
	return (typeof it == "string" || it instanceof String);
1921
}
1922
 
1923
dojo.lang.isAlien = function(/*anything*/ it){
1924
	// summary: Return true if it is not a built-in function. False if not.
1925
	if(!it){ return false; }
1926
	return !dojo.lang.isFunction(it) && /\{\s*\[native code\]\s*\}/.test(String(it)); // Boolean
1927
}
1928
 
1929
dojo.lang.isBoolean = function(/*anything*/ it){
1930
	// summary:	Return true if it is a Boolean.
1931
	return (it instanceof Boolean || typeof it == "boolean"); // Boolean
1932
}
1933
 
1934
/**
1935
 * The following is***() functions are somewhat "unsafe". Fortunately,
1936
 * there are workarounds the the language provides and are mentioned
1937
 * in the WARNING messages.
1938
 *
1939
 */
1940
dojo.lang.isNumber = function(/*anything*/ it){
1941
	// summary:	Return true if it is a number.
1942
	// description:
1943
	//		WARNING - In most cases, isNaN(it) is sufficient to determine whether or not
1944
	// 		something is a number or can be used as such. For example, a number or string
1945
	// 		can be used interchangably when accessing array items (array["1"] is the same as
1946
	// 		array[1]) and isNaN will return false for both values ("1" and 1). However,
1947
	// 		isNumber("1")  will return false, which is generally not too useful.
1948
	// 		Also, isNumber(NaN) returns true, again, this isn't generally useful, but there
1949
	// 		are corner cases (like when you want to make sure that two things are really
1950
	// 		the same type of thing). That is really where isNumber "shines".
1951
	//
1952
	// Recommendation - Use isNaN(it) when possible
1953
 
1954
	return (it instanceof Number || typeof it == "number"); // Boolean
1955
}
1956
 
1957
/*
1958
 * FIXME: Should isUndefined go away since it is error prone?
1959
 */
1960
dojo.lang.isUndefined = function(/*anything*/ it){
1961
	// summary: Return true if it is not defined.
1962
	// description:
1963
	//		WARNING - In some cases, isUndefined will not behave as you
1964
	// 		might expect. If you do isUndefined(foo) and there is no earlier
1965
	// 		reference to foo, an error will be thrown before isUndefined is
1966
	// 		called. It behaves correctly if you scope yor object first, i.e.
1967
	// 		isUndefined(foo.bar) where foo is an object and bar isn't a
1968
	// 		property of the object.
1969
	//
1970
	// Recommendation - Use typeof foo == "undefined" when possible
1971
 
1972
	return ((typeof(it) == "undefined")&&(it == undefined)); // Boolean
1973
}
1974
 
1975
// end Crockford functions
1976
 
1977
dojo.provide("dojo.lang.extras");
1978
 
1979
 
1980
 
1981
dojo.lang.setTimeout = function(/*Function*/func, /*int*/delay /*, ...*/){
1982
	// summary:
1983
	//		Sets a timeout in milliseconds to execute a function in a given
1984
	//		context with optional arguments.
1985
	// usage:
1986
	//		dojo.lang.setTimeout(Object context, function func, number delay[, arg1[, ...]]);
1987
	//		dojo.lang.setTimeout(function func, number delay[, arg1[, ...]]);
1988
 
1989
	var context = window, argsStart = 2;
1990
	if(!dojo.lang.isFunction(func)){
1991
		context = func;
1992
		func = delay;
1993
		delay = arguments[2];
1994
		argsStart++;
1995
	}
1996
 
1997
	if(dojo.lang.isString(func)){
1998
		func = context[func];
1999
	}
2000
 
2001
	var args = [];
2002
	for (var i = argsStart; i < arguments.length; i++){
2003
		args.push(arguments[i]);
2004
	}
2005
	return dojo.global().setTimeout(function(){ func.apply(context, args); }, delay); // int
2006
}
2007
 
2008
dojo.lang.clearTimeout = function(/*int*/timer){
2009
	// summary: clears timer by number from the execution queue
2010
 
2011
	// FIXME:
2012
	//		why do we have this function? It's not portable outside of browser
2013
	//		environments and it's a stupid wrapper on something that browsers
2014
	//		provide anyway.
2015
	dojo.global().clearTimeout(timer);
2016
}
2017
 
2018
dojo.lang.getNameInObj = function(/*Object*/ns, /*unknown*/item){
2019
	// summary:
2020
	//		looks for a value in the object ns with a value matching item and
2021
	//		returns the property name
2022
	// ns: if null, dj_global is used
2023
	// item: value to return a name for
2024
	if(!ns){ ns = dj_global; }
2025
 
2026
	for(var x in ns){
2027
		if(ns[x] === item){
2028
			return new String(x); // String
2029
		}
2030
	}
2031
	return null; // null
2032
}
2033
 
2034
dojo.lang.shallowCopy = function(/*Object*/obj, /*Boolean?*/deep){
2035
	// summary:
2036
	//		copies object obj one level deep, or full depth if deep is true
2037
	var i, ret;
2038
 
2039
	if(obj === null){ /*obj: null*/ return null; } // null
2040
 
2041
	if(dojo.lang.isObject(obj)){
2042
		// obj: Object
2043
		ret = new obj.constructor();
2044
		for(i in obj){
2045
			if(dojo.lang.isUndefined(ret[i])){
2046
				ret[i] = deep ? dojo.lang.shallowCopy(obj[i], deep) : obj[i];
2047
			}
2048
		}
2049
	}else if(dojo.lang.isArray(obj)){
2050
		// obj: Array
2051
		ret = [];
2052
		for(i=0; i<obj.length; i++){
2053
			ret[i] = deep ? dojo.lang.shallowCopy(obj[i], deep) : obj[i];
2054
		}
2055
	}else{
2056
		// obj: Object
2057
		ret = obj;
2058
	}
2059
 
2060
	return ret; // Object
2061
}
2062
 
2063
dojo.lang.firstValued = function(/* ... */){
2064
	// summary: Return the first argument that isn't undefined
2065
 
2066
	for(var i = 0; i < arguments.length; i++){
2067
		if(typeof arguments[i] != "undefined"){
2068
			return arguments[i]; // Object
2069
		}
2070
	}
2071
	return undefined; // undefined
2072
}
2073
 
2074
dojo.lang.getObjPathValue = function(/*String*/objpath, /*Object?*/context, /*Boolean?*/create){
2075
	// summary:
2076
	//		Gets a value from a reference specified as a string descriptor,
2077
	//		(e.g. "A.B") in the given context.
2078
	// context: if not specified, dj_global is used
2079
	// create: if true, undefined objects in the path are created.
2080
	with(dojo.parseObjPath(objpath, context, create)){
2081
		return dojo.evalProp(prop, obj, create); // Object
2082
	}
2083
}
2084
 
2085
dojo.lang.setObjPathValue = function(/*String*/objpath, /*anything*/value, /*Object?*/context, /*Boolean?*/create){
2086
	// summary:
2087
	//		Sets a value on a reference specified as a string descriptor.
2088
	//		(e.g. "A.B") in the given context. This is similar to straight
2089
	//		assignment, except that the object structure in question can
2090
	//		optionally be created if it does not exist.
2091
	//	context: if not specified, dj_global is used
2092
	//	create: if true, undefined objects in the path are created.
2093
 
2094
	// FIXME: why is this function valuable? It should be scheduled for
2095
	// removal on the grounds that dojo.parseObjPath does most of it's work and
2096
	// is more straightforward and has fewer dependencies. Also, the order of
2097
	// arguments is bone-headed. "context" should clearly come after "create".
2098
	// *sigh*
2099
	dojo.deprecated("dojo.lang.setObjPathValue", "use dojo.parseObjPath and the '=' operator", "0.6");
2100
 
2101
	if(arguments.length < 4){
2102
		create = true;
2103
	}
2104
	with(dojo.parseObjPath(objpath, context, create)){
2105
		if(obj && (create || (prop in obj))){
2106
			obj[prop] = value;
2107
		}
2108
	}
2109
}
2110
 
2111
dojo.provide("dojo.io.common");
2112
 
2113
 
2114
 
2115
/******************************************************************************
2116
 *	Notes about dojo.io design:
2117
 *
2118
 *	The dojo.io.* package has the unenviable task of making a lot of different
2119
 *	types of I/O feel natural, despite a universal lack of good (or even
2120
 *	reasonable!) I/O capability in the host environment. So lets pin this down
2121
 *	a little bit further.
2122
 *
2123
 *	Rhino:
2124
 *		perhaps the best situation anywhere. Access to Java classes allows you
2125
 *		to do anything one might want in terms of I/O, both synchronously and
2126
 *		async. Can open TCP sockets and perform low-latency client/server
2127
 *		interactions. HTTP transport is available through Java HTTP client and
2128
 *		server classes. Wish it were always this easy.
2129
 *
2130
 *	xpcshell:
2131
 *		XPCOM for I/O.
2132
 *
2133
 *	spidermonkey:
2134
 *		S.O.L.
2135
 *
2136
 *	Browsers:
2137
 *		Browsers generally do not provide any useable filesystem access. We are
2138
 *		therefore limited to HTTP for moving information to and from Dojo
2139
 *		instances living in a browser.
2140
 *
2141
 *		XMLHTTP:
2142
 *			Sync or async, allows reading of arbitrary text files (including
2143
 *			JS, which can then be eval()'d), writing requires server
2144
 *			cooperation and is limited to HTTP mechanisms (POST and GET).
2145
 *
2146
 *		<iframe> hacks:
2147
 *			iframe document hacks allow browsers to communicate asynchronously
2148
 *			with a server via HTTP POST and GET operations. With significant
2149
 *			effort and server cooperation, low-latency data transit between
2150
 *			client and server can be acheived via iframe mechanisms (repubsub).
2151
 *
2152
 *		SVG:
2153
 *			Adobe's SVG viewer implements helpful primitives for XML-based
2154
 *			requests, but receipt of arbitrary text data seems unlikely w/o
2155
 *			<![CDATA[]]> sections.
2156
 *
2157
 *
2158
 *	A discussion between Dylan, Mark, Tom, and Alex helped to lay down a lot
2159
 *	the IO API interface. A transcript of it can be found at:
2160
 *		http://dojotoolkit.org/viewcvs/viewcvs.py/documents/irc/irc_io_api_log.txt?rev=307&view=auto
2161
 *
2162
 *	Also referenced in the design of the API was the DOM 3 L&S spec:
2163
 *		http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/load-save.html
2164
 ******************************************************************************/
2165
 
2166
// a map of the available transport options. Transports should add themselves
2167
// by calling add(name)
2168
dojo.io.transports = [];
2169
dojo.io.hdlrFuncNames = [ "load", "error", "timeout" ]; // we're omitting a progress() event for now
2170
 
2171
dojo.io.Request = function(/*String*/ url, /*String*/ mimetype, /*String*/ transport, /*String or Boolean*/ changeUrl){
2172
// summary:
2173
//		Constructs a Request object that is used by dojo.io.bind().
2174
// description:
2175
//		dojo.io.bind() will create one of these for you if
2176
//		you call dojo.io.bind() with an plain object containing the bind parameters.
2177
//		This method can either take the arguments specified, or an Object containing all of the parameters that you
2178
//		want to use to create the dojo.io.Request (similar to how dojo.io.bind() is called.
2179
//		The named parameters to this constructor represent the minimum set of parameters need
2180
	if((arguments.length == 1)&&(arguments[0].constructor == Object)){
2181
		this.fromKwArgs(arguments[0]);
2182
	}else{
2183
		this.url = url;
2184
		if(mimetype){ this.mimetype = mimetype; }
2185
		if(transport){ this.transport = transport; }
2186
		if(arguments.length >= 4){ this.changeUrl = changeUrl; }
2187
	}
2188
}
2189
 
2190
dojo.lang.extend(dojo.io.Request, {
2191
 
2192
	/** The URL to hit */
2193
	url: "",
2194
 
2195
	/** The mime type used to interrpret the response body */
2196
	mimetype: "text/plain",
2197
 
2198
	/** The HTTP method to use */
2199
	method: "GET",
2200
 
2201
	/** An Object containing key-value pairs to be included with the request */
2202
	content: undefined, // Object
2203
 
2204
	/** The transport medium to use */
2205
	transport: undefined, // String
2206
 
2207
	/** If defined the URL of the page is physically changed */
2208
	changeUrl: undefined, // String
2209
 
2210
	/** A form node to use in the request */
2211
	formNode: undefined, // HTMLFormElement
2212
 
2213
	/** Whether the request should be made synchronously */
2214
	sync: false,
2215
 
2216
	bindSuccess: false,
2217
 
2218
	/** Cache/look for the request in the cache before attempting to request?
2219
	 *  NOTE: this isn't a browser cache, this is internal and would only cache in-page
2220
	 */
2221
	useCache: false,
2222
 
2223
	/** Prevent the browser from caching this by adding a query string argument to the URL */
2224
	preventCache: false,
2225
 
2226
	// events stuff
2227
	load: function(/*String*/type, /*Object*/data, /*Object*/transportImplementation, /*Object*/kwArgs){
2228
		// summary:
2229
		//		Called on successful completion of a bind.
2230
		//		type: String
2231
		//				A string with value "load"
2232
		//		data: Object
2233
		//				The object representing the result of the bind. The actual structure
2234
		//				of the data object will depend on the mimetype that was given to bind
2235
		//				in the bind arguments.
2236
		//		transportImplementation: Object
2237
		//				The object that implements a particular transport. Structure is depedent
2238
		//				on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
2239
		//				XMLHttpRequest object from the browser.
2240
		//		kwArgs: Object
2241
		//				Object that contains the request parameters that were given to the
2242
		//				bind call. Useful for storing and retrieving state from when bind
2243
		//				was called.
2244
	},
2245
	error: function(/*String*/type, /*Object*/error, /*Object*/transportImplementation, /*Object*/kwArgs){
2246
		// summary:
2247
		//		Called when there is an error with a bind.
2248
		//		type: String
2249
		//				A string with value "error"
2250
		//		error: Object
2251
		//				The error object. Should be a dojo.io.Error object, but not guaranteed.
2252
		//		transportImplementation: Object
2253
		//				The object that implements a particular transport. Structure is depedent
2254
		//				on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
2255
		//				XMLHttpRequest object from the browser.
2256
		//		kwArgs: Object
2257
		//				Object that contains the request parameters that were given to the
2258
		//				bind call. Useful for storing and retrieving state from when bind
2259
		//				was called.
2260
	},
2261
	timeout: function(/*String*/type, /*Object*/empty, /*Object*/transportImplementation, /*Object*/kwArgs){
2262
		// summary:
2263
		//		Called when there is an error with a bind. Only implemented in certain transports at this time.
2264
		//		type: String
2265
		//				A string with value "timeout"
2266
		//		empty: Object
2267
		//				Should be null. Just a spacer argument so that load, error, timeout and handle have the
2268
		//				same signatures.
2269
		//		transportImplementation: Object
2270
		//				The object that implements a particular transport. Structure is depedent
2271
		//				on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
2272
		//				XMLHttpRequest object from the browser. May be null for the timeout case for
2273
		//				some transports.
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
	handle: function(/*String*/type, /*Object*/data, /*Object*/transportImplementation, /*Object*/kwArgs){
2280
		// summary:
2281
		//		The handle method can be defined instead of defining separate load, error and timeout
2282
		//		callbacks.
2283
		//		type: String
2284
		//				A string with the type of callback: "load", "error", or "timeout".
2285
		//		data: Object
2286
		//				See the above callbacks for what this parameter could be.
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.
2291
		//		kwArgs: Object
2292
		//				Object that contains the request parameters that were given to the
2293
		//				bind call. Useful for storing and retrieving state from when bind
2294
		//				was called.
2295
	},
2296
 
2297
	//FIXME: change IframeIO.js to use timeouts?
2298
	// The number of seconds to wait until firing a timeout callback.
2299
	// If it is zero, that means, don't do a timeout check.
2300
	timeoutSeconds: 0,
2301
 
2302
	// the abort method needs to be filled in by the transport that accepts the
2303
	// bind() request
2304
	abort: function(){ },
2305
 
2306
	// backButton: function(){ },
2307
	// forwardButton: function(){ },
2308
 
2309
	fromKwArgs: function(/*Object*/ kwArgs){
2310
		// summary:
2311
		//		Creates a dojo.io.Request from a simple object (kwArgs object).
2312
 
2313
		// normalize args
2314
		if(kwArgs["url"]){ kwArgs.url = kwArgs.url.toString(); }
2315
		if(kwArgs["formNode"]) { kwArgs.formNode = dojo.byId(kwArgs.formNode); }
2316
		if(!kwArgs["method"] && kwArgs["formNode"] && kwArgs["formNode"].method) {
2317
			kwArgs.method = kwArgs["formNode"].method;
2318
		}
2319
 
2320
		// backwards compatibility
2321
		if(!kwArgs["handle"] && kwArgs["handler"]){ kwArgs.handle = kwArgs.handler; }
2322
		if(!kwArgs["load"] && kwArgs["loaded"]){ kwArgs.load = kwArgs.loaded; }
2323
		if(!kwArgs["changeUrl"] && kwArgs["changeURL"]) { kwArgs.changeUrl = kwArgs.changeURL; }
2324
 
2325
		// encoding fun!
2326
		kwArgs.encoding = dojo.lang.firstValued(kwArgs["encoding"], djConfig["bindEncoding"], "");
2327
 
2328
		kwArgs.sendTransport = dojo.lang.firstValued(kwArgs["sendTransport"], djConfig["ioSendTransport"], false);
2329
 
2330
		var isFunction = dojo.lang.isFunction;
2331
		for(var x=0; x<dojo.io.hdlrFuncNames.length; x++){
2332
			var fn = dojo.io.hdlrFuncNames[x];
2333
			if(kwArgs[fn] && isFunction(kwArgs[fn])){ continue; }
2334
			if(kwArgs["handle"] && isFunction(kwArgs["handle"])){
2335
				kwArgs[fn] = kwArgs.handle;
2336
			}
2337
			// handler is aliased above, shouldn't need this check
2338
			/* else if(dojo.lang.isObject(kwArgs.handler)){
2339
				if(isFunction(kwArgs.handler[fn])){
2340
					kwArgs[fn] = kwArgs.handler[fn]||kwArgs.handler["handle"]||function(){};
2341
				}
2342
			}*/
2343
		}
2344
		dojo.lang.mixin(this, kwArgs);
2345
	}
2346
 
2347
});
2348
 
2349
dojo.io.Error = function(/*String*/ msg, /*String*/ type, /*Number*/num){
2350
	// summary:
2351
	//		Constructs an object representing a bind error.
2352
	this.message = msg;
2353
	this.type =  type || "unknown"; // must be one of "io", "parse", "unknown"
2354
	this.number = num || 0; // per-substrate error number, not normalized
2355
}
2356
 
2357
dojo.io.transports.addTransport = function(/*String*/name){
2358
	// summary:
2359
	//		Used to register transports that can support bind calls.
2360
	this.push(name);
2361
	// FIXME: do we need to handle things that aren't direct children of the
2362
	// dojo.io module? (say, dojo.io.foo.fooTransport?)
2363
	this[name] = dojo.io[name];
2364
}
2365
 
2366
// binding interface, the various implementations register their capabilities
2367
// and the bind() method dispatches
2368
dojo.io.bind = function(/*dojo.io.Request or Object*/request){
2369
	// summary:
2370
	//		Binding interface for IO. Loading different IO transports, like
2371
	//		dojo.io.BrowserIO or dojo.io.IframeIO, will register with bind
2372
	//		to handle particular types of bind calls.
2373
	//		request: Object
2374
	//				Object containing bind arguments. This object is converted to
2375
	//				a dojo.io.Request object, and that request object is the return
2376
	//				value for this method.
2377
	if(!(request instanceof dojo.io.Request)){
2378
		try{
2379
			request = new dojo.io.Request(request);
2380
		}catch(e){ dojo.debug(e); }
2381
	}
2382
 
2383
	// if the request asks for a particular implementation, use it
2384
	var tsName = "";
2385
	if(request["transport"]){
2386
		tsName = request["transport"];
2387
		if(!this[tsName]){
2388
			dojo.io.sendBindError(request, "No dojo.io.bind() transport with name '"
2389
				+ request["transport"] + "'.");
2390
			return request; //dojo.io.Request
2391
		}
2392
		if(!this[tsName].canHandle(request)){
2393
			dojo.io.sendBindError(request, "dojo.io.bind() transport with name '"
2394
				+ request["transport"] + "' cannot handle this type of request.");
2395
			return request;	//dojo.io.Request
2396
		}
2397
	}else{
2398
		// otherwise we do our best to auto-detect what available transports
2399
		// will handle
2400
		for(var x=0; x<dojo.io.transports.length; x++){
2401
			var tmp = dojo.io.transports[x];
2402
			if((this[tmp])&&(this[tmp].canHandle(request))){
2403
				tsName = tmp;
2404
				break;
2405
			}
2406
		}
2407
		if(tsName == ""){
2408
			dojo.io.sendBindError(request, "None of the loaded transports for dojo.io.bind()"
2409
				+ " can handle the request.");
2410
			return request; //dojo.io.Request
2411
		}
2412
	}
2413
	this[tsName].bind(request);
2414
	request.bindSuccess = true;
2415
	return request; //dojo.io.Request
2416
}
2417
 
2418
dojo.io.sendBindError = function(/* Object */request, /* String */message){
2419
	// summary:
2420
	//		Used internally by dojo.io.bind() to return/raise a bind error.
2421
 
2422
	//Need to be careful since not all hostenvs support setTimeout.
2423
	if((typeof request.error == "function" || typeof request.handle == "function")
2424
		&& (typeof setTimeout == "function" || typeof setTimeout == "object")){
2425
		var errorObject = new dojo.io.Error(message);
2426
		setTimeout(function(){
2427
			request[(typeof request.error == "function") ? "error" : "handle"]("error", errorObject, null, request);
2428
		}, 50);
2429
	}else{
2430
		dojo.raise(message);
2431
	}
2432
}
2433
 
2434
dojo.io.queueBind = function(/*dojo.io.Request or Object*/request){
2435
	// summary:
2436
	//		queueBind will use dojo.io.bind() but guarantee that only one bind
2437
	//		call is handled at a time.
2438
	// description:
2439
	//		If queueBind is called while a bind call
2440
	//		is in process, it will queue up the other calls to bind and call them
2441
	//		in order as bind calls complete.
2442
	//		request: Object
2443
	//			Same sort of request object as used for dojo.io.bind().
2444
	if(!(request instanceof dojo.io.Request)){
2445
		try{
2446
			request = new dojo.io.Request(request);
2447
		}catch(e){ dojo.debug(e); }
2448
	}
2449
 
2450
	// make sure we get called if/when we get a response
2451
	var oldLoad = request.load;
2452
	request.load = function(){
2453
		dojo.io._queueBindInFlight = false;
2454
		var ret = oldLoad.apply(this, arguments);
2455
		dojo.io._dispatchNextQueueBind();
2456
		return ret;
2457
	}
2458
 
2459
	var oldErr = request.error;
2460
	request.error = function(){
2461
		dojo.io._queueBindInFlight = false;
2462
		var ret = oldErr.apply(this, arguments);
2463
		dojo.io._dispatchNextQueueBind();
2464
		return ret;
2465
	}
2466
 
2467
	dojo.io._bindQueue.push(request);
2468
	dojo.io._dispatchNextQueueBind();
2469
	return request; //dojo.io.Request
2470
}
2471
 
2472
dojo.io._dispatchNextQueueBind = function(){
2473
	// summary:
2474
	//	Private method used by dojo.io.queueBind().
2475
	if(!dojo.io._queueBindInFlight){
2476
		dojo.io._queueBindInFlight = true;
2477
		if(dojo.io._bindQueue.length > 0){
2478
			dojo.io.bind(dojo.io._bindQueue.shift());
2479
		}else{
2480
			dojo.io._queueBindInFlight = false;
2481
		}
2482
	}
2483
}
2484
dojo.io._bindQueue = [];
2485
dojo.io._queueBindInFlight = false;
2486
 
2487
dojo.io.argsFromMap = function(/*Object*/map, /*String?*/encoding, /*String?*/last){
2488
	// summary:
2489
	//		Converts name/values pairs in the map object to an URL-encoded string
2490
	//		with format of name1=value1&name2=value2...
2491
	//		map: Object
2492
	//			Object that has the contains the names and values.
2493
	//		encoding: String?
2494
	//			String to specify how to encode the name and value. If the encoding string
2495
	//			contains "utf" (case-insensitive), then encodeURIComponent is used. Otherwise
2496
	//			dojo.string.encodeAscii is used.
2497
	//		last: String?
2498
	//			The last parameter in the list. Helps with final string formatting?
2499
	var enc = /utf/i.test(encoding||"") ? encodeURIComponent : dojo.string.encodeAscii;
2500
	var mapped = [];
2501
	var control = new Object();
2502
	for(var name in map){
2503
		var domap = function(elt){
2504
			var val = enc(name)+"="+enc(elt);
2505
			mapped[(last == name) ? "push" : "unshift"](val);
2506
		}
2507
		if(!control[name]){
2508
			var value = map[name];
2509
			// FIXME: should be isArrayLike?
2510
			if (dojo.lang.isArray(value)){
2511
				dojo.lang.forEach(value, domap);
2512
			}else{
2513
				domap(value);
2514
			}
2515
		}
2516
	}
2517
	return mapped.join("&"); //String
2518
}
2519
 
2520
dojo.io.setIFrameSrc = function(/*DOMNode*/ iframe, /*String*/ src, /*Boolean*/ replace){
2521
	//summary:
2522
	//		Sets the URL that is loaded in an IFrame. The replace parameter indicates whether
2523
	//		location.replace() should be used when changing the location of the iframe.
2524
	try{
2525
		var r = dojo.render.html;
2526
		// dojo.debug(iframe);
2527
		if(!replace){
2528
			if(r.safari){
2529
				iframe.location = src;
2530
			}else{
2531
				frames[iframe.name].location = src;
2532
			}
2533
		}else{
2534
			// Fun with DOM 0 incompatibilities!
2535
			var idoc;
2536
			if(r.ie){
2537
				idoc = iframe.contentWindow.document;
2538
			}else if(r.safari){
2539
				idoc = iframe.document;
2540
			}else{ //  if(r.moz){
2541
				idoc = iframe.contentWindow;
2542
			}
2543
 
2544
			//For Safari (at least 2.0.3) and Opera, if the iframe
2545
			//has just been created but it doesn't have content
2546
			//yet, then iframe.document may be null. In that case,
2547
			//use iframe.location and return.
2548
			if(!idoc){
2549
				iframe.location = src;
2550
				return;
2551
			}else{
2552
				idoc.location.replace(src);
2553
			}
2554
		}
2555
	}catch(e){
2556
		dojo.debug(e);
2557
		dojo.debug("setIFrameSrc: "+e);
2558
	}
2559
}
2560
 
2561
/*
2562
dojo.io.sampleTranport = new function(){
2563
	this.canHandle = function(kwArgs){
2564
		// canHandle just tells dojo.io.bind() if this is a good transport to
2565
		// use for the particular type of request.
2566
		if(
2567
			(
2568
				(kwArgs["mimetype"] == "text/plain") ||
2569
				(kwArgs["mimetype"] == "text/html") ||
2570
				(kwArgs["mimetype"] == "text/javascript")
2571
			)&&(
2572
				(kwArgs["method"] == "get") ||
2573
				( (kwArgs["method"] == "post") && (!kwArgs["formNode"]) )
2574
			)
2575
		){
2576
			return true;
2577
		}
2578
 
2579
		return false;
2580
	}
2581
 
2582
	this.bind = function(kwArgs){
2583
		var hdlrObj = {};
2584
 
2585
		// set up a handler object
2586
		for(var x=0; x<dojo.io.hdlrFuncNames.length; x++){
2587
			var fn = dojo.io.hdlrFuncNames[x];
2588
			if(typeof kwArgs.handler == "object"){
2589
				if(typeof kwArgs.handler[fn] == "function"){
2590
					hdlrObj[fn] = kwArgs.handler[fn]||kwArgs.handler["handle"];
2591
				}
2592
			}else if(typeof kwArgs[fn] == "function"){
2593
				hdlrObj[fn] = kwArgs[fn];
2594
			}else{
2595
				hdlrObj[fn] = kwArgs["handle"]||function(){};
2596
			}
2597
		}
2598
 
2599
		// build a handler function that calls back to the handler obj
2600
		var hdlrFunc = function(evt){
2601
			if(evt.type == "onload"){
2602
				hdlrObj.load("load", evt.data, evt);
2603
			}else if(evt.type == "onerr"){
2604
				var errObj = new dojo.io.Error("sampleTransport Error: "+evt.msg);
2605
				hdlrObj.error("error", errObj);
2606
			}
2607
		}
2608
 
2609
		// the sample transport would attach the hdlrFunc() when sending the
2610
		// request down the pipe at this point
2611
		var tgtURL = kwArgs.url+"?"+dojo.io.argsFromMap(kwArgs.content);
2612
		// sampleTransport.sendRequest(tgtURL, hdlrFunc);
2613
	}
2614
 
2615
	dojo.io.transports.addTransport("sampleTranport");
2616
}
2617
*/
2618
 
2619
dojo.provide("dojo.lang.array");
2620
 
2621
 
2622
 
2623
// FIXME: Is this worthless since you can do: if(name in obj)
2624
// is this the right place for this?
2625
 
2626
dojo.lang.mixin(dojo.lang, {
2627
	has: function(/*Object*/obj, /*String*/name){
2628
		// summary: is there a property with the passed name in obj?
2629
		try{
2630
			return typeof obj[name] != "undefined"; // Boolean
2631
		}catch(e){ return false; } // Boolean
2632
	},
2633
 
2634
	isEmpty: function(/*Object*/obj){
2635
		// summary:
2636
		//		can be used to determine if the passed object is "empty". In
2637
		//		the case of array-like objects, the length, property is
2638
		//		examined, but for other types of objects iteration is used to
2639
		//		examine the iterable "surface area" to determine if any
2640
		//		non-prototypal properties have been assigned. This iteration is
2641
		//		prototype-extension safe.
2642
		if(dojo.lang.isObject(obj)){
2643
			var tmp = {};
2644
			var count = 0;
2645
			for(var x in obj){
2646
				if(obj[x] && (!tmp[x])){
2647
					count++;
2648
					break;
2649
				}
2650
			}
2651
			return count == 0; // boolean
2652
		}else if(dojo.lang.isArrayLike(obj) || dojo.lang.isString(obj)){
2653
			return obj.length == 0; // boolean
2654
		}
2655
	},
2656
 
2657
	map: function(/*Array*/arr, /*Object|Function*/obj, /*Function?*/unary_func){
2658
		// summary:
2659
		//		returns a new array constituded from the return values of
2660
		//		passing each element of arr into unary_func. The obj parameter
2661
		//		may be passed to enable the passed function to be called in
2662
		//		that scope. In environments that support JavaScript 1.6, this
2663
		//		function is a passthrough to the built-in map() function
2664
		//		provided by Array instances. For details on this, see:
2665
		// 			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:map
2666
		// examples:
2667
		//		dojo.lang.map([1, 2, 3, 4], function(item){ return item+1 });
2668
		//		// returns [2, 3, 4, 5]
2669
		var isString = dojo.lang.isString(arr);
2670
		if(isString){
2671
			// arr: String
2672
			arr = arr.split("");
2673
		}
2674
		if(dojo.lang.isFunction(obj)&&(!unary_func)){
2675
			unary_func = obj;
2676
			obj = dj_global;
2677
		}else if(dojo.lang.isFunction(obj) && unary_func){
2678
			// ff 1.5 compat
2679
			var tmpObj = obj;
2680
			obj = unary_func;
2681
			unary_func = tmpObj;
2682
		}
2683
		if(Array.map){
2684
			var outArr = Array.map(arr, unary_func, obj);
2685
		}else{
2686
			var outArr = [];
2687
			for(var i=0;i<arr.length;++i){
2688
				outArr.push(unary_func.call(obj, arr[i]));
2689
			}
2690
		}
2691
		if(isString) {
2692
			return outArr.join(""); // String
2693
		} else {
2694
			return outArr; // Array
2695
		}
2696
	},
2697
 
2698
	reduce: function(/*Array*/arr, initialValue, /*Object|Function*/obj, /*Function*/binary_func){
2699
		// summary:
2700
		// 		similar to Python's builtin reduce() function. The result of
2701
		// 		the previous computation is passed as the first argument to
2702
		// 		binary_func along with the next value from arr. The result of
2703
		// 		this call is used along with the subsequent value from arr, and
2704
		// 		this continues until arr is exhausted. The return value is the
2705
		// 		last result. The "obj" and "initialValue" parameters may be
2706
		// 		safely omitted and the order of obj and binary_func may be
2707
		// 		reversed. The default order of the obj and binary_func argument
2708
		// 		will probably be reversed in a future release, and this call
2709
		// 		order is supported today.
2710
		// examples:
2711
		//		dojo.lang.reduce([1, 2, 3, 4], function(last, next){ return last+next});
2712
		//		returns 10
2713
		var reducedValue = initialValue;
2714
		if(arguments.length == 2){
2715
			binary_func = initialValue;
2716
			reducedValue = arr[0];
2717
			arr = arr.slice(1);
2718
		}else if(arguments.length == 3){
2719
			if(dojo.lang.isFunction(obj)){
2720
				binary_func = obj;
2721
				obj = null;
2722
			}
2723
		}else{
2724
			// un-fsck the default order
2725
			// FIXME:
2726
			//		could be wrong for some strange function object cases. Not
2727
			//		sure how to test for them.
2728
			if(dojo.lang.isFunction(obj)){
2729
				var tmp = binary_func;
2730
				binary_func = obj;
2731
				obj = tmp;
2732
			}
2733
		}
2734
 
2735
		var ob = obj || dj_global;
2736
		dojo.lang.map(arr,
2737
			function(val){
2738
				reducedValue = binary_func.call(ob, reducedValue, val);
2739
			}
2740
		);
2741
		return reducedValue;
2742
	},
2743
 
2744
	forEach: function(/*Array*/anArray, /*Function*/callback, /*Object?*/thisObject){
2745
		// summary:
2746
		//		for every item in anArray, call callback with that item as its
2747
		//		only parameter. Return values are ignored. This funciton
2748
		//		corresponds (and wraps) the JavaScript 1.6 forEach method. For
2749
		//		more details, see:
2750
		//			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:forEach
2751
		if(dojo.lang.isString(anArray)){
2752
			// anArray: String
2753
			anArray = anArray.split("");
2754
		}
2755
		if(Array.forEach){
2756
			Array.forEach(anArray, callback, thisObject);
2757
		}else{
2758
			// FIXME: there are several ways of handilng thisObject. Is dj_global always the default context?
2759
			if(!thisObject){
2760
				thisObject=dj_global;
2761
			}
2762
			for(var i=0,l=anArray.length; i<l; i++){
2763
				callback.call(thisObject, anArray[i], i, anArray);
2764
			}
2765
		}
2766
	},
2767
 
2768
	_everyOrSome: function(/*Boolean*/every, /*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
2769
		if(dojo.lang.isString(arr)){
2770
			//arr: String
2771
			arr = arr.split("");
2772
		}
2773
		if(Array.every){
2774
			return Array[ every ? "every" : "some" ](arr, callback, thisObject);
2775
		}else{
2776
			if(!thisObject){
2777
				thisObject = dj_global;
2778
			}
2779
			for(var i=0,l=arr.length; i<l; i++){
2780
				var result = callback.call(thisObject, arr[i], i, arr);
2781
				if(every && !result){
2782
					return false; // Boolean
2783
				}else if((!every)&&(result)){
2784
					return true; // Boolean
2785
				}
2786
			}
2787
			return Boolean(every); // Boolean
2788
		}
2789
	},
2790
 
2791
	every: function(/*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
2792
		// summary:
2793
		//		determines whether or not every item in the array satisfies the
2794
		//		condition implemented by callback. thisObject may be used to
2795
		//		scope the call to callback. The function signature is derived
2796
		//		from the JavaScript 1.6 Array.every() function. More
2797
		//		information on this can be found here:
2798
		//			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:every
2799
		// examples:
2800
		//		dojo.lang.every([1, 2, 3, 4], function(item){ return item>1; });
2801
		//		// returns false
2802
		//		dojo.lang.every([1, 2, 3, 4], function(item){ return item>0; });
2803
		//		// returns true
2804
		return this._everyOrSome(true, arr, callback, thisObject); // Boolean
2805
	},
2806
 
2807
	some: function(/*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
2808
		// summary:
2809
		//		determines whether or not any item in the array satisfies the
2810
		//		condition implemented by callback. thisObject may be used to
2811
		//		scope the call to callback. The function signature is derived
2812
		//		from the JavaScript 1.6 Array.some() function. More
2813
		//		information on this can be found here:
2814
		//			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:some
2815
		// examples:
2816
		//		dojo.lang.some([1, 2, 3, 4], function(item){ return item>1; });
2817
		//		// returns true
2818
		//		dojo.lang.some([1, 2, 3, 4], function(item){ return item<1; });
2819
		//		// returns false
2820
		return this._everyOrSome(false, arr, callback, thisObject); // Boolean
2821
	},
2822
 
2823
	filter: function(/*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
2824
		// summary:
2825
		//		returns a new Array with those items from arr that match the
2826
		//		condition implemented by callback.thisObject may be used to
2827
		//		scope the call to callback. The function signature is derived
2828
		//		from the JavaScript 1.6 Array.filter() function, although
2829
		//		special accomidation is made in our implementation for strings.
2830
		//		More information on the JS 1.6 API can be found here:
2831
		//			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:filter
2832
		// examples:
2833
		//		dojo.lang.some([1, 2, 3, 4], function(item){ return item>1; });
2834
		//		// returns [2, 3, 4]
2835
		var isString = dojo.lang.isString(arr);
2836
		if(isString){ /*arr: String*/arr = arr.split(""); }
2837
		var outArr;
2838
		if(Array.filter){
2839
			outArr = Array.filter(arr, callback, thisObject);
2840
		}else{
2841
			if(!thisObject){
2842
				if(arguments.length >= 3){ dojo.raise("thisObject doesn't exist!"); }
2843
				thisObject = dj_global;
2844
			}
2845
 
2846
			outArr = [];
2847
			for(var i = 0; i < arr.length; i++){
2848
				if(callback.call(thisObject, arr[i], i, arr)){
2849
					outArr.push(arr[i]);
2850
				}
2851
			}
2852
		}
2853
		if(isString){
2854
			return outArr.join(""); // String
2855
		} else {
2856
			return outArr; // Array
2857
		}
2858
	},
2859
 
2860
	unnest: function(/* ... */){
2861
		// summary:
2862
		//		Creates a 1-D array out of all the arguments passed,
2863
		//		unravelling any array-like objects in the process
2864
		// usage:
2865
		//		unnest(1, 2, 3) ==> [1, 2, 3]
2866
		//		unnest(1, [2, [3], [[[4]]]]) ==> [1, 2, 3, 4]
2867
 
2868
		var out = [];
2869
		for(var i = 0; i < arguments.length; i++){
2870
			if(dojo.lang.isArrayLike(arguments[i])){
2871
				var add = dojo.lang.unnest.apply(this, arguments[i]);
2872
				out = out.concat(add);
2873
			}else{
2874
				out.push(arguments[i]);
2875
			}
2876
		}
2877
		return out; // Array
2878
	},
2879
 
2880
	toArray: function(/*Object*/arrayLike, /*Number*/startOffset){
2881
		// summary:
2882
		//		Converts an array-like object (i.e. arguments, DOMCollection)
2883
		//		to an array. Returns a new Array object.
2884
		var array = [];
2885
		for(var i = startOffset||0; i < arrayLike.length; i++){
2886
			array.push(arrayLike[i]);
2887
		}
2888
		return array; // Array
2889
	}
2890
});
2891
 
2892
dojo.provide("dojo.lang.func");
2893
 
2894
 
2895
dojo.lang.hitch = function(/*Object*/thisObject, /*Function|String*/method){
2896
	// summary:
2897
	//		Returns a function that will only ever execute in the a given scope
2898
	//		(thisObject). This allows for easy use of object member functions
2899
	//		in callbacks and other places in which the "this" keyword may
2900
	//		otherwise not reference the expected scope. Note that the order of
2901
	//		arguments may be reversed in a future version.
2902
	// thisObject: the scope to run the method in
2903
	// method:
2904
	//		a function to be "bound" to thisObject or the name of the method in
2905
	//		thisObject to be used as the basis for the binding
2906
	// usage:
2907
	//		dojo.lang.hitch(foo, "bar")(); // runs foo.bar() in the scope of foo
2908
	//		dojo.lang.hitch(foo, myFunction); // returns a function that runs myFunction in the scope of foo
2909
 
2910
	// FIXME:
2911
	//		should this be extended to "fixate" arguments in a manner similar
2912
	//		to dojo.lang.curry, but without the default execution of curry()?
2913
	var fcn = (dojo.lang.isString(method) ? thisObject[method] : method) || function(){};
2914
	return function(){
2915
		return fcn.apply(thisObject, arguments); // Function
2916
	};
2917
}
2918
 
2919
dojo.lang.anonCtr = 0;
2920
dojo.lang.anon = {};
2921
 
2922
dojo.lang.nameAnonFunc = function(/*Function*/anonFuncPtr, /*Object*/thisObj, /*Boolean*/searchForNames){
2923
	// summary:
2924
	//		Creates a reference to anonFuncPtr in thisObj with a completely
2925
	//		unique name. The new name is returned as a String.  If
2926
	//		searchForNames is true, an effort will be made to locate an
2927
	//		existing reference to anonFuncPtr in thisObj, and if one is found,
2928
	//		the existing name will be returned instead. The default is for
2929
	//		searchForNames to be false.
2930
	var nso = (thisObj|| dojo.lang.anon);
2931
	if( (searchForNames) ||
2932
		((dj_global["djConfig"])&&(djConfig["slowAnonFuncLookups"] == true)) ){
2933
		for(var x in nso){
2934
			try{
2935
				if(nso[x] === anonFuncPtr){
2936
					return x;
2937
				}
2938
			}catch(e){} // window.external fails in IE embedded in Eclipse (Eclipse bug #151165)
2939
		}
2940
	}
2941
	var ret = "__"+dojo.lang.anonCtr++;
2942
	while(typeof nso[ret] != "undefined"){
2943
		ret = "__"+dojo.lang.anonCtr++;
2944
	}
2945
	nso[ret] = anonFuncPtr;
2946
	return ret; // String
2947
}
2948
 
2949
dojo.lang.forward = function(funcName){
2950
	// summary:
2951
	// 		Returns a function that forwards a method call to
2952
	// 		this.funcName(...).  Unlike dojo.lang.hitch(), the "this" scope is
2953
	// 		not fixed on a single object. Ported from MochiKit.
2954
	return function(){
2955
		return this[funcName].apply(this, arguments);
2956
	}; // Function
2957
}
2958
 
2959
dojo.lang.curry = function(thisObj, func /* args ... */){
2960
	// summary:
2961
	//		similar to the curry() method found in many functional programming
2962
	//		environments, this function returns an "argument accumulator"
2963
	//		function, bound to a particular scope, and "primed" with a variable
2964
	//		number of arguments. The curry method is unique in that it returns
2965
	//		a function that may return other "partial" function which can be
2966
	//		called repeatedly. New functions are returned until the arity of
2967
	//		the original function is reached, at which point the underlying
2968
	//		function (func) is called in the scope thisObj with all of the
2969
	//		accumulated arguments (plus any extras) in positional order.
2970
	// examples:
2971
	//		assuming a function defined like this:
2972
	//			var foo = {
2973
	//				bar: function(arg1, arg2, arg3){
2974
	//					dojo.debug.apply(dojo, arguments);
2975
	//				}
2976
	//			};
2977
	//
2978
	//		dojo.lang.curry() can be used most simply in this way:
2979
	//
2980
	//			tmp = dojo.lang.curry(foo, foo.bar, "arg one", "thinger");
2981
	//			tmp("blah", "this is superfluous");
2982
	//			// debugs: "arg one thinger blah this is superfluous"
2983
	//			tmp("blah");
2984
	//			// debugs: "arg one thinger blah"
2985
	//			tmp();
2986
	//			// returns a function exactly like tmp that expects one argument
2987
	//
2988
	//		other intermittent functions could be created until the 3
2989
	//		positional arguments are filled:
2990
	//
2991
	//			tmp = dojo.lang.curry(foo, foo.bar, "arg one");
2992
	//			tmp2 = tmp("arg two");
2993
	//			tmp2("blah blah");
2994
	//			// debugs: "arg one arg two blah blah"
2995
	//			tmp2("oy");
2996
	//			// debugs: "arg one arg two oy"
2997
	//
2998
	//		curry() can also be used to call the function if enough arguments
2999
	//		are passed in the initial invocation:
3000
	//
3001
	//			dojo.lang.curry(foo, foo.bar, "one", "two", "three", "four");
3002
	//			// debugs: "one two three four"
3003
	//			dojo.lang.curry(foo, foo.bar, "one", "two", "three");
3004
	//			// debugs: "one two three"
3005
 
3006
 
3007
	// FIXME: the order of func and thisObj should be changed!!!
3008
	var outerArgs = [];
3009
	thisObj = thisObj||dj_global;
3010
	if(dojo.lang.isString(func)){
3011
		func = thisObj[func];
3012
	}
3013
	for(var x=2; x<arguments.length; x++){
3014
		outerArgs.push(arguments[x]);
3015
	}
3016
	// since the event system replaces the original function with a new
3017
	// join-point runner with an arity of 0, we check to see if it's left us
3018
	// any clues about the original arity in lieu of the function's actual
3019
	// length property
3020
	var ecount = (func["__preJoinArity"]||func.length) - outerArgs.length;
3021
	// borrowed from svend tofte
3022
	function gather(nextArgs, innerArgs, expected){
3023
		var texpected = expected;
3024
		var totalArgs = innerArgs.slice(0); // copy
3025
		for(var x=0; x<nextArgs.length; x++){
3026
			totalArgs.push(nextArgs[x]);
3027
		}
3028
		// check the list of provided nextArgs to see if it, plus the
3029
		// number of innerArgs already supplied, meets the total
3030
		// expected.
3031
		expected = expected-nextArgs.length;
3032
		if(expected<=0){
3033
			var res = func.apply(thisObj, totalArgs);
3034
			expected = texpected;
3035
			return res;
3036
		}else{
3037
			return function(){
3038
				return gather(arguments,// check to see if we've been run
3039
										// with enough args
3040
							totalArgs,	// a copy
3041
							expected);	// how many more do we need to run?;
3042
			};
3043
		}
3044
	}
3045
	return gather([], outerArgs, ecount);
3046
}
3047
 
3048
dojo.lang.curryArguments = function(/*Object*/thisObj, /*Function*/func, /*Array*/args, /*Integer, optional*/offset){
3049
	// summary:
3050
	//		similar to dojo.lang.curry(), except that a list of arguments to
3051
	//		start the curry with may be provided as an array instead of as
3052
	//		positional arguments. An offset may be specified from the 0 index
3053
	//		to skip some elements in args.
3054
	var targs = [];
3055
	var x = offset||0;
3056
	for(x=offset; x<args.length; x++){
3057
		targs.push(args[x]); // ensure that it's an arr
3058
	}
3059
	return dojo.lang.curry.apply(dojo.lang, [thisObj, func].concat(targs));
3060
}
3061
 
3062
dojo.lang.tryThese = function(/*...*/){
3063
	// summary:
3064
	//		executes each function argument in turn, returning the return value
3065
	//		from the first one which does not throw an exception in execution.
3066
	//		Any number of functions may be passed.
3067
	for(var x=0; x<arguments.length; x++){
3068
		try{
3069
			if(typeof arguments[x] == "function"){
3070
				var ret = (arguments[x]());
3071
				if(ret){
3072
					return ret;
3073
				}
3074
			}
3075
		}catch(e){
3076
			dojo.debug(e);
3077
		}
3078
	}
3079
}
3080
 
3081
dojo.lang.delayThese = function(/*Array*/farr, /*Function, optional*/cb, /*Integer*/delay, /*Function, optional*/onend){
3082
	// summary:
3083
	//		executes a series of functions contained in farr, but spaces out
3084
	//		calls to each function by the millisecond delay provided. If cb is
3085
	//		provided, it will be called directly after each item in farr is
3086
	//		called and if onend is passed, it will be called when all items
3087
	//		have completed executing.
3088
 
3089
	/**
3090
	 * alternate: (array funcArray, function callback, function onend)
3091
	 * alternate: (array funcArray, function callback)
3092
	 * alternate: (array funcArray)
3093
	 */
3094
	if(!farr.length){
3095
		if(typeof onend == "function"){
3096
			onend();
3097
		}
3098
		return;
3099
	}
3100
	if((typeof delay == "undefined")&&(typeof cb == "number")){
3101
		delay = cb;
3102
		cb = function(){};
3103
	}else if(!cb){
3104
		cb = function(){};
3105
		if(!delay){ delay = 0; }
3106
	}
3107
	setTimeout(function(){
3108
		(farr.shift())();
3109
		cb();
3110
		dojo.lang.delayThese(farr, cb, delay, onend);
3111
	}, delay);
3112
}
3113
 
3114
dojo.provide("dojo.string.extras");
3115
 
3116
 
3117
 
3118
 
3119
 
3120
//TODO: should we use ${} substitution syntax instead, like widgets do?
3121
dojo.string.substituteParams = function(/*string*/template, /* object - optional or ... */hash){
3122
// summary:
3123
//	Performs parameterized substitutions on a string. Throws an exception if any parameter is unmatched.
3124
//
3125
// description:
3126
//	For example,
3127
//		dojo.string.substituteParams("File '%{0}' is not found in directory '%{1}'.","foo.html","/temp");
3128
//	returns
3129
//		"File 'foo.html' is not found in directory '/temp'."
3130
//
3131
// template: the original string template with %{values} to be replaced
3132
// hash: name/value pairs (type object) to provide substitutions.  Alternatively, substitutions may be
3133
//	included as arguments 1..n to this function, corresponding to template parameters 0..n-1
3134
 
3135
	var map = (typeof hash == 'object') ? hash : dojo.lang.toArray(arguments, 1);
3136
 
3137
	return template.replace(/\%\{(\w+)\}/g, function(match, key){
3138
		if(typeof(map[key]) != "undefined" && map[key] != null){
3139
			return map[key];
3140
		}
3141
		dojo.raise("Substitution not found: " + key);
3142
	}); // string
3143
};
3144
 
3145
dojo.string.capitalize = function(/*string*/str){
3146
// summary:
3147
//	Uppercases the first letter of each word
3148
 
3149
	if(!dojo.lang.isString(str)){ return ""; }
3150
	if(arguments.length == 0){ str = this; }
3151
 
3152
	var words = str.split(' ');
3153
	for(var i=0; i<words.length; i++){
3154
		words[i] = words[i].charAt(0).toUpperCase() + words[i].substring(1);
3155
	}
3156
	return words.join(" "); // string
3157
}
3158
 
3159
dojo.string.isBlank = function(/*string*/str){
3160
// summary:
3161
//	Return true if the entire string is whitespace characters
3162
 
3163
	if(!dojo.lang.isString(str)){ return true; }
3164
	return (dojo.string.trim(str).length == 0); // boolean
3165
}
3166
 
3167
//FIXME: not sure exactly what encodeAscii is trying to do, or if it's working right
3168
dojo.string.encodeAscii = function(/*string*/str){
3169
	if(!dojo.lang.isString(str)){ return str; } // unknown
3170
	var ret = "";
3171
	var value = escape(str);
3172
	var match, re = /%u([0-9A-F]{4})/i;
3173
	while((match = value.match(re))){
3174
		var num = Number("0x"+match[1]);
3175
		var newVal = escape("&#" + num + ";");
3176
		ret += value.substring(0, match.index) + newVal;
3177
		value = value.substring(match.index+match[0].length);
3178
	}
3179
	ret += value.replace(/\+/g, "%2B");
3180
	return ret; // string
3181
}
3182
 
3183
dojo.string.escape = function(/*string*/type, /*string*/str){
3184
// summary:
3185
//	Adds escape sequences for special characters according to the convention of 'type'
3186
//
3187
// type: one of xml|html|xhtml|sql|regexp|regex|javascript|jscript|js|ascii
3188
// str: the string to be escaped
3189
 
3190
	var args = dojo.lang.toArray(arguments, 1);
3191
	switch(type.toLowerCase()){
3192
		case "xml":
3193
		case "html":
3194
		case "xhtml":
3195
			return dojo.string.escapeXml.apply(this, args); // string
3196
		case "sql":
3197
			return dojo.string.escapeSql.apply(this, args); // string
3198
		case "regexp":
3199
		case "regex":
3200
			return dojo.string.escapeRegExp.apply(this, args); // string
3201
		case "javascript":
3202
		case "jscript":
3203
		case "js":
3204
			return dojo.string.escapeJavaScript.apply(this, args); // string
3205
		case "ascii":
3206
			// so it's encode, but it seems useful
3207
			return dojo.string.encodeAscii.apply(this, args); // string
3208
		default:
3209
			return str; // string
3210
	}
3211
}
3212
 
3213
dojo.string.escapeXml = function(/*string*/str, /*boolean*/noSingleQuotes){
3214
//summary:
3215
//	Adds escape sequences for special characters in XML: &<>"'
3216
//  Optionally skips escapes for single quotes
3217
 
3218
	str = str.replace(/&/gm, "&amp;").replace(/</gm, "&lt;")
3219
		.replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
3220
	if(!noSingleQuotes){ str = str.replace(/'/gm, "&#39;"); }
3221
	return str; // string
3222
}
3223
 
3224
dojo.string.escapeSql = function(/*string*/str){
3225
//summary:
3226
//	Adds escape sequences for single quotes in SQL expressions
3227
 
3228
	return str.replace(/'/gm, "''"); //string
3229
}
3230
 
3231
dojo.string.escapeRegExp = function(/*string*/str){
3232
//summary:
3233
//	Adds escape sequences for special characters in regular expressions
3234
 
3235
	return str.replace(/\\/gm, "\\\\").replace(/([\f\b\n\t\r[\^$|?*+(){}])/gm, "\\$1"); // string
3236
}
3237
 
3238
//FIXME: should this one also escape backslash?
3239
dojo.string.escapeJavaScript = function(/*string*/str){
3240
//summary:
3241
//	Adds escape sequences for single and double quotes as well
3242
//	as non-visible characters in JavaScript string literal expressions
3243
 
3244
	return str.replace(/(["'\f\b\n\t\r])/gm, "\\$1"); // string
3245
}
3246
 
3247
//FIXME: looks a lot like escapeJavaScript, just adds quotes? deprecate one?
3248
dojo.string.escapeString = function(/*string*/str){
3249
//summary:
3250
//	Adds escape sequences for non-visual characters, double quote and backslash
3251
//	and surrounds with double quotes to form a valid string literal.
3252
	return ('"' + str.replace(/(["\\])/g, '\\$1') + '"'
3253
		).replace(/[\f]/g, "\\f"
3254
		).replace(/[\b]/g, "\\b"
3255
		).replace(/[\n]/g, "\\n"
3256
		).replace(/[\t]/g, "\\t"
3257
		).replace(/[\r]/g, "\\r"); // string
3258
}
3259
 
3260
// TODO: make an HTML version
3261
dojo.string.summary = function(/*string*/str, /*number*/len){
3262
// summary:
3263
//	Truncates 'str' after 'len' characters and appends periods as necessary so that it ends with "..."
3264
 
3265
	if(!len || str.length <= len){
3266
		return str; // string
3267
	}
3268
 
3269
	return str.substring(0, len).replace(/\.+$/, "") + "..."; // string
3270
}
3271
 
3272
dojo.string.endsWith = function(/*string*/str, /*string*/end, /*boolean*/ignoreCase){
3273
// summary:
3274
//	Returns true if 'str' ends with 'end'
3275
 
3276
	if(ignoreCase){
3277
		str = str.toLowerCase();
3278
		end = end.toLowerCase();
3279
	}
3280
	if((str.length - end.length) < 0){
3281
		return false; // boolean
3282
	}
3283
	return str.lastIndexOf(end) == str.length - end.length; // boolean
3284
}
3285
 
3286
dojo.string.endsWithAny = function(/*string*/str /* , ... */){
3287
// summary:
3288
//	Returns true if 'str' ends with any of the arguments[2 -> n]
3289
 
3290
	for(var i = 1; i < arguments.length; i++) {
3291
		if(dojo.string.endsWith(str, arguments[i])) {
3292
			return true; // boolean
3293
		}
3294
	}
3295
	return false; // boolean
3296
}
3297
 
3298
dojo.string.startsWith = function(/*string*/str, /*string*/start, /*boolean*/ignoreCase){
3299
// summary:
3300
//	Returns true if 'str' starts with 'start'
3301
 
3302
	if(ignoreCase) {
3303
		str = str.toLowerCase();
3304
		start = start.toLowerCase();
3305
	}
3306
	return str.indexOf(start) == 0; // boolean
3307
}
3308
 
3309
dojo.string.startsWithAny = function(/*string*/str /* , ... */){
3310
// summary:
3311
//	Returns true if 'str' starts with any of the arguments[2 -> n]
3312
 
3313
	for(var i = 1; i < arguments.length; i++) {
3314
		if(dojo.string.startsWith(str, arguments[i])) {
3315
			return true; // boolean
3316
		}
3317
	}
3318
	return false; // boolean
3319
}
3320
 
3321
dojo.string.has = function(/*string*/str /* , ... */) {
3322
// summary:
3323
//	Returns true if 'str' contains any of the arguments 2 -> n
3324
 
3325
	for(var i = 1; i < arguments.length; i++) {
3326
		if(str.indexOf(arguments[i]) > -1){
3327
			return true; // boolean
3328
		}
3329
	}
3330
	return false; // boolean
3331
}
3332
 
3333
dojo.string.normalizeNewlines = function(/*string*/text, /*string? (\n or \r)*/newlineChar){
3334
// summary:
3335
//	Changes occurences of CR and LF in text to CRLF, or if newlineChar is provided as '\n' or '\r',
3336
//	substitutes newlineChar for occurrences of CR/LF and CRLF
3337
 
3338
	if (newlineChar == "\n"){
3339
		text = text.replace(/\r\n/g, "\n");
3340
		text = text.replace(/\r/g, "\n");
3341
	} else if (newlineChar == "\r"){
3342
		text = text.replace(/\r\n/g, "\r");
3343
		text = text.replace(/\n/g, "\r");
3344
	}else{
3345
		text = text.replace(/([^\r])\n/g, "$1\r\n").replace(/\r([^\n])/g, "\r\n$1");
3346
	}
3347
	return text; // string
3348
}
3349
 
3350
dojo.string.splitEscaped = function(/*string*/str, /*string of length=1*/charac){
3351
// summary:
3352
//	Splits 'str' into an array separated by 'charac', but skips characters escaped with a backslash
3353
 
3354
	var components = [];
3355
	for (var i = 0, prevcomma = 0; i < str.length; i++){
3356
		if (str.charAt(i) == '\\'){ i++; continue; }
3357
		if (str.charAt(i) == charac){
3358
			components.push(str.substring(prevcomma, i));
3359
			prevcomma = i + 1;
3360
		}
3361
	}
3362
	components.push(str.substr(prevcomma));
3363
	return components; // array
3364
}
3365
 
3366
dojo.provide("dojo.dom");
3367
 
3368
dojo.dom.ELEMENT_NODE                  = 1;
3369
dojo.dom.ATTRIBUTE_NODE                = 2;
3370
dojo.dom.TEXT_NODE                     = 3;
3371
dojo.dom.CDATA_SECTION_NODE            = 4;
3372
dojo.dom.ENTITY_REFERENCE_NODE         = 5;
3373
dojo.dom.ENTITY_NODE                   = 6;
3374
dojo.dom.PROCESSING_INSTRUCTION_NODE   = 7;
3375
dojo.dom.COMMENT_NODE                  = 8;
3376
dojo.dom.DOCUMENT_NODE                 = 9;
3377
dojo.dom.DOCUMENT_TYPE_NODE            = 10;
3378
dojo.dom.DOCUMENT_FRAGMENT_NODE        = 11;
3379
dojo.dom.NOTATION_NODE                 = 12;
3380
 
3381
dojo.dom.dojoml = "http://www.dojotoolkit.org/2004/dojoml";
3382
 
3383
/**
3384
 *	comprehensive list of XML namespaces
3385
**/
3386
dojo.dom.xmlns = {
3387
	//	summary
3388
	//	aliases for various common XML namespaces
3389
	svg : "http://www.w3.org/2000/svg",
3390
	smil : "http://www.w3.org/2001/SMIL20/",
3391
	mml : "http://www.w3.org/1998/Math/MathML",
3392
	cml : "http://www.xml-cml.org",
3393
	xlink : "http://www.w3.org/1999/xlink",
3394
	xhtml : "http://www.w3.org/1999/xhtml",
3395
	xul : "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
3396
	xbl : "http://www.mozilla.org/xbl",
3397
	fo : "http://www.w3.org/1999/XSL/Format",
3398
	xsl : "http://www.w3.org/1999/XSL/Transform",
3399
	xslt : "http://www.w3.org/1999/XSL/Transform",
3400
	xi : "http://www.w3.org/2001/XInclude",
3401
	xforms : "http://www.w3.org/2002/01/xforms",
3402
	saxon : "http://icl.com/saxon",
3403
	xalan : "http://xml.apache.org/xslt",
3404
	xsd : "http://www.w3.org/2001/XMLSchema",
3405
	dt: "http://www.w3.org/2001/XMLSchema-datatypes",
3406
	xsi : "http://www.w3.org/2001/XMLSchema-instance",
3407
	rdf : "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
3408
	rdfs : "http://www.w3.org/2000/01/rdf-schema#",
3409
	dc : "http://purl.org/dc/elements/1.1/",
3410
	dcq: "http://purl.org/dc/qualifiers/1.0",
3411
	"soap-env" : "http://schemas.xmlsoap.org/soap/envelope/",
3412
	wsdl : "http://schemas.xmlsoap.org/wsdl/",
3413
	AdobeExtensions : "http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
3414
};
3415
 
3416
dojo.dom.isNode = function(/* object */wh){
3417
	//	summary:
3418
	//		checks to see if wh is actually a node.
3419
	if(typeof Element == "function") {
3420
		try {
3421
			return wh instanceof Element;	//	boolean
3422
		} catch(e) {}
3423
	} else {
3424
		// best-guess
3425
		return wh && !isNaN(wh.nodeType);	//	boolean
3426
	}
3427
}
3428
 
3429
dojo.dom.getUniqueId = function(){
3430
	//	summary:
3431
	//		returns a unique string for use with any DOM element
3432
	var _document = dojo.doc();
3433
	do {
3434
		var id = "dj_unique_" + (++arguments.callee._idIncrement);
3435
	}while(_document.getElementById(id));
3436
	return id;	//	string
3437
}
3438
dojo.dom.getUniqueId._idIncrement = 0;
3439
 
3440
dojo.dom.firstElement = dojo.dom.getFirstChildElement = function(/* Element */parentNode, /* string? */tagName){
3441
	//	summary:
3442
	//		returns the first child element matching tagName
3443
	var node = parentNode.firstChild;
3444
	while(node && node.nodeType != dojo.dom.ELEMENT_NODE){
3445
		node = node.nextSibling;
3446
	}
3447
	if(tagName && node && node.tagName && node.tagName.toLowerCase() != tagName.toLowerCase()) {
3448
		node = dojo.dom.nextElement(node, tagName);
3449
	}
3450
	return node;	//	Element
3451
}
3452
 
3453
dojo.dom.lastElement = dojo.dom.getLastChildElement = function(/* Element */parentNode, /* string? */tagName){
3454
	//	summary:
3455
	//		returns the last child element matching tagName
3456
	var node = parentNode.lastChild;
3457
	while(node && node.nodeType != dojo.dom.ELEMENT_NODE) {
3458
		node = node.previousSibling;
3459
	}
3460
	if(tagName && node && node.tagName && node.tagName.toLowerCase() != tagName.toLowerCase()) {
3461
		node = dojo.dom.prevElement(node, tagName);
3462
	}
3463
	return node;	//	Element
3464
}
3465
 
3466
dojo.dom.nextElement = dojo.dom.getNextSiblingElement = function(/* Node */node, /* string? */tagName){
3467
	//	summary:
3468
	//		returns the next sibling element matching tagName
3469
	if(!node) { return null; }
3470
	do {
3471
		node = node.nextSibling;
3472
	} while(node && node.nodeType != dojo.dom.ELEMENT_NODE);
3473
 
3474
	if(node && tagName && tagName.toLowerCase() != node.tagName.toLowerCase()) {
3475
		return dojo.dom.nextElement(node, tagName);
3476
	}
3477
	return node;	//	Element
3478
}
3479
 
3480
dojo.dom.prevElement = dojo.dom.getPreviousSiblingElement = function(/* Node */node, /* string? */tagName){
3481
	//	summary:
3482
	//		returns the previous sibling element matching tagName
3483
	if(!node) { return null; }
3484
	if(tagName) { tagName = tagName.toLowerCase(); }
3485
	do {
3486
		node = node.previousSibling;
3487
	} while(node && node.nodeType != dojo.dom.ELEMENT_NODE);
3488
 
3489
	if(node && tagName && tagName.toLowerCase() != node.tagName.toLowerCase()) {
3490
		return dojo.dom.prevElement(node, tagName);
3491
	}
3492
	return node;	//	Element
3493
}
3494
 
3495
// TODO: hmph
3496
/*this.forEachChildTag = function(node, unaryFunc) {
3497
	var child = this.getFirstChildTag(node);
3498
	while(child) {
3499
		if(unaryFunc(child) == "break") { break; }
3500
		child = this.getNextSiblingTag(child);
3501
	}
3502
}*/
3503
 
3504
dojo.dom.moveChildren = function(/*Element*/srcNode, /*Element*/destNode, /*boolean?*/trim){
3505
	//	summary:
3506
	//		Moves children from srcNode to destNode and returns the count of
3507
	//		children moved; will trim off text nodes if trim == true
3508
	var count = 0;
3509
	if(trim) {
3510
		while(srcNode.hasChildNodes() &&
3511
			srcNode.firstChild.nodeType == dojo.dom.TEXT_NODE) {
3512
			srcNode.removeChild(srcNode.firstChild);
3513
		}
3514
		while(srcNode.hasChildNodes() &&
3515
			srcNode.lastChild.nodeType == dojo.dom.TEXT_NODE) {
3516
			srcNode.removeChild(srcNode.lastChild);
3517
		}
3518
	}
3519
	while(srcNode.hasChildNodes()){
3520
		destNode.appendChild(srcNode.firstChild);
3521
		count++;
3522
	}
3523
	return count;	//	number
3524
}
3525
 
3526
dojo.dom.copyChildren = function(/*Element*/srcNode, /*Element*/destNode, /*boolean?*/trim){
3527
	//	summary:
3528
	//		Copies children from srcNde to destNode and returns the count of
3529
	//		children copied; will trim off text nodes if trim == true
3530
	var clonedNode = srcNode.cloneNode(true);
3531
	return this.moveChildren(clonedNode, destNode, trim);	//	number
3532
}
3533
 
3534
dojo.dom.replaceChildren = function(/*Element*/node, /*Node*/newChild){
3535
	//	summary:
3536
	//		Removes all children of node and appends newChild. All the existing
3537
	//		children will be destroyed.
3538
	// FIXME: what if newChild is an array-like object?
3539
	var nodes = [];
3540
	if(dojo.render.html.ie){
3541
		for(var i=0;i<node.childNodes.length;i++){
3542
			nodes.push(node.childNodes[i]);
3543
		}
3544
	}
3545
	dojo.dom.removeChildren(node);
3546
	node.appendChild(newChild);
3547
	for(var i=0;i<nodes.length;i++){
3548
		dojo.dom.destroyNode(nodes[i]);
3549
	}
3550
}
3551
 
3552
dojo.dom.removeChildren = function(/*Element*/node){
3553
	//	summary:
3554
	//		removes all children from node and returns the count of children removed.
3555
	//		The children nodes are not destroyed. Be sure to call destroyNode on them
3556
	//		after they are not used anymore.
3557
	var count = node.childNodes.length;
3558
	while(node.hasChildNodes()){ dojo.dom.removeNode(node.firstChild); }
3559
	return count; // int
3560
}
3561
 
3562
dojo.dom.replaceNode = function(/*Element*/node, /*Element*/newNode){
3563
	//	summary:
3564
	//		replaces node with newNode and returns a reference to the removed node.
3565
	//		To prevent IE memory leak, call destroyNode on the returned node when
3566
	//		it is no longer needed.
3567
	return node.parentNode.replaceChild(newNode, node); // Node
3568
}
3569
 
3570
dojo.dom.destroyNode = function(/*Node*/node){
3571
	// summary:
3572
	//		destroy a node (it can not be used any more). For IE, this is the
3573
	//		right function to call to prevent memory leaks. While for other
3574
	//		browsers, this is identical to dojo.dom.removeNode
3575
	if(node.parentNode){
3576
		node = dojo.dom.removeNode(node);
3577
	}
3578
	if(node.nodeType != 3){ // ingore TEXT_NODE
3579
		if(dojo.evalObjPath("dojo.event.browser.clean", false)){
3580
			dojo.event.browser.clean(node);
3581
		}
3582
		if(dojo.render.html.ie){
3583
			node.outerHTML=''; //prevent ugly IE mem leak associated with Node.removeChild (ticket #1727)
3584
		}
3585
	}
3586
}
3587
 
3588
dojo.dom.removeNode = function(/*Node*/node){
3589
	// summary:
3590
	//		if node has a parent, removes node from parent and returns a
3591
	//		reference to the removed child.
3592
	//		To prevent IE memory leak, call destroyNode on the returned node when
3593
	//		it is no longer needed.
3594
	//	node:
3595
	//		the node to remove from its parent.
3596
 
3597
	if(node && node.parentNode){
3598
		// return a ref to the removed child
3599
		return node.parentNode.removeChild(node); //Node
3600
	}
3601
}
3602
 
3603
dojo.dom.getAncestors = function(/*Node*/node, /*function?*/filterFunction, /*boolean?*/returnFirstHit){
3604
	//	summary:
3605
	//		returns all ancestors matching optional filterFunction; will return
3606
	//		only the first if returnFirstHit
3607
	var ancestors = [];
3608
	var isFunction = (filterFunction && (filterFunction instanceof Function || typeof filterFunction == "function"));
3609
	while(node){
3610
		if(!isFunction || filterFunction(node)){
3611
			ancestors.push(node);
3612
		}
3613
		if(returnFirstHit && ancestors.length > 0){
3614
			return ancestors[0]; 	//	Node
3615
		}
3616
 
3617
		node = node.parentNode;
3618
	}
3619
	if(returnFirstHit){ return null; }
3620
	return ancestors;	//	array
3621
}
3622
 
3623
dojo.dom.getAncestorsByTag = function(/*Node*/node, /*String*/tag, /*boolean?*/returnFirstHit){
3624
	//	summary:
3625
	//		returns all ancestors matching tag (as tagName), will only return
3626
	//		first one if returnFirstHit
3627
	tag = tag.toLowerCase();
3628
	return dojo.dom.getAncestors(node, function(el){
3629
		return ((el.tagName)&&(el.tagName.toLowerCase() == tag));
3630
	}, returnFirstHit);	//	Node || array
3631
}
3632
 
3633
dojo.dom.getFirstAncestorByTag = function(/*Node*/node, /*string*/tag){
3634
	//	summary:
3635
	//		Returns first ancestor of node with tag tagName
3636
	return dojo.dom.getAncestorsByTag(node, tag, true);	//	Node
3637
}
3638
 
3639
dojo.dom.isDescendantOf = function(/* Node */node, /* Node */ancestor, /* boolean? */guaranteeDescendant){
3640
	//	summary
3641
	//	Returns boolean if node is a descendant of ancestor
3642
	// guaranteeDescendant allows us to be a "true" isDescendantOf function
3643
	if(guaranteeDescendant && node) { node = node.parentNode; }
3644
	while(node) {
3645
		if(node == ancestor){
3646
			return true; 	//	boolean
3647
		}
3648
		node = node.parentNode;
3649
	}
3650
	return false;	//	boolean
3651
}
3652
 
3653
dojo.dom.innerXML = function(/*Node*/node){
3654
	//	summary:
3655
	//		Implementation of MS's innerXML function.
3656
	if(node.innerXML){
3657
		return node.innerXML;	//	string
3658
	}else if (node.xml){
3659
		return node.xml;		//	string
3660
	}else if(typeof XMLSerializer != "undefined"){
3661
		return (new XMLSerializer()).serializeToString(node);	//	string
3662
	}
3663
}
3664
 
3665
dojo.dom.createDocument = function(){
3666
	//	summary:
3667
	//		cross-browser implementation of creating an XML document object.
3668
	var doc = null;
3669
	var _document = dojo.doc();
3670
 
3671
	if(!dj_undef("ActiveXObject")){
3672
		var prefixes = [ "MSXML2", "Microsoft", "MSXML", "MSXML3" ];
3673
		for(var i = 0; i<prefixes.length; i++){
3674
			try{
3675
				doc = new ActiveXObject(prefixes[i]+".XMLDOM");
3676
			}catch(e){ /* squelch */ };
3677
 
3678
			if(doc){ break; }
3679
		}
3680
	}else if((_document.implementation)&&
3681
		(_document.implementation.createDocument)){
3682
		doc = _document.implementation.createDocument("", "", null);
3683
	}
3684
 
3685
	return doc;	//	DOMDocument
3686
}
3687
 
3688
dojo.dom.createDocumentFromText = function(/*string*/str, /*string?*/mimetype){
3689
	//	summary:
3690
	//		attempts to create a Document object based on optional mime-type,
3691
	//		using str as the contents of the document
3692
	if(!mimetype){ mimetype = "text/xml"; }
3693
	if(!dj_undef("DOMParser")){
3694
		var parser = new DOMParser();
3695
		return parser.parseFromString(str, mimetype);	//	DOMDocument
3696
	}else if(!dj_undef("ActiveXObject")){
3697
		var domDoc = dojo.dom.createDocument();
3698
		if(domDoc){
3699
			domDoc.async = false;
3700
			domDoc.loadXML(str);
3701
			return domDoc;	//	DOMDocument
3702
		}else{
3703
			dojo.debug("toXml didn't work?");
3704
		}
3705
	/*
3706
	}else if((dojo.render.html.capable)&&(dojo.render.html.safari)){
3707
		// FIXME: this doesn't appear to work!
3708
		// from: http://web-graphics.com/mtarchive/001606.php
3709
		// var xml = '<?xml version="1.0"?>'+str;
3710
		var mtype = "text/xml";
3711
		var xml = '<?xml version="1.0"?>'+str;
3712
		var url = "data:"+mtype+";charset=utf-8,"+encodeURIComponent(xml);
3713
		var req = new XMLHttpRequest();
3714
		req.open("GET", url, false);
3715
		req.overrideMimeType(mtype);
3716
		req.send(null);
3717
		return req.responseXML;
3718
	*/
3719
	}else{
3720
		var _document = dojo.doc();
3721
		if(_document.createElement){
3722
			// FIXME: this may change all tags to uppercase!
3723
			var tmp = _document.createElement("xml");
3724
			tmp.innerHTML = str;
3725
			if(_document.implementation && _document.implementation.createDocument){
3726
				var xmlDoc = _document.implementation.createDocument("foo", "", null);
3727
				for(var i = 0; i < tmp.childNodes.length; i++) {
3728
					xmlDoc.importNode(tmp.childNodes.item(i), true);
3729
				}
3730
				return xmlDoc;	//	DOMDocument
3731
			}
3732
			// FIXME: probably not a good idea to have to return an HTML fragment
3733
			// FIXME: the tmp.doc.firstChild is as tested from IE, so it may not
3734
			// work that way across the board
3735
			return ((tmp.document)&&
3736
				(tmp.document.firstChild ?  tmp.document.firstChild : tmp));	//	DOMDocument
3737
		}
3738
	}
3739
	return null;
3740
}
3741
 
3742
dojo.dom.prependChild = function(/*Element*/node, /*Element*/parent){
3743
	//	summary:
3744
	//		prepends node to parent's children nodes
3745
	if(parent.firstChild) {
3746
		parent.insertBefore(node, parent.firstChild);
3747
	} else {
3748
		parent.appendChild(node);
3749
	}
3750
	return true;	//	boolean
3751
}
3752
 
3753
dojo.dom.insertBefore = function(/*Node*/node, /*Node*/ref, /*boolean?*/force){
3754
	//	summary:
3755
	//		Try to insert node before ref
3756
	if(	(force != true)&&
3757
		(node === ref || node.nextSibling === ref)){ return false; }
3758
	var parent = ref.parentNode;
3759
	parent.insertBefore(node, ref);
3760
	return true;	//	boolean
3761
}
3762
 
3763
dojo.dom.insertAfter = function(/*Node*/node, /*Node*/ref, /*boolean?*/force){
3764
	//	summary:
3765
	//		Try to insert node after ref
3766
	var pn = ref.parentNode;
3767
	if(ref == pn.lastChild){
3768
		if((force != true)&&(node === ref)){
3769
			return false;	//	boolean
3770
		}
3771
		pn.appendChild(node);
3772
	}else{
3773
		return this.insertBefore(node, ref.nextSibling, force);	//	boolean
3774
	}
3775
	return true;	//	boolean
3776
}
3777
 
3778
dojo.dom.insertAtPosition = function(/*Node*/node, /*Node*/ref, /*string*/position){
3779
	//	summary:
3780
	//		attempt to insert node in relation to ref based on position
3781
	if((!node)||(!ref)||(!position)){
3782
		return false;	//	boolean
3783
	}
3784
	switch(position.toLowerCase()){
3785
		case "before":
3786
			return dojo.dom.insertBefore(node, ref);	//	boolean
3787
		case "after":
3788
			return dojo.dom.insertAfter(node, ref);		//	boolean
3789
		case "first":
3790
			if(ref.firstChild){
3791
				return dojo.dom.insertBefore(node, ref.firstChild);	//	boolean
3792
			}else{
3793
				ref.appendChild(node);
3794
				return true;	//	boolean
3795
			}
3796
			break;
3797
		default: // aka: last
3798
			ref.appendChild(node);
3799
			return true;	//	boolean
3800
	}
3801
}
3802
 
3803
dojo.dom.insertAtIndex = function(/*Node*/node, /*Element*/containingNode, /*number*/insertionIndex){
3804
	//	summary:
3805
	//		insert node into child nodes nodelist of containingNode at
3806
	//		insertionIndex. insertionIndex should be between 0 and
3807
	//		the number of the childNodes in containingNode. insertionIndex
3808
	//		specifys after how many childNodes in containingNode the node
3809
	//		shall be inserted. If 0 is given, node will be appended to
3810
	//		containingNode.
3811
	var siblingNodes = containingNode.childNodes;
3812
 
3813
	// if there aren't any kids yet, just add it to the beginning
3814
 
3815
	if (!siblingNodes.length || siblingNodes.length == insertionIndex){
3816
		containingNode.appendChild(node);
3817
		return true;	//	boolean
3818
	}
3819
 
3820
	if(insertionIndex == 0){
3821
		return dojo.dom.prependChild(node, containingNode);	//	boolean
3822
	}
3823
	// otherwise we need to walk the childNodes
3824
	// and find our spot
3825
 
3826
	return dojo.dom.insertAfter(node, siblingNodes[insertionIndex-1]);	//	boolean
3827
}
3828
 
3829
dojo.dom.textContent = function(/*Node*/node, /*string*/text){
3830
	//	summary:
3831
	//		implementation of the DOM Level 3 attribute; scan node for text
3832
	if (arguments.length>1) {
3833
		var _document = dojo.doc();
3834
		dojo.dom.replaceChildren(node, _document.createTextNode(text));
3835
		return text;	//	string
3836
	} else {
3837
		if(node.textContent != undefined){ //FF 1.5
3838
			return node.textContent;	//	string
3839
		}
3840
		var _result = "";
3841
		if (node == null) { return _result; }
3842
		for (var i = 0; i < node.childNodes.length; i++) {
3843
			switch (node.childNodes[i].nodeType) {
3844
				case 1: // ELEMENT_NODE
3845
				case 5: // ENTITY_REFERENCE_NODE
3846
					_result += dojo.dom.textContent(node.childNodes[i]);
3847
					break;
3848
				case 3: // TEXT_NODE
3849
				case 2: // ATTRIBUTE_NODE
3850
				case 4: // CDATA_SECTION_NODE
3851
					_result += node.childNodes[i].nodeValue;
3852
					break;
3853
				default:
3854
					break;
3855
			}
3856
		}
3857
		return _result;	//	string
3858
	}
3859
}
3860
 
3861
dojo.dom.hasParent = function(/*Node*/node){
3862
	//	summary:
3863
	//		returns whether or not node is a child of another node.
3864
	return Boolean(node && node.parentNode && dojo.dom.isNode(node.parentNode));	//	boolean
3865
}
3866
 
3867
/**
3868
 * Examples:
3869
 *
3870
 * myFooNode = <foo />
3871
 * isTag(myFooNode, "foo"); // returns "foo"
3872
 * isTag(myFooNode, "bar"); // returns ""
3873
 * isTag(myFooNode, "FOO"); // returns ""
3874
 * isTag(myFooNode, "hey", "foo", "bar"); // returns "foo"
3875
**/
3876
dojo.dom.isTag = function(/* Node */node /* ... */){
3877
	//	summary:
3878
	//		determines if node has any of the provided tag names and returns
3879
	//		the tag name that matches, empty string otherwise.
3880
	if(node && node.tagName) {
3881
		for(var i=1; i<arguments.length; i++){
3882
			if(node.tagName==String(arguments[i])){
3883
				return String(arguments[i]);	//	string
3884
			}
3885
		}
3886
	}
3887
	return "";	//	string
3888
}
3889
 
3890
dojo.dom.setAttributeNS = function(	/*Element*/elem, /*string*/namespaceURI,
3891
									/*string*/attrName, /*string*/attrValue){
3892
	//	summary:
3893
	//		implementation of DOM2 setAttributeNS that works cross browser.
3894
	if(elem == null || ((elem == undefined)&&(typeof elem == "undefined"))){
3895
		dojo.raise("No element given to dojo.dom.setAttributeNS");
3896
	}
3897
 
3898
	if(!((elem.setAttributeNS == undefined)&&(typeof elem.setAttributeNS == "undefined"))){ // w3c
3899
		elem.setAttributeNS(namespaceURI, attrName, attrValue);
3900
	}else{ // IE
3901
		// get a root XML document
3902
		var ownerDoc = elem.ownerDocument;
3903
		var attribute = ownerDoc.createNode(
3904
			2, // node type
3905
			attrName,
3906
			namespaceURI
3907
		);
3908
 
3909
		// set value
3910
		attribute.nodeValue = attrValue;
3911
 
3912
		// attach to element
3913
		elem.setAttributeNode(attribute);
3914
	}
3915
}
3916
 
3917
dojo.provide("dojo.undo.browser");
3918
 
3919
 
3920
try{
3921
	if((!djConfig["preventBackButtonFix"])&&(!dojo.hostenv.post_load_)){
3922
		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>");
3923
	}
3924
}catch(e){/* squelch */}
3925
 
3926
if(dojo.render.html.opera){
3927
	dojo.debug("Opera is not supported with dojo.undo.browser, so back/forward detection will not work.");
3928
}
3929
 
3930
dojo.undo.browser = {
3931
	initialHref: (!dj_undef("window")) ? window.location.href : "",
3932
	initialHash: (!dj_undef("window")) ? window.location.hash : "",
3933
 
3934
	moveForward: false,
3935
	historyStack: [],
3936
	forwardStack: [],
3937
	historyIframe: null,
3938
	bookmarkAnchor: null,
3939
	locationTimer: null,
3940
 
3941
	/**
3942
	 *
3943
	 */
3944
	setInitialState: function(/*Object*/args){
3945
		//summary: Sets the state object and back callback for the very first page that is loaded.
3946
		//description: It is recommended that you call this method as part of an event listener that is registered via
3947
		//dojo.addOnLoad().
3948
		//args: Object
3949
		//		See the addToHistory() function for the list of valid args properties.
3950
		this.initialState = this._createState(this.initialHref, args, this.initialHash);
3951
	},
3952
 
3953
	//FIXME: Would like to support arbitrary back/forward jumps. Have to rework iframeLoaded among other things.
3954
	//FIXME: is there a slight race condition in moz using change URL with the timer check and when
3955
	//       the hash gets set? I think I have seen a back/forward call in quick succession, but not consistent.
3956
	addToHistory: function(args){
3957
		//summary: adds a state object (args) to the history list. You must set
3958
		//djConfig.preventBackButtonFix = false to use dojo.undo.browser.
3959
 
3960
		//args: Object
3961
		//		args can have the following properties:
3962
		//		To support getting back button notifications, the object argument should implement a
3963
		//		function called either "back", "backButton", or "handle". The string "back" will be
3964
		//		passed as the first and only argument to this callback.
3965
		//		- To support getting forward button notifications, the object argument should implement a
3966
		//		function called either "forward", "forwardButton", or "handle". The string "forward" will be
3967
		//		passed as the first and only argument to this callback.
3968
		//		- If you want the browser location string to change, define "changeUrl" on the object. If the
3969
		//		value of "changeUrl" is true, then a unique number will be appended to the URL as a fragment
3970
		//		identifier (http://some.domain.com/path#uniquenumber). If it is any other value that does
3971
		//		not evaluate to false, that value will be used as the fragment identifier. For example,
3972
		//		if changeUrl: 'page1', then the URL will look like: http://some.domain.com/path#page1
3973
	 	//		Full example:
3974
		//		dojo.undo.browser.addToHistory({
3975
		//		  back: function() { alert('back pressed'); },
3976
		//		  forward: function() { alert('forward pressed'); },
3977
		//		  changeUrl: true
3978
		//		});
3979
		//
3980
		//	BROWSER NOTES:
3981
		//  Safari 1.2:
3982
		//	back button "works" fine, however it's not possible to actually
3983
		//	DETECT that you've moved backwards by inspecting window.location.
3984
		//	Unless there is some other means of locating.
3985
		//	FIXME: perhaps we can poll on history.length?
3986
		//	Safari 2.0.3+ (and probably 1.3.2+):
3987
		//	works fine, except when changeUrl is used. When changeUrl is used,
3988
		//	Safari jumps all the way back to whatever page was shown before
3989
		//	the page that uses dojo.undo.browser support.
3990
		//	IE 5.5 SP2:
3991
		//	back button behavior is macro. It does not move back to the
3992
		//	previous hash value, but to the last full page load. This suggests
3993
		//	that the iframe is the correct way to capture the back button in
3994
		//	these cases.
3995
		//	Don't test this page using local disk for MSIE. MSIE will not create
3996
		//	a history list for iframe_history.html if served from a file: URL.
3997
		//	The XML served back from the XHR tests will also not be properly
3998
		//	created if served from local disk. Serve the test pages from a web
3999
		//	server to test in that browser.
4000
		//	IE 6.0:
4001
		//	same behavior as IE 5.5 SP2
4002
		//	Firefox 1.0+:
4003
		//	the back button will return us to the previous hash on the same
4004
		//	page, thereby not requiring an iframe hack, although we do then
4005
		//	need to run a timer to detect inter-page movement.
4006
 
4007
		//If addToHistory is called, then that means we prune the
4008
		//forward stack -- the user went back, then wanted to
4009
		//start a new forward path.
4010
		this.forwardStack = [];
4011
 
4012
		var hash = null;
4013
		var url = null;
4014
		if(!this.historyIframe){
4015
			if(djConfig["useXDomain"] && !djConfig["dojoIframeHistoryUrl"]){
4016
				dojo.debug("dojo.undo.browser: When using cross-domain Dojo builds,"
4017
					+ " please save iframe_history.html to your domain and set djConfig.dojoIframeHistoryUrl"
4018
					+ " to the path on your domain to iframe_history.html");
4019
			}
4020
			this.historyIframe = window.frames["djhistory"];
4021
		}
4022
		if(!this.bookmarkAnchor){
4023
			this.bookmarkAnchor = document.createElement("a");
4024
			dojo.body().appendChild(this.bookmarkAnchor);
4025
			this.bookmarkAnchor.style.display = "none";
4026
		}
4027
		if(args["changeUrl"]){
4028
			hash = "#"+ ((args["changeUrl"]!==true) ? args["changeUrl"] : (new Date()).getTime());
4029
 
4030
			//If the current hash matches the new one, just replace the history object with
4031
			//this new one. It doesn't make sense to track different state objects for the same
4032
			//logical URL. This matches the browser behavior of only putting in one history
4033
			//item no matter how many times you click on the same #hash link, at least in Firefox
4034
			//and Safari, and there is no reliable way in those browsers to know if a #hash link
4035
			//has been clicked on multiple times. So making this the standard behavior in all browsers
4036
			//so that dojo.undo.browser's behavior is the same in all browsers.
4037
			if(this.historyStack.length == 0 && this.initialState.urlHash == hash){
4038
				this.initialState = this._createState(url, args, hash);
4039
				return;
4040
			}else if(this.historyStack.length > 0 && this.historyStack[this.historyStack.length - 1].urlHash == hash){
4041
				this.historyStack[this.historyStack.length - 1] = this._createState(url, args, hash);
4042
				return;
4043
			}
4044
 
4045
			this.changingUrl = true;
4046
			setTimeout("window.location.href = '"+hash+"'; dojo.undo.browser.changingUrl = false;", 1);
4047
			this.bookmarkAnchor.href = hash;
4048
 
4049
			if(dojo.render.html.ie){
4050
				url = this._loadIframeHistory();
4051
 
4052
				var oldCB = args["back"]||args["backButton"]||args["handle"];
4053
 
4054
				//The function takes handleName as a parameter, in case the
4055
				//callback we are overriding was "handle". In that case,
4056
				//we will need to pass the handle name to handle.
4057
				var tcb = function(handleName){
4058
					if(window.location.hash != ""){
4059
						setTimeout("window.location.href = '"+hash+"';", 1);
4060
					}
4061
					//Use apply to set "this" to args, and to try to avoid memory leaks.
4062
					oldCB.apply(this, [handleName]);
4063
				}
4064
 
4065
				//Set interceptor function in the right place.
4066
				if(args["back"]){
4067
					args.back = tcb;
4068
				}else if(args["backButton"]){
4069
					args.backButton = tcb;
4070
				}else if(args["handle"]){
4071
					args.handle = tcb;
4072
				}
4073
 
4074
				var oldFW = args["forward"]||args["forwardButton"]||args["handle"];
4075
 
4076
				//The function takes handleName as a parameter, in case the
4077
				//callback we are overriding was "handle". In that case,
4078
				//we will need to pass the handle name to handle.
4079
				var tfw = function(handleName){
4080
					if(window.location.hash != ""){
4081
						window.location.href = hash;
4082
					}
4083
					if(oldFW){ // we might not actually have one
4084
						//Use apply to set "this" to args, and to try to avoid memory leaks.
4085
						oldFW.apply(this, [handleName]);
4086
					}
4087
				}
4088
 
4089
				//Set interceptor function in the right place.
4090
				if(args["forward"]){
4091
					args.forward = tfw;
4092
				}else if(args["forwardButton"]){
4093
					args.forwardButton = tfw;
4094
				}else if(args["handle"]){
4095
					args.handle = tfw;
4096
				}
4097
 
4098
			}else if(dojo.render.html.moz){
4099
				// start the timer
4100
				if(!this.locationTimer){
4101
					this.locationTimer = setInterval("dojo.undo.browser.checkLocation();", 200);
4102
				}
4103
			}
4104
		}else{
4105
			url = this._loadIframeHistory();
4106
		}
4107
 
4108
		this.historyStack.push(this._createState(url, args, hash));
4109
	},
4110
 
4111
	checkLocation: function(){
4112
		//summary: private method. Do not call this directly.
4113
		if (!this.changingUrl){
4114
			var hsl = this.historyStack.length;
4115
 
4116
			if((window.location.hash == this.initialHash||window.location.href == this.initialHref)&&(hsl == 1)){
4117
				// FIXME: could this ever be a forward button?
4118
				// we can't clear it because we still need to check for forwards. Ugg.
4119
				// clearInterval(this.locationTimer);
4120
				this.handleBackButton();
4121
				return;
4122
			}
4123
 
4124
			// first check to see if we could have gone forward. We always halt on
4125
			// a no-hash item.
4126
			if(this.forwardStack.length > 0){
4127
				if(this.forwardStack[this.forwardStack.length-1].urlHash == window.location.hash){
4128
					this.handleForwardButton();
4129
					return;
4130
				}
4131
			}
4132
 
4133
			// ok, that didn't work, try someplace back in the history stack
4134
			if((hsl >= 2)&&(this.historyStack[hsl-2])){
4135
				if(this.historyStack[hsl-2].urlHash==window.location.hash){
4136
					this.handleBackButton();
4137
					return;
4138
				}
4139
			}
4140
		}
4141
	},
4142
 
4143
	iframeLoaded: function(evt, ifrLoc){
4144
		//summary: private method. Do not call this directly.
4145
		if(!dojo.render.html.opera){
4146
			var query = this._getUrlQuery(ifrLoc.href);
4147
			if(query == null){
4148
				// alert("iframeLoaded");
4149
				// we hit the end of the history, so we should go back
4150
				if(this.historyStack.length == 1){
4151
					this.handleBackButton();
4152
				}
4153
				return;
4154
			}
4155
			if(this.moveForward){
4156
				// we were expecting it, so it's not either a forward or backward movement
4157
				this.moveForward = false;
4158
				return;
4159
			}
4160
 
4161
			//Check the back stack first, since it is more likely.
4162
			//Note that only one step back or forward is supported.
4163
			if(this.historyStack.length >= 2 && query == this._getUrlQuery(this.historyStack[this.historyStack.length-2].url)){
4164
				this.handleBackButton();
4165
			}
4166
			else if(this.forwardStack.length > 0 && query == this._getUrlQuery(this.forwardStack[this.forwardStack.length-1].url)){
4167
				this.handleForwardButton();
4168
			}
4169
		}
4170
	},
4171
 
4172
	handleBackButton: function(){
4173
		//summary: private method. Do not call this directly.
4174
 
4175
		//The "current" page is always at the top of the history stack.
4176
		var current = this.historyStack.pop();
4177
		if(!current){ return; }
4178
		var last = this.historyStack[this.historyStack.length-1];
4179
		if(!last && this.historyStack.length == 0){
4180
			last = this.initialState;
4181
		}
4182
		if (last){
4183
			if(last.kwArgs["back"]){
4184
				last.kwArgs["back"]();
4185
			}else if(last.kwArgs["backButton"]){
4186
				last.kwArgs["backButton"]();
4187
			}else if(last.kwArgs["handle"]){
4188
				last.kwArgs.handle("back");
4189
			}
4190
		}
4191
		this.forwardStack.push(current);
4192
	},
4193
 
4194
	handleForwardButton: function(){
4195
		//summary: private method. Do not call this directly.
4196
 
4197
		var last = this.forwardStack.pop();
4198
		if(!last){ return; }
4199
		if(last.kwArgs["forward"]){
4200
			last.kwArgs.forward();
4201
		}else if(last.kwArgs["forwardButton"]){
4202
			last.kwArgs.forwardButton();
4203
		}else if(last.kwArgs["handle"]){
4204
			last.kwArgs.handle("forward");
4205
		}
4206
		this.historyStack.push(last);
4207
	},
4208
 
4209
	_createState: function(url, args, hash){
4210
		//summary: private method. Do not call this directly.
4211
 
4212
		return {"url": url, "kwArgs": args, "urlHash": hash};	//Object
4213
	},
4214
 
4215
	_getUrlQuery: function(url){
4216
		//summary: private method. Do not call this directly.
4217
		var segments = url.split("?");
4218
		if (segments.length < 2){
4219
			return null; //null
4220
		}
4221
		else{
4222
			return segments[1]; //String
4223
		}
4224
	},
4225
 
4226
	_loadIframeHistory: function(){
4227
		//summary: private method. Do not call this directly.
4228
		var url = (djConfig["dojoIframeHistoryUrl"] || dojo.hostenv.getBaseScriptUri()+'iframe_history.html')
4229
			+ "?" + (new Date()).getTime();
4230
		this.moveForward = true;
4231
		dojo.io.setIFrameSrc(this.historyIframe, url, false);
4232
		return url; //String
4233
	}
4234
}
4235
 
4236
dojo.provide("dojo.io.BrowserIO");
4237
 
4238
 
4239
 
4240
 
4241
 
4242
 
4243
 
4244
 
4245
if(!dj_undef("window")) {
4246
 
4247
dojo.io.checkChildrenForFile = function(/*DOMNode*/node){
4248
	//summary: Checks any child nodes of node for an input type="file" element.
4249
	var hasFile = false;
4250
	var inputs = node.getElementsByTagName("input");
4251
	dojo.lang.forEach(inputs, function(input){
4252
		if(hasFile){ return; }
4253
		if(input.getAttribute("type")=="file"){
4254
			hasFile = true;
4255
		}
4256
	});
4257
	return hasFile; //boolean
4258
}
4259
 
4260
dojo.io.formHasFile = function(/*DOMNode*/formNode){
4261
	//summary: Just calls dojo.io.checkChildrenForFile().
4262
	return dojo.io.checkChildrenForFile(formNode); //boolean
4263
}
4264
 
4265
dojo.io.updateNode = function(/*DOMNode*/node, /*String or Object*/urlOrArgs){
4266
	//summary: Updates a DOMnode with the result of a dojo.io.bind() call.
4267
	//node: DOMNode
4268
	//urlOrArgs: String or Object
4269
	//		Either a String that has an URL, or an object containing dojo.io.bind()
4270
	//		arguments.
4271
	node = dojo.byId(node);
4272
	var args = urlOrArgs;
4273
	if(dojo.lang.isString(urlOrArgs)){
4274
		args = { url: urlOrArgs };
4275
	}
4276
	args.mimetype = "text/html";
4277
	args.load = function(t, d, e){
4278
		while(node.firstChild){
4279
			dojo.dom.destroyNode(node.firstChild);
4280
		}
4281
		node.innerHTML = d;
4282
	};
4283
	dojo.io.bind(args);
4284
}
4285
 
4286
dojo.io.formFilter = function(/*DOMNode*/node) {
4287
	//summary: Returns true if the node is an input element that is enabled, has
4288
	//a name, and whose type is one of the following values: ["file", "submit", "image", "reset", "button"]
4289
	var type = (node.type||"").toLowerCase();
4290
	return !node.disabled && node.name
4291
		&& !dojo.lang.inArray(["file", "submit", "image", "reset", "button"], type); //boolean
4292
}
4293
 
4294
// TODO: Move to htmlUtils
4295
dojo.io.encodeForm = function(/*DOMNode*/formNode, /*String?*/encoding, /*Function?*/formFilter){
4296
	//summary: Converts the names and values of form elements into an URL-encoded
4297
	//string (name=value&name=value...).
4298
	//formNode: DOMNode
4299
	//encoding: String?
4300
	//		The encoding to use for the values. Specify a string that starts with
4301
	//		"utf" (for instance, "utf8"), to use encodeURIComponent() as the encoding
4302
	//		function. Otherwise, dojo.string.encodeAscii will be used.
4303
	//formFilter: Function?
4304
	//	A function used to filter out form elements. The element node will be passed
4305
	//	to the formFilter function, and a boolean result is expected (true indicating
4306
	//	indicating that the element should have its name/value included in the output).
4307
	//	If no formFilter is specified, then dojo.io.formFilter() will be used.
4308
	if((!formNode)||(!formNode.tagName)||(!formNode.tagName.toLowerCase() == "form")){
4309
		dojo.raise("Attempted to encode a non-form element.");
4310
	}
4311
	if(!formFilter) { formFilter = dojo.io.formFilter; }
4312
	var enc = /utf/i.test(encoding||"") ? encodeURIComponent : dojo.string.encodeAscii;
4313
	var values = [];
4314
 
4315
	for(var i = 0; i < formNode.elements.length; i++){
4316
		var elm = formNode.elements[i];
4317
		if(!elm || elm.tagName.toLowerCase() == "fieldset" || !formFilter(elm)) { continue; }
4318
		var name = enc(elm.name);
4319
		var type = elm.type.toLowerCase();
4320
 
4321
		if(type == "select-multiple"){
4322
			for(var j = 0; j < elm.options.length; j++){
4323
				if(elm.options[j].selected) {
4324
					values.push(name + "=" + enc(elm.options[j].value));
4325
				}
4326
			}
4327
		}else if(dojo.lang.inArray(["radio", "checkbox"], type)){
4328
			if(elm.checked){
4329
				values.push(name + "=" + enc(elm.value));
4330
			}
4331
		}else{
4332
			values.push(name + "=" + enc(elm.value));
4333
		}
4334
	}
4335
 
4336
	// now collect input type="image", which doesn't show up in the elements array
4337
	var inputs = formNode.getElementsByTagName("input");
4338
	for(var i = 0; i < inputs.length; i++) {
4339
		var input = inputs[i];
4340
		if(input.type.toLowerCase() == "image" && input.form == formNode
4341
			&& formFilter(input)) {
4342
			var name = enc(input.name);
4343
			values.push(name + "=" + enc(input.value));
4344
			values.push(name + ".x=0");
4345
			values.push(name + ".y=0");
4346
		}
4347
	}
4348
	return values.join("&") + "&"; //String
4349
}
4350
 
4351
dojo.io.FormBind = function(/*DOMNode or Object*/args) {
4352
	//summary: constructor for a dojo.io.FormBind object. See the Dojo Book for
4353
	//some information on usage: http://manual.dojotoolkit.org/WikiHome/DojoDotBook/Book23
4354
	//args: DOMNode or Object
4355
	//		args can either be the DOMNode for a form element, or an object containing
4356
	//		dojo.io.bind() arguments, one of which should be formNode with the value of
4357
	//		a form element DOMNode.
4358
	this.bindArgs = {};
4359
 
4360
	if(args && args.formNode) {
4361
		this.init(args);
4362
	} else if(args) {
4363
		this.init({formNode: args});
4364
	}
4365
}
4366
dojo.lang.extend(dojo.io.FormBind, {
4367
	form: null,
4368
 
4369
	bindArgs: null,
4370
 
4371
	clickedButton: null,
4372
 
4373
	init: function(/*DOMNode or Object*/args) {
4374
		//summary: Internal function called by the dojo.io.FormBind() constructor
4375
		//do not call this method directly.
4376
		var form = dojo.byId(args.formNode);
4377
 
4378
		if(!form || !form.tagName || form.tagName.toLowerCase() != "form") {
4379
			throw new Error("FormBind: Couldn't apply, invalid form");
4380
		} else if(this.form == form) {
4381
			return;
4382
		} else if(this.form) {
4383
			throw new Error("FormBind: Already applied to a form");
4384
		}
4385
 
4386
		dojo.lang.mixin(this.bindArgs, args);
4387
		this.form = form;
4388
 
4389
		this.connect(form, "onsubmit", "submit");
4390
 
4391
		for(var i = 0; i < form.elements.length; i++) {
4392
			var node = form.elements[i];
4393
			if(node && node.type && dojo.lang.inArray(["submit", "button"], node.type.toLowerCase())) {
4394
				this.connect(node, "onclick", "click");
4395
			}
4396
		}
4397
 
4398
		var inputs = form.getElementsByTagName("input");
4399
		for(var i = 0; i < inputs.length; i++) {
4400
			var input = inputs[i];
4401
			if(input.type.toLowerCase() == "image" && input.form == form) {
4402
				this.connect(input, "onclick", "click");
4403
			}
4404
		}
4405
	},
4406
 
4407
	onSubmit: function(/*DOMNode*/form) {
4408
		//summary: Function used to verify that the form is OK to submit.
4409
		//Override this function if you want specific form validation done.
4410
		return true; //boolean
4411
	},
4412
 
4413
	submit: function(/*Event*/e) {
4414
		//summary: internal function that is connected as a listener to the
4415
		//form's onsubmit event.
4416
		e.preventDefault();
4417
		if(this.onSubmit(this.form)) {
4418
			dojo.io.bind(dojo.lang.mixin(this.bindArgs, {
4419
				formFilter: dojo.lang.hitch(this, "formFilter")
4420
			}));
4421
		}
4422
	},
4423
 
4424
	click: function(/*Event*/e) {
4425
		//summary: internal method that is connected as a listener to the
4426
		//form's elements whose click event can submit a form.
4427
		var node = e.currentTarget;
4428
		if(node.disabled) { return; }
4429
		this.clickedButton = node;
4430
	},
4431
 
4432
	formFilter: function(/*DOMNode*/node) {
4433
		//summary: internal function used to know which form element values to include
4434
		//		in the dojo.io.bind() request.
4435
		var type = (node.type||"").toLowerCase();
4436
		var accept = false;
4437
		if(node.disabled || !node.name) {
4438
			accept = false;
4439
		} else if(dojo.lang.inArray(["submit", "button", "image"], type)) {
4440
			if(!this.clickedButton) { this.clickedButton = node; }
4441
			accept = node == this.clickedButton;
4442
		} else {
4443
			accept = !dojo.lang.inArray(["file", "submit", "reset", "button"], type);
4444
		}
4445
		return accept; //boolean
4446
	},
4447
 
4448
	// in case you don't have dojo.event.* pulled in
4449
	connect: function(/*Object*/srcObj, /*Function*/srcFcn, /*Function*/targetFcn) {
4450
		//summary: internal function used to connect event listeners to form elements
4451
		//that trigger events. Used in case dojo.event is not loaded.
4452
		if(dojo.evalObjPath("dojo.event.connect")) {
4453
			dojo.event.connect(srcObj, srcFcn, this, targetFcn);
4454
		} else {
4455
			var fcn = dojo.lang.hitch(this, targetFcn);
4456
			srcObj[srcFcn] = function(e) {
4457
				if(!e) { e = window.event; }
4458
				if(!e.currentTarget) { e.currentTarget = e.srcElement; }
4459
				if(!e.preventDefault) { e.preventDefault = function() { window.event.returnValue = false; } }
4460
				fcn(e);
4461
			}
4462
		}
4463
	}
4464
});
4465
 
4466
dojo.io.XMLHTTPTransport = new function(){
4467
	//summary: The object that implements the dojo.io.bind transport for XMLHttpRequest.
4468
	var _this = this;
4469
 
4470
	var _cache = {}; // FIXME: make this public? do we even need to?
4471
	this.useCache = false; // if this is true, we'll cache unless kwArgs.useCache = false
4472
	this.preventCache = false; // if this is true, we'll always force GET requests to cache
4473
 
4474
	// FIXME: Should this even be a function? or do we just hard code it in the next 2 functions?
4475
	function getCacheKey(url, query, method) {
4476
		return url + "|" + query + "|" + method.toLowerCase();
4477
	}
4478
 
4479
	function addToCache(url, query, method, http) {
4480
		_cache[getCacheKey(url, query, method)] = http;
4481
	}
4482
 
4483
	function getFromCache(url, query, method) {
4484
		return _cache[getCacheKey(url, query, method)];
4485
	}
4486
 
4487
	this.clearCache = function() {
4488
		_cache = {};
4489
	}
4490
 
4491
	// moved successful load stuff here
4492
	function doLoad(kwArgs, http, url, query, useCache) {
4493
		if(	((http.status>=200)&&(http.status<300))|| 	// allow any 2XX response code
4494
			(http.status==304)|| 						// get it out of the cache
4495
			(location.protocol=="file:" && (http.status==0 || http.status==undefined))||
4496
			(location.protocol=="chrome:" && (http.status==0 || http.status==undefined))
4497
		){
4498
			var ret;
4499
			if(kwArgs.method.toLowerCase() == "head"){
4500
				var headers = http.getAllResponseHeaders();
4501
				ret = {};
4502
				ret.toString = function(){ return headers; }
4503
				var values = headers.split(/[\r\n]+/g);
4504
				for(var i = 0; i < values.length; i++) {
4505
					var pair = values[i].match(/^([^:]+)\s*:\s*(.+)$/i);
4506
					if(pair) {
4507
						ret[pair[1]] = pair[2];
4508
					}
4509
				}
4510
			}else if(kwArgs.mimetype == "text/javascript"){
4511
				try{
4512
					ret = dj_eval(http.responseText);
4513
				}catch(e){
4514
					dojo.debug(e);
4515
					dojo.debug(http.responseText);
4516
					ret = null;
4517
				}
4518
			}else if(kwArgs.mimetype == "text/json" || kwArgs.mimetype == "application/json"){
4519
				try{
4520
					ret = dj_eval("("+http.responseText+")");
4521
				}catch(e){
4522
					dojo.debug(e);
4523
					dojo.debug(http.responseText);
4524
					ret = false;
4525
				}
4526
			}else if((kwArgs.mimetype == "application/xml")||
4527
						(kwArgs.mimetype == "text/xml")){
4528
				ret = http.responseXML;
4529
				if(!ret || typeof ret == "string" || !http.getResponseHeader("Content-Type")) {
4530
					ret = dojo.dom.createDocumentFromText(http.responseText);
4531
				}
4532
			}else{
4533
				ret = http.responseText;
4534
			}
4535
 
4536
			if(useCache){ // only cache successful responses
4537
				addToCache(url, query, kwArgs.method, http);
4538
			}
4539
			kwArgs[(typeof kwArgs.load == "function") ? "load" : "handle"]("load", ret, http, kwArgs);
4540
		}else{
4541
			var errObj = new dojo.io.Error("XMLHttpTransport Error: "+http.status+" "+http.statusText);
4542
			kwArgs[(typeof kwArgs.error == "function") ? "error" : "handle"]("error", errObj, http, kwArgs);
4543
		}
4544
	}
4545
 
4546
	// set headers (note: Content-Type will get overriden if kwArgs.contentType is set)
4547
	function setHeaders(http, kwArgs){
4548
		if(kwArgs["headers"]) {
4549
			for(var header in kwArgs["headers"]) {
4550
				if(header.toLowerCase() == "content-type" && !kwArgs["contentType"]) {
4551
					kwArgs["contentType"] = kwArgs["headers"][header];
4552
				} else {
4553
					http.setRequestHeader(header, kwArgs["headers"][header]);
4554
				}
4555
			}
4556
		}
4557
	}
4558
 
4559
	this.inFlight = [];
4560
	this.inFlightTimer = null;
4561
 
4562
	this.startWatchingInFlight = function(){
4563
		//summary: internal method used to trigger a timer to watch all inflight
4564
		//XMLHttpRequests.
4565
		if(!this.inFlightTimer){
4566
			// setInterval broken in mozilla x86_64 in some circumstances, see
4567
			// https://bugzilla.mozilla.org/show_bug.cgi?id=344439
4568
			// using setTimeout instead
4569
			this.inFlightTimer = setTimeout("dojo.io.XMLHTTPTransport.watchInFlight();", 10);
4570
		}
4571
	}
4572
 
4573
	this.watchInFlight = function(){
4574
		//summary: internal method that checks each inflight XMLHttpRequest to see
4575
		//if it has completed or if the timeout situation applies.
4576
		var now = null;
4577
		// make sure sync calls stay thread safe, if this callback is called during a sync call
4578
		// and this results in another sync call before the first sync call ends the browser hangs
4579
		if(!dojo.hostenv._blockAsync && !_this._blockAsync){
4580
			for(var x=this.inFlight.length-1; x>=0; x--){
4581
				try{
4582
					var tif = this.inFlight[x];
4583
					if(!tif || tif.http._aborted || !tif.http.readyState){
4584
						this.inFlight.splice(x, 1); continue;
4585
					}
4586
					if(4==tif.http.readyState){
4587
						// remove it so we can clean refs
4588
						this.inFlight.splice(x, 1);
4589
						doLoad(tif.req, tif.http, tif.url, tif.query, tif.useCache);
4590
					}else if (tif.startTime){
4591
						//See if this is a timeout case.
4592
						if(!now){
4593
							now = (new Date()).getTime();
4594
						}
4595
						if(tif.startTime + (tif.req.timeoutSeconds * 1000) < now){
4596
							//Stop the request.
4597
							if(typeof tif.http.abort == "function"){
4598
								tif.http.abort();
4599
							}
4600
 
4601
							// remove it so we can clean refs
4602
							this.inFlight.splice(x, 1);
4603
							tif.req[(typeof tif.req.timeout == "function") ? "timeout" : "handle"]("timeout", null, tif.http, tif.req);
4604
						}
4605
					}
4606
				}catch(e){
4607
					try{
4608
						var errObj = new dojo.io.Error("XMLHttpTransport.watchInFlight Error: " + e);
4609
						tif.req[(typeof tif.req.error == "function") ? "error" : "handle"]("error", errObj, tif.http, tif.req);
4610
					}catch(e2){
4611
						dojo.debug("XMLHttpTransport error callback failed: " + e2);
4612
					}
4613
				}
4614
			}
4615
		}
4616
 
4617
		clearTimeout(this.inFlightTimer);
4618
		if(this.inFlight.length == 0){
4619
			this.inFlightTimer = null;
4620
			return;
4621
		}
4622
		this.inFlightTimer = setTimeout("dojo.io.XMLHTTPTransport.watchInFlight();", 10);
4623
	}
4624
 
4625
	var hasXmlHttp = dojo.hostenv.getXmlhttpObject() ? true : false;
4626
	this.canHandle = function(/*dojo.io.Request*/kwArgs){
4627
		//summary: Tells dojo.io.bind() if this is a good transport to
4628
		//use for the particular type of request. This type of transport cannot
4629
		//handle forms that have an input type="file" element.
4630
 
4631
		// FIXME: we need to determine when form values need to be
4632
		// multi-part mime encoded and avoid using this transport for those
4633
		// requests.
4634
		return hasXmlHttp
4635
			&& dojo.lang.inArray(["text/plain", "text/html", "application/xml", "text/xml", "text/javascript", "text/json", "application/json"], (kwArgs["mimetype"].toLowerCase()||""))
4636
			&& !( kwArgs["formNode"] && dojo.io.formHasFile(kwArgs["formNode"]) ); //boolean
4637
	}
4638
 
4639
	this.multipartBoundary = "45309FFF-BD65-4d50-99C9-36986896A96F";	// unique guid as a boundary value for multipart posts
4640
 
4641
	this.bind = function(/*dojo.io.Request*/kwArgs){
4642
		//summary: function that sends the request to the server.
4643
 
4644
		//This function will attach an abort() function to the kwArgs dojo.io.Request object,
4645
		//so if you need to abort the request, you can call that method on the request object.
4646
		//The following are acceptable properties in kwArgs (in addition to the
4647
		//normal dojo.io.Request object properties).
4648
		//url: String: URL the server URL to use for the request.
4649
		//method: String: the HTTP method to use (GET, POST, etc...).
4650
		//mimetype: Specifies what format the result data should be given to the load/handle callback. Valid values are:
4651
		//		text/javascript, text/json, application/json, application/xml, text/xml. Any other mimetype will give back a text
4652
		//		string.
4653
		//transport: String: specify "XMLHTTPTransport" to force the use of this XMLHttpRequest transport.
4654
		//headers: Object: The object property names and values will be sent as HTTP request header
4655
		//		names and values.
4656
		//sendTransport: boolean: If true, then dojo.transport=xmlhttp will be added to the request.
4657
		//encoding: String: The type of encoding to use when dealing with the content kwArgs property.
4658
		//content: Object: The content object is converted into a name=value&name=value string, by
4659
		//		using dojo.io.argsFromMap(). The encoding kwArgs property is passed to dojo.io.argsFromMap()
4660
		//		for use in encoding the names and values. The resulting string is added to the request.
4661
		//formNode: DOMNode: a form element node. This should not normally be used. Use new dojo.io.FormBind() instead.
4662
		//		If formNode is used, then the names and values of the form elements will be converted
4663
		//		to a name=value&name=value string and added to the request. The encoding kwArgs property is used
4664
		//		to encode the names and values.
4665
		//postContent: String: Raw name=value&name=value string to be included as part of the request.
4666
		//back or backButton: Function: A function to be called if the back button is pressed. If this kwArgs property
4667
		//		is used, then back button support via dojo.undo.browser will be used. See notes for dojo.undo.browser on usage.
4668
		//		You need to set djConfig.preventBackButtonFix = false to enable back button support.
4669
		//changeUrl: boolean or String: Used as part of back button support. See notes for dojo.undo.browser on usage.
4670
		//user: String: The user name. Used in conjuction with password. Passed to XMLHttpRequest.open().
4671
		//password: String: The user's password. Used in conjuction with user. Passed to XMLHttpRequest.open().
4672
		//file: Object or Array of Objects: an object simulating a file to be uploaded. file objects should have the following properties:
4673
		//		name or fileName: the name of the file
4674
		//		contentType: the MIME content type for the file.
4675
		//		content: the actual content of the file.
4676
		//multipart: boolean: indicates whether this should be a multipart mime request. If kwArgs.file exists, then this
4677
		//		property is set to true automatically.
4678
		//sync: boolean: if true, then a synchronous XMLHttpRequest call is done,
4679
		//		if false (the default), then an asynchronous call is used.
4680
		//preventCache: boolean: If true, then a cache busting parameter is added to the request URL.
4681
		//		default value is false.
4682
		//useCache: boolean: If true, then XMLHttpTransport will keep an internal cache of the server
4683
		//		response and use that response if a similar request is done again.
4684
		//		A similar request is one that has the same URL, query string and HTTP method value.
4685
		//		default is false.
4686
		if(!kwArgs["url"]){
4687
			// are we performing a history action?
4688
			if( !kwArgs["formNode"]
4689
				&& (kwArgs["backButton"] || kwArgs["back"] || kwArgs["changeUrl"] || kwArgs["watchForURL"])
4690
				&& (!djConfig.preventBackButtonFix)) {
4691
        dojo.deprecated("Using dojo.io.XMLHTTPTransport.bind() to add to browser history without doing an IO request",
4692
        				"Use dojo.undo.browser.addToHistory() instead.", "0.4");
4693
				dojo.undo.browser.addToHistory(kwArgs);
4694
				return true;
4695
			}
4696
		}
4697
 
4698
		// build this first for cache purposes
4699
		var url = kwArgs.url;
4700
		var query = "";
4701
		if(kwArgs["formNode"]){
4702
			var ta = kwArgs.formNode.getAttribute("action");
4703
			if((ta)&&(!kwArgs["url"])){ url = ta; }
4704
			var tp = kwArgs.formNode.getAttribute("method");
4705
			if((tp)&&(!kwArgs["method"])){ kwArgs.method = tp; }
4706
			query += dojo.io.encodeForm(kwArgs.formNode, kwArgs.encoding, kwArgs["formFilter"]);
4707
		}
4708
 
4709
		if(url.indexOf("#") > -1) {
4710
			dojo.debug("Warning: dojo.io.bind: stripping hash values from url:", url);
4711
			url = url.split("#")[0];
4712
		}
4713
 
4714
		if(kwArgs["file"]){
4715
			// force post for file transfer
4716
			kwArgs.method = "post";
4717
		}
4718
 
4719
		if(!kwArgs["method"]){
4720
			kwArgs.method = "get";
4721
		}
4722
 
4723
		// guess the multipart value
4724
		if(kwArgs.method.toLowerCase() == "get"){
4725
			// GET cannot use multipart
4726
			kwArgs.multipart = false;
4727
		}else{
4728
			if(kwArgs["file"]){
4729
				// enforce multipart when sending files
4730
				kwArgs.multipart = true;
4731
			}else if(!kwArgs["multipart"]){
4732
				// default
4733
				kwArgs.multipart = false;
4734
			}
4735
		}
4736
 
4737
		if(kwArgs["backButton"] || kwArgs["back"] || kwArgs["changeUrl"]){
4738
			dojo.undo.browser.addToHistory(kwArgs);
4739
		}
4740
 
4741
		var content = kwArgs["content"] || {};
4742
 
4743
		if(kwArgs.sendTransport) {
4744
			content["dojo.transport"] = "xmlhttp";
4745
		}
4746
 
4747
		do { // break-block
4748
			if(kwArgs.postContent){
4749
				query = kwArgs.postContent;
4750
				break;
4751
			}
4752
 
4753
			if(content) {
4754
				query += dojo.io.argsFromMap(content, kwArgs.encoding);
4755
			}
4756
 
4757
			if(kwArgs.method.toLowerCase() == "get" || !kwArgs.multipart){
4758
				break;
4759
			}
4760
 
4761
			var	t = [];
4762
			if(query.length){
4763
				var q = query.split("&");
4764
				for(var i = 0; i < q.length; ++i){
4765
					if(q[i].length){
4766
						var p = q[i].split("=");
4767
						t.push(	"--" + this.multipartBoundary,
4768
								"Content-Disposition: form-data; name=\"" + p[0] + "\"",
4769
								"",
4770
								p[1]);
4771
					}
4772
				}
4773
			}
4774
 
4775
			if(kwArgs.file){
4776
				if(dojo.lang.isArray(kwArgs.file)){
4777
					for(var i = 0; i < kwArgs.file.length; ++i){
4778
						var o = kwArgs.file[i];
4779
						t.push(	"--" + this.multipartBoundary,
4780
								"Content-Disposition: form-data; name=\"" + o.name + "\"; filename=\"" + ("fileName" in o ? o.fileName : o.name) + "\"",
4781
								"Content-Type: " + ("contentType" in o ? o.contentType : "application/octet-stream"),
4782
								"",
4783
								o.content);
4784
					}
4785
				}else{
4786
					var o = kwArgs.file;
4787
					t.push(	"--" + this.multipartBoundary,
4788
							"Content-Disposition: form-data; name=\"" + o.name + "\"; filename=\"" + ("fileName" in o ? o.fileName : o.name) + "\"",
4789
							"Content-Type: " + ("contentType" in o ? o.contentType : "application/octet-stream"),
4790
							"",
4791
							o.content);
4792
				}
4793
			}
4794
 
4795
			if(t.length){
4796
				t.push("--"+this.multipartBoundary+"--", "");
4797
				query = t.join("\r\n");
4798
			}
4799
		}while(false);
4800
 
4801
		// kwArgs.Connection = "close";
4802
 
4803
		var async = kwArgs["sync"] ? false : true;
4804
 
4805
		var preventCache = kwArgs["preventCache"] ||
4806
			(this.preventCache == true && kwArgs["preventCache"] != false);
4807
		var useCache = kwArgs["useCache"] == true ||
4808
			(this.useCache == true && kwArgs["useCache"] != false );
4809
 
4810
		// preventCache is browser-level (add query string junk), useCache
4811
		// is for the local cache. If we say preventCache, then don't attempt
4812
		// to look in the cache, but if useCache is true, we still want to cache
4813
		// the response
4814
		if(!preventCache && useCache){
4815
			var cachedHttp = getFromCache(url, query, kwArgs.method);
4816
			if(cachedHttp){
4817
				doLoad(kwArgs, cachedHttp, url, query, false);
4818
				return;
4819
			}
4820
		}
4821
 
4822
		// much of this is from getText, but reproduced here because we need
4823
		// more flexibility
4824
		var http = dojo.hostenv.getXmlhttpObject(kwArgs);
4825
		var received = false;
4826
 
4827
		// build a handler function that calls back to the handler obj
4828
		if(async){
4829
			var startTime =
4830
			// FIXME: setting up this callback handler leaks on IE!!!
4831
			this.inFlight.push({
4832
				"req":		kwArgs,
4833
				"http":		http,
4834
				"url":	 	url,
4835
				"query":	query,
4836
				"useCache":	useCache,
4837
				"startTime": kwArgs.timeoutSeconds ? (new Date()).getTime() : 0
4838
			});
4839
			this.startWatchingInFlight();
4840
		}else{
4841
			// block async callbacks until sync is in, needed in khtml, others?
4842
			_this._blockAsync = true;
4843
		}
4844
 
4845
		if(kwArgs.method.toLowerCase() == "post"){
4846
			// FIXME: need to hack in more flexible Content-Type setting here!
4847
			if (!kwArgs.user) {
4848
				http.open("POST", url, async);
4849
			}else{
4850
        http.open("POST", url, async, kwArgs.user, kwArgs.password);
4851
			}
4852
			setHeaders(http, kwArgs);
4853
			http.setRequestHeader("Content-Type", kwArgs.multipart ? ("multipart/form-data; boundary=" + this.multipartBoundary) :
4854
				(kwArgs.contentType || "application/x-www-form-urlencoded"));
4855
			try{
4856
				http.send(query);
4857
			}catch(e){
4858
				if(typeof http.abort == "function"){
4859
					http.abort();
4860
				}
4861
				doLoad(kwArgs, {status: 404}, url, query, useCache);
4862
			}
4863
		}else{
4864
			var tmpUrl = url;
4865
			if(query != "") {
4866
				tmpUrl += (tmpUrl.indexOf("?") > -1 ? "&" : "?") + query;
4867
			}
4868
			if(preventCache) {
4869
				tmpUrl += (dojo.string.endsWithAny(tmpUrl, "?", "&")
4870
					? "" : (tmpUrl.indexOf("?") > -1 ? "&" : "?")) + "dojo.preventCache=" + new Date().valueOf();
4871
			}
4872
			if (!kwArgs.user) {
4873
				http.open(kwArgs.method.toUpperCase(), tmpUrl, async);
4874
			}else{
4875
				http.open(kwArgs.method.toUpperCase(), tmpUrl, async, kwArgs.user, kwArgs.password);
4876
			}
4877
			setHeaders(http, kwArgs);
4878
			try {
4879
				http.send(null);
4880
			}catch(e)	{
4881
				if(typeof http.abort == "function"){
4882
					http.abort();
4883
				}
4884
				doLoad(kwArgs, {status: 404}, url, query, useCache);
4885
			}
4886
		}
4887
 
4888
		if( !async ) {
4889
			doLoad(kwArgs, http, url, query, useCache);
4890
			_this._blockAsync = false;
4891
		}
4892
 
4893
		kwArgs.abort = function(){
4894
			try{// khtml doesent reset readyState on abort, need this workaround
4895
				http._aborted = true;
4896
			}catch(e){/*squelsh*/}
4897
			return http.abort();
4898
		}
4899
 
4900
		return;
4901
	}
4902
	dojo.io.transports.addTransport("XMLHTTPTransport");
4903
}
4904
 
4905
}
4906
 
4907
dojo.provide("dojo.io.cookie");
4908
 
4909
dojo.io.cookie.setCookie = function(/*String*/name, /*String*/value,
4910
									/*Number?*/days, /*String?*/path,
4911
									/*String?*/domain, /*boolean?*/secure){
4912
	//summary: sets a cookie.
4913
	var expires = -1;
4914
	if((typeof days == "number")&&(days >= 0)){
4915
		var d = new Date();
4916
		d.setTime(d.getTime()+(days*24*60*60*1000));
4917
		expires = d.toGMTString();
4918
	}
4919
	value = escape(value);
4920
	document.cookie = name + "=" + value + ";"
4921
		+ (expires != -1 ? " expires=" + expires + ";" : "")
4922
		+ (path ? "path=" + path : "")
4923
		+ (domain ? "; domain=" + domain : "")
4924
		+ (secure ? "; secure" : "");
4925
}
4926
 
4927
dojo.io.cookie.set = dojo.io.cookie.setCookie;
4928
 
4929
dojo.io.cookie.getCookie = function(/*String*/name){
4930
	//summary: Gets a cookie with the given name.
4931
 
4932
	// FIXME: Which cookie should we return?
4933
	//        If there are cookies set for different sub domains in the current
4934
	//        scope there could be more than one cookie with the same name.
4935
	//        I think taking the last one in the list takes the one from the
4936
	//        deepest subdomain, which is what we're doing here.
4937
	var idx = document.cookie.lastIndexOf(name+'=');
4938
	if(idx == -1) { return null; }
4939
	var value = document.cookie.substring(idx+name.length+1);
4940
	var end = value.indexOf(';');
4941
	if(end == -1) { end = value.length; }
4942
	value = value.substring(0, end);
4943
	value = unescape(value);
4944
	return value; //String
4945
}
4946
 
4947
dojo.io.cookie.get = dojo.io.cookie.getCookie;
4948
 
4949
dojo.io.cookie.deleteCookie = function(/*String*/name){
4950
	//summary: Deletes a cookie with the given name.
4951
	dojo.io.cookie.setCookie(name, "-", 0);
4952
}
4953
 
4954
dojo.io.cookie.setObjectCookie = function(	/*String*/name, /*Object*/obj,
4955
											/*Number?*/days, /*String?*/path,
4956
											/*String?*/domain, /*boolean?*/secure,
4957
											/*boolean?*/clearCurrent){
4958
	//summary: Takes an object, serializes it to a cookie value, and either
4959
	//sets a cookie with the serialized value.
4960
	//description: If clearCurrent is true, then any current cookie value
4961
	//for this object will be replaced with the the new serialized object value.
4962
	//If clearCurrent is false, then the existing cookie value will be modified
4963
	//with any changes from the new object value.
4964
	//Objects must be simple name/value pairs where the value is either a string
4965
	//or a number. Any other value will be ignored.
4966
	if(arguments.length == 5){ // for backwards compat
4967
		clearCurrent = domain;
4968
		domain = null;
4969
		secure = null;
4970
	}
4971
	var pairs = [], cookie, value = "";
4972
	if(!clearCurrent){
4973
		cookie = dojo.io.cookie.getObjectCookie(name);
4974
	}
4975
	if(days >= 0){
4976
		if(!cookie){ cookie = {}; }
4977
		for(var prop in obj){
4978
			if(obj[prop] == null){
4979
				delete cookie[prop];
4980
			}else if((typeof obj[prop] == "string")||(typeof obj[prop] == "number")){
4981
				cookie[prop] = obj[prop];
4982
			}
4983
		}
4984
		prop = null;
4985
		for(var prop in cookie){
4986
			pairs.push(escape(prop) + "=" + escape(cookie[prop]));
4987
		}
4988
		value = pairs.join("&");
4989
	}
4990
	dojo.io.cookie.setCookie(name, value, days, path, domain, secure);
4991
}
4992
 
4993
dojo.io.cookie.getObjectCookie = function(/*String*/name){
4994
	//summary: Gets an object value for the given cookie name. The complement of
4995
	//dojo.io.cookie.setObjectCookie().
4996
	var values = null, cookie = dojo.io.cookie.getCookie(name);
4997
	if(cookie){
4998
		values = {};
4999
		var pairs = cookie.split("&");
5000
		for(var i = 0; i < pairs.length; i++){
5001
			var pair = pairs[i].split("=");
5002
			var value = pair[1];
5003
			if( isNaN(value) ){ value = unescape(pair[1]); }
5004
			values[ unescape(pair[0]) ] = value;
5005
		}
5006
	}
5007
	return values;
5008
}
5009
 
5010
dojo.io.cookie.isSupported = function(){
5011
	//summary: Tests the browser to see if cookies are enabled.
5012
	if(typeof navigator.cookieEnabled != "boolean"){
5013
		dojo.io.cookie.setCookie("__TestingYourBrowserForCookieSupport__",
5014
			"CookiesAllowed", 90, null);
5015
		var cookieVal = dojo.io.cookie.getCookie("__TestingYourBrowserForCookieSupport__");
5016
		navigator.cookieEnabled = (cookieVal == "CookiesAllowed");
5017
		if(navigator.cookieEnabled){
5018
			// FIXME: should we leave this around?
5019
			this.deleteCookie("__TestingYourBrowserForCookieSupport__");
5020
		}
5021
	}
5022
	return navigator.cookieEnabled; //boolean
5023
}
5024
 
5025
// need to leave this in for backwards-compat from 0.1 for when it gets pulled in by dojo.io.*
5026
if(!dojo.io.cookies){ dojo.io.cookies = dojo.io.cookie; }
5027
 
5028
dojo.kwCompoundRequire({
5029
	common: ["dojo.io.common"],
5030
	rhino: ["dojo.io.RhinoIO"],
5031
	browser: ["dojo.io.BrowserIO", "dojo.io.cookie"],
5032
	dashboard: ["dojo.io.BrowserIO", "dojo.io.cookie"]
5033
});
5034
dojo.provide("dojo.io.*");
5035
 
5036
dojo.provide("dojo.event.common");
5037
 
5038
 
5039
 
5040
 
5041
 
5042
// TODO: connection filter functions
5043
//			these are functions that accept a method invocation (like around
5044
//			advice) and return a boolean based on it. That value determines
5045
//			whether or not the connection proceeds. It could "feel" like around
5046
//			advice for those who know what it is (calling proceed() or not),
5047
//			but I think presenting it as a "filter" and/or calling it with the
5048
//			function args and not the MethodInvocation might make it more
5049
//			palletable to "normal" users than around-advice currently is
5050
// TODO: execution scope mangling
5051
//			YUI's event facility by default executes listeners in the context
5052
//			of the source object. This is very odd, but should probably be
5053
//			supported as an option (both for the source and for the dest). It
5054
//			can be thought of as a connection-specific hitch().
5055
// TODO: more resiliency for 4+ arguments to connect()
5056
 
5057
dojo.event = new function(){
5058
	this._canTimeout = dojo.lang.isFunction(dj_global["setTimeout"])||dojo.lang.isAlien(dj_global["setTimeout"]);
5059
 
5060
	// FIXME: where should we put this method (not here!)?
5061
	function interpolateArgs(args, searchForNames){
5062
		var dl = dojo.lang;
5063
		var ao = {
5064
			srcObj: dj_global,
5065
			srcFunc: null,
5066
			adviceObj: dj_global,
5067
			adviceFunc: null,
5068
			aroundObj: null,
5069
			aroundFunc: null,
5070
			adviceType: (args.length>2) ? args[0] : "after",
5071
			precedence: "last",
5072
			once: false,
5073
			delay: null,
5074
			rate: 0,
5075
			adviceMsg: false,
5076
			maxCalls: -1
5077
		};
5078
 
5079
		switch(args.length){
5080
			case 0: return;
5081
			case 1: return;
5082
			case 2:
5083
				ao.srcFunc = args[0];
5084
				ao.adviceFunc = args[1];
5085
				break;
5086
			case 3:
5087
				if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isString(args[2]))){
5088
					ao.adviceType = "after";
5089
					ao.srcObj = args[0];
5090
					ao.srcFunc = args[1];
5091
					ao.adviceFunc = args[2];
5092
				}else if((dl.isString(args[1]))&&(dl.isString(args[2]))){
5093
					ao.srcFunc = args[1];
5094
					ao.adviceFunc = args[2];
5095
				}else if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isFunction(args[2]))){
5096
					ao.adviceType = "after";
5097
					ao.srcObj = args[0];
5098
					ao.srcFunc = args[1];
5099
					var tmpName  = dl.nameAnonFunc(args[2], ao.adviceObj, searchForNames);
5100
					ao.adviceFunc = tmpName;
5101
				}else if((dl.isFunction(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))){
5102
					ao.adviceType = "after";
5103
					ao.srcObj = dj_global;
5104
					var tmpName  = dl.nameAnonFunc(args[0], ao.srcObj, searchForNames);
5105
					ao.srcFunc = tmpName;
5106
					ao.adviceObj = args[1];
5107
					ao.adviceFunc = args[2];
5108
				}
5109
				break;
5110
			case 4:
5111
				if((dl.isObject(args[0]))&&(dl.isObject(args[2]))){
5112
					// we can assume that we've got an old-style "connect" from
5113
					// the sigslot school of event attachment. We therefore
5114
					// assume after-advice.
5115
					ao.adviceType = "after";
5116
					ao.srcObj = args[0];
5117
					ao.srcFunc = args[1];
5118
					ao.adviceObj = args[2];
5119
					ao.adviceFunc = args[3];
5120
				}else if((dl.isString(args[0]))&&(dl.isString(args[1]))&&(dl.isObject(args[2]))){
5121
					ao.adviceType = args[0];
5122
					ao.srcObj = dj_global;
5123
					ao.srcFunc = args[1];
5124
					ao.adviceObj = args[2];
5125
					ao.adviceFunc = args[3];
5126
				}else if((dl.isString(args[0]))&&(dl.isFunction(args[1]))&&(dl.isObject(args[2]))){
5127
					ao.adviceType = args[0];
5128
					ao.srcObj = dj_global;
5129
					var tmpName  = dl.nameAnonFunc(args[1], dj_global, searchForNames);
5130
					ao.srcFunc = tmpName;
5131
					ao.adviceObj = args[2];
5132
					ao.adviceFunc = args[3];
5133
				}else if((dl.isString(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))&&(dl.isFunction(args[3]))){
5134
					ao.srcObj = args[1];
5135
					ao.srcFunc = args[2];
5136
					var tmpName  = dl.nameAnonFunc(args[3], dj_global, searchForNames);
5137
					ao.adviceObj = dj_global;
5138
					ao.adviceFunc = tmpName;
5139
				}else if(dl.isObject(args[1])){
5140
					ao.srcObj = args[1];
5141
					ao.srcFunc = args[2];
5142
					ao.adviceObj = dj_global;
5143
					ao.adviceFunc = args[3];
5144
				}else if(dl.isObject(args[2])){
5145
					ao.srcObj = dj_global;
5146
					ao.srcFunc = args[1];
5147
					ao.adviceObj = args[2];
5148
					ao.adviceFunc = args[3];
5149
				}else{
5150
					ao.srcObj = ao.adviceObj = ao.aroundObj = dj_global;
5151
					ao.srcFunc = args[1];
5152
					ao.adviceFunc = args[2];
5153
					ao.aroundFunc = args[3];
5154
				}
5155
				break;
5156
			case 6:
5157
				ao.srcObj = args[1];
5158
				ao.srcFunc = args[2];
5159
				ao.adviceObj = args[3]
5160
				ao.adviceFunc = args[4];
5161
				ao.aroundFunc = args[5];
5162
				ao.aroundObj = dj_global;
5163
				break;
5164
			default:
5165
				ao.srcObj = args[1];
5166
				ao.srcFunc = args[2];
5167
				ao.adviceObj = args[3]
5168
				ao.adviceFunc = args[4];
5169
				ao.aroundObj = args[5];
5170
				ao.aroundFunc = args[6];
5171
				ao.once = args[7];
5172
				ao.delay = args[8];
5173
				ao.rate = args[9];
5174
				ao.adviceMsg = args[10];
5175
				ao.maxCalls = (!isNaN(parseInt(args[11]))) ? args[11] : -1;
5176
				break;
5177
		}
5178
 
5179
		if(dl.isFunction(ao.aroundFunc)){
5180
			var tmpName  = dl.nameAnonFunc(ao.aroundFunc, ao.aroundObj, searchForNames);
5181
			ao.aroundFunc = tmpName;
5182
		}
5183
 
5184
		if(dl.isFunction(ao.srcFunc)){
5185
			ao.srcFunc = dl.getNameInObj(ao.srcObj, ao.srcFunc);
5186
		}
5187
 
5188
		if(dl.isFunction(ao.adviceFunc)){
5189
			ao.adviceFunc = dl.getNameInObj(ao.adviceObj, ao.adviceFunc);
5190
		}
5191
 
5192
		if((ao.aroundObj)&&(dl.isFunction(ao.aroundFunc))){
5193
			ao.aroundFunc = dl.getNameInObj(ao.aroundObj, ao.aroundFunc);
5194
		}
5195
 
5196
		if(!ao.srcObj){
5197
			dojo.raise("bad srcObj for srcFunc: "+ao.srcFunc);
5198
		}
5199
		if(!ao.adviceObj){
5200
			dojo.raise("bad adviceObj for adviceFunc: "+ao.adviceFunc);
5201
		}
5202
 
5203
		if(!ao.adviceFunc){
5204
			dojo.debug("bad adviceFunc for srcFunc: "+ao.srcFunc);
5205
			dojo.debugShallow(ao);
5206
		}
5207
 
5208
		return ao;
5209
	}
5210
 
5211
	this.connect = function(/*...*/){
5212
		// summary:
5213
		//		dojo.event.connect is the glue that holds most Dojo-based
5214
		//		applications together. Most combinations of arguments are
5215
		//		supported, with the connect() method attempting to disambiguate
5216
		//		the implied types of positional parameters. The following will
5217
		//		all work:
5218
		//			dojo.event.connect("globalFunctionName1", "globalFunctionName2");
5219
		//			dojo.event.connect(functionReference1, functionReference2);
5220
		//			dojo.event.connect("globalFunctionName1", functionReference2);
5221
		//			dojo.event.connect(functionReference1, "globalFunctionName2");
5222
		//			dojo.event.connect(scope1, "functionName1", "globalFunctionName2");
5223
		//			dojo.event.connect("globalFunctionName1", scope2, "functionName2");
5224
		//			dojo.event.connect(scope1, "functionName1", scope2, "functionName2");
5225
		//			dojo.event.connect("after", scope1, "functionName1", scope2, "functionName2");
5226
		//			dojo.event.connect("before", scope1, "functionName1", scope2, "functionName2");
5227
		//			dojo.event.connect("around", 	scope1, "functionName1",
5228
		//											scope2, "functionName2",
5229
		//											aroundFunctionReference);
5230
		//			dojo.event.connect("around", 	scope1, "functionName1",
5231
		//											scope2, "functionName2",
5232
		//											scope3, "aroundFunctionName");
5233
		//			dojo.event.connect("before-around", 	scope1, "functionName1",
5234
		//													scope2, "functionName2",
5235
		//													aroundFunctionReference);
5236
		//			dojo.event.connect("after-around", 		scope1, "functionName1",
5237
		//													scope2, "functionName2",
5238
		//													aroundFunctionReference);
5239
		//			dojo.event.connect("after-around", 		scope1, "functionName1",
5240
		//													scope2, "functionName2",
5241
		//													scope3, "aroundFunctionName");
5242
		//			dojo.event.connect("around", 	scope1, "functionName1",
5243
		//											scope2, "functionName2",
5244
		//											scope3, "aroundFunctionName", true, 30);
5245
		//			dojo.event.connect("around", 	scope1, "functionName1",
5246
		//											scope2, "functionName2",
5247
		//											scope3, "aroundFunctionName", null, null, 10);
5248
		// adviceType:
5249
		//		Optional. String. One of "before", "after", "around",
5250
		//		"before-around", or "after-around". FIXME
5251
		// srcObj:
5252
		//		the scope in which to locate/execute the named srcFunc. Along
5253
		//		with srcFunc, this creates a way to dereference the function to
5254
		//		call. So if the function in question is "foo.bar", the
5255
		//		srcObj/srcFunc pair would be foo and "bar", where "bar" is a
5256
		//		string and foo is an object reference.
5257
		// srcFunc:
5258
		//		the name of the function to connect to. When it is executed,
5259
		//		the listener being registered with this call will be called.
5260
		//		The adviceType defines the call order between the source and
5261
		//		the target functions.
5262
		// adviceObj:
5263
		//		the scope in which to locate/execute the named adviceFunc.
5264
		// adviceFunc:
5265
		//		the name of the function being conected to srcObj.srcFunc
5266
		// aroundObj:
5267
		//		the scope in which to locate/execute the named aroundFunc.
5268
		// aroundFunc:
5269
		//		the name of, or a reference to, the function that will be used
5270
		//		to mediate the advice call. Around advice requires a special
5271
		//		unary function that will be passed a "MethodInvocation" object.
5272
		//		These objects have several important properties, namely:
5273
		//			- args
5274
		//				a mutable array of arguments to be passed into the
5275
		//				wrapped function
5276
		//			- proceed
5277
		//				a function that "continues" the invocation. The result
5278
		//				of this function is the return of the wrapped function.
5279
		//				You can then manipulate this return before passing it
5280
		//				back out (or take further action based on it).
5281
		// once:
5282
		//		boolean that determines whether or not this connect() will
5283
		//		create a new connection if an identical connect() has already
5284
		//		been made. Defaults to "false".
5285
		// delay:
5286
		//		an optional delay (in ms), as an integer, for dispatch of a
5287
		//		listener after the source has been fired.
5288
		// rate:
5289
		//		an optional rate throttling parameter (integer, in ms). When
5290
		//		specified, this particular connection will not fire more than
5291
		//		once in the interval specified by the rate
5292
		// adviceMsg:
5293
		//		boolean. Should the listener have all the parameters passed in
5294
		//		as a single argument?
5295
 
5296
		/*
5297
				ao.adviceType = args[0];
5298
				ao.srcObj = args[1];
5299
				ao.srcFunc = args[2];
5300
				ao.adviceObj = args[3]
5301
				ao.adviceFunc = args[4];
5302
				ao.aroundObj = args[5];
5303
				ao.aroundFunc = args[6];
5304
				ao.once = args[7];
5305
				ao.delay = args[8];
5306
				ao.rate = args[9];
5307
				ao.adviceMsg = args[10];
5308
				ao.maxCalls = args[11];
5309
		*/
5310
		if(arguments.length == 1){
5311
			var ao = arguments[0];
5312
		}else{
5313
			var ao = interpolateArgs(arguments, true);
5314
		}
5315
		/*
5316
		if(dojo.lang.isString(ao.srcFunc) && (ao.srcFunc.toLowerCase() == "onkey") ){
5317
			if(dojo.render.html.ie){
5318
				ao.srcFunc = "onkeydown";
5319
				this.connect(ao);
5320
			}
5321
			ao.srcFunc = "onkeypress";
5322
		}
5323
		*/
5324
 
5325
		if(dojo.lang.isArray(ao.srcObj) && ao.srcObj!=""){
5326
			var tmpAO = {};
5327
			for(var x in ao){
5328
				tmpAO[x] = ao[x];
5329
			}
5330
			var mjps = [];
5331
			dojo.lang.forEach(ao.srcObj, function(src){
5332
				if((dojo.render.html.capable)&&(dojo.lang.isString(src))){
5333
					src = dojo.byId(src);
5334
					// dojo.debug(src);
5335
				}
5336
				tmpAO.srcObj = src;
5337
				// dojo.debug(tmpAO.srcObj, tmpAO.srcFunc);
5338
				// dojo.debug(tmpAO.adviceObj, tmpAO.adviceFunc);
5339
				mjps.push(dojo.event.connect.call(dojo.event, tmpAO));
5340
			});
5341
			return mjps;
5342
		}
5343
 
5344
		// FIXME: just doing a "getForMethod()" seems to be enough to put this into infinite recursion!!
5345
		var mjp = dojo.event.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc);
5346
		if(ao.adviceFunc){
5347
			var mjp2 = dojo.event.MethodJoinPoint.getForMethod(ao.adviceObj, ao.adviceFunc);
5348
		}
5349
 
5350
		mjp.kwAddAdvice(ao);
5351
 
5352
		// advanced users might want to fsck w/ the join point manually
5353
		return mjp; // a MethodJoinPoint object
5354
	}
5355
 
5356
	this.log = function(/*object or funcName*/ a1, /*funcName*/ a2){
5357
		// summary:
5358
		//		a function that will wrap and log all calls to the specified
5359
		//		a1.a2() function. If only a1 is passed, it'll be used as a
5360
		//		function or function name on the global context. Logging will
5361
		//		be sent to dojo.debug
5362
		// a1:
5363
		//		if a2 is passed, this should be an object. If not, it can be a
5364
		//		function or function name.
5365
		// a2:
5366
		//		a function name
5367
		var kwArgs;
5368
		if((arguments.length == 1)&&(typeof a1 == "object")){
5369
			kwArgs = a1;
5370
		}else{
5371
			kwArgs = {
5372
				srcObj: a1,
5373
				srcFunc: a2
5374
			};
5375
		}
5376
		kwArgs.adviceFunc = function(){
5377
			var argsStr = [];
5378
			for(var x=0; x<arguments.length; x++){
5379
				argsStr.push(arguments[x]);
5380
			}
5381
			dojo.debug("("+kwArgs.srcObj+")."+kwArgs.srcFunc, ":", argsStr.join(", "));
5382
		};
5383
		this.kwConnect(kwArgs);
5384
	}
5385
 
5386
	this.connectBefore = function(){
5387
		// summary:
5388
		//	 	takes the same parameters as dojo.event.connect(), except that
5389
		//	 	the advice type will always be "before"
5390
		var args = ["before"];
5391
		for(var i = 0; i < arguments.length; i++){ args.push(arguments[i]); }
5392
		return this.connect.apply(this, args); // a MethodJoinPoint object
5393
	}
5394
 
5395
	this.connectAround = function(){
5396
		// summary:
5397
		//	 	takes the same parameters as dojo.event.connect(), except that
5398
		//	 	the advice type will always be "around"
5399
		var args = ["around"];
5400
		for(var i = 0; i < arguments.length; i++){ args.push(arguments[i]); }
5401
		return this.connect.apply(this, args); // a MethodJoinPoint object
5402
	}
5403
 
5404
	this.connectOnce = function(){
5405
		// summary:
5406
		//	 	takes the same parameters as dojo.event.connect(), except that
5407
		//	 	the "once" flag will always be set to "true"
5408
		var ao = interpolateArgs(arguments, true);
5409
		ao.once = true;
5410
		return this.connect(ao); // a MethodJoinPoint object
5411
	}
5412
 
5413
	this.connectRunOnce = function(){
5414
		// summary:
5415
		//	 	takes the same parameters as dojo.event.connect(), except that
5416
		//	 	the "maxCalls" flag will always be set to 1
5417
		var ao = interpolateArgs(arguments, true);
5418
		ao.maxCalls = 1;
5419
		return this.connect(ao); // a MethodJoinPoint object
5420
	}
5421
 
5422
	this._kwConnectImpl = function(kwArgs, disconnect){
5423
		var fn = (disconnect) ? "disconnect" : "connect";
5424
		if(typeof kwArgs["srcFunc"] == "function"){
5425
			kwArgs.srcObj = kwArgs["srcObj"]||dj_global;
5426
			var tmpName  = dojo.lang.nameAnonFunc(kwArgs.srcFunc, kwArgs.srcObj, true);
5427
			kwArgs.srcFunc = tmpName;
5428
		}
5429
		if(typeof kwArgs["adviceFunc"] == "function"){
5430
			kwArgs.adviceObj = kwArgs["adviceObj"]||dj_global;
5431
			var tmpName  = dojo.lang.nameAnonFunc(kwArgs.adviceFunc, kwArgs.adviceObj, true);
5432
			kwArgs.adviceFunc = tmpName;
5433
		}
5434
		kwArgs.srcObj = kwArgs["srcObj"]||dj_global;
5435
		kwArgs.adviceObj = kwArgs["adviceObj"]||kwArgs["targetObj"]||dj_global;
5436
		kwArgs.adviceFunc = kwArgs["adviceFunc"]||kwArgs["targetFunc"];
5437
		// pass kwargs to avoid unrolling/repacking
5438
		return dojo.event[fn](kwArgs);
5439
	}
5440
 
5441
	this.kwConnect = function(/*Object*/ kwArgs){
5442
		// summary:
5443
		//		A version of dojo.event.connect() that takes a map of named
5444
		//		parameters instead of the positional parameters that
5445
		//		dojo.event.connect() uses. For many advanced connection types,
5446
		//		this can be a much more readable (and potentially faster)
5447
		//		alternative.
5448
		// kwArgs:
5449
		// 		An object that can have the following properties:
5450
		//			- adviceType
5451
		//			- srcObj
5452
		//			- srcFunc
5453
		//			- adviceObj
5454
		//			- adviceFunc
5455
		//			- aroundObj
5456
		//			- aroundFunc
5457
		//			- once
5458
		//			- delay
5459
		//			- rate
5460
		//			- adviceMsg
5461
		//		As with connect, only srcFunc and adviceFunc are generally
5462
		//		required
5463
 
5464
		return this._kwConnectImpl(kwArgs, false); // a MethodJoinPoint object
5465
 
5466
	}
5467
 
5468
	this.disconnect = function(){
5469
		// summary:
5470
		//		Takes the same parameters as dojo.event.connect() but destroys
5471
		//		an existing connection instead of building a new one. For
5472
		//		multiple identical connections, multiple disconnect() calls
5473
		//		will unroll one each time it's called.
5474
		if(arguments.length == 1){
5475
			var ao = arguments[0];
5476
		}else{
5477
			var ao = interpolateArgs(arguments, true);
5478
		}
5479
		if(!ao.adviceFunc){ return; } // nothing to disconnect
5480
		if(dojo.lang.isString(ao.srcFunc) && (ao.srcFunc.toLowerCase() == "onkey") ){
5481
			if(dojo.render.html.ie){
5482
				ao.srcFunc = "onkeydown";
5483
				this.disconnect(ao);
5484
			}
5485
			ao.srcFunc = "onkeypress";
5486
		}
5487
		if(!ao.srcObj[ao.srcFunc]){ return null; } // prevent un-necessaray joinpoint creation
5488
		var mjp = dojo.event.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc, true);
5489
		mjp.removeAdvice(ao.adviceObj, ao.adviceFunc, ao.adviceType, ao.once); // a MethodJoinPoint object
5490
		return mjp;
5491
	}
5492
 
5493
	this.kwDisconnect = function(kwArgs){
5494
		// summary:
5495
		//		Takes the same parameters as dojo.event.kwConnect() but
5496
		//		destroys an existing connection instead of building a new one.
5497
		return this._kwConnectImpl(kwArgs, true);
5498
	}
5499
}
5500
 
5501
// exactly one of these is created whenever a method with a joint point is run,
5502
// if there is at least one 'around' advice.
5503
dojo.event.MethodInvocation = function(/*dojo.event.MethodJoinPoint*/join_point, /*Object*/obj, /*Array*/args){
5504
	// summary:
5505
	//		a class the models the call into a function. This is used under the
5506
	//		covers for all method invocations on both ends of a
5507
	//		connect()-wrapped function dispatch. This allows us to "pickle"
5508
	//		calls, such as in the case of around advice.
5509
	// join_point:
5510
	//		a dojo.event.MethodJoinPoint object that represents a connection
5511
	// obj:
5512
	//		the scope the call will execute in
5513
	// args:
5514
	//		an array of parameters that will get passed to the callee
5515
	this.jp_ = join_point;
5516
	this.object = obj;
5517
	this.args = [];
5518
	// make sure we don't lock into a mutable object which can change under us.
5519
	// It's ok if the individual items change, though.
5520
	for(var x=0; x<args.length; x++){
5521
		this.args[x] = args[x];
5522
	}
5523
	// the index of the 'around' that is currently being executed.
5524
	this.around_index = -1;
5525
}
5526
 
5527
dojo.event.MethodInvocation.prototype.proceed = function(){
5528
	// summary:
5529
	//		proceed with the method call that's represented by this invocation
5530
	//		object
5531
	this.around_index++;
5532
	if(this.around_index >= this.jp_.around.length){
5533
		return this.jp_.object[this.jp_.methodname].apply(this.jp_.object, this.args);
5534
		// return this.jp_.run_before_after(this.object, this.args);
5535
	}else{
5536
		var ti = this.jp_.around[this.around_index];
5537
		var mobj = ti[0]||dj_global;
5538
		var meth = ti[1];
5539
		return mobj[meth].call(mobj, this);
5540
	}
5541
}
5542
 
5543
 
5544
dojo.event.MethodJoinPoint = function(/*Object*/obj, /*String*/funcName){
5545
	this.object = obj||dj_global;
5546
	this.methodname = funcName;
5547
	this.methodfunc = this.object[funcName];
5548
	this.squelch = false;
5549
	// this.before = [];
5550
	// this.after = [];
5551
	// this.around = [];
5552
}
5553
 
5554
dojo.event.MethodJoinPoint.getForMethod = function(/*Object*/obj, /*String*/funcName){
5555
	// summary:
5556
	//		"static" class function for returning a MethodJoinPoint from a
5557
	//		scoped function. If one doesn't exist, one is created.
5558
	// obj:
5559
	//		the scope to search for the function in
5560
	// funcName:
5561
	//		the name of the function to return a MethodJoinPoint for
5562
	if(!obj){ obj = dj_global; }
5563
	var ofn = obj[funcName];
5564
	if(!ofn){
5565
		// supply a do-nothing method implementation
5566
		ofn = obj[funcName] = function(){};
5567
		if(!obj[funcName]){
5568
			// e.g. cannot add to inbuilt objects in IE6
5569
			dojo.raise("Cannot set do-nothing method on that object "+funcName);
5570
		}
5571
	}else if((typeof ofn != "function")&&(!dojo.lang.isFunction(ofn))&&(!dojo.lang.isAlien(ofn))){
5572
		// FIXME: should we throw an exception here instead?
5573
		return null;
5574
	}
5575
	// we hide our joinpoint instance in obj[funcName + '$joinpoint']
5576
	var jpname = funcName + "$joinpoint";
5577
	var jpfuncname = funcName + "$joinpoint$method";
5578
	var joinpoint = obj[jpname];
5579
	if(!joinpoint){
5580
		var isNode = false;
5581
		if(dojo.event["browser"]){
5582
			if( (obj["attachEvent"])||
5583
				(obj["nodeType"])||
5584
				(obj["addEventListener"]) ){
5585
				isNode = true;
5586
				dojo.event.browser.addClobberNodeAttrs(obj, [jpname, jpfuncname, funcName]);
5587
			}
5588
		}
5589
		var origArity = ofn.length;
5590
		obj[jpfuncname] = ofn;
5591
		// joinpoint = obj[jpname] = new dojo.event.MethodJoinPoint(obj, funcName);
5592
		joinpoint = obj[jpname] = new dojo.event.MethodJoinPoint(obj, jpfuncname);
5593
 
5594
		if(!isNode){
5595
			obj[funcName] = function(){
5596
				// var args = [];
5597
				// for(var x=0; x<arguments.length; x++){
5598
					// args.push(arguments[x]);
5599
				// }
5600
				// return joinpoint.run.apply(joinpoint, args);
5601
				return joinpoint.run.apply(joinpoint, arguments);
5602
			}
5603
		}else{
5604
			obj[funcName] = function(){
5605
				var args = [];
5606
 
5607
				if(!arguments.length){
5608
					var evt = null;
5609
					try{
5610
						if(obj.ownerDocument){
5611
							evt = obj.ownerDocument.parentWindow.event;
5612
						}else if(obj.documentElement){
5613
							evt = obj.documentElement.ownerDocument.parentWindow.event;
5614
						}else if(obj.event){ //obj is a window
5615
							evt = obj.event;
5616
						}else{
5617
							evt = window.event;
5618
						}
5619
					}catch(e){
5620
						evt = window.event;
5621
					}
5622
 
5623
					if(evt){
5624
						args.push(dojo.event.browser.fixEvent(evt, this));
5625
					}
5626
				}else{
5627
					for(var x=0; x<arguments.length; x++){
5628
						if((x==0)&&(dojo.event.browser.isEvent(arguments[x]))){
5629
							args.push(dojo.event.browser.fixEvent(arguments[x], this));
5630
						}else{
5631
							args.push(arguments[x]);
5632
						}
5633
					}
5634
				}
5635
				// return joinpoint.run.apply(joinpoint, arguments);
5636
				return joinpoint.run.apply(joinpoint, args);
5637
			}
5638
		}
5639
		obj[funcName].__preJoinArity = origArity;
5640
	}
5641
	return joinpoint; // dojo.event.MethodJoinPoint
5642
}
5643
 
5644
dojo.lang.extend(dojo.event.MethodJoinPoint, {
5645
	squelch: false,
5646
 
5647
	unintercept: function(){
5648
		// summary:
5649
		//		destroy the connection to all listeners that may have been
5650
		//		registered on this joinpoint
5651
		this.object[this.methodname] = this.methodfunc;
5652
		this.before = [];
5653
		this.after = [];
5654
		this.around = [];
5655
	},
5656
 
5657
	disconnect: dojo.lang.forward("unintercept"),
5658
 
5659
	run: function(){
5660
		// summary:
5661
		//		execute the connection represented by this join point. The
5662
		//		arguments passed to run() will be passed to the function and
5663
		//		its listeners.
5664
		var obj = this.object||dj_global;
5665
		var args = arguments;
5666
 
5667
		// optimization. We only compute once the array version of the arguments
5668
		// pseudo-arr in order to prevent building it each time advice is unrolled.
5669
		var aargs = [];
5670
		for(var x=0; x<args.length; x++){
5671
			aargs[x] = args[x];
5672
		}
5673
 
5674
		var unrollAdvice  = function(marr){
5675
			if(!marr){
5676
				dojo.debug("Null argument to unrollAdvice()");
5677
				return;
5678
			}
5679
 
5680
			var callObj = marr[0]||dj_global;
5681
			var callFunc = marr[1];
5682
 
5683
			if(!callObj[callFunc]){
5684
				dojo.raise("function \"" + callFunc + "\" does not exist on \"" + callObj + "\"");
5685
			}
5686
 
5687
			var aroundObj = marr[2]||dj_global;
5688
			var aroundFunc = marr[3];
5689
			var msg = marr[6];
5690
			var maxCount = marr[7];
5691
			if(maxCount > -1){
5692
				if(maxCount == 0){
5693
					return;
5694
				}
5695
				marr[7]--;
5696
			}
5697
			var undef;
5698
 
5699
			var to = {
5700
				args: [],
5701
				jp_: this,
5702
				object: obj,
5703
				proceed: function(){
5704
					return callObj[callFunc].apply(callObj, to.args);
5705
				}
5706
			};
5707
			to.args = aargs;
5708
 
5709
			var delay = parseInt(marr[4]);
5710
			var hasDelay = ((!isNaN(delay))&&(marr[4]!==null)&&(typeof marr[4] != "undefined"));
5711
			if(marr[5]){
5712
				var rate = parseInt(marr[5]);
5713
				var cur = new Date();
5714
				var timerSet = false;
5715
				if((marr["last"])&&((cur-marr.last)<=rate)){
5716
					if(dojo.event._canTimeout){
5717
						if(marr["delayTimer"]){
5718
							clearTimeout(marr.delayTimer);
5719
						}
5720
						var tod = parseInt(rate*2); // is rate*2 naive?
5721
						var mcpy = dojo.lang.shallowCopy(marr);
5722
						marr.delayTimer = setTimeout(function(){
5723
							// FIXME: on IE at least, event objects from the
5724
							// browser can go out of scope. How (or should?) we
5725
							// deal with it?
5726
							mcpy[5] = 0;
5727
							unrollAdvice(mcpy);
5728
						}, tod);
5729
					}
5730
					return;
5731
				}else{
5732
					marr.last = cur;
5733
				}
5734
			}
5735
 
5736
			// FIXME: need to enforce rates for a connection here!
5737
 
5738
			if(aroundFunc){
5739
				// NOTE: around advice can't delay since we might otherwise depend
5740
				// on execution order!
5741
				aroundObj[aroundFunc].call(aroundObj, to);
5742
			}else{
5743
				// var tmjp = dojo.event.MethodJoinPoint.getForMethod(obj, methname);
5744
				if((hasDelay)&&((dojo.render.html)||(dojo.render.svg))){  // FIXME: the render checks are grotty!
5745
					dj_global["setTimeout"](function(){
5746
						if(msg){
5747
							callObj[callFunc].call(callObj, to);
5748
						}else{
5749
							callObj[callFunc].apply(callObj, args);
5750
						}
5751
					}, delay);
5752
				}else{ // many environments can't support delay!
5753
					if(msg){
5754
						callObj[callFunc].call(callObj, to);
5755
					}else{
5756
						callObj[callFunc].apply(callObj, args);
5757
					}
5758
				}
5759
			}
5760
		};
5761
 
5762
		var unRollSquelch = function(){
5763
			if(this.squelch){
5764
				try{
5765
					return unrollAdvice.apply(this, arguments);
5766
				}catch(e){
5767
					dojo.debug(e);
5768
				}
5769
			}else{
5770
				return unrollAdvice.apply(this, arguments);
5771
			}
5772
		};
5773
 
5774
		if((this["before"])&&(this.before.length>0)){
5775
			// pass a cloned array, if this event disconnects this event forEach on this.before wont work
5776
			dojo.lang.forEach(this.before.concat(new Array()), unRollSquelch);
5777
		}
5778
 
5779
		var result;
5780
		try{
5781
			if((this["around"])&&(this.around.length>0)){
5782
				var mi = new dojo.event.MethodInvocation(this, obj, args);
5783
				result = mi.proceed();
5784
			}else if(this.methodfunc){
5785
				result = this.object[this.methodname].apply(this.object, args);
5786
			}
5787
		}catch(e){
5788
			if(!this.squelch){
5789
				dojo.debug(e,"when calling",this.methodname,"on",this.object,"with arguments",args);
5790
				dojo.raise(e);
5791
			}
5792
		}
5793
 
5794
		if((this["after"])&&(this.after.length>0)){
5795
			// see comment on this.before above
5796
			dojo.lang.forEach(this.after.concat(new Array()), unRollSquelch);
5797
		}
5798
 
5799
		return (this.methodfunc) ? result : null;
5800
	},
5801
 
5802
	getArr: function(/*String*/kind){
5803
		// summary: return a list of listeners of the past "kind"
5804
		// kind:
5805
		//		can be one of: "before", "after", "around", "before-around", or
5806
		//		"after-around"
5807
		var type = "after";
5808
		// FIXME: we should be able to do this through props or Array.in()
5809
		if((typeof kind == "string")&&(kind.indexOf("before")!=-1)){
5810
			type = "before";
5811
		}else if(kind=="around"){
5812
			type = "around";
5813
		}
5814
		if(!this[type]){ this[type] = []; }
5815
		return this[type]; // Array
5816
	},
5817
 
5818
	kwAddAdvice: function(/*Object*/args){
5819
		// summary:
5820
		//		adds advice to the joinpoint with arguments in a map
5821
		// args:
5822
		// 		An object that can have the following properties:
5823
		//			- adviceType
5824
		//			- adviceObj
5825
		//			- adviceFunc
5826
		//			- aroundObj
5827
		//			- aroundFunc
5828
		//			- once
5829
		//			- delay
5830
		//			- rate
5831
		//			- adviceMsg
5832
		//			- maxCalls
5833
		this.addAdvice(	args["adviceObj"], args["adviceFunc"],
5834
						args["aroundObj"], args["aroundFunc"],
5835
						args["adviceType"], args["precedence"],
5836
						args["once"], args["delay"], args["rate"],
5837
						args["adviceMsg"], args["maxCalls"]);
5838
	},
5839
 
5840
	addAdvice: function(	thisAdviceObj, thisAdvice,
5841
							thisAroundObj, thisAround,
5842
							adviceType, precedence,
5843
							once, delay, rate, asMessage,
5844
							maxCalls){
5845
		// summary:
5846
		//		add advice to this joinpoint using positional parameters
5847
		// thisAdviceObj:
5848
		//		the scope in which to locate/execute the named adviceFunc.
5849
		// thisAdviceFunc:
5850
		//		the name of the function being conected
5851
		// thisAroundObj:
5852
		//		the scope in which to locate/execute the named aroundFunc.
5853
		// thisAroundFunc:
5854
		//		the name of the function that will be used to mediate the
5855
		//		advice call.
5856
		// adviceType:
5857
		//		Optional. String. One of "before", "after", "around",
5858
		//		"before-around", or "after-around". FIXME
5859
		// once:
5860
		//		boolean that determines whether or not this advice will create
5861
		//		a new connection if an identical advice set has already been
5862
		//		provided. Defaults to "false".
5863
		// delay:
5864
		//		an optional delay (in ms), as an integer, for dispatch of a
5865
		//		listener after the source has been fired.
5866
		// rate:
5867
		//		an optional rate throttling parameter (integer, in ms). When
5868
		//		specified, this particular connection will not fire more than
5869
		//		once in the interval specified by the rate
5870
		// adviceMsg:
5871
		//		boolean. Should the listener have all the parameters passed in
5872
		//		as a single argument?
5873
		// maxCalls:
5874
		//		Integer. The maximum number of times this connection can be
5875
		//		used before being auto-disconnected. -1 signals that the
5876
		//		connection should never be disconnected.
5877
		var arr = this.getArr(adviceType);
5878
		if(!arr){
5879
			dojo.raise("bad this: " + this);
5880
		}
5881
 
5882
		var ao = [thisAdviceObj, thisAdvice, thisAroundObj, thisAround, delay, rate, asMessage, maxCalls];
5883
 
5884
		if(once){
5885
			if(this.hasAdvice(thisAdviceObj, thisAdvice, adviceType, arr) >= 0){
5886
				return;
5887
			}
5888
		}
5889
 
5890
		if(precedence == "first"){
5891
			arr.unshift(ao);
5892
		}else{
5893
			arr.push(ao);
5894
		}
5895
	},
5896
 
5897
	hasAdvice: function(thisAdviceObj, thisAdvice, adviceType, arr){
5898
		// summary:
5899
		//		returns the array index of the first existing connection
5900
		//		betweened the passed advice and this joinpoint. Will be -1 if
5901
		//		none exists.
5902
		// thisAdviceObj:
5903
		//		the scope in which to locate/execute the named adviceFunc.
5904
		// thisAdviceFunc:
5905
		//		the name of the function being conected
5906
		// adviceType:
5907
		//		Optional. String. One of "before", "after", "around",
5908
		//		"before-around", or "after-around". FIXME
5909
		// arr:
5910
		//		Optional. The list of advices to search. Will be found via
5911
		//		adviceType if not passed
5912
		if(!arr){ arr = this.getArr(adviceType); }
5913
		var ind = -1;
5914
		for(var x=0; x<arr.length; x++){
5915
			var aao = (typeof thisAdvice == "object") ? (new String(thisAdvice)).toString() : thisAdvice;
5916
			var a1o = (typeof arr[x][1] == "object") ? (new String(arr[x][1])).toString() : arr[x][1];
5917
			if((arr[x][0] == thisAdviceObj)&&(a1o == aao)){
5918
				ind = x;
5919
			}
5920
		}
5921
		return ind; // Integer
5922
	},
5923
 
5924
	removeAdvice: function(thisAdviceObj, thisAdvice, adviceType, once){
5925
		// summary:
5926
		//		returns the array index of the first existing connection
5927
		//		betweened the passed advice and this joinpoint. Will be -1 if
5928
		//		none exists.
5929
		// thisAdviceObj:
5930
		//		the scope in which to locate/execute the named adviceFunc.
5931
		// thisAdviceFunc:
5932
		//		the name of the function being conected
5933
		// adviceType:
5934
		//		Optional. String. One of "before", "after", "around",
5935
		//		"before-around", or "after-around". FIXME
5936
		// once:
5937
		//		Optional. Should this only remove the first occurance of the
5938
		//		connection?
5939
		var arr = this.getArr(adviceType);
5940
		var ind = this.hasAdvice(thisAdviceObj, thisAdvice, adviceType, arr);
5941
		if(ind == -1){
5942
			return false;
5943
		}
5944
		while(ind != -1){
5945
			arr.splice(ind, 1);
5946
			if(once){ break; }
5947
			ind = this.hasAdvice(thisAdviceObj, thisAdvice, adviceType, arr);
5948
		}
5949
		return true;
5950
	}
5951
});
5952
 
5953
 
5954
dojo.provide("dojo.event.topic");
5955
 
5956
dojo.event.topic = new function(){
5957
	this.topics = {};
5958
 
5959
	this.getTopic = function(/*String*/topic){
5960
		// summary:
5961
		//		returns a topic implementation object of type
5962
		//		dojo.event.topic.TopicImpl
5963
		// topic:
5964
		//		a unique, opaque string that names the topic
5965
		if(!this.topics[topic]){
5966
			this.topics[topic] = new this.TopicImpl(topic);
5967
		}
5968
		return this.topics[topic]; // a dojo.event.topic.TopicImpl object
5969
	}
5970
 
5971
	this.registerPublisher = function(/*String*/topic, /*Object*/obj, /*String*/funcName){
5972
		// summary:
5973
		//		registers a function as a publisher on a topic. Subsequent
5974
		//		calls to the function will cause a publish event on the topic
5975
		//		with the arguments passed to the function passed to registered
5976
		//		listeners.
5977
		// topic:
5978
		//		a unique, opaque string that names the topic
5979
		// obj:
5980
		//		the scope to locate the function in
5981
		// funcName:
5982
		//		the name of the function to register
5983
		var topic = this.getTopic(topic);
5984
		topic.registerPublisher(obj, funcName);
5985
	}
5986
 
5987
	this.subscribe = function(/*String*/topic, /*Object*/obj, /*String*/funcName){
5988
		// summary:
5989
		//		susbscribes the function to the topic. Subsequent events
5990
		//		dispached to the topic will create a function call for the
5991
		//		obj.funcName() function.
5992
		// topic:
5993
		//		a unique, opaque string that names the topic
5994
		// obj:
5995
		//		the scope to locate the function in
5996
		// funcName:
5997
		//		the name of the function to being registered as a listener
5998
		var topic = this.getTopic(topic);
5999
		topic.subscribe(obj, funcName);
6000
	}
6001
 
6002
	this.unsubscribe = function(/*String*/topic, /*Object*/obj, /*String*/funcName){
6003
		// summary:
6004
		//		unsubscribes the obj.funcName() from the topic
6005
		// topic:
6006
		//		a unique, opaque string that names the topic
6007
		// obj:
6008
		//		the scope to locate the function in
6009
		// funcName:
6010
		//		the name of the function to being unregistered as a listener
6011
		var topic = this.getTopic(topic);
6012
		topic.unsubscribe(obj, funcName);
6013
	}
6014
 
6015
	this.destroy = function(/*String*/topic){
6016
		// summary:
6017
		//		destroys the topic and unregisters all listeners
6018
		// topic:
6019
		//		a unique, opaque string that names the topic
6020
		this.getTopic(topic).destroy();
6021
		delete this.topics[topic];
6022
	}
6023
 
6024
	this.publishApply = function(/*String*/topic, /*Array*/args){
6025
		// summary:
6026
		//		dispatches an event to the topic using the args array as the
6027
		//		source for the call arguments to each listener. This is similar
6028
		//		to JavaScript's built-in Function.apply()
6029
		// topic:
6030
		//		a unique, opaque string that names the topic
6031
		// args:
6032
		//		the arguments to be passed into listeners of the topic
6033
		var topic = this.getTopic(topic);
6034
		topic.sendMessage.apply(topic, args);
6035
	}
6036
 
6037
	this.publish = function(/*String*/topic, /*Object*/message){
6038
		// summary:
6039
		//		manually "publish" to the passed topic
6040
		// topic:
6041
		//		a unique, opaque string that names the topic
6042
		// message:
6043
		//		can be an array of parameters (similar to publishApply), or
6044
		//		will be treated as one of many arguments to be passed along in
6045
		//		a "flat" unrolling
6046
		var topic = this.getTopic(topic);
6047
		// if message is an array, we treat it as a set of arguments,
6048
		// otherwise, we just pass on the arguments passed in as-is
6049
		var args = [];
6050
		// could we use concat instead here?
6051
		for(var x=1; x<arguments.length; x++){
6052
			args.push(arguments[x]);
6053
		}
6054
		topic.sendMessage.apply(topic, args);
6055
	}
6056
}
6057
 
6058
dojo.event.topic.TopicImpl = function(topicName){
6059
	// summary: a class to represent topics
6060
 
6061
	this.topicName = topicName;
6062
 
6063
	this.subscribe = function(/*Object*/listenerObject, /*Function or String*/listenerMethod){
6064
		// summary:
6065
		//		use dojo.event.connect() to attach the passed listener to the
6066
		//		topic represented by this object
6067
		// listenerObject:
6068
		//		if a string and listenerMethod is ommitted, this is treated as
6069
		//		the name of a function in the global namespace. If
6070
		//		listenerMethod is provided, this is the scope to find/execute
6071
		//		the function in.
6072
		// listenerMethod:
6073
		//		Optional. The function to register.
6074
		var tf = listenerMethod||listenerObject;
6075
		var to = (!listenerMethod) ? dj_global : listenerObject;
6076
		return dojo.event.kwConnect({ // dojo.event.MethodJoinPoint
6077
			srcObj:		this,
6078
			srcFunc:	"sendMessage",
6079
			adviceObj:	to,
6080
			adviceFunc: tf
6081
		});
6082
	}
6083
 
6084
	this.unsubscribe = function(/*Object*/listenerObject, /*Function or String*/listenerMethod){
6085
		// summary:
6086
		//		use dojo.event.disconnect() to attach the passed listener to the
6087
		//		topic represented by this object
6088
		// listenerObject:
6089
		//		if a string and listenerMethod is ommitted, this is treated as
6090
		//		the name of a function in the global namespace. If
6091
		//		listenerMethod is provided, this is the scope to find the
6092
		//		function in.
6093
		// listenerMethod:
6094
		//		Optional. The function to unregister.
6095
		var tf = (!listenerMethod) ? listenerObject : listenerMethod;
6096
		var to = (!listenerMethod) ? null : listenerObject;
6097
		return dojo.event.kwDisconnect({ // dojo.event.MethodJoinPoint
6098
			srcObj:		this,
6099
			srcFunc:	"sendMessage",
6100
			adviceObj:	to,
6101
			adviceFunc: tf
6102
		});
6103
	}
6104
 
6105
	this._getJoinPoint = function(){
6106
		return dojo.event.MethodJoinPoint.getForMethod(this, "sendMessage");
6107
	}
6108
 
6109
	this.setSquelch = function(/*Boolean*/shouldSquelch){
6110
		// summary:
6111
		//		determine whether or not exceptions in the calling of a
6112
		//		listener in the chain should stop execution of the chain.
6113
		this._getJoinPoint().squelch = shouldSquelch;
6114
	}
6115
 
6116
	this.destroy = function(){
6117
		// summary: disconnects all listeners from this topic
6118
		this._getJoinPoint().disconnect();
6119
	}
6120
 
6121
	this.registerPublisher = function(	/*Object*/publisherObject,
6122
										/*Function or String*/publisherMethod){
6123
		// summary:
6124
		//		registers the passed function as a publisher on this topic.
6125
		//		Each time the function is called, an event will be published on
6126
		//		this topic.
6127
		// publisherObject:
6128
		//		if a string and listenerMethod is ommitted, this is treated as
6129
		//		the name of a function in the global namespace. If
6130
		//		listenerMethod is provided, this is the scope to find the
6131
		//		function in.
6132
		// publisherMethod:
6133
		//		Optional. The function to register.
6134
		dojo.event.connect(publisherObject, publisherMethod, this, "sendMessage");
6135
	}
6136
 
6137
	this.sendMessage = function(message){
6138
		// summary: a stub to be called when a message is sent to the topic.
6139
 
6140
		// The message has been propagated
6141
	}
6142
}
6143
 
6144
 
6145
dojo.provide("dojo.event.browser");
6146
 
6147
 
6148
// FIXME: any particular reason this is in the global scope?
6149
dojo._ie_clobber = new function(){
6150
	this.clobberNodes = [];
6151
 
6152
	function nukeProp(node, prop){
6153
		// try{ node.removeAttribute(prop); 	}catch(e){ /* squelch */ }
6154
		try{ node[prop] = null; 			}catch(e){ /* squelch */ }
6155
		try{ delete node[prop]; 			}catch(e){ /* squelch */ }
6156
		// FIXME: JotLive needs this, but I'm not sure if it's too slow or not
6157
		try{ node.removeAttribute(prop);	}catch(e){ /* squelch */ }
6158
	}
6159
 
6160
	this.clobber = function(nodeRef){
6161
		var na;
6162
		var tna;
6163
		if(nodeRef){
6164
			tna = nodeRef.all || nodeRef.getElementsByTagName("*");
6165
			na = [nodeRef];
6166
			for(var x=0; x<tna.length; x++){
6167
				// if we're gonna be clobbering the thing, at least make sure
6168
				// we aren't trying to do it twice
6169
				if(tna[x]["__doClobber__"]){
6170
					na.push(tna[x]);
6171
				}
6172
			}
6173
		}else{
6174
			try{ window.onload = null; }catch(e){}
6175
			na = (this.clobberNodes.length) ? this.clobberNodes : document.all;
6176
		}
6177
		tna = null;
6178
		var basis = {};
6179
		for(var i = na.length-1; i>=0; i=i-1){
6180
			var el = na[i];
6181
			try{
6182
				if(el && el["__clobberAttrs__"]){
6183
					for(var j=0; j<el.__clobberAttrs__.length; j++){
6184
						nukeProp(el, el.__clobberAttrs__[j]);
6185
					}
6186
					nukeProp(el, "__clobberAttrs__");
6187
					nukeProp(el, "__doClobber__");
6188
				}
6189
			}catch(e){ /* squelch! */};
6190
		}
6191
		na = null;
6192
	}
6193
}
6194
 
6195
if(dojo.render.html.ie){
6196
	dojo.addOnUnload(function(){
6197
		dojo._ie_clobber.clobber();
6198
		try{
6199
			if((dojo["widget"])&&(dojo.widget["manager"])){
6200
				dojo.widget.manager.destroyAll();
6201
			}
6202
		}catch(e){}
6203
 
6204
		// Workaround for IE leak recommended in ticket #1727 by schallm
6205
		if(dojo.widget){
6206
			for(var name in dojo.widget._templateCache){
6207
				if(dojo.widget._templateCache[name].node){
6208
					dojo.dom.destroyNode(dojo.widget._templateCache[name].node);
6209
					dojo.widget._templateCache[name].node = null;
6210
					delete dojo.widget._templateCache[name].node;
6211
				}
6212
			}
6213
		}
6214
 
6215
		try{ window.onload = null; }catch(e){}
6216
		try{ window.onunload = null; }catch(e){}
6217
		dojo._ie_clobber.clobberNodes = [];
6218
		// CollectGarbage();
6219
	});
6220
}
6221
 
6222
dojo.event.browser = new function(){
6223
 
6224
	var clobberIdx = 0;
6225
 
6226
	this.normalizedEventName = function(/*String*/eventName){
6227
		switch(eventName){
6228
			case "CheckboxStateChange":
6229
			case "DOMAttrModified":
6230
			case "DOMMenuItemActive":
6231
			case "DOMMenuItemInactive":
6232
			case "DOMMouseScroll":
6233
			case "DOMNodeInserted":
6234
			case "DOMNodeRemoved":
6235
			case "RadioStateChange":
6236
				return eventName;
6237
				break;
6238
			default:
6239
				var lcn = eventName.toLowerCase();
6240
				return (lcn.indexOf("on") == 0) ? lcn.substr(2) : lcn;
6241
				break;
6242
		}
6243
	}
6244
 
6245
	this.clean = function(/*DOMNode*/node){
6246
		// summary:
6247
		//		removes native event handlers so that destruction of the node
6248
		//		will not leak memory. On most browsers this is a no-op, but
6249
		//		it's critical for manual node removal on IE.
6250
		// node:
6251
		//		A DOM node. All of it's children will also be cleaned.
6252
		if(dojo.render.html.ie){
6253
			dojo._ie_clobber.clobber(node);
6254
		}
6255
	}
6256
 
6257
	this.addClobberNode = function(/*DOMNode*/node){
6258
		// summary:
6259
		//		register the passed node to support event stripping
6260
		// node:
6261
		//		A DOM node
6262
		if(!dojo.render.html.ie){ return; }
6263
		if(!node["__doClobber__"]){
6264
			node.__doClobber__ = true;
6265
			dojo._ie_clobber.clobberNodes.push(node);
6266
			// this might not be the most efficient thing to do, but it's
6267
			// much less error prone than other approaches which were
6268
			// previously tried and failed
6269
			node.__clobberAttrs__ = [];
6270
		}
6271
	}
6272
 
6273
	this.addClobberNodeAttrs = function(/*DOMNode*/node, /*Array*/props){
6274
		// summary:
6275
		//		register the passed node to support event stripping
6276
		// node:
6277
		//		A DOM node to stip properties from later
6278
		// props:
6279
		//		A list of propeties to strip from the node
6280
		if(!dojo.render.html.ie){ return; }
6281
		this.addClobberNode(node);
6282
		for(var x=0; x<props.length; x++){
6283
			node.__clobberAttrs__.push(props[x]);
6284
		}
6285
	}
6286
 
6287
	this.removeListener = function(	/*DOMNode*/ node,
6288
									/*String*/	evtName,
6289
									/*Function*/fp,
6290
									/*Boolean*/	capture){
6291
		// summary:
6292
		//		clobbers the listener from the node
6293
		// evtName:
6294
		//		the name of the handler to remove the function from
6295
		// node:
6296
		//		DOM node to attach the event to
6297
		// fp:
6298
		//		the function to register
6299
		// capture:
6300
		//		Optional. should this listener prevent propigation?
6301
		if(!capture){ var capture = false; }
6302
		evtName = dojo.event.browser.normalizedEventName(evtName);
6303
		if(evtName == "key"){
6304
			if(dojo.render.html.ie){
6305
				this.removeListener(node, "onkeydown", fp, capture);
6306
			}
6307
			evtName = "keypress";
6308
		}
6309
		// FIXME: this is mostly a punt, we aren't actually doing anything on IE
6310
		if(node.removeEventListener){
6311
			node.removeEventListener(evtName, fp, capture);
6312
		}
6313
	}
6314
 
6315
	this.addListener = function(/*DOMNode*/node, /*String*/evtName, /*Function*/fp, /*Boolean*/capture, /*Boolean*/dontFix){
6316
		// summary:
6317
		//		adds a listener to the node
6318
		// evtName:
6319
		//		the name of the handler to add the listener to can be either of
6320
		//		the form "onclick" or "click"
6321
		// node:
6322
		//		DOM node to attach the event to
6323
		// fp:
6324
		//		the function to register
6325
		// capture:
6326
		//		Optional. Should this listener prevent propigation?
6327
		// dontFix:
6328
		//		Optional. Should we avoid registering a new closure around the
6329
		//		listener to enable fixEvent for dispatch of the registered
6330
		//		function?
6331
		if(!node){ return; } // FIXME: log and/or bail?
6332
		if(!capture){ var capture = false; }
6333
		evtName = dojo.event.browser.normalizedEventName(evtName);
6334
		if(evtName == "key"){
6335
			if(dojo.render.html.ie){
6336
				this.addListener(node, "onkeydown", fp, capture, dontFix);
6337
			}
6338
			evtName = "keypress";
6339
		}
6340
 
6341
		if(!dontFix){
6342
			// build yet another closure around fp in order to inject fixEvent
6343
			// around the resulting event
6344
			var newfp = function(evt){
6345
				if(!evt){ evt = window.event; }
6346
				var ret = fp(dojo.event.browser.fixEvent(evt, this));
6347
				if(capture){
6348
					dojo.event.browser.stopEvent(evt);
6349
				}
6350
				return ret;
6351
			}
6352
		}else{
6353
			newfp = fp;
6354
		}
6355
 
6356
		if(node.addEventListener){
6357
			node.addEventListener(evtName, newfp, capture);
6358
			return newfp;
6359
		}else{
6360
			evtName = "on"+evtName;
6361
			if(typeof node[evtName] == "function" ){
6362
				var oldEvt = node[evtName];
6363
				node[evtName] = function(e){
6364
					oldEvt(e);
6365
					return newfp(e);
6366
				}
6367
			}else{
6368
				node[evtName]=newfp;
6369
			}
6370
			if(dojo.render.html.ie){
6371
				this.addClobberNodeAttrs(node, [evtName]);
6372
			}
6373
			return newfp;
6374
		}
6375
	}
6376
 
6377
	this.isEvent = function(/*Object*/obj){
6378
		// summary:
6379
		//		Tries to determine whether or not the object is a DOM event.
6380
 
6381
		// FIXME: event detection hack ... could test for additional attributes
6382
		// if necessary
6383
		return (typeof obj != "undefined")&&(obj)&&(typeof Event != "undefined")&&(obj.eventPhase); // Boolean
6384
		// Event does not support instanceof in Opera, otherwise:
6385
		//return (typeof Event != "undefined")&&(obj instanceof Event);
6386
	}
6387
 
6388
	this.currentEvent = null;
6389
 
6390
	this.callListener = function(/*Function*/listener, /*DOMNode*/curTarget){
6391
		// summary:
6392
		//		calls the specified listener in the context of the passed node
6393
		//		with the current DOM event object as the only parameter
6394
		// listener:
6395
		//		the function to call
6396
		// curTarget:
6397
		//		the Node to call the function in the scope of
6398
		if(typeof listener != 'function'){
6399
			dojo.raise("listener not a function: " + listener);
6400
		}
6401
		dojo.event.browser.currentEvent.currentTarget = curTarget;
6402
		return listener.call(curTarget, dojo.event.browser.currentEvent);
6403
	}
6404
 
6405
	this._stopPropagation = function(){
6406
		dojo.event.browser.currentEvent.cancelBubble = true;
6407
	}
6408
 
6409
	this._preventDefault = function(){
6410
		dojo.event.browser.currentEvent.returnValue = false;
6411
	}
6412
 
6413
	this.keys = {
6414
		KEY_BACKSPACE: 8,
6415
		KEY_TAB: 9,
6416
		KEY_CLEAR: 12,
6417
		KEY_ENTER: 13,
6418
		KEY_SHIFT: 16,
6419
		KEY_CTRL: 17,
6420
		KEY_ALT: 18,
6421
		KEY_PAUSE: 19,
6422
		KEY_CAPS_LOCK: 20,
6423
		KEY_ESCAPE: 27,
6424
		KEY_SPACE: 32,
6425
		KEY_PAGE_UP: 33,
6426
		KEY_PAGE_DOWN: 34,
6427
		KEY_END: 35,
6428
		KEY_HOME: 36,
6429
		KEY_LEFT_ARROW: 37,
6430
		KEY_UP_ARROW: 38,
6431
		KEY_RIGHT_ARROW: 39,
6432
		KEY_DOWN_ARROW: 40,
6433
		KEY_INSERT: 45,
6434
		KEY_DELETE: 46,
6435
		KEY_HELP: 47,
6436
		KEY_LEFT_WINDOW: 91,
6437
		KEY_RIGHT_WINDOW: 92,
6438
		KEY_SELECT: 93,
6439
		KEY_NUMPAD_0: 96,
6440
		KEY_NUMPAD_1: 97,
6441
		KEY_NUMPAD_2: 98,
6442
		KEY_NUMPAD_3: 99,
6443
		KEY_NUMPAD_4: 100,
6444
		KEY_NUMPAD_5: 101,
6445
		KEY_NUMPAD_6: 102,
6446
		KEY_NUMPAD_7: 103,
6447
		KEY_NUMPAD_8: 104,
6448
		KEY_NUMPAD_9: 105,
6449
		KEY_NUMPAD_MULTIPLY: 106,
6450
		KEY_NUMPAD_PLUS: 107,
6451
		KEY_NUMPAD_ENTER: 108,
6452
		KEY_NUMPAD_MINUS: 109,
6453
		KEY_NUMPAD_PERIOD: 110,
6454
		KEY_NUMPAD_DIVIDE: 111,
6455
		KEY_F1: 112,
6456
		KEY_F2: 113,
6457
		KEY_F3: 114,
6458
		KEY_F4: 115,
6459
		KEY_F5: 116,
6460
		KEY_F6: 117,
6461
		KEY_F7: 118,
6462
		KEY_F8: 119,
6463
		KEY_F9: 120,
6464
		KEY_F10: 121,
6465
		KEY_F11: 122,
6466
		KEY_F12: 123,
6467
		KEY_F13: 124,
6468
		KEY_F14: 125,
6469
		KEY_F15: 126,
6470
		KEY_NUM_LOCK: 144,
6471
		KEY_SCROLL_LOCK: 145
6472
	};
6473
 
6474
	// reverse lookup
6475
	this.revKeys = [];
6476
	for(var key in this.keys){
6477
		this.revKeys[this.keys[key]] = key;
6478
	}
6479
 
6480
	this.fixEvent = function(/*Event*/evt, /*DOMNode*/sender){
6481
		// summary:
6482
		//		normalizes properties on the event object including event
6483
		//		bubbling methods, keystroke normalization, and x/y positions
6484
		// evt: the native event object
6485
		// sender: the node to treat as "currentTarget"
6486
		if(!evt){
6487
			if(window["event"]){
6488
				evt = window.event;
6489
			}
6490
		}
6491
 
6492
		if((evt["type"])&&(evt["type"].indexOf("key") == 0)){ // key events
6493
			evt.keys = this.revKeys;
6494
			// FIXME: how can we eliminate this iteration?
6495
			for(var key in this.keys){
6496
				evt[key] = this.keys[key];
6497
			}
6498
			if(evt["type"] == "keydown" && dojo.render.html.ie){
6499
				switch(evt.keyCode){
6500
					case evt.KEY_SHIFT:
6501
					case evt.KEY_CTRL:
6502
					case evt.KEY_ALT:
6503
					case evt.KEY_CAPS_LOCK:
6504
					case evt.KEY_LEFT_WINDOW:
6505
					case evt.KEY_RIGHT_WINDOW:
6506
					case evt.KEY_SELECT:
6507
					case evt.KEY_NUM_LOCK:
6508
					case evt.KEY_SCROLL_LOCK:
6509
					// I'll get these in keypress after the OS munges them based on numlock
6510
					case evt.KEY_NUMPAD_0:
6511
					case evt.KEY_NUMPAD_1:
6512
					case evt.KEY_NUMPAD_2:
6513
					case evt.KEY_NUMPAD_3:
6514
					case evt.KEY_NUMPAD_4:
6515
					case evt.KEY_NUMPAD_5:
6516
					case evt.KEY_NUMPAD_6:
6517
					case evt.KEY_NUMPAD_7:
6518
					case evt.KEY_NUMPAD_8:
6519
					case evt.KEY_NUMPAD_9:
6520
					case evt.KEY_NUMPAD_PERIOD:
6521
						break; // just ignore the keys that can morph
6522
					case evt.KEY_NUMPAD_MULTIPLY:
6523
					case evt.KEY_NUMPAD_PLUS:
6524
					case evt.KEY_NUMPAD_ENTER:
6525
					case evt.KEY_NUMPAD_MINUS:
6526
					case evt.KEY_NUMPAD_DIVIDE:
6527
						break; // I could handle these but just pick them up in keypress
6528
					case evt.KEY_PAUSE:
6529
					case evt.KEY_TAB:
6530
					case evt.KEY_BACKSPACE:
6531
					case evt.KEY_ENTER:
6532
					case evt.KEY_ESCAPE:
6533
					case evt.KEY_PAGE_UP:
6534
					case evt.KEY_PAGE_DOWN:
6535
					case evt.KEY_END:
6536
					case evt.KEY_HOME:
6537
					case evt.KEY_LEFT_ARROW:
6538
					case evt.KEY_UP_ARROW:
6539
					case evt.KEY_RIGHT_ARROW:
6540
					case evt.KEY_DOWN_ARROW:
6541
					case evt.KEY_INSERT:
6542
					case evt.KEY_DELETE:
6543
					case evt.KEY_F1:
6544
					case evt.KEY_F2:
6545
					case evt.KEY_F3:
6546
					case evt.KEY_F4:
6547
					case evt.KEY_F5:
6548
					case evt.KEY_F6:
6549
					case evt.KEY_F7:
6550
					case evt.KEY_F8:
6551
					case evt.KEY_F9:
6552
					case evt.KEY_F10:
6553
					case evt.KEY_F11:
6554
					case evt.KEY_F12:
6555
					case evt.KEY_F12:
6556
					case evt.KEY_F13:
6557
					case evt.KEY_F14:
6558
					case evt.KEY_F15:
6559
					case evt.KEY_CLEAR:
6560
					case evt.KEY_HELP:
6561
						evt.key = evt.keyCode;
6562
						break;
6563
					default:
6564
						if(evt.ctrlKey || evt.altKey){
6565
							var unifiedCharCode = evt.keyCode;
6566
							// if lower case but keycode is uppercase, convert it
6567
							if(unifiedCharCode >= 65 && unifiedCharCode <= 90 && evt.shiftKey == false){
6568
								unifiedCharCode += 32;
6569
							}
6570
							if(unifiedCharCode >= 1 && unifiedCharCode <= 26 && evt.ctrlKey){
6571
								unifiedCharCode += 96; // 001-032 = ctrl+[a-z]
6572
							}
6573
							evt.key = String.fromCharCode(unifiedCharCode);
6574
						}
6575
				}
6576
			} else if(evt["type"] == "keypress"){
6577
				if(dojo.render.html.opera){
6578
					if(evt.which == 0){
6579
						evt.key = evt.keyCode;
6580
					}else if(evt.which > 0){
6581
						switch(evt.which){
6582
							case evt.KEY_SHIFT:
6583
							case evt.KEY_CTRL:
6584
							case evt.KEY_ALT:
6585
							case evt.KEY_CAPS_LOCK:
6586
							case evt.KEY_NUM_LOCK:
6587
							case evt.KEY_SCROLL_LOCK:
6588
								break;
6589
							case evt.KEY_PAUSE:
6590
							case evt.KEY_TAB:
6591
							case evt.KEY_BACKSPACE:
6592
							case evt.KEY_ENTER:
6593
							case evt.KEY_ESCAPE:
6594
								evt.key = evt.which;
6595
								break;
6596
							default:
6597
								var unifiedCharCode = evt.which;
6598
								if((evt.ctrlKey || evt.altKey || evt.metaKey) && (evt.which >= 65 && evt.which <= 90 && evt.shiftKey == false)){
6599
									unifiedCharCode += 32;
6600
								}
6601
								evt.key = String.fromCharCode(unifiedCharCode);
6602
						}
6603
					}
6604
				}else if(dojo.render.html.ie){ // catch some IE keys that are hard to get in keyDown
6605
					// key combinations were handled in onKeyDown
6606
					if(!evt.ctrlKey && !evt.altKey && evt.keyCode >= evt.KEY_SPACE){
6607
						evt.key = String.fromCharCode(evt.keyCode);
6608
					}
6609
				}else if(dojo.render.html.safari){
6610
					switch(evt.keyCode){
6611
						case 25: evt.key = evt.KEY_TAB; evt.shift = true;break;
6612
						case 63232: evt.key = evt.KEY_UP_ARROW; break;
6613
						case 63233: evt.key = evt.KEY_DOWN_ARROW; break;
6614
						case 63234: evt.key = evt.KEY_LEFT_ARROW; break;
6615
						case 63235: evt.key = evt.KEY_RIGHT_ARROW; break;
6616
						case 63236: evt.key = evt.KEY_F1; break;
6617
						case 63237: evt.key = evt.KEY_F2; break;
6618
						case 63238: evt.key = evt.KEY_F3; break;
6619
						case 63239: evt.key = evt.KEY_F4; break;
6620
						case 63240: evt.key = evt.KEY_F5; break;
6621
						case 63241: evt.key = evt.KEY_F6; break;
6622
						case 63242: evt.key = evt.KEY_F7; break;
6623
						case 63243: evt.key = evt.KEY_F8; break;
6624
						case 63244: evt.key = evt.KEY_F9; break;
6625
						case 63245: evt.key = evt.KEY_F10; break;
6626
						case 63246: evt.key = evt.KEY_F11; break;
6627
						case 63247: evt.key = evt.KEY_F12; break;
6628
						case 63250: evt.key = evt.KEY_PAUSE; break;
6629
						case 63272: evt.key = evt.KEY_DELETE; break;
6630
						case 63273: evt.key = evt.KEY_HOME; break;
6631
						case 63275: evt.key = evt.KEY_END; break;
6632
						case 63276: evt.key = evt.KEY_PAGE_UP; break;
6633
						case 63277: evt.key = evt.KEY_PAGE_DOWN; break;
6634
						case 63302: evt.key = evt.KEY_INSERT; break;
6635
						case 63248://prtscr
6636
						case 63249://scrolllock
6637
						case 63289://numlock
6638
							break;
6639
						default:
6640
							evt.key = evt.charCode >= evt.KEY_SPACE ? String.fromCharCode(evt.charCode) : evt.keyCode;
6641
					}
6642
				}else{
6643
					evt.key = evt.charCode > 0 ? String.fromCharCode(evt.charCode) : evt.keyCode;
6644
				}
6645
			}
6646
		}
6647
		if(dojo.render.html.ie){
6648
			if(!evt.target){ evt.target = evt.srcElement; }
6649
			if(!evt.currentTarget){ evt.currentTarget = (sender ? sender : evt.srcElement); }
6650
			if(!evt.layerX){ evt.layerX = evt.offsetX; }
6651
			if(!evt.layerY){ evt.layerY = evt.offsetY; }
6652
			// FIXME: scroll position query is duped from dojo.html to avoid dependency on that entire module
6653
			// DONOT replace the following to use dojo.body(), in IE, document.documentElement should be used
6654
			// here rather than document.body
6655
			var doc = (evt.srcElement && evt.srcElement.ownerDocument) ? evt.srcElement.ownerDocument : document;
6656
			var docBody = ((dojo.render.html.ie55)||(doc["compatMode"] == "BackCompat")) ? doc.body : doc.documentElement;
6657
			if(!evt.pageX){ evt.pageX = evt.clientX + (docBody.scrollLeft || 0) }
6658
			if(!evt.pageY){ evt.pageY = evt.clientY + (docBody.scrollTop || 0) }
6659
			// mouseover
6660
			if(evt.type == "mouseover"){ evt.relatedTarget = evt.fromElement; }
6661
			// mouseout
6662
			if(evt.type == "mouseout"){ evt.relatedTarget = evt.toElement; }
6663
			this.currentEvent = evt;
6664
			evt.callListener = this.callListener;
6665
			evt.stopPropagation = this._stopPropagation;
6666
			evt.preventDefault = this._preventDefault;
6667
		}
6668
		return evt; // Event
6669
	}
6670
 
6671
	this.stopEvent = function(/*Event*/evt){
6672
		// summary:
6673
		//		prevents propigation and clobbers the default action of the
6674
		//		passed event
6675
		// evt: Optional for IE. The native event object.
6676
		if(window.event){
6677
			evt.cancelBubble = true;
6678
			evt.returnValue = false;
6679
		}else{
6680
			evt.preventDefault();
6681
			evt.stopPropagation();
6682
		}
6683
	}
6684
}
6685
 
6686
dojo.kwCompoundRequire({
6687
	common: ["dojo.event.common", "dojo.event.topic"],
6688
	browser: ["dojo.event.browser"],
6689
	dashboard: ["dojo.event.browser"]
6690
});
6691
dojo.provide("dojo.event.*");
6692
 
6693
dojo.provide("dojo.gfx.color");
6694
 
6695
 
6696
 
6697
// TODO: rewrite the "x2y" methods to take advantage of the parsing
6698
//       abilities of the Color object. Also, beef up the Color
6699
//       object (as possible) to parse most common formats
6700
 
6701
// takes an r, g, b, a(lpha) value, [r, g, b, a] array, "rgb(...)" string, hex string (#aaa, #aaaaaa, aaaaaaa)
6702
dojo.gfx.color.Color = function(r, g, b, a) {
6703
	// dojo.debug("r:", r[0], "g:", r[1], "b:", r[2]);
6704
	if(dojo.lang.isArray(r)){
6705
		this.r = r[0];
6706
		this.g = r[1];
6707
		this.b = r[2];
6708
		this.a = r[3]||1.0;
6709
	}else if(dojo.lang.isString(r)){
6710
		var rgb = dojo.gfx.color.extractRGB(r);
6711
		this.r = rgb[0];
6712
		this.g = rgb[1];
6713
		this.b = rgb[2];
6714
		this.a = g||1.0;
6715
	}else if(r instanceof dojo.gfx.color.Color){
6716
		// why does this create a new instance if we were passed one?
6717
		this.r = r.r;
6718
		this.b = r.b;
6719
		this.g = r.g;
6720
		this.a = r.a;
6721
	}else{
6722
		this.r = r;
6723
		this.g = g;
6724
		this.b = b;
6725
		this.a = a;
6726
	}
6727
}
6728
 
6729
dojo.gfx.color.Color.fromArray = function(arr) {
6730
	return new dojo.gfx.color.Color(arr[0], arr[1], arr[2], arr[3]);
6731
}
6732
 
6733
dojo.extend(dojo.gfx.color.Color, {
6734
	toRgb: function(includeAlpha) {
6735
		if(includeAlpha) {
6736
			return this.toRgba();
6737
		} else {
6738
			return [this.r, this.g, this.b];
6739
		}
6740
	},
6741
	toRgba: function() {
6742
		return [this.r, this.g, this.b, this.a];
6743
	},
6744
	toHex: function() {
6745
		return dojo.gfx.color.rgb2hex(this.toRgb());
6746
	},
6747
	toCss: function() {
6748
		return "rgb(" + this.toRgb().join() + ")";
6749
	},
6750
	toString: function() {
6751
		return this.toHex(); // decent default?
6752
	},
6753
	blend: function(color, weight){
6754
		var rgb = null;
6755
		if(dojo.lang.isArray(color)){
6756
			rgb = color;
6757
		}else if(color instanceof dojo.gfx.color.Color){
6758
			rgb = color.toRgb();
6759
		}else{
6760
			rgb = new dojo.gfx.color.Color(color).toRgb();
6761
		}
6762
		return dojo.gfx.color.blend(this.toRgb(), rgb, weight);
6763
	}
6764
});
6765
 
6766
dojo.gfx.color.named = {
6767
	white:      [255,255,255],
6768
	black:      [0,0,0],
6769
	red:        [255,0,0],
6770
	green:	    [0,255,0],
6771
	lime:	    [0,255,0],
6772
	blue:       [0,0,255],
6773
	navy:       [0,0,128],
6774
	gray:       [128,128,128],
6775
	silver:     [192,192,192]
6776
};
6777
 
6778
dojo.gfx.color.blend = function(a, b, weight){
6779
	// summary:
6780
	//		blend colors a and b (both as RGB array or hex strings) with weight
6781
	//		from -1 to +1, 0 being a 50/50 blend
6782
	if(typeof a == "string"){
6783
		return dojo.gfx.color.blendHex(a, b, weight);
6784
	}
6785
	if(!weight){
6786
		weight = 0;
6787
	}
6788
	weight = Math.min(Math.max(-1, weight), 1);
6789
 
6790
	// alex: this interface blows.
6791
	// map -1 to 1 to the range 0 to 1
6792
	weight = ((weight + 1)/2);
6793
 
6794
	var c = [];
6795
 
6796
	// var stop = (1000*weight);
6797
	for(var x = 0; x < 3; x++){
6798
		c[x] = parseInt( b[x] + ( (a[x] - b[x]) * weight) );
6799
	}
6800
	return c;
6801
}
6802
 
6803
// very convenient blend that takes and returns hex values
6804
// (will get called automatically by blend when blend gets strings)
6805
dojo.gfx.color.blendHex = function(a, b, weight) {
6806
	return dojo.gfx.color.rgb2hex(dojo.gfx.color.blend(dojo.gfx.color.hex2rgb(a), dojo.gfx.color.hex2rgb(b), weight));
6807
}
6808
 
6809
// get RGB array from css-style color declarations
6810
dojo.gfx.color.extractRGB = function(color) {
6811
	var hex = "0123456789abcdef";
6812
	color = color.toLowerCase();
6813
	if( color.indexOf("rgb") == 0 ) {
6814
		var matches = color.match(/rgba*\((\d+), *(\d+), *(\d+)/i);
6815
		var ret = matches.splice(1, 3);
6816
		return ret;
6817
	} else {
6818
		var colors = dojo.gfx.color.hex2rgb(color);
6819
		if(colors) {
6820
			return colors;
6821
		} else {
6822
			// named color (how many do we support?)
6823
			return dojo.gfx.color.named[color] || [255, 255, 255];
6824
		}
6825
	}
6826
}
6827
 
6828
dojo.gfx.color.hex2rgb = function(hex) {
6829
	var hexNum = "0123456789ABCDEF";
6830
	var rgb = new Array(3);
6831
	if( hex.indexOf("#") == 0 ) { hex = hex.substring(1); }
6832
	hex = hex.toUpperCase();
6833
	if(hex.replace(new RegExp("["+hexNum+"]", "g"), "") != "") {
6834
		return null;
6835
	}
6836
	if( hex.length == 3 ) {
6837
		rgb[0] = hex.charAt(0) + hex.charAt(0)
6838
		rgb[1] = hex.charAt(1) + hex.charAt(1)
6839
		rgb[2] = hex.charAt(2) + hex.charAt(2);
6840
	} else {
6841
		rgb[0] = hex.substring(0, 2);
6842
		rgb[1] = hex.substring(2, 4);
6843
		rgb[2] = hex.substring(4);
6844
	}
6845
	for(var i = 0; i < rgb.length; i++) {
6846
		rgb[i] = hexNum.indexOf(rgb[i].charAt(0)) * 16 + hexNum.indexOf(rgb[i].charAt(1));
6847
	}
6848
	return rgb;
6849
}
6850
 
6851
dojo.gfx.color.rgb2hex = function(r, g, b) {
6852
	if(dojo.lang.isArray(r)) {
6853
		g = r[1] || 0;
6854
		b = r[2] || 0;
6855
		r = r[0] || 0;
6856
	}
6857
	var ret = dojo.lang.map([r, g, b], function(x) {
6858
		x = new Number(x);
6859
		var s = x.toString(16);
6860
		while(s.length < 2) { s = "0" + s; }
6861
		return s;
6862
	});
6863
	ret.unshift("#");
6864
	return ret.join("");
6865
}
6866
 
6867
dojo.provide("dojo.lfx.Animation");
6868
 
6869
 
6870
 
6871
/*
6872
	Animation package based on Dan Pupius' work: http://pupius.co.uk/js/Toolkit.Drawing.js
6873
*/
6874
dojo.lfx.Line = function(/*int*/ start, /*int*/ end){
6875
	// summary: dojo.lfx.Line is the object used to generate values
6876
	//			from a start value to an end value
6877
	this.start = start;
6878
	this.end = end;
6879
	if(dojo.lang.isArray(start)){
6880
		/* start: Array
6881
		   end: Array
6882
		   pId: a */
6883
		var diff = [];
6884
		dojo.lang.forEach(this.start, function(s,i){
6885
			diff[i] = this.end[i] - s;
6886
		}, this);
6887
 
6888
		this.getValue = function(/*float*/ n){
6889
			var res = [];
6890
			dojo.lang.forEach(this.start, function(s, i){
6891
				res[i] = (diff[i] * n) + s;
6892
			}, this);
6893
			return res; // Array
6894
		}
6895
	}else{
6896
		var diff = end - start;
6897
 
6898
		this.getValue = function(/*float*/ n){
6899
			//	summary: returns the point on the line
6900
			//	n: a floating point number greater than 0 and less than 1
6901
			return (diff * n) + this.start; // Decimal
6902
		}
6903
	}
6904
}
6905
 
6906
if((dojo.render.html.khtml)&&(!dojo.render.html.safari)){
6907
	// the cool kids are obviously not using konqueror...
6908
	// found a very wierd bug in floats constants, 1.5 evals as 1
6909
	// seems somebody mixed up ints and floats in 3.5.4 ??
6910
	// FIXME: investigate more and post a KDE bug (Fredrik)
6911
	dojo.lfx.easeDefault = function(/*Decimal?*/ n){
6912
		//	summary: Returns the point for point n on a sin wave.
6913
		return (parseFloat("0.5")+((Math.sin( (n+parseFloat("1.5")) * Math.PI))/2));
6914
	}
6915
}else{
6916
	dojo.lfx.easeDefault = function(/*Decimal?*/ n){
6917
		return (0.5+((Math.sin( (n+1.5) * Math.PI))/2));
6918
	}
6919
}
6920
 
6921
dojo.lfx.easeIn = function(/*Decimal?*/ n){
6922
	//	summary: returns the point on an easing curve
6923
	//	n: a floating point number greater than 0 and less than 1
6924
	return Math.pow(n, 3);
6925
}
6926
 
6927
dojo.lfx.easeOut = function(/*Decimal?*/ n){
6928
	//	summary: returns the point on the line
6929
	//	n: a floating point number greater than 0 and less than 1
6930
	return ( 1 - Math.pow(1 - n, 3) );
6931
}
6932
 
6933
dojo.lfx.easeInOut = function(/*Decimal?*/ n){
6934
	//	summary: returns the point on the line
6935
	//	n: a floating point number greater than 0 and less than 1
6936
	return ( (3 * Math.pow(n, 2)) - (2 * Math.pow(n, 3)) );
6937
}
6938
 
6939
dojo.lfx.IAnimation = function(){
6940
	// summary: dojo.lfx.IAnimation is an interface that implements
6941
	//			commonly used functions of animation objects
6942
}
6943
dojo.lang.extend(dojo.lfx.IAnimation, {
6944
	// public properties
6945
	curve: null,
6946
	duration: 1000,
6947
	easing: null,
6948
	repeatCount: 0,
6949
	rate: 10,
6950
 
6951
	// events
6952
	handler: null,
6953
	beforeBegin: null,
6954
	onBegin: null,
6955
	onAnimate: null,
6956
	onEnd: null,
6957
	onPlay: null,
6958
	onPause: null,
6959
	onStop: null,
6960
 
6961
	// public methods
6962
	play: null,
6963
	pause: null,
6964
	stop: null,
6965
 
6966
	connect: function(/*Event*/ evt, /*Object*/ scope, /*Function*/ newFunc){
6967
		// summary: Convenience function.  Quickly connect to an event
6968
		//			of this object and save the old functions connected to it.
6969
		// evt: The name of the event to connect to.
6970
		// scope: the scope in which to run newFunc.
6971
		// newFunc: the function to run when evt is fired.
6972
		if(!newFunc){
6973
			/* scope: Function
6974
			   newFunc: null
6975
			   pId: f */
6976
			newFunc = scope;
6977
			scope = this;
6978
		}
6979
		newFunc = dojo.lang.hitch(scope, newFunc);
6980
		var oldFunc = this[evt]||function(){};
6981
		this[evt] = function(){
6982
			var ret = oldFunc.apply(this, arguments);
6983
			newFunc.apply(this, arguments);
6984
			return ret;
6985
		}
6986
		return this; // dojo.lfx.IAnimation
6987
	},
6988
 
6989
	fire: function(/*Event*/ evt, /*Array*/ args){
6990
		// summary: Convenience function.  Fire event "evt" and pass it
6991
		//			the arguments specified in "args".
6992
		// evt: The event to fire.
6993
		// args: The arguments to pass to the event.
6994
		if(this[evt]){
6995
			this[evt].apply(this, (args||[]));
6996
		}
6997
		return this; // dojo.lfx.IAnimation
6998
	},
6999
 
7000
	repeat: function(/*int*/ count){
7001
		// summary: Set the repeat count of this object.
7002
		// count: How many times to repeat the animation.
7003
		this.repeatCount = count;
7004
		return this; // dojo.lfx.IAnimation
7005
	},
7006
 
7007
	// private properties
7008
	_active: false,
7009
	_paused: false
7010
});
7011
 
7012
dojo.lfx.Animation = function(	/*Object*/ handlers,
7013
								/*int*/ duration,
7014
								/*dojo.lfx.Line*/ curve,
7015
								/*function*/ easing,
7016
								/*int*/ repeatCount,
7017
								/*int*/ rate){
7018
	//	summary
7019
	//		a generic animation object that fires callbacks into it's handlers
7020
	//		object at various states
7021
	//	handlers: { handler: Function?, onstart: Function?, onstop: Function?, onanimate: Function? }
7022
	dojo.lfx.IAnimation.call(this);
7023
	if(dojo.lang.isNumber(handlers)||(!handlers && duration.getValue)){
7024
		// no handlers argument:
7025
		rate = repeatCount;
7026
		repeatCount = easing;
7027
		easing = curve;
7028
		curve = duration;
7029
		duration = handlers;
7030
		handlers = null;
7031
	}else if(handlers.getValue||dojo.lang.isArray(handlers)){
7032
		// no handlers or duration:
7033
		rate = easing;
7034
		repeatCount = curve;
7035
		easing = duration;
7036
		curve = handlers;
7037
		duration = null;
7038
		handlers = null;
7039
	}
7040
	if(dojo.lang.isArray(curve)){
7041
		/* curve: Array
7042
		   pId: a */
7043
		this.curve = new dojo.lfx.Line(curve[0], curve[1]);
7044
	}else{
7045
		this.curve = curve;
7046
	}
7047
	if(duration != null && duration > 0){ this.duration = duration; }
7048
	if(repeatCount){ this.repeatCount = repeatCount; }
7049
	if(rate){ this.rate = rate; }
7050
	if(handlers){
7051
		dojo.lang.forEach([
7052
				"handler", "beforeBegin", "onBegin",
7053
				"onEnd", "onPlay", "onStop", "onAnimate"
7054
			], function(item){
7055
				if(handlers[item]){
7056
					this.connect(item, handlers[item]);
7057
				}
7058
			}, this);
7059
	}
7060
	if(easing && dojo.lang.isFunction(easing)){
7061
		this.easing=easing;
7062
	}
7063
}
7064
dojo.inherits(dojo.lfx.Animation, dojo.lfx.IAnimation);
7065
dojo.lang.extend(dojo.lfx.Animation, {
7066
	// "private" properties
7067
	_startTime: null,
7068
	_endTime: null,
7069
	_timer: null,
7070
	_percent: 0,
7071
	_startRepeatCount: 0,
7072
 
7073
	// public methods
7074
	play: function(/*int?*/ delay, /*bool?*/ gotoStart){
7075
		// summary: Start the animation.
7076
		// delay: How many milliseconds to delay before starting.
7077
		// gotoStart: If true, starts the animation from the beginning; otherwise,
7078
		//            starts it from its current position.
7079
		if(gotoStart){
7080
			clearTimeout(this._timer);
7081
			this._active = false;
7082
			this._paused = false;
7083
			this._percent = 0;
7084
		}else if(this._active && !this._paused){
7085
			return this; // dojo.lfx.Animation
7086
		}
7087
 
7088
		this.fire("handler", ["beforeBegin"]);
7089
		this.fire("beforeBegin");
7090
 
7091
		if(delay > 0){
7092
			setTimeout(dojo.lang.hitch(this, function(){ this.play(null, gotoStart); }), delay);
7093
			return this; // dojo.lfx.Animation
7094
		}
7095
 
7096
		this._startTime = new Date().valueOf();
7097
		if(this._paused){
7098
			this._startTime -= (this.duration * this._percent / 100);
7099
		}
7100
		this._endTime = this._startTime + this.duration;
7101
 
7102
		this._active = true;
7103
		this._paused = false;
7104
 
7105
		var step = this._percent / 100;
7106
		var value = this.curve.getValue(step);
7107
		if(this._percent == 0 ){
7108
			if(!this._startRepeatCount){
7109
				this._startRepeatCount = this.repeatCount;
7110
			}
7111
			this.fire("handler", ["begin", value]);
7112
			this.fire("onBegin", [value]);
7113
		}
7114
 
7115
		this.fire("handler", ["play", value]);
7116
		this.fire("onPlay", [value]);
7117
 
7118
		this._cycle();
7119
		return this; // dojo.lfx.Animation
7120
	},
7121
 
7122
	pause: function(){
7123
		// summary: Pauses a running animation.
7124
		clearTimeout(this._timer);
7125
		if(!this._active){ return this; /*dojo.lfx.Animation*/}
7126
		this._paused = true;
7127
		var value = this.curve.getValue(this._percent / 100);
7128
		this.fire("handler", ["pause", value]);
7129
		this.fire("onPause", [value]);
7130
		return this; // dojo.lfx.Animation
7131
	},
7132
 
7133
	gotoPercent: function(/*Decimal*/ pct, /*bool?*/ andPlay){
7134
		// summary: Sets the progress of the animation.
7135
		// pct: A percentage in decimal notation (between and including 0.0 and 1.0).
7136
		// andPlay: If true, play the animation after setting the progress.
7137
		clearTimeout(this._timer);
7138
		this._active = true;
7139
		this._paused = true;
7140
		this._percent = pct;
7141
		if(andPlay){ this.play(); }
7142
		return this; // dojo.lfx.Animation
7143
	},
7144
 
7145
	stop: function(/*bool?*/ gotoEnd){
7146
		// summary: Stops a running animation.
7147
		// gotoEnd: If true, the animation will end.
7148
		clearTimeout(this._timer);
7149
		var step = this._percent / 100;
7150
		if(gotoEnd){
7151
			step = 1;
7152
		}
7153
		var value = this.curve.getValue(step);
7154
		this.fire("handler", ["stop", value]);
7155
		this.fire("onStop", [value]);
7156
		this._active = false;
7157
		this._paused = false;
7158
		return this; // dojo.lfx.Animation
7159
	},
7160
 
7161
	status: function(){
7162
		// summary: Returns a string representation of the status of
7163
		//			the animation.
7164
		if(this._active){
7165
			return this._paused ? "paused" : "playing"; // String
7166
		}else{
7167
			return "stopped"; // String
7168
		}
7169
		return this;
7170
	},
7171
 
7172
	// "private" methods
7173
	_cycle: function(){
7174
		clearTimeout(this._timer);
7175
		if(this._active){
7176
			var curr = new Date().valueOf();
7177
			var step = (curr - this._startTime) / (this._endTime - this._startTime);
7178
 
7179
			if(step >= 1){
7180
				step = 1;
7181
				this._percent = 100;
7182
			}else{
7183
				this._percent = step * 100;
7184
			}
7185
 
7186
			// Perform easing
7187
			if((this.easing)&&(dojo.lang.isFunction(this.easing))){
7188
				step = this.easing(step);
7189
			}
7190
 
7191
			var value = this.curve.getValue(step);
7192
			this.fire("handler", ["animate", value]);
7193
			this.fire("onAnimate", [value]);
7194
 
7195
			if( step < 1 ){
7196
				this._timer = setTimeout(dojo.lang.hitch(this, "_cycle"), this.rate);
7197
			}else{
7198
				this._active = false;
7199
				this.fire("handler", ["end"]);
7200
				this.fire("onEnd");
7201
 
7202
				if(this.repeatCount > 0){
7203
					this.repeatCount--;
7204
					this.play(null, true);
7205
				}else if(this.repeatCount == -1){
7206
					this.play(null, true);
7207
				}else{
7208
					if(this._startRepeatCount){
7209
						this.repeatCount = this._startRepeatCount;
7210
						this._startRepeatCount = 0;
7211
					}
7212
				}
7213
			}
7214
		}
7215
		return this; // dojo.lfx.Animation
7216
	}
7217
});
7218
 
7219
dojo.lfx.Combine = function(/*dojo.lfx.IAnimation...*/ animations){
7220
	// summary: An animation object to play animations passed to it at the same time.
7221
	dojo.lfx.IAnimation.call(this);
7222
	this._anims = [];
7223
	this._animsEnded = 0;
7224
 
7225
	var anims = arguments;
7226
	if(anims.length == 1 && (dojo.lang.isArray(anims[0]) || dojo.lang.isArrayLike(anims[0]))){
7227
		/* animations: dojo.lfx.IAnimation[]
7228
		   pId: a */
7229
		anims = anims[0];
7230
	}
7231
 
7232
	dojo.lang.forEach(anims, function(anim){
7233
		this._anims.push(anim);
7234
		anim.connect("onEnd", dojo.lang.hitch(this, "_onAnimsEnded"));
7235
	}, this);
7236
}
7237
dojo.inherits(dojo.lfx.Combine, dojo.lfx.IAnimation);
7238
dojo.lang.extend(dojo.lfx.Combine, {
7239
	// private members
7240
	_animsEnded: 0,
7241
 
7242
	// public methods
7243
	play: function(/*int?*/ delay, /*bool?*/ gotoStart){
7244
		// summary: Start the animations.
7245
		// delay: How many milliseconds to delay before starting.
7246
		// gotoStart: If true, starts the animations from the beginning; otherwise,
7247
		//            starts them from their current position.
7248
		if( !this._anims.length ){ return this; /*dojo.lfx.Combine*/}
7249
 
7250
		this.fire("beforeBegin");
7251
 
7252
		if(delay > 0){
7253
			setTimeout(dojo.lang.hitch(this, function(){ this.play(null, gotoStart); }), delay);
7254
			return this; // dojo.lfx.Combine
7255
		}
7256
 
7257
		if(gotoStart || this._anims[0].percent == 0){
7258
			this.fire("onBegin");
7259
		}
7260
		this.fire("onPlay");
7261
		this._animsCall("play", null, gotoStart);
7262
		return this; // dojo.lfx.Combine
7263
	},
7264
 
7265
	pause: function(){
7266
		// summary: Pauses the running animations.
7267
		this.fire("onPause");
7268
		this._animsCall("pause");
7269
		return this; // dojo.lfx.Combine
7270
	},
7271
 
7272
	stop: function(/*bool?*/ gotoEnd){
7273
		// summary: Stops the running animations.
7274
		// gotoEnd: If true, the animations will end.
7275
		this.fire("onStop");
7276
		this._animsCall("stop", gotoEnd);
7277
		return this; // dojo.lfx.Combine
7278
	},
7279
 
7280
	// private methods
7281
	_onAnimsEnded: function(){
7282
		this._animsEnded++;
7283
		if(this._animsEnded >= this._anims.length){
7284
			this.fire("onEnd");
7285
		}
7286
		return this; // dojo.lfx.Combine
7287
	},
7288
 
7289
	_animsCall: function(/*String*/ funcName){
7290
		var args = [];
7291
		if(arguments.length > 1){
7292
			for(var i = 1 ; i < arguments.length ; i++){
7293
				args.push(arguments[i]);
7294
			}
7295
		}
7296
		var _this = this;
7297
		dojo.lang.forEach(this._anims, function(anim){
7298
			anim[funcName](args);
7299
		}, _this);
7300
		return this; // dojo.lfx.Combine
7301
	}
7302
});
7303
 
7304
dojo.lfx.Chain = function(/*dojo.lfx.IAnimation...*/ animations) {
7305
	// summary: An animation object to play animations passed to it
7306
	//			one after another.
7307
	dojo.lfx.IAnimation.call(this);
7308
	this._anims = [];
7309
	this._currAnim = -1;
7310
 
7311
	var anims = arguments;
7312
	if(anims.length == 1 && (dojo.lang.isArray(anims[0]) || dojo.lang.isArrayLike(anims[0]))){
7313
		/* animations: dojo.lfx.IAnimation[]
7314
		   pId: a */
7315
		anims = anims[0];
7316
	}
7317
 
7318
	var _this = this;
7319
	dojo.lang.forEach(anims, function(anim, i, anims_arr){
7320
		this._anims.push(anim);
7321
		if(i < anims_arr.length - 1){
7322
			anim.connect("onEnd", dojo.lang.hitch(this, "_playNext") );
7323
		}else{
7324
			anim.connect("onEnd", dojo.lang.hitch(this, function(){ this.fire("onEnd"); }) );
7325
		}
7326
	}, this);
7327
}
7328
dojo.inherits(dojo.lfx.Chain, dojo.lfx.IAnimation);
7329
dojo.lang.extend(dojo.lfx.Chain, {
7330
	// private members
7331
	_currAnim: -1,
7332
 
7333
	// public methods
7334
	play: function(/*int?*/ delay, /*bool?*/ gotoStart){
7335
		// summary: Start the animation sequence.
7336
		// delay: How many milliseconds to delay before starting.
7337
		// gotoStart: If true, starts the sequence from the beginning; otherwise,
7338
		//            starts it from its current position.
7339
		if( !this._anims.length ) { return this; /*dojo.lfx.Chain*/}
7340
		if( gotoStart || !this._anims[this._currAnim] ) {
7341
			this._currAnim = 0;
7342
		}
7343
 
7344
		var currentAnimation = this._anims[this._currAnim];
7345
 
7346
		this.fire("beforeBegin");
7347
		if(delay > 0){
7348
			setTimeout(dojo.lang.hitch(this, function(){ this.play(null, gotoStart); }), delay);
7349
			return this; // dojo.lfx.Chain
7350
		}
7351
 
7352
		if(currentAnimation){
7353
			if(this._currAnim == 0){
7354
				this.fire("handler", ["begin", this._currAnim]);
7355
				this.fire("onBegin", [this._currAnim]);
7356
			}
7357
			this.fire("onPlay", [this._currAnim]);
7358
			currentAnimation.play(null, gotoStart);
7359
		}
7360
		return this; // dojo.lfx.Chain
7361
	},
7362
 
7363
	pause: function(){
7364
		// summary: Pauses the running animation sequence.
7365
		if( this._anims[this._currAnim] ) {
7366
			this._anims[this._currAnim].pause();
7367
			this.fire("onPause", [this._currAnim]);
7368
		}
7369
		return this; // dojo.lfx.Chain
7370
	},
7371
 
7372
	playPause: function(){
7373
		// summary: If the animation sequence is playing, pause it; otherwise,
7374
		//			play it.
7375
		if(this._anims.length == 0){ return this; }
7376
		if(this._currAnim == -1){ this._currAnim = 0; }
7377
		var currAnim = this._anims[this._currAnim];
7378
		if( currAnim ) {
7379
			if( !currAnim._active || currAnim._paused ) {
7380
				this.play();
7381
			} else {
7382
				this.pause();
7383
			}
7384
		}
7385
		return this; // dojo.lfx.Chain
7386
	},
7387
 
7388
	stop: function(){
7389
		// summary: Stops the running animations.
7390
		var currAnim = this._anims[this._currAnim];
7391
		if(currAnim){
7392
			currAnim.stop();
7393
			this.fire("onStop", [this._currAnim]);
7394
		}
7395
		return currAnim; // dojo.lfx.IAnimation
7396
	},
7397
 
7398
	// private methods
7399
	_playNext: function(){
7400
		if( this._currAnim == -1 || this._anims.length == 0 ) { return this; }
7401
		this._currAnim++;
7402
		if( this._anims[this._currAnim] ){
7403
			this._anims[this._currAnim].play(null, true);
7404
		}
7405
		return this; // dojo.lfx.Chain
7406
	}
7407
});
7408
 
7409
dojo.lfx.combine = function(/*dojo.lfx.IAnimation...*/ animations){
7410
	// summary: Convenience function.  Returns a dojo.lfx.Combine created
7411
	//			using the animations passed in.
7412
	var anims = arguments;
7413
	if(dojo.lang.isArray(arguments[0])){
7414
		/* animations: dojo.lfx.IAnimation[]
7415
		   pId: a */
7416
		anims = arguments[0];
7417
	}
7418
	if(anims.length == 1){ return anims[0]; }
7419
	return new dojo.lfx.Combine(anims); // dojo.lfx.Combine
7420
}
7421
 
7422
dojo.lfx.chain = function(/*dojo.lfx.IAnimation...*/ animations){
7423
	// summary: Convenience function.  Returns a dojo.lfx.Chain created
7424
	//			using the animations passed in.
7425
	var anims = arguments;
7426
	if(dojo.lang.isArray(arguments[0])){
7427
		/* animations: dojo.lfx.IAnimation[]
7428
		   pId: a */
7429
		anims = arguments[0];
7430
	}
7431
	if(anims.length == 1){ return anims[0]; }
7432
	return new dojo.lfx.Chain(anims); // dojo.lfx.Combine
7433
}
7434
 
7435
dojo.provide("dojo.html.common");
7436
 
7437
 
7438
 
7439
dojo.lang.mixin(dojo.html, dojo.dom);
7440
 
7441
dojo.html.body = function(){
7442
	dojo.deprecated("dojo.html.body() moved to dojo.body()", "0.5");
7443
	return dojo.body();
7444
}
7445
 
7446
// FIXME: we are going to assume that we can throw any and every rendering
7447
// engine into the IE 5.x box model. In Mozilla, we do this w/ CSS.
7448
// Need to investigate for KHTML and Opera
7449
 
7450
dojo.html.getEventTarget = function(/* DOMEvent */evt){
7451
	//	summary
7452
	//	Returns the target of an event
7453
	if(!evt) { evt = dojo.global().event || {} };
7454
	var t = (evt.srcElement ? evt.srcElement : (evt.target ? evt.target : null));
7455
	while((t)&&(t.nodeType!=1)){ t = t.parentNode; }
7456
	return t;	//	HTMLElement
7457
}
7458
 
7459
dojo.html.getViewport = function(){
7460
	//	summary
7461
	//	Returns the dimensions of the viewable area of a browser window
7462
	var _window = dojo.global();
7463
	var _document = dojo.doc();
7464
	var w = 0;
7465
	var h = 0;
7466
 
7467
	if(dojo.render.html.mozilla){
7468
		// mozilla
7469
		w = _document.documentElement.clientWidth;
7470
		h = _window.innerHeight;
7471
	}else if(!dojo.render.html.opera && _window.innerWidth){
7472
		//in opera9, dojo.body().clientWidth should be used, instead
7473
		//of window.innerWidth/document.documentElement.clientWidth
7474
		//so we have to check whether it is opera
7475
		w = _window.innerWidth;
7476
		h = _window.innerHeight;
7477
	} else if (!dojo.render.html.opera && dojo.exists(_document, "documentElement.clientWidth")){
7478
		// IE6 Strict
7479
		var w2 = _document.documentElement.clientWidth;
7480
		// this lets us account for scrollbars
7481
		if(!w || w2 && w2 < w) {
7482
			w = w2;
7483
		}
7484
		h = _document.documentElement.clientHeight;
7485
	} else if (dojo.body().clientWidth){
7486
		// IE, Opera
7487
		w = dojo.body().clientWidth;
7488
		h = dojo.body().clientHeight;
7489
	}
7490
	return { width: w, height: h };	//	object
7491
}
7492
 
7493
dojo.html.getScroll = function(){
7494
	//	summary
7495
	//	Returns the scroll position of the document
7496
	var _window = dojo.global();
7497
	var _document = dojo.doc();
7498
	var top = _window.pageYOffset || _document.documentElement.scrollTop || dojo.body().scrollTop || 0;
7499
	var left = _window.pageXOffset || _document.documentElement.scrollLeft || dojo.body().scrollLeft || 0;
7500
	return {
7501
		top: top,
7502
		left: left,
7503
		offset:{ x: left, y: top }	//	note the change, NOT an Array with added properties.
7504
	};	//	object
7505
}
7506
 
7507
dojo.html.getParentByType = function(/* HTMLElement */node, /* string */type) {
7508
	//	summary
7509
	//	Returns the first ancestor of node with tagName type.
7510
	var _document = dojo.doc();
7511
	var parent = dojo.byId(node);
7512
	type = type.toLowerCase();
7513
	while((parent)&&(parent.nodeName.toLowerCase()!=type)){
7514
		if(parent==(_document["body"]||_document["documentElement"])){
7515
			return null;
7516
		}
7517
		parent = parent.parentNode;
7518
	}
7519
	return parent;	//	HTMLElement
7520
}
7521
 
7522
dojo.html.getAttribute = function(/* HTMLElement */node, /* string */attr){
7523
	//	summary
7524
	//	Returns the value of attribute attr from node.
7525
	node = dojo.byId(node);
7526
	// FIXME: need to add support for attr-specific accessors
7527
	if((!node)||(!node.getAttribute)){
7528
		// if(attr !== 'nwType'){
7529
		//	alert("getAttr of '" + attr + "' with bad node");
7530
		// }
7531
		return null;
7532
	}
7533
	var ta = typeof attr == 'string' ? attr : new String(attr);
7534
 
7535
	// first try the approach most likely to succeed
7536
	var v = node.getAttribute(ta.toUpperCase());
7537
	if((v)&&(typeof v == 'string')&&(v!="")){
7538
		return v;	//	string
7539
	}
7540
 
7541
	// try returning the attributes value, if we couldn't get it as a string
7542
	if(v && v.value){
7543
		return v.value;	//	string
7544
	}
7545
 
7546
	// this should work on Opera 7, but it's a little on the crashy side
7547
	if((node.getAttributeNode)&&(node.getAttributeNode(ta))){
7548
		return (node.getAttributeNode(ta)).value;	//	string
7549
	}else if(node.getAttribute(ta)){
7550
		return node.getAttribute(ta);	//	string
7551
	}else if(node.getAttribute(ta.toLowerCase())){
7552
		return node.getAttribute(ta.toLowerCase());	//	string
7553
	}
7554
	return null;	//	string
7555
}
7556
 
7557
dojo.html.hasAttribute = function(/* HTMLElement */node, /* string */attr){
7558
	//	summary
7559
	//	Determines whether or not the specified node carries a value for the attribute in question.
7560
	return dojo.html.getAttribute(dojo.byId(node), attr) ? true : false;	//	boolean
7561
}
7562
 
7563
dojo.html.getCursorPosition = function(/* DOMEvent */e){
7564
	//	summary
7565
	//	Returns the mouse position relative to the document (not the viewport).
7566
	//	For example, if you have a document that is 10000px tall,
7567
	//	but your browser window is only 100px tall,
7568
	//	if you scroll to the bottom of the document and call this function it
7569
	//	will return {x: 0, y: 10000}
7570
	//	NOTE: for events delivered via dojo.event.connect() and/or dojoAttachEvent (for widgets),
7571
	//	you can just access evt.pageX and evt.pageY, rather than calling this function.
7572
	e = e || dojo.global().event;
7573
	var cursor = {x:0, y:0};
7574
	if(e.pageX || e.pageY){
7575
		cursor.x = e.pageX;
7576
		cursor.y = e.pageY;
7577
	}else{
7578
		var de = dojo.doc().documentElement;
7579
		var db = dojo.body();
7580
		cursor.x = e.clientX + ((de||db)["scrollLeft"]) - ((de||db)["clientLeft"]);
7581
		cursor.y = e.clientY + ((de||db)["scrollTop"]) - ((de||db)["clientTop"]);
7582
	}
7583
	return cursor;	//	object
7584
}
7585
 
7586
dojo.html.isTag = function(/* HTMLElement */node) {
7587
	//	summary
7588
	//	Like dojo.dom.isTag, except case-insensitive
7589
	node = dojo.byId(node);
7590
	if(node && node.tagName) {
7591
		for (var i=1; i<arguments.length; i++){
7592
			if (node.tagName.toLowerCase()==String(arguments[i]).toLowerCase()){
7593
				return String(arguments[i]).toLowerCase();	//	string
7594
			}
7595
		}
7596
	}
7597
	return "";	//	string
7598
}
7599
 
7600
//define dojo.html.createExternalElement for IE to workaround the annoying activation "feature" in new IE
7601
//details: http://msdn.microsoft.com/library/default.asp?url=/workshop/author/dhtml/overview/activating_activex.asp
7602
if(dojo.render.html.ie && !dojo.render.html.ie70){
7603
	//only define createExternalElement for IE in none https to avoid "mixed content" warning dialog
7604
	if(window.location.href.substr(0,6).toLowerCase() != "https:"){
7605
		(function(){
7606
			// FIXME: this seems not to work correctly on IE 7!!
7607
 
7608
			//The trick is to define a function in a script.src property:
7609
			// <script src="javascript:'function createExternalElement(){...}'"></script>,
7610
			//which will be treated as an external javascript file in IE
7611
			var xscript = dojo.doc().createElement('script');
7612
			xscript.src = "javascript:'dojo.html.createExternalElement=function(doc, tag){ return doc.createElement(tag); }'";
7613
			dojo.doc().getElementsByTagName("head")[0].appendChild(xscript);
7614
		})();
7615
	}
7616
}else{
7617
	//for other browsers, simply use document.createElement
7618
	//is enough
7619
	dojo.html.createExternalElement = function(/* HTMLDocument */doc, /* string */tag){
7620
		//	summary
7621
		//	Creates an element in the HTML document, here for ActiveX activation workaround.
7622
		return doc.createElement(tag);	//	HTMLElement
7623
	}
7624
}
7625
 
7626
dojo.html._callDeprecated = function(inFunc, replFunc, args, argName, retValue){
7627
	dojo.deprecated("dojo.html." + inFunc,
7628
					"replaced by dojo.html." + replFunc + "(" + (argName ? "node, {"+ argName + ": " + argName + "}" : "" ) + ")" + (retValue ? "." + retValue : ""), "0.5");
7629
	var newArgs = [];
7630
	if(argName){ var argsIn = {}; argsIn[argName] = args[1]; newArgs.push(args[0]); newArgs.push(argsIn); }
7631
	else { newArgs = args }
7632
	var ret = dojo.html[replFunc].apply(dojo.html, args);
7633
	if(retValue){ return ret[retValue]; }
7634
	else { return ret; }
7635
}
7636
 
7637
dojo.html.getViewportWidth = function(){
7638
	return dojo.html._callDeprecated("getViewportWidth", "getViewport", arguments, null, "width");
7639
}
7640
dojo.html.getViewportHeight = function(){
7641
	return dojo.html._callDeprecated("getViewportHeight", "getViewport", arguments, null, "height");
7642
}
7643
dojo.html.getViewportSize = function(){
7644
	return dojo.html._callDeprecated("getViewportSize", "getViewport", arguments);
7645
}
7646
dojo.html.getScrollTop = function(){
7647
	return dojo.html._callDeprecated("getScrollTop", "getScroll", arguments, null, "top");
7648
}
7649
dojo.html.getScrollLeft = function(){
7650
	return dojo.html._callDeprecated("getScrollLeft", "getScroll", arguments, null, "left");
7651
}
7652
dojo.html.getScrollOffset = function(){
7653
	return dojo.html._callDeprecated("getScrollOffset", "getScroll", arguments, null, "offset");
7654
}
7655
 
7656
dojo.provide("dojo.uri.Uri");
7657
 
7658
dojo.uri = new function() {
7659
	this.dojoUri = function (/*dojo.uri.Uri||String*/uri) {
7660
		// summary: returns a Uri object resolved relative to the dojo root
7661
		return new dojo.uri.Uri(dojo.hostenv.getBaseScriptUri(), uri);
7662
	}
7663
 
7664
	this.moduleUri = function(/*String*/module, /*dojo.uri.Uri||String*/uri){
7665
		// summary: returns a Uri object relative to a module
7666
		// description: Examples: dojo.uri.moduleUri("dojo.widget","templates/template.html"), or dojo.uri.moduleUri("acme","images/small.png")
7667
		var loc = dojo.hostenv.getModuleSymbols(module).join('/');
7668
		if(!loc){
7669
			return null;
7670
		}
7671
		if(loc.lastIndexOf("/") != loc.length-1){
7672
			loc += "/";
7673
		}
7674
 
7675
		//If the path is an absolute path (starts with a / or is on another domain/xdomain)
7676
		//then don't add the baseScriptUri.
7677
		var colonIndex = loc.indexOf(":");
7678
		var slashIndex = loc.indexOf("/");
7679
		if(loc.charAt(0) != "/" && (colonIndex == -1 || colonIndex > slashIndex)){
7680
			loc = dojo.hostenv.getBaseScriptUri() + loc;
7681
		}
7682
 
7683
		return new dojo.uri.Uri(loc,uri);
7684
	}
7685
 
7686
	this.Uri = function (/*dojo.uri.Uri||String...*/) {
7687
		// summary: Constructor to create an object representing a URI.
7688
		// description:
7689
		//  Each argument is evaluated in order relative to the next until
7690
		//  a canonical uri is produced. To get an absolute Uri relative
7691
		//  to the current document use
7692
		//      new dojo.uri.Uri(document.baseURI, uri)
7693
 
7694
		// TODO: support for IPv6, see RFC 2732
7695
 
7696
		// resolve uri components relative to each other
7697
		var uri = arguments[0];
7698
		for (var i = 1; i < arguments.length; i++) {
7699
			if(!arguments[i]) { continue; }
7700
 
7701
			// Safari doesn't support this.constructor so we have to be explicit
7702
			var relobj = new dojo.uri.Uri(arguments[i].toString());
7703
			var uriobj = new dojo.uri.Uri(uri.toString());
7704
 
7705
			if ((relobj.path=="")&&(relobj.scheme==null)&&(relobj.authority==null)&&(relobj.query==null)) {
7706
				if (relobj.fragment != null) { uriobj.fragment = relobj.fragment; }
7707
				relobj = uriobj;
7708
			} else if (relobj.scheme == null) {
7709
				relobj.scheme = uriobj.scheme;
7710
 
7711
				if (relobj.authority == null) {
7712
					relobj.authority = uriobj.authority;
7713
 
7714
					if (relobj.path.charAt(0) != "/") {
7715
						var path = uriobj.path.substring(0,
7716
							uriobj.path.lastIndexOf("/") + 1) + relobj.path;
7717
 
7718
						var segs = path.split("/");
7719
						for (var j = 0; j < segs.length; j++) {
7720
							if (segs[j] == ".") {
7721
								if (j == segs.length - 1) { segs[j] = ""; }
7722
								else { segs.splice(j, 1); j--; }
7723
							} else if (j > 0 && !(j == 1 && segs[0] == "") &&
7724
								segs[j] == ".." && segs[j-1] != "..") {
7725
 
7726
								if (j == segs.length - 1) { segs.splice(j, 1); segs[j - 1] = ""; }
7727
								else { segs.splice(j - 1, 2); j -= 2; }
7728
							}
7729
						}
7730
						relobj.path = segs.join("/");
7731
					}
7732
				}
7733
			}
7734
 
7735
			uri = "";
7736
			if (relobj.scheme != null) { uri += relobj.scheme + ":"; }
7737
			if (relobj.authority != null) { uri += "//" + relobj.authority; }
7738
			uri += relobj.path;
7739
			if (relobj.query != null) { uri += "?" + relobj.query; }
7740
			if (relobj.fragment != null) { uri += "#" + relobj.fragment; }
7741
		}
7742
 
7743
		this.uri = uri.toString();
7744
 
7745
		// break the uri into its main components
7746
		var regexp = "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$";
7747
		var r = this.uri.match(new RegExp(regexp));
7748
 
7749
		this.scheme = r[2] || (r[1] ? "" : null);
7750
		this.authority = r[4] || (r[3] ? "" : null);
7751
		this.path = r[5]; // can never be undefined
7752
		this.query = r[7] || (r[6] ? "" : null);
7753
		this.fragment  = r[9] || (r[8] ? "" : null);
7754
 
7755
		if (this.authority != null) {
7756
			// server based naming authority
7757
			regexp = "^((([^:]+:)?([^@]+))@)?([^:]*)(:([0-9]+))?$";
7758
			r = this.authority.match(new RegExp(regexp));
7759
 
7760
			this.user = r[3] || null;
7761
			this.password = r[4] || null;
7762
			this.host = r[5];
7763
			this.port = r[7] || null;
7764
		}
7765
 
7766
		this.toString = function(){ return this.uri; }
7767
	}
7768
};
7769
 
7770
dojo.provide("dojo.html.style");
7771
 
7772
 
7773
 
7774
dojo.html.getClass = function(/* HTMLElement */node){
7775
	//	summary
7776
	//	Returns the string value of the list of CSS classes currently assigned directly
7777
	//	to the node in question. Returns an empty string if no class attribute is found;
7778
	node = dojo.byId(node);
7779
	if(!node){ return ""; }
7780
	var cs = "";
7781
	if(node.className){
7782
		cs = node.className;
7783
	}else if(dojo.html.hasAttribute(node, "class")){
7784
		cs = dojo.html.getAttribute(node, "class");
7785
	}
7786
	return cs.replace(/^\s+|\s+$/g, "");	//	string
7787
}
7788
 
7789
dojo.html.getClasses = function(/* HTMLElement */node) {
7790
	//	summary
7791
	//	Returns an array of CSS classes currently assigned directly to the node in question.
7792
	//	Returns an empty array if no classes are found;
7793
	var c = dojo.html.getClass(node);
7794
	return (c == "") ? [] : c.split(/\s+/g);	//	array
7795
}
7796
 
7797
dojo.html.hasClass = function(/* HTMLElement */node, /* string */classname){
7798
	//	summary
7799
	//	Returns whether or not the specified classname is a portion of the
7800
	//	class list currently applied to the node. Does not cover cascaded
7801
	//	styles, only classes directly applied to the node.
7802
	return (new RegExp('(^|\\s+)'+classname+'(\\s+|$)')).test(dojo.html.getClass(node))	//	boolean
7803
}
7804
 
7805
dojo.html.prependClass = function(/* HTMLElement */node, /* string */classStr){
7806
	//	summary
7807
	//	Adds the specified class to the beginning of the class list on the
7808
	//	passed node. This gives the specified class the highest precidence
7809
	//	when style cascading is calculated for the node. Returns true or
7810
	//	false; indicating success or failure of the operation, respectively.
7811
	classStr += " " + dojo.html.getClass(node);
7812
	return dojo.html.setClass(node, classStr);	//	boolean
7813
}
7814
 
7815
dojo.html.addClass = function(/* HTMLElement */node, /* string */classStr){
7816
	//	summary
7817
	//	Adds the specified class to the end of the class list on the
7818
	//	passed &node;. Returns &true; or &false; indicating success or failure.
7819
	if (dojo.html.hasClass(node, classStr)) {
7820
	  return false;
7821
	}
7822
	classStr = (dojo.html.getClass(node) + " " + classStr).replace(/^\s+|\s+$/g,"");
7823
	return dojo.html.setClass(node, classStr);	//	boolean
7824
}
7825
 
7826
dojo.html.setClass = function(/* HTMLElement */node, /* string */classStr){
7827
	//	summary
7828
	//	Clobbers the existing list of classes for the node, replacing it with
7829
	//	the list given in the 2nd argument. Returns true or false
7830
	//	indicating success or failure.
7831
	node = dojo.byId(node);
7832
	var cs = new String(classStr);
7833
	try{
7834
		if(typeof node.className == "string"){
7835
			node.className = cs;
7836
		}else if(node.setAttribute){
7837
			node.setAttribute("class", classStr);
7838
			node.className = cs;
7839
		}else{
7840
			return false;
7841
		}
7842
	}catch(e){
7843
		dojo.debug("dojo.html.setClass() failed", e);
7844
	}
7845
	return true;
7846
}
7847
 
7848
dojo.html.removeClass = function(/* HTMLElement */node, /* string */classStr, /* boolean? */allowPartialMatches){
7849
	//	summary
7850
	//	Removes the className from the node;. Returns true or false indicating success or failure.
7851
	try{
7852
		if (!allowPartialMatches) {
7853
			var newcs = dojo.html.getClass(node).replace(new RegExp('(^|\\s+)'+classStr+'(\\s+|$)'), "$1$2");
7854
		} else {
7855
			var newcs = dojo.html.getClass(node).replace(classStr,'');
7856
		}
7857
		dojo.html.setClass(node, newcs);
7858
	}catch(e){
7859
		dojo.debug("dojo.html.removeClass() failed", e);
7860
	}
7861
	return true;	//	boolean
7862
}
7863
 
7864
dojo.html.replaceClass = function(/* HTMLElement */node, /* string */newClass, /* string */oldClass) {
7865
	//	summary
7866
	//	Replaces 'oldClass' and adds 'newClass' to node
7867
	dojo.html.removeClass(node, oldClass);
7868
	dojo.html.addClass(node, newClass);
7869
}
7870
 
7871
// Enum type for getElementsByClass classMatchType arg:
7872
dojo.html.classMatchType = {
7873
	ContainsAll : 0, // all of the classes are part of the node's class (default)
7874
	ContainsAny : 1, // any of the classes are part of the node's class
7875
	IsOnly : 2 // only all of the classes are part of the node's class
7876
}
7877
 
7878
 
7879
dojo.html.getElementsByClass = function(
7880
	/* string */classStr,
7881
	/* HTMLElement? */parent,
7882
	/* string? */nodeType,
7883
	/* integer? */classMatchType,
7884
	/* boolean? */useNonXpath
7885
){
7886
	//	summary
7887
	//	Returns an array of nodes for the given classStr, children of a
7888
	//	parent, and optionally of a certain nodeType
7889
	// FIXME: temporarily set to false because of several dojo tickets related
7890
	// to the xpath version not working consistently in firefox.
7891
	useNonXpath = false;
7892
	var _document = dojo.doc();
7893
	parent = dojo.byId(parent) || _document;
7894
	var classes = classStr.split(/\s+/g);
7895
	var nodes = [];
7896
	if( classMatchType != 1 && classMatchType != 2 ) classMatchType = 0; // make it enum
7897
	var reClass = new RegExp("(\\s|^)((" + classes.join(")|(") + "))(\\s|$)");
7898
	var srtLength = classes.join(" ").length;
7899
	var candidateNodes = [];
7900
 
7901
	if(!useNonXpath && _document.evaluate) { // supports dom 3 xpath
7902
		var xpath = ".//" + (nodeType || "*") + "[contains(";
7903
		if(classMatchType != dojo.html.classMatchType.ContainsAny){
7904
			xpath += "concat(' ',@class,' '), ' " +
7905
			classes.join(" ') and contains(concat(' ',@class,' '), ' ") +
7906
			" ')";
7907
			if (classMatchType == 2) {
7908
				xpath += " and string-length(@class)="+srtLength+"]";
7909
			}else{
7910
				xpath += "]";
7911
			}
7912
		}else{
7913
			xpath += "concat(' ',@class,' '), ' " +
7914
			classes.join(" ') or contains(concat(' ',@class,' '), ' ") +
7915
			" ')]";
7916
		}
7917
		var xpathResult = _document.evaluate(xpath, parent, null, XPathResult.ANY_TYPE, null);
7918
		var result = xpathResult.iterateNext();
7919
		while(result){
7920
			try{
7921
				candidateNodes.push(result);
7922
				result = xpathResult.iterateNext();
7923
			}catch(e){ break; }
7924
		}
7925
		return candidateNodes;	//	NodeList
7926
	}else{
7927
		if(!nodeType){
7928
			nodeType = "*";
7929
		}
7930
		candidateNodes = parent.getElementsByTagName(nodeType);
7931
 
7932
		var node, i = 0;
7933
		outer:
7934
		while(node = candidateNodes[i++]){
7935
			var nodeClasses = dojo.html.getClasses(node);
7936
			if(nodeClasses.length == 0){ continue outer; }
7937
			var matches = 0;
7938
 
7939
			for(var j = 0; j < nodeClasses.length; j++){
7940
				if(reClass.test(nodeClasses[j])){
7941
					if(classMatchType == dojo.html.classMatchType.ContainsAny){
7942
						nodes.push(node);
7943
						continue outer;
7944
					}else{
7945
						matches++;
7946
					}
7947
				}else{
7948
					if(classMatchType == dojo.html.classMatchType.IsOnly){
7949
						continue outer;
7950
					}
7951
				}
7952
			}
7953
 
7954
			if(matches == classes.length){
7955
				if(	(classMatchType == dojo.html.classMatchType.IsOnly)&&
7956
					(matches == nodeClasses.length)){
7957
					nodes.push(node);
7958
				}else if(classMatchType == dojo.html.classMatchType.ContainsAll){
7959
					nodes.push(node);
7960
				}
7961
			}
7962
		}
7963
		return nodes;	//	NodeList
7964
	}
7965
}
7966
dojo.html.getElementsByClassName = dojo.html.getElementsByClass;
7967
 
7968
dojo.html.toCamelCase = function(/* string */selector){
7969
	//	summary
7970
	//	Translates a CSS selector string to a camel-cased one.
7971
	var arr = selector.split('-'), cc = arr[0];
7972
	for(var i = 1; i < arr.length; i++) {
7973
		cc += arr[i].charAt(0).toUpperCase() + arr[i].substring(1);
7974
	}
7975
	return cc;	//	string
7976
}
7977
 
7978
dojo.html.toSelectorCase = function(/* string */selector){
7979
	//	summary
7980
	//	Translates a camel cased string to a selector cased one.
7981
	return selector.replace(/([A-Z])/g, "-$1" ).toLowerCase();	//	string
7982
}
7983
 
7984
if (dojo.render.html.ie) {
7985
	// IE branch
7986
	dojo.html.getComputedStyle = function(/*HTMLElement|String*/node, /*String*/property, /*String*/value) {
7987
		// summary
7988
		// Get the computed style value for style "property" on "node" (IE).
7989
		node = dojo.byId(node);  // FIXME: remove ability to access nodes by id for this time-critical function
7990
		if(!node || !node.style){return value;}
7991
		// FIXME: standardize on camel-case input to improve speed
7992
		return node.currentStyle[dojo.html.toCamelCase(property)]; // String
7993
	}
7994
	// SJM: getComputedStyle should be abandoned and replaced with the below function.
7995
	// All our supported browsers can return CSS2 compliant CssStyleDeclaration objects
7996
	// which can be queried directly for multiple styles.
7997
	dojo.html.getComputedStyles = function(/*HTMLElement*/node) {
7998
		// summary
7999
		// Get a style object containing computed styles for HTML Element node (IE).
8000
		return node.currentStyle; // CSSStyleDeclaration
8001
	}
8002
} else {
8003
	// non-IE branch
8004
	dojo.html.getComputedStyle = function(/*HTMLElement|String*/node, /*String*/property, /*Any*/value) {
8005
		// summary
8006
		// Get the computed style value for style "property" on "node" (non-IE).
8007
		node = dojo.byId(node);
8008
		if(!node || !node.style){return value;}
8009
		var s = document.defaultView.getComputedStyle(node, null);
8010
		// s may be null on Safari
8011
		return (s&&s[dojo.html.toCamelCase(property)])||''; // String
8012
	}
8013
	// SJM: getComputedStyle should be abandoned and replaced with the below function.
8014
	// All our supported browsers can return CSS2 compliant CssStyleDeclaration objects
8015
	// which can be queried directly for multiple styles.
8016
	dojo.html.getComputedStyles = function(node) {
8017
		// summary
8018
		// Get a style object containing computed styles for HTML Element node (non-IE).
8019
		return document.defaultView.getComputedStyle(node, null); // CSSStyleDeclaration
8020
	}
8021
}
8022
 
8023
dojo.html.getStyleProperty = function(/* HTMLElement */node, /* string */cssSelector){
8024
	//	summary
8025
	//	Returns the value of the passed style
8026
	node = dojo.byId(node);
8027
	return (node && node.style ? node.style[dojo.html.toCamelCase(cssSelector)] : undefined);	//	string
8028
}
8029
 
8030
dojo.html.getStyle = function(/* HTMLElement */node, /* string */cssSelector){
8031
	//	summary
8032
	//	Returns the computed value of the passed style
8033
	var value = dojo.html.getStyleProperty(node, cssSelector);
8034
	return (value ? value : dojo.html.getComputedStyle(node, cssSelector));	//	string || integer
8035
}
8036
 
8037
dojo.html.setStyle = function(/* HTMLElement */node, /* string */cssSelector, /* string */value){
8038
	//	summary
8039
	//	Set the value of passed style on node
8040
	node = dojo.byId(node);
8041
	if(node && node.style){
8042
		var camelCased = dojo.html.toCamelCase(cssSelector);
8043
		node.style[camelCased] = value;
8044
	}
8045
}
8046
 
8047
dojo.html.setStyleText = function (/* HTMLElement */target, /* string */text) {
8048
	//	summary
8049
	//	Try to set the entire cssText property of the passed target; equiv of setting style attribute.
8050
	try {
8051
	 	target.style.cssText = text;
8052
	} catch (e) {
8053
		target.setAttribute("style", text);
8054
	}
8055
}
8056
 
8057
dojo.html.copyStyle = function(/* HTMLElement */target, /* HTMLElement */source){
8058
	//	summary
8059
	// work around for opera which doesn't have cssText, and for IE which fails on setAttribute
8060
	if(!source.style.cssText){
8061
		target.setAttribute("style", source.getAttribute("style"));
8062
	}else{
8063
		target.style.cssText = source.style.cssText;
8064
	}
8065
	dojo.html.addClass(target, dojo.html.getClass(source));
8066
}
8067
 
8068
dojo.html.getUnitValue = function(/* HTMLElement */node, /* string */cssSelector, /* boolean? */autoIsZero){
8069
	//	summary
8070
	//	Get the value of passed selector, with the specific units used
8071
	var s = dojo.html.getComputedStyle(node, cssSelector);
8072
	if((!s)||((s == 'auto')&&(autoIsZero))){
8073
		return { value: 0, units: 'px' };	//	object
8074
	}
8075
	// FIXME: is regex inefficient vs. parseInt or some manual test?
8076
	var match = s.match(/(\-?[\d.]+)([a-z%]*)/i);
8077
	if (!match){return dojo.html.getUnitValue.bad;}
8078
	return { value: Number(match[1]), units: match[2].toLowerCase() };	//	object
8079
}
8080
dojo.html.getUnitValue.bad = { value: NaN, units: '' };
8081
 
8082
if (dojo.render.html.ie) {
8083
	// IE branch
8084
	dojo.html.toPixelValue = function(/* HTMLElement */element, /* String */styleValue){
8085
		// summary
8086
		//  Extract value in pixels from styleValue (IE version).
8087
		//  If a value cannot be extracted, zero is returned.
8088
		if(!styleValue){return 0;}
8089
		if(styleValue.slice(-2) == 'px'){return parseFloat(styleValue);}
8090
		var pixelValue = 0;
8091
		with(element){
8092
			var sLeft = style.left;
8093
			var rsLeft = runtimeStyle.left;
8094
			runtimeStyle.left = currentStyle.left;
8095
			try {
8096
				style.left = styleValue || 0;
8097
				pixelValue = style.pixelLeft;
8098
				style.left = sLeft;
8099
				runtimeStyle.left = rsLeft;
8100
			}catch(e){
8101
				// FIXME: it's possible for styleValue to be incompatible with
8102
				// style.left. In particular, border width values of
8103
				// "thick", "medium", or "thin" will provoke an exception.
8104
			}
8105
		}
8106
		return pixelValue; // Number
8107
	}
8108
} else {
8109
	// non-IE branch
8110
	dojo.html.toPixelValue = function(/* HTMLElement */element, /* String */styleValue){
8111
		// summary
8112
		//  Extract value in pixels from styleValue (non-IE version).
8113
		//  If a value cannot be extracted, zero is returned.
8114
		return (styleValue && (styleValue.slice(-2)=='px') ? parseFloat(styleValue) : 0); // Number
8115
	}
8116
}
8117
 
8118
dojo.html.getPixelValue = function(/* HTMLElement */node, /* string */styleProperty, /* boolean? */autoIsZero){
8119
	// summary
8120
	//  Get a computed style value, in pixels.
8121
	// node: HTMLElement
8122
	//  Node to interrogate
8123
	// styleProperty: String
8124
	//  Style property to query, in either css-selector or camelCase (property) format.
8125
	// autoIsZero: Boolean
8126
	//  Deprecated. Any value that cannot be converted to pixels is returned as zero.
8127
	//
8128
	//  summary
8129
	//  Get the value of passed selector in pixels.
8130
	//
8131
	return dojo.html.toPixelValue(node, dojo.html.getComputedStyle(node, styleProperty));
8132
}
8133
 
8134
dojo.html.setPositivePixelValue = function(/* HTMLElement */node, /* string */selector, /* integer */value){
8135
	//	summary
8136
	//	Attempt to set the value of selector on node as a positive pixel value.
8137
	if(isNaN(value)){return false;}
8138
	node.style[selector] = Math.max(0, value) + 'px';
8139
	return true;	//	boolean
8140
}
8141
 
8142
dojo.html.styleSheet = null;
8143
 
8144
// FIXME: this is a really basic stub for adding and removing cssRules, but
8145
// it assumes that you know the index of the cssRule that you want to add
8146
// or remove, making it less than useful.  So we need something that can
8147
// search for the selector that you you want to remove.
8148
dojo.html.insertCssRule = function(/* string */selector, /* string */declaration, /* integer? */index) {
8149
	//	summary
8150
	//	Attempt to insert declaration as selector on the internal stylesheet; if index try to set it there.
8151
	if (!dojo.html.styleSheet) {
8152
		if (document.createStyleSheet) { // IE
8153
			dojo.html.styleSheet = document.createStyleSheet();
8154
		} else if (document.styleSheets[0]) { // rest
8155
			// FIXME: should create a new style sheet here
8156
			// fall back on an exsiting style sheet
8157
			dojo.html.styleSheet = document.styleSheets[0];
8158
		} else {
8159
			return null;	//	integer
8160
		} // fail
8161
	}
8162
 
8163
	if (arguments.length < 3) { // index may == 0
8164
		if (dojo.html.styleSheet.cssRules) { // W3
8165
			index = dojo.html.styleSheet.cssRules.length;
8166
		} else if (dojo.html.styleSheet.rules) { // IE
8167
			index = dojo.html.styleSheet.rules.length;
8168
		} else {
8169
			return null;	//	integer
8170
		} // fail
8171
	}
8172
 
8173
	if (dojo.html.styleSheet.insertRule) { // W3
8174
		var rule = selector + " { " + declaration + " }";
8175
		return dojo.html.styleSheet.insertRule(rule, index);	//	integer
8176
	} else if (dojo.html.styleSheet.addRule) { // IE
8177
		return dojo.html.styleSheet.addRule(selector, declaration, index);	//	integer
8178
	} else {
8179
		return null; // integer
8180
	} // fail
8181
}
8182
 
8183
dojo.html.removeCssRule = function(/* integer? */index){
8184
	//	summary
8185
	//	Attempt to remove the rule at index.
8186
	if(!dojo.html.styleSheet){
8187
		dojo.debug("no stylesheet defined for removing rules");
8188
		return false;
8189
	}
8190
	if(dojo.render.html.ie){
8191
		if(!index){
8192
			index = dojo.html.styleSheet.rules.length;
8193
			dojo.html.styleSheet.removeRule(index);
8194
		}
8195
	}else if(document.styleSheets[0]){
8196
		if(!index){
8197
			index = dojo.html.styleSheet.cssRules.length;
8198
		}
8199
		dojo.html.styleSheet.deleteRule(index);
8200
	}
8201
	return true;	//	boolean
8202
}
8203
 
8204
dojo.html._insertedCssFiles = []; // cache container needed because IE reformats cssText when added to DOM
8205
dojo.html.insertCssFile = function(/* string */URI, /* HTMLDocument? */doc, /* boolean? */checkDuplicates, /* boolean */fail_ok){
8206
	//	summary
8207
	// calls css by XmlHTTP and inserts it into DOM as <style [widgetType="widgetType"]> *downloaded cssText*</style>
8208
	if(!URI){ return; }
8209
	if(!doc){ doc = document; }
8210
	var cssStr = dojo.hostenv.getText(URI, false, fail_ok);
8211
	if(cssStr===null){ return; }
8212
	cssStr = dojo.html.fixPathsInCssText(cssStr, URI);
8213
 
8214
	if(checkDuplicates){
8215
		var idx = -1, node, ent = dojo.html._insertedCssFiles;
8216
		for(var i = 0; i < ent.length; i++){
8217
			if((ent[i].doc == doc) && (ent[i].cssText == cssStr)){
8218
				idx = i; node = ent[i].nodeRef;
8219
				break;
8220
			}
8221
		}
8222
		// make sure we havent deleted our node
8223
		if(node){
8224
			var styles = doc.getElementsByTagName("style");
8225
			for(var i = 0; i < styles.length; i++){
8226
				if(styles[i] == node){
8227
					return;
8228
				}
8229
			}
8230
			// delete this entry
8231
			dojo.html._insertedCssFiles.shift(idx, 1);
8232
		}
8233
	}
8234
 
8235
	var style = dojo.html.insertCssText(cssStr, doc);
8236
	dojo.html._insertedCssFiles.push({'doc': doc, 'cssText': cssStr, 'nodeRef': style});
8237
 
8238
	// insert custom attribute ex dbgHref="../foo.css" usefull when debugging in DOM inspectors, no?
8239
	if(style && djConfig.isDebug){
8240
		style.setAttribute("dbgHref", URI);
8241
	}
8242
	return style;	//	HTMLStyleElement
8243
}
8244
 
8245
dojo.html.insertCssText = function(/* string */cssStr, /* HTMLDocument? */doc, /* string? */URI){
8246
	//	summary
8247
	//	Attempt to insert CSS rules into the document through inserting a style element
8248
	// DomNode Style  = insertCssText(String ".dojoMenu {color: green;}"[, DomDoc document, dojo.uri.Uri Url ])
8249
	if(!cssStr){
8250
		return; //	HTMLStyleElement
8251
	}
8252
	if(!doc){ doc = document; }
8253
	if(URI){// fix paths in cssStr
8254
		cssStr = dojo.html.fixPathsInCssText(cssStr, URI);
8255
	}
8256
	var style = doc.createElement("style");
8257
	style.setAttribute("type", "text/css");
8258
	// IE is b0rken enough to require that we add the element to the doc
8259
	// before changing it's properties
8260
	var head = doc.getElementsByTagName("head")[0];
8261
	if(!head){ // must have a head tag
8262
		dojo.debug("No head tag in document, aborting styles");
8263
		return;	//	HTMLStyleElement
8264
	}else{
8265
		head.appendChild(style);
8266
	}
8267
	if(style.styleSheet){// IE
8268
		var setFunc = function(){
8269
			try{
8270
				style.styleSheet.cssText = cssStr;
8271
			}catch(e){ dojo.debug(e); }
8272
		};
8273
		if(style.styleSheet.disabled){
8274
			setTimeout(setFunc, 10);
8275
		}else{
8276
			setFunc();
8277
		}
8278
	}else{ // w3c
8279
		var cssText = doc.createTextNode(cssStr);
8280
		style.appendChild(cssText);
8281
	}
8282
	return style;	//	HTMLStyleElement
8283
}
8284
 
8285
dojo.html.fixPathsInCssText = function(/* string */cssStr, /* string */URI){
8286
	//	summary
8287
	// usage: cssText comes from dojoroot/src/widget/templates/Foobar.css
8288
	// 	it has .dojoFoo { background-image: url(images/bar.png);} then uri should point to dojoroot/src/widget/templates/
8289
	if(!cssStr || !URI){ return; }
8290
	var match, str = "", url = "", urlChrs = "[\\t\\s\\w\\(\\)\\/\\.\\\\'\"-:#=&?~]+";
8291
	var regex = new RegExp('url\\(\\s*('+urlChrs+')\\s*\\)');
8292
	var regexProtocol = /(file|https?|ftps?):\/\//;
8293
	regexTrim = new RegExp("^[\\s]*(['\"]?)("+urlChrs+")\\1[\\s]*?$");
8294
	if(dojo.render.html.ie55 || dojo.render.html.ie60){
8295
		var regexIe = new RegExp("AlphaImageLoader\\((.*)src\=['\"]("+urlChrs+")['\"]");
8296
		// TODO: need to decide how to handle relative paths and AlphaImageLoader see #1441
8297
		// current implementation breaks on build with intern_strings
8298
		while(match = regexIe.exec(cssStr)){
8299
			url = match[2].replace(regexTrim, "$2");
8300
			if(!regexProtocol.exec(url)){
8301
				url = (new dojo.uri.Uri(URI, url).toString());
8302
			}
8303
			str += cssStr.substring(0, match.index) + "AlphaImageLoader(" + match[1] + "src='" + url + "'";
8304
			cssStr = cssStr.substr(match.index + match[0].length);
8305
		}
8306
		cssStr = str + cssStr;
8307
		str = "";
8308
	}
8309
 
8310
	while(match = regex.exec(cssStr)){
8311
		url = match[1].replace(regexTrim, "$2");
8312
		if(!regexProtocol.exec(url)){
8313
			url = (new dojo.uri.Uri(URI, url).toString());
8314
		}
8315
		str += cssStr.substring(0, match.index) + "url(" + url + ")";
8316
		cssStr = cssStr.substr(match.index + match[0].length);
8317
	}
8318
	return str + cssStr;	//	string
8319
}
8320
 
8321
dojo.html.setActiveStyleSheet = function(/* string */title){
8322
	//	summary
8323
	//	Activate style sheet with specified title.
8324
	var i = 0, a, els = dojo.doc().getElementsByTagName("link");
8325
	while (a = els[i++]) {
8326
		if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title")){
8327
			a.disabled = true;
8328
			if (a.getAttribute("title") == title) { a.disabled = false; }
8329
		}
8330
	}
8331
}
8332
 
8333
dojo.html.getActiveStyleSheet = function(){
8334
	//	summary
8335
	//	return the title of the currently active stylesheet
8336
	var i = 0, a, els = dojo.doc().getElementsByTagName("link");
8337
	while (a = els[i++]) {
8338
		if (a.getAttribute("rel").indexOf("style") != -1
8339
			&& a.getAttribute("title")
8340
			&& !a.disabled
8341
		){
8342
			return a.getAttribute("title");	//	string
8343
		}
8344
	}
8345
	return null;	//	string
8346
}
8347
 
8348
dojo.html.getPreferredStyleSheet = function(){
8349
	//	summary
8350
	//	Return the preferred stylesheet title (i.e. link without alt attribute)
8351
	var i = 0, a, els = dojo.doc().getElementsByTagName("link");
8352
	while (a = els[i++]) {
8353
		if(a.getAttribute("rel").indexOf("style") != -1
8354
			&& a.getAttribute("rel").indexOf("alt") == -1
8355
			&& a.getAttribute("title")
8356
		){
8357
			return a.getAttribute("title"); 	//	string
8358
		}
8359
	}
8360
	return null;	//	string
8361
}
8362
 
8363
dojo.html.applyBrowserClass = function(/* HTMLElement */node){
8364
	//	summary
8365
	//	Applies pre-set class names based on browser & version to the passed node.
8366
	//	Modified version of Morris' CSS hack.
8367
	var drh=dojo.render.html;
8368
	var classes = {
8369
		dj_ie: drh.ie,
8370
		dj_ie55: drh.ie55,
8371
		dj_ie6: drh.ie60,
8372
		dj_ie7: drh.ie70,
8373
		dj_iequirks: drh.ie && drh.quirks,
8374
		dj_opera: drh.opera,
8375
		dj_opera8: drh.opera && (Math.floor(dojo.render.version)==8),
8376
		dj_opera9: drh.opera && (Math.floor(dojo.render.version)==9),
8377
		dj_khtml: drh.khtml,
8378
		dj_safari: drh.safari,
8379
		dj_gecko: drh.mozilla
8380
	}; // no dojo unsupported browsers
8381
	for(var p in classes){
8382
		if(classes[p]){
8383
			dojo.html.addClass(node, p);
8384
		}
8385
	}
8386
};
8387
 
8388
dojo.provide("dojo.html.display");
8389
 
8390
 
8391
dojo.html._toggle = function(node, tester, setter){
8392
	node = dojo.byId(node);
8393
	setter(node, !tester(node));
8394
	return tester(node);
8395
}
8396
 
8397
dojo.html.show = function(/* HTMLElement */node){
8398
	//	summary
8399
	//	Show the passed element by reverting display property set by dojo.html.hide
8400
	node = dojo.byId(node);
8401
	if(dojo.html.getStyleProperty(node, 'display')=='none'){
8402
		dojo.html.setStyle(node, 'display', (node.dojoDisplayCache||''));
8403
		node.dojoDisplayCache = undefined;	// cannot use delete on a node in IE6
8404
	}
8405
}
8406
 
8407
dojo.html.hide = function(/* HTMLElement */node){
8408
	//	summary
8409
	//	Hide the passed element by setting display:none
8410
	node = dojo.byId(node);
8411
	if(typeof node["dojoDisplayCache"] == "undefined"){ // it could == '', so we cannot say !node.dojoDisplayCount
8412
		var d = dojo.html.getStyleProperty(node, 'display')
8413
		if(d!='none'){
8414
			node.dojoDisplayCache = d;
8415
		}
8416
	}
8417
	dojo.html.setStyle(node, 'display', 'none');
8418
}
8419
 
8420
dojo.html.setShowing = function(/* HTMLElement */node, /* boolean? */showing){
8421
	//	summary
8422
	// Calls show() if showing is true, hide() otherwise
8423
	dojo.html[(showing ? 'show' : 'hide')](node);
8424
}
8425
 
8426
dojo.html.isShowing = function(/* HTMLElement */node){
8427
	//	summary
8428
	//	Returns whether the element is displayed or not.
8429
	// FIXME: returns true if node is bad, isHidden would be easier to make correct
8430
	return (dojo.html.getStyleProperty(node, 'display') != 'none');	//	boolean
8431
}
8432
 
8433
dojo.html.toggleShowing = function(/* HTMLElement */node){
8434
	//	summary
8435
	// Call setShowing() on node with the complement of isShowing(), then return the new value of isShowing()
8436
	return dojo.html._toggle(node, dojo.html.isShowing, dojo.html.setShowing);	//	boolean
8437
}
8438
 
8439
// Simple mapping of tag names to display values
8440
// FIXME: simplistic
8441
dojo.html.displayMap = { tr: '', td: '', th: '', img: 'inline', span: 'inline', input: 'inline', button: 'inline' };
8442
 
8443
dojo.html.suggestDisplayByTagName = function(/* HTMLElement */node){
8444
	//	summary
8445
	// Suggest a value for the display property that will show 'node' based on it's tag
8446
	node = dojo.byId(node);
8447
	if(node && node.tagName){
8448
		var tag = node.tagName.toLowerCase();
8449
		return (tag in dojo.html.displayMap ? dojo.html.displayMap[tag] : 'block');	//	string
8450
	}
8451
}
8452
 
8453
dojo.html.setDisplay = function(/* HTMLElement */node, /* string */display){
8454
	//	summary
8455
	// 	Sets the value of style.display to value of 'display' parameter if it is a string.
8456
	// 	Otherwise, if 'display' is false, set style.display to 'none'.
8457
	// 	Finally, set 'display' to a suggested display value based on the node's tag
8458
	dojo.html.setStyle(node, 'display', ((display instanceof String || typeof display == "string") ? display : (display ? dojo.html.suggestDisplayByTagName(node) : 'none')));
8459
}
8460
 
8461
dojo.html.isDisplayed = function(/* HTMLElement */node){
8462
	//	summary
8463
	// 	Is true if the the computed display style for node is not 'none'
8464
	// 	FIXME: returns true if node is bad, isNotDisplayed would be easier to make correct
8465
	return (dojo.html.getComputedStyle(node, 'display') != 'none');	//	boolean
8466
}
8467
 
8468
dojo.html.toggleDisplay = function(/* HTMLElement */node){
8469
	//	summary
8470
	// 	Call setDisplay() on node with the complement of isDisplayed(), then
8471
	// 	return the new value of isDisplayed()
8472
	return dojo.html._toggle(node, dojo.html.isDisplayed, dojo.html.setDisplay);	//	boolean
8473
}
8474
 
8475
dojo.html.setVisibility = function(/* HTMLElement */node, /* string */visibility){
8476
	//	summary
8477
	// 	Sets the value of style.visibility to value of 'visibility' parameter if it is a string.
8478
	// 	Otherwise, if 'visibility' is false, set style.visibility to 'hidden'. Finally, set style.visibility to 'visible'.
8479
	dojo.html.setStyle(node, 'visibility', ((visibility instanceof String || typeof visibility == "string") ? visibility : (visibility ? 'visible' : 'hidden')));
8480
}
8481
 
8482
dojo.html.isVisible = function(/* HTMLElement */node){
8483
	//	summary
8484
	// 	Returns true if the the computed visibility style for node is not 'hidden'
8485
	// 	FIXME: returns true if node is bad, isInvisible would be easier to make correct
8486
	return (dojo.html.getComputedStyle(node, 'visibility') != 'hidden');	//	boolean
8487
}
8488
 
8489
dojo.html.toggleVisibility = function(node){
8490
	//	summary
8491
	// Call setVisibility() on node with the complement of isVisible(), then return the new value of isVisible()
8492
	return dojo.html._toggle(node, dojo.html.isVisible, dojo.html.setVisibility);	//	boolean
8493
}
8494
 
8495
dojo.html.setOpacity = function(/* HTMLElement */node, /* float */opacity, /* boolean? */dontFixOpacity){
8496
	//	summary
8497
	//	Sets the opacity of node in a cross-browser way.
8498
	//	float between 0.0 (transparent) and 1.0 (opaque)
8499
	node = dojo.byId(node);
8500
	var h = dojo.render.html;
8501
	if(!dontFixOpacity){
8502
		if( opacity >= 1.0){
8503
			if(h.ie){
8504
				dojo.html.clearOpacity(node);
8505
				return;
8506
			}else{
8507
				opacity = 0.999999;
8508
			}
8509
		}else if( opacity < 0.0){ opacity = 0; }
8510
	}
8511
	if(h.ie){
8512
		if(node.nodeName.toLowerCase() == "tr"){
8513
			// FIXME: is this too naive? will we get more than we want?
8514
			var tds = node.getElementsByTagName("td");
8515
			for(var x=0; x<tds.length; x++){
8516
				tds[x].style.filter = "Alpha(Opacity="+opacity*100+")";
8517
			}
8518
		}
8519
		node.style.filter = "Alpha(Opacity="+opacity*100+")";
8520
	}else if(h.moz){
8521
		node.style.opacity = opacity; // ffox 1.0 directly supports "opacity"
8522
		node.style.MozOpacity = opacity;
8523
	}else if(h.safari){
8524
		node.style.opacity = opacity; // 1.3 directly supports "opacity"
8525
		node.style.KhtmlOpacity = opacity;
8526
	}else{
8527
		node.style.opacity = opacity;
8528
	}
8529
}
8530
 
8531
dojo.html.clearOpacity = function(/* HTMLElement */node){
8532
	//	summary
8533
	//	Clears any opacity setting on the passed element.
8534
	node = dojo.byId(node);
8535
	var ns = node.style;
8536
	var h = dojo.render.html;
8537
	if(h.ie){
8538
		try {
8539
			if( node.filters && node.filters.alpha ){
8540
				ns.filter = ""; // FIXME: may get rid of other filter effects
8541
			}
8542
		} catch(e) {
8543
			/*
8544
			 * IE7 gives error if node.filters not set;
8545
			 * don't know why or how to workaround (other than this)
8546
			 */
8547
		}
8548
	}else if(h.moz){
8549
		ns.opacity = 1;
8550
		ns.MozOpacity = 1;
8551
	}else if(h.safari){
8552
		ns.opacity = 1;
8553
		ns.KhtmlOpacity = 1;
8554
	}else{
8555
		ns.opacity = 1;
8556
	}
8557
}
8558
 
8559
dojo.html.getOpacity = function(/* HTMLElement */node){
8560
	//	summary
8561
	//	Returns the opacity of the passed element
8562
	node = dojo.byId(node);
8563
	var h = dojo.render.html;
8564
	if(h.ie){
8565
		var opac = (node.filters && node.filters.alpha &&
8566
			typeof node.filters.alpha.opacity == "number"
8567
			? node.filters.alpha.opacity : 100) / 100;
8568
	}else{
8569
		var opac = node.style.opacity || node.style.MozOpacity ||
8570
			node.style.KhtmlOpacity || 1;
8571
	}
8572
	return opac >= 0.999999 ? 1.0 : Number(opac);	//	float
8573
}
8574
 
8575
 
8576
dojo.provide("dojo.html.color");
8577
 
8578
 
8579
 
8580
 
8581
dojo.html.getBackgroundColor = function(/* HTMLElement */node){
8582
	//	summary
8583
	//	returns the background color of the passed node as a 32-bit color (RGBA)
8584
	node = dojo.byId(node);
8585
	var color;
8586
	do{
8587
		color = dojo.html.getStyle(node, "background-color");
8588
		// Safari doesn't say "transparent"
8589
		if(color.toLowerCase() == "rgba(0, 0, 0, 0)") { color = "transparent"; }
8590
		if(node == document.getElementsByTagName("body")[0]) { node = null; break; }
8591
		node = node.parentNode;
8592
	}while(node && dojo.lang.inArray(["transparent", ""], color));
8593
	if(color == "transparent"){
8594
		color = [255, 255, 255, 0];
8595
	}else{
8596
		color = dojo.gfx.color.extractRGB(color);
8597
	}
8598
	return color;	//	array
8599
}
8600
 
8601
dojo.provide("dojo.html.layout");
8602
 
8603
 
8604
 
8605
 
8606
 
8607
dojo.html.sumAncestorProperties = function(/* HTMLElement */node, /* string */prop){
8608
	//	summary
8609
	//	Returns the sum of the passed property on all ancestors of node.
8610
	node = dojo.byId(node);
8611
	if(!node){ return 0; } // FIXME: throw an error?
8612
 
8613
	var retVal = 0;
8614
	while(node){
8615
		if(dojo.html.getComputedStyle(node, 'position') == 'fixed'){
8616
			return 0;
8617
		}
8618
		var val = node[prop];
8619
		if(val){
8620
			retVal += val - 0;
8621
			if(node==dojo.body()){ break; }// opera and khtml #body & #html has the same values, we only need one value
8622
		}
8623
		node = node.parentNode;
8624
	}
8625
	return retVal;	//	integer
8626
}
8627
 
8628
dojo.html.setStyleAttributes = function(/* HTMLElement */node, /* string */attributes) {
8629
	//	summary
8630
	//	allows a dev to pass a string similar to what you'd pass in style="", and apply it to a node.
8631
	node = dojo.byId(node);
8632
	var splittedAttribs=attributes.replace(/(;)?\s*$/, "").split(";");
8633
	for(var i=0; i<splittedAttribs.length; i++){
8634
		var nameValue=splittedAttribs[i].split(":");
8635
		var name=nameValue[0].replace(/\s*$/, "").replace(/^\s*/, "").toLowerCase();
8636
		var value=nameValue[1].replace(/\s*$/, "").replace(/^\s*/, "");
8637
		switch(name){
8638
			case "opacity":
8639
				dojo.html.setOpacity(node, value);
8640
				break;
8641
			case "content-height":
8642
				dojo.html.setContentBox(node, {height: value});
8643
				break;
8644
			case "content-width":
8645
				dojo.html.setContentBox(node, {width: value});
8646
				break;
8647
			case "outer-height":
8648
				dojo.html.setMarginBox(node, {height: value});
8649
				break;
8650
			case "outer-width":
8651
				dojo.html.setMarginBox(node, {width: value});
8652
				break;
8653
			default:
8654
				node.style[dojo.html.toCamelCase(name)]=value;
8655
		}
8656
	}
8657
}
8658
 
8659
dojo.html.boxSizing = {
8660
	MARGIN_BOX: "margin-box",
8661
	BORDER_BOX: "border-box",
8662
	PADDING_BOX: "padding-box",
8663
	CONTENT_BOX: "content-box"
8664
};
8665
 
8666
dojo.html.getAbsolutePosition = dojo.html.abs = function(/* HTMLElement */node, /* boolean? */includeScroll, /* string? */boxType){
8667
	//	summary
8668
	//	Gets the absolute position of the passed element based on the document itself.
8669
	node = dojo.byId(node, node.ownerDocument);
8670
	var ret = {
8671
		x: 0,
8672
		y: 0
8673
	};
8674
 
8675
	var bs = dojo.html.boxSizing;
8676
	if(!boxType) { boxType = bs.CONTENT_BOX; }
8677
	var nativeBoxType = 2; //BORDER box
8678
	var targetBoxType;
8679
	switch(boxType){
8680
		case bs.MARGIN_BOX:
8681
			targetBoxType = 3;
8682
			break;
8683
		case bs.BORDER_BOX:
8684
			targetBoxType = 2;
8685
			break;
8686
		case bs.PADDING_BOX:
8687
		default:
8688
			targetBoxType = 1;
8689
			break;
8690
		case bs.CONTENT_BOX:
8691
			targetBoxType = 0;
8692
			break;
8693
	}
8694
 
8695
	var h = dojo.render.html;
8696
	var db = document["body"]||document["documentElement"];
8697
 
8698
	if(h.ie){
8699
		with(node.getBoundingClientRect()){
8700
			ret.x = left-2;
8701
			ret.y = top-2;
8702
		}
8703
	}else if(document.getBoxObjectFor){
8704
		// mozilla
8705
		nativeBoxType = 1; //getBoxObjectFor return padding box coordinate
8706
		try{
8707
			var bo = document.getBoxObjectFor(node);
8708
			ret.x = bo.x - dojo.html.sumAncestorProperties(node, "scrollLeft");
8709
			ret.y = bo.y - dojo.html.sumAncestorProperties(node, "scrollTop");
8710
		}catch(e){
8711
			// squelch
8712
		}
8713
	}else{
8714
		if(node["offsetParent"]){
8715
			var endNode;
8716
			// in Safari, if the node is an absolutely positioned child of
8717
			// the body and the body has a margin the offset of the child
8718
			// and the body contain the body's margins, so we need to end
8719
			// at the body
8720
			if(	(h.safari)&&
8721
				(node.style.getPropertyValue("position") == "absolute")&&
8722
				(node.parentNode == db)){
8723
				endNode = db;
8724
			}else{
8725
				endNode = db.parentNode;
8726
			}
8727
 
8728
			//TODO: set correct nativeBoxType for safari/konqueror
8729
 
8730
			if(node.parentNode != db){
8731
				var nd = node;
8732
				if(dojo.render.html.opera){ nd = db; }
8733
				ret.x -= dojo.html.sumAncestorProperties(nd, "scrollLeft");
8734
				ret.y -= dojo.html.sumAncestorProperties(nd, "scrollTop");
8735
			}
8736
			var curnode = node;
8737
			do{
8738
				var n = curnode["offsetLeft"];
8739
				//FIXME: ugly hack to workaround the submenu in
8740
				//popupmenu2 does not shown up correctly in opera.
8741
				//Someone have a better workaround?
8742
				if(!h.opera || n>0){
8743
					ret.x += isNaN(n) ? 0 : n;
8744
				}
8745
				var m = curnode["offsetTop"];
8746
				ret.y += isNaN(m) ? 0 : m;
8747
				curnode = curnode.offsetParent;
8748
			}while((curnode != endNode)&&(curnode != null));
8749
		}else if(node["x"]&&node["y"]){
8750
			ret.x += isNaN(node.x) ? 0 : node.x;
8751
			ret.y += isNaN(node.y) ? 0 : node.y;
8752
		}
8753
	}
8754
 
8755
	// account for document scrolling!
8756
	if(includeScroll){
8757
		var scroll = dojo.html.getScroll();
8758
		ret.y += scroll.top;
8759
		ret.x += scroll.left;
8760
	}
8761
 
8762
	var extentFuncArray=[dojo.html.getPaddingExtent, dojo.html.getBorderExtent, dojo.html.getMarginExtent];
8763
	if(nativeBoxType > targetBoxType){
8764
		for(var i=targetBoxType;i<nativeBoxType;++i){
8765
			ret.y += extentFuncArray[i](node, 'top');
8766
			ret.x += extentFuncArray[i](node, 'left');
8767
		}
8768
	}else if(nativeBoxType < targetBoxType){
8769
		for(var i=targetBoxType;i>nativeBoxType;--i){
8770
			ret.y -= extentFuncArray[i-1](node, 'top');
8771
			ret.x -= extentFuncArray[i-1](node, 'left');
8772
		}
8773
	}
8774
	ret.top = ret.y;
8775
	ret.left = ret.x;
8776
	return ret;	//	object
8777
}
8778
 
8779
dojo.html.isPositionAbsolute = function(/* HTMLElement */node){
8780
	//	summary
8781
	//	Returns true if the element is absolutely positioned.
8782
	return (dojo.html.getComputedStyle(node, 'position') == 'absolute');	//	boolean
8783
}
8784
 
8785
dojo.html._sumPixelValues = function(/* HTMLElement */node, selectors, autoIsZero){
8786
	var total = 0;
8787
	for(var x=0; x<selectors.length; x++){
8788
		total += dojo.html.getPixelValue(node, selectors[x], autoIsZero);
8789
	}
8790
	return total;
8791
}
8792
 
8793
dojo.html.getMargin = function(/* HTMLElement */node){
8794
	//	summary
8795
	//	Returns the width and height of the passed node's margin
8796
	return {
8797
		width: dojo.html._sumPixelValues(node, ["margin-left", "margin-right"], (dojo.html.getComputedStyle(node, 'position') == 'absolute')),
8798
		height: dojo.html._sumPixelValues(node, ["margin-top", "margin-bottom"], (dojo.html.getComputedStyle(node, 'position') == 'absolute'))
8799
	};	//	object
8800
}
8801
 
8802
dojo.html.getBorder = function(/* HTMLElement */node){
8803
	//	summary
8804
	//	Returns the width and height of the passed node's border
8805
	return {
8806
		width: dojo.html.getBorderExtent(node, 'left') + dojo.html.getBorderExtent(node, 'right'),
8807
		height: dojo.html.getBorderExtent(node, 'top') + dojo.html.getBorderExtent(node, 'bottom')
8808
	};	//	object
8809
}
8810
 
8811
dojo.html.getBorderExtent = function(/* HTMLElement */node, /* string */side){
8812
	//	summary
8813
	//	returns the width of the requested border
8814
	return (dojo.html.getStyle(node, 'border-' + side + '-style') == 'none' ? 0 : dojo.html.getPixelValue(node, 'border-' + side + '-width'));	// integer
8815
}
8816
 
8817
dojo.html.getMarginExtent = function(/* HTMLElement */node, /* string */side){
8818
	//	summary
8819
	//	returns the width of the requested margin
8820
	return dojo.html._sumPixelValues(node, ["margin-" + side], dojo.html.isPositionAbsolute(node));	//	integer
8821
}
8822
 
8823
dojo.html.getPaddingExtent = function(/* HTMLElement */node, /* string */side){
8824
	//	summary
8825
	//	Returns the width of the requested padding
8826
	return dojo.html._sumPixelValues(node, ["padding-" + side], true);	//	integer
8827
}
8828
 
8829
dojo.html.getPadding = function(/* HTMLElement */node){
8830
	//	summary
8831
	//	Returns the width and height of the passed node's padding
8832
	return {
8833
		width: dojo.html._sumPixelValues(node, ["padding-left", "padding-right"], true),
8834
		height: dojo.html._sumPixelValues(node, ["padding-top", "padding-bottom"], true)
8835
	};	//	object
8836
}
8837
 
8838
dojo.html.getPadBorder = function(/* HTMLElement */node){
8839
	//	summary
8840
	//	Returns the width and height of the passed node's padding and border
8841
	var pad = dojo.html.getPadding(node);
8842
	var border = dojo.html.getBorder(node);
8843
	return { width: pad.width + border.width, height: pad.height + border.height };	//	object
8844
}
8845
 
8846
dojo.html.getBoxSizing = function(/* HTMLElement */node){
8847
	//	summary
8848
	//	Returns which box model the passed element is working with
8849
	var h = dojo.render.html;
8850
	var bs = dojo.html.boxSizing;
8851
	if(((h.ie)||(h.opera)) && node.nodeName.toLowerCase() != "img"){
8852
		var cm = document["compatMode"];
8853
		if((cm == "BackCompat")||(cm == "QuirksMode")){
8854
			return bs.BORDER_BOX; 	//	string
8855
		}else{
8856
			return bs.CONTENT_BOX; 	//	string
8857
		}
8858
	}else{
8859
		if(arguments.length == 0){ node = document.documentElement; }
8860
		var sizing;
8861
		if(!h.ie){
8862
			sizing = dojo.html.getStyle(node, "-moz-box-sizing");
8863
			if(!sizing){
8864
				sizing = dojo.html.getStyle(node, "box-sizing");
8865
			}
8866
		}
8867
		return (sizing ? sizing : bs.CONTENT_BOX);	//	string
8868
	}
8869
}
8870
 
8871
dojo.html.isBorderBox = function(/* HTMLElement */node){
8872
	//	summary
8873
	//	returns whether the passed element is using border box sizing or not.
8874
	return (dojo.html.getBoxSizing(node) == dojo.html.boxSizing.BORDER_BOX);	//	boolean
8875
}
8876
 
8877
dojo.html.getBorderBox = function(/* HTMLElement */node){
8878
	//	summary
8879
	//	Returns the dimensions of the passed element based on border-box sizing.
8880
	node = dojo.byId(node);
8881
	return { width: node.offsetWidth, height: node.offsetHeight };	//	object
8882
}
8883
 
8884
dojo.html.getPaddingBox = function(/* HTMLElement */node){
8885
	//	summary
8886
	//	Returns the dimensions of the padding box (see http://www.w3.org/TR/CSS21/box.html)
8887
	var box = dojo.html.getBorderBox(node);
8888
	var border = dojo.html.getBorder(node);
8889
	return {
8890
		width: box.width - border.width,
8891
		height:box.height - border.height
8892
	};	//	object
8893
}
8894
 
8895
dojo.html.getContentBox = function(/* HTMLElement */node){
8896
	//	summary
8897
	//	Returns the dimensions of the content box (see http://www.w3.org/TR/CSS21/box.html)
8898
	node = dojo.byId(node);
8899
	var padborder = dojo.html.getPadBorder(node);
8900
	return {
8901
		width: node.offsetWidth - padborder.width,
8902
		height: node.offsetHeight - padborder.height
8903
	};	//	object
8904
}
8905
 
8906
dojo.html.setContentBox = function(/* HTMLElement */node, /* object */args){
8907
	//	summary
8908
	//	Sets the dimensions of the passed node according to content sizing.
8909
	node = dojo.byId(node);
8910
	var width = 0; var height = 0;
8911
	var isbb = dojo.html.isBorderBox(node);
8912
	var padborder = (isbb ? dojo.html.getPadBorder(node) : { width: 0, height: 0});
8913
	var ret = {};
8914
	if(typeof args.width != "undefined"){
8915
		width = args.width + padborder.width;
8916
		ret.width = dojo.html.setPositivePixelValue(node, "width", width);
8917
	}
8918
	if(typeof args.height != "undefined"){
8919
		height = args.height + padborder.height;
8920
		ret.height = dojo.html.setPositivePixelValue(node, "height", height);
8921
	}
8922
	return ret;	//	object
8923
}
8924
 
8925
dojo.html.getMarginBox = function(/* HTMLElement */node){
8926
	//	summary
8927
	//	returns the dimensions of the passed node including any margins.
8928
	var borderbox = dojo.html.getBorderBox(node);
8929
	var margin = dojo.html.getMargin(node);
8930
	return { width: borderbox.width + margin.width, height: borderbox.height + margin.height };	//	object
8931
}
8932
 
8933
dojo.html.setMarginBox = function(/* HTMLElement */node, /* object */args){
8934
	//	summary
8935
	//	Sets the dimensions of the passed node using margin box calcs.
8936
	node = dojo.byId(node);
8937
	var width = 0; var height = 0;
8938
	var isbb = dojo.html.isBorderBox(node);
8939
	var padborder = (!isbb ? dojo.html.getPadBorder(node) : { width: 0, height: 0 });
8940
	var margin = dojo.html.getMargin(node);
8941
	var ret = {};
8942
	if(typeof args.width != "undefined"){
8943
		width = args.width - padborder.width;
8944
		width -= margin.width;
8945
		ret.width = dojo.html.setPositivePixelValue(node, "width", width);
8946
	}
8947
	if(typeof args.height != "undefined"){
8948
		height = args.height - padborder.height;
8949
		height -= margin.height;
8950
		ret.height = dojo.html.setPositivePixelValue(node, "height", height);
8951
	}
8952
	return ret;	//	object
8953
}
8954
 
8955
dojo.html.getElementBox = function(/* HTMLElement */node, /* string */type){
8956
	//	summary
8957
	//	return dimesions of a node based on the passed box model type.
8958
	var bs = dojo.html.boxSizing;
8959
	switch(type){
8960
		case bs.MARGIN_BOX:
8961
			return dojo.html.getMarginBox(node);	//	object
8962
		case bs.BORDER_BOX:
8963
			return dojo.html.getBorderBox(node);	//	object
8964
		case bs.PADDING_BOX:
8965
			return dojo.html.getPaddingBox(node);	//	object
8966
		case bs.CONTENT_BOX:
8967
		default:
8968
			return dojo.html.getContentBox(node);	//	object
8969
	}
8970
}
8971
// in: coordinate array [x,y,w,h] or dom node
8972
// return: coordinate object
8973
dojo.html.toCoordinateObject = dojo.html.toCoordinateArray = function(/* array */coords, /* boolean? */includeScroll, /* string? */boxtype) {
8974
	//	summary
8975
	//	Converts an array of coordinates into an object of named arguments.
8976
	if(coords instanceof Array || typeof coords == "array"){
8977
		dojo.deprecated("dojo.html.toCoordinateArray", "use dojo.html.toCoordinateObject({left: , top: , width: , height: }) instead", "0.5");
8978
		// coords is already an array (of format [x,y,w,h]), just return it
8979
		while ( coords.length < 4 ) { coords.push(0); }
8980
		while ( coords.length > 4 ) { coords.pop(); }
8981
		var ret = {
8982
			left: coords[0],
8983
			top: coords[1],
8984
			width: coords[2],
8985
			height: coords[3]
8986
		};
8987
	}else if(!coords.nodeType && !(coords instanceof String || typeof coords == "string") &&
8988
			 ('width' in coords || 'height' in coords || 'left' in coords ||
8989
			  'x' in coords || 'top' in coords || 'y' in coords)){
8990
		// coords is a coordinate object or at least part of one
8991
		var ret = {
8992
			left: coords.left||coords.x||0,
8993
			top: coords.top||coords.y||0,
8994
			width: coords.width||0,
8995
			height: coords.height||0
8996
		};
8997
	}else{
8998
		// coords is an dom object (or dom object id); return it's coordinates
8999
		var node = dojo.byId(coords);
9000
		var pos = dojo.html.abs(node, includeScroll, boxtype);
9001
		var marginbox = dojo.html.getMarginBox(node);
9002
		var ret = {
9003
			left: pos.left,
9004
			top: pos.top,
9005
			width: marginbox.width,
9006
			height: marginbox.height
9007
		};
9008
	}
9009
	ret.x = ret.left;
9010
	ret.y = ret.top;
9011
	return ret;	//	object
9012
}
9013
 
9014
dojo.html.setMarginBoxWidth = dojo.html.setOuterWidth = function(node, width){
9015
	return dojo.html._callDeprecated("setMarginBoxWidth", "setMarginBox", arguments, "width");
9016
}
9017
dojo.html.setMarginBoxHeight = dojo.html.setOuterHeight = function(){
9018
	return dojo.html._callDeprecated("setMarginBoxHeight", "setMarginBox", arguments, "height");
9019
}
9020
dojo.html.getMarginBoxWidth = dojo.html.getOuterWidth = function(){
9021
	return dojo.html._callDeprecated("getMarginBoxWidth", "getMarginBox", arguments, null, "width");
9022
}
9023
dojo.html.getMarginBoxHeight = dojo.html.getOuterHeight = function(){
9024
	return dojo.html._callDeprecated("getMarginBoxHeight", "getMarginBox", arguments, null, "height");
9025
}
9026
dojo.html.getTotalOffset = function(node, type, includeScroll){
9027
	return dojo.html._callDeprecated("getTotalOffset", "getAbsolutePosition", arguments, null, type);
9028
}
9029
dojo.html.getAbsoluteX = function(node, includeScroll){
9030
	return dojo.html._callDeprecated("getAbsoluteX", "getAbsolutePosition", arguments, null, "x");
9031
}
9032
dojo.html.getAbsoluteY = function(node, includeScroll){
9033
	return dojo.html._callDeprecated("getAbsoluteY", "getAbsolutePosition", arguments, null, "y");
9034
}
9035
dojo.html.totalOffsetLeft = function(node, includeScroll){
9036
	return dojo.html._callDeprecated("totalOffsetLeft", "getAbsolutePosition", arguments, null, "left");
9037
}
9038
dojo.html.totalOffsetTop = function(node, includeScroll){
9039
	return dojo.html._callDeprecated("totalOffsetTop", "getAbsolutePosition", arguments, null, "top");
9040
}
9041
dojo.html.getMarginWidth = function(node){
9042
	return dojo.html._callDeprecated("getMarginWidth", "getMargin", arguments, null, "width");
9043
}
9044
dojo.html.getMarginHeight = function(node){
9045
	return dojo.html._callDeprecated("getMarginHeight", "getMargin", arguments, null, "height");
9046
}
9047
dojo.html.getBorderWidth = function(node){
9048
	return dojo.html._callDeprecated("getBorderWidth", "getBorder", arguments, null, "width");
9049
}
9050
dojo.html.getBorderHeight = function(node){
9051
	return dojo.html._callDeprecated("getBorderHeight", "getBorder", arguments, null, "height");
9052
}
9053
dojo.html.getPaddingWidth = function(node){
9054
	return dojo.html._callDeprecated("getPaddingWidth", "getPadding", arguments, null, "width");
9055
}
9056
dojo.html.getPaddingHeight = function(node){
9057
	return dojo.html._callDeprecated("getPaddingHeight", "getPadding", arguments, null, "height");
9058
}
9059
dojo.html.getPadBorderWidth = function(node){
9060
	return dojo.html._callDeprecated("getPadBorderWidth", "getPadBorder", arguments, null, "width");
9061
}
9062
dojo.html.getPadBorderHeight = function(node){
9063
	return dojo.html._callDeprecated("getPadBorderHeight", "getPadBorder", arguments, null, "height");
9064
}
9065
dojo.html.getBorderBoxWidth = dojo.html.getInnerWidth = function(){
9066
	return dojo.html._callDeprecated("getBorderBoxWidth", "getBorderBox", arguments, null, "width");
9067
}
9068
dojo.html.getBorderBoxHeight = dojo.html.getInnerHeight = function(){
9069
	return dojo.html._callDeprecated("getBorderBoxHeight", "getBorderBox", arguments, null, "height");
9070
}
9071
dojo.html.getContentBoxWidth = dojo.html.getContentWidth = function(){
9072
	return dojo.html._callDeprecated("getContentBoxWidth", "getContentBox", arguments, null, "width");
9073
}
9074
dojo.html.getContentBoxHeight = dojo.html.getContentHeight = function(){
9075
	return dojo.html._callDeprecated("getContentBoxHeight", "getContentBox", arguments, null, "height");
9076
}
9077
dojo.html.setContentBoxWidth = dojo.html.setContentWidth = function(node, width){
9078
	return dojo.html._callDeprecated("setContentBoxWidth", "setContentBox", arguments, "width");
9079
}
9080
dojo.html.setContentBoxHeight = dojo.html.setContentHeight = function(node, height){
9081
	return dojo.html._callDeprecated("setContentBoxHeight", "setContentBox", arguments, "height");
9082
}
9083
 
9084
dojo.provide("dojo.lfx.html");
9085
 
9086
 
9087
 
9088
 
9089
 
9090
 
9091
 
9092
 
9093
dojo.lfx.html._byId = function(nodes){
9094
	if(!nodes){ return []; }
9095
	if(dojo.lang.isArrayLike(nodes)){
9096
		if(!nodes.alreadyChecked){
9097
			var n = [];
9098
			dojo.lang.forEach(nodes, function(node){
9099
				n.push(dojo.byId(node));
9100
			});
9101
			n.alreadyChecked = true;
9102
			return n;
9103
		}else{
9104
			return nodes;
9105
		}
9106
	}else{
9107
		var n = [];
9108
		n.push(dojo.byId(nodes));
9109
		n.alreadyChecked = true;
9110
		return n;
9111
	}
9112
}
9113
 
9114
dojo.lfx.html.propertyAnimation = function(	/*DOMNode[]*/ nodes,
9115
											/*Object[]*/ propertyMap,
9116
											/*int*/ duration,
9117
											/*function*/ easing,
9118
											/*Object*/ handlers){
9119
	// summary: Returns an animation that will transition the properties of "nodes"
9120
	//			depending how they are defined in "propertyMap".
9121
	// nodes: An array of DOMNodes or one DOMNode.
9122
	// propertyMap: { property: String, start: Decimal?, end: Decimal?, units: String? }
9123
	//				An array of objects defining properties to change.
9124
	// duration: Duration of the animation in milliseconds.
9125
	// easing: An easing function.
9126
	// handlers: { handler: Function?, onstart: Function?, onstop: Function?, onanimate: Function? }
9127
	nodes = dojo.lfx.html._byId(nodes);
9128
 
9129
	var targs = {
9130
		"propertyMap": propertyMap,
9131
		"nodes": nodes,
9132
		"duration": duration,
9133
		"easing": easing||dojo.lfx.easeDefault
9134
	};
9135
 
9136
	var setEmUp = function(args){
9137
		if(args.nodes.length==1){
9138
			// FIXME: we're only supporting start-value filling when one node is
9139
			// passed
9140
 
9141
			var pm = args.propertyMap;
9142
			if(!dojo.lang.isArray(args.propertyMap)){
9143
				// it's stupid to have to pack an array with a set of objects
9144
				// when you can just pass in an object list
9145
				var parr = [];
9146
				for(var pname in pm){
9147
					pm[pname].property = pname;
9148
					parr.push(pm[pname]);
9149
				}
9150
				pm = args.propertyMap = parr;
9151
			}
9152
			dojo.lang.forEach(pm, function(prop){
9153
				if(dj_undef("start", prop)){
9154
					if(prop.property != "opacity"){
9155
						prop.start = parseInt(dojo.html.getComputedStyle(args.nodes[0], prop.property));
9156
					}else{
9157
						prop.start = dojo.html.getOpacity(args.nodes[0]);
9158
					}
9159
				}
9160
			});
9161
		}
9162
	}
9163
 
9164
	var coordsAsInts = function(coords){
9165
		var cints = [];
9166
		dojo.lang.forEach(coords, function(c){
9167
			cints.push(Math.round(c));
9168
		});
9169
		return cints;
9170
	}
9171
 
9172
	var setStyle = function(n, style){
9173
		n = dojo.byId(n);
9174
		if(!n || !n.style){ return; }
9175
		for(var s in style){
9176
			try{
9177
				if(s == "opacity"){
9178
					dojo.html.setOpacity(n, style[s]);
9179
				}else{
9180
						n.style[s] = style[s];
9181
				}
9182
			}catch(e){ dojo.debug(e); }
9183
		}
9184
	}
9185
 
9186
	var propLine = function(properties){
9187
		this._properties = properties;
9188
		this.diffs = new Array(properties.length);
9189
		dojo.lang.forEach(properties, function(prop, i){
9190
			// calculate the end - start to optimize a bit
9191
			if(dojo.lang.isFunction(prop.start)){
9192
				prop.start = prop.start(prop, i);
9193
			}
9194
			if(dojo.lang.isFunction(prop.end)){
9195
				prop.end = prop.end(prop, i);
9196
			}
9197
			if(dojo.lang.isArray(prop.start)){
9198
				// don't loop through the arrays
9199
				this.diffs[i] = null;
9200
			}else if(prop.start instanceof dojo.gfx.color.Color){
9201
				// save these so we don't have to call toRgb() every getValue() call
9202
				prop.startRgb = prop.start.toRgb();
9203
				prop.endRgb = prop.end.toRgb();
9204
			}else{
9205
				this.diffs[i] = prop.end - prop.start;
9206
			}
9207
		}, this);
9208
 
9209
		this.getValue = function(n){
9210
			var ret = {};
9211
			dojo.lang.forEach(this._properties, function(prop, i){
9212
				var value = null;
9213
				if(dojo.lang.isArray(prop.start)){
9214
					// FIXME: what to do here?
9215
				}else if(prop.start instanceof dojo.gfx.color.Color){
9216
					value = (prop.units||"rgb") + "(";
9217
					for(var j = 0 ; j < prop.startRgb.length ; j++){
9218
						value += Math.round(((prop.endRgb[j] - prop.startRgb[j]) * n) + prop.startRgb[j]) + (j < prop.startRgb.length - 1 ? "," : "");
9219
					}
9220
					value += ")";
9221
				}else{
9222
					value = ((this.diffs[i]) * n) + prop.start + (prop.property != "opacity" ? prop.units||"px" : "");
9223
				}
9224
				ret[dojo.html.toCamelCase(prop.property)] = value;
9225
			}, this);
9226
			return ret;
9227
		}
9228
	}
9229
 
9230
	var anim = new dojo.lfx.Animation({
9231
			beforeBegin: function(){
9232
				setEmUp(targs);
9233
				anim.curve = new propLine(targs.propertyMap);
9234
			},
9235
			onAnimate: function(propValues){
9236
				dojo.lang.forEach(targs.nodes, function(node){
9237
					setStyle(node, propValues);
9238
				});
9239
			}
9240
		},
9241
		targs.duration,
9242
		null,
9243
		targs.easing
9244
	);
9245
	if(handlers){
9246
		for(var x in handlers){
9247
			if(dojo.lang.isFunction(handlers[x])){
9248
				anim.connect(x, anim, handlers[x]);
9249
			}
9250
		}
9251
	}
9252
 
9253
	return anim; // dojo.lfx.Animation
9254
}
9255
 
9256
dojo.lfx.html._makeFadeable = function(nodes){
9257
	var makeFade = function(node){
9258
		if(dojo.render.html.ie){
9259
			// only set the zoom if the "tickle" value would be the same as the
9260
			// default
9261
			if( (node.style.zoom.length == 0) &&
9262
				(dojo.html.getStyle(node, "zoom") == "normal") ){
9263
				// make sure the node "hasLayout"
9264
				// NOTE: this has been tested with larger and smaller user-set text
9265
				// sizes and works fine
9266
				node.style.zoom = "1";
9267
				// node.style.zoom = "normal";
9268
			}
9269
			// don't set the width to auto if it didn't already cascade that way.
9270
			// We don't want to f anyones designs
9271
			if(	(node.style.width.length == 0) &&
9272
				(dojo.html.getStyle(node, "width") == "auto") ){
9273
				node.style.width = "auto";
9274
			}
9275
		}
9276
	}
9277
	if(dojo.lang.isArrayLike(nodes)){
9278
		dojo.lang.forEach(nodes, makeFade);
9279
	}else{
9280
		makeFade(nodes);
9281
	}
9282
}
9283
 
9284
dojo.lfx.html.fade = function(/*DOMNode[]*/ nodes,
9285
							  /*Object*/values,
9286
							  /*int?*/ duration,
9287
							  /*Function?*/ easing,
9288
							  /*Function?*/ callback){
9289
	// summary:Returns an animation that will fade the "nodes" from the start to end values passed.
9290
	// nodes: An array of DOMNodes or one DOMNode.
9291
	// values: { start: Decimal?, end: Decimal? }
9292
	// duration: Duration of the animation in milliseconds.
9293
	// easing: An easing function.
9294
	// callback: Function to run at the end of the animation.
9295
	nodes = dojo.lfx.html._byId(nodes);
9296
	var props = { property: "opacity" };
9297
	if(!dj_undef("start", values)){
9298
		props.start = values.start;
9299
	}else{
9300
		props.start = function(){ return dojo.html.getOpacity(nodes[0]); };
9301
	}
9302
 
9303
	if(!dj_undef("end", values)){
9304
		props.end = values.end;
9305
	}else{
9306
		dojo.raise("dojo.lfx.html.fade needs an end value");
9307
	}
9308
 
9309
	var anim = dojo.lfx.propertyAnimation(nodes, [ props ], duration, easing);
9310
	anim.connect("beforeBegin", function(){
9311
		dojo.lfx.html._makeFadeable(nodes);
9312
	});
9313
	if(callback){
9314
		anim.connect("onEnd", function(){ callback(nodes, anim); });
9315
	}
9316
 
9317
	return anim; // dojo.lfx.Animation
9318
}
9319
 
9320
dojo.lfx.html.fadeIn = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
9321
	// summary: Returns an animation that will fade "nodes" from its current opacity to fully opaque.
9322
	// nodes: An array of DOMNodes or one DOMNode.
9323
	// duration: Duration of the animation in milliseconds.
9324
	// easing: An easing function.
9325
	// callback: Function to run at the end of the animation.
9326
	return dojo.lfx.html.fade(nodes, { end: 1 }, duration, easing, callback); // dojo.lfx.Animation
9327
}
9328
 
9329
dojo.lfx.html.fadeOut = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
9330
	// summary: Returns an animation that will fade "nodes" from its current opacity to fully transparent.
9331
	// nodes: An array of DOMNodes or one DOMNode.
9332
	// duration: Duration of the animation in milliseconds.
9333
	// easing: An easing function.
9334
	// callback: Function to run at the end of the animation.
9335
	return dojo.lfx.html.fade(nodes, { end: 0 }, duration, easing, callback); // dojo.lfx.Animation
9336
}
9337
 
9338
dojo.lfx.html.fadeShow = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
9339
	// summary: Returns an animation that will fade "nodes" from transparent to opaque and shows
9340
	//			"nodes" at the end if it is hidden.
9341
	// nodes: An array of DOMNodes or one DOMNode.
9342
	// duration: Duration of the animation in milliseconds.
9343
	// easing: An easing function.
9344
	// callback: Function to run at the end of the animation.
9345
	nodes=dojo.lfx.html._byId(nodes);
9346
	dojo.lang.forEach(nodes, function(node){
9347
		dojo.html.setOpacity(node, 0.0);
9348
	});
9349
 
9350
	var anim = dojo.lfx.html.fadeIn(nodes, duration, easing, callback);
9351
	anim.connect("beforeBegin", function(){
9352
		if(dojo.lang.isArrayLike(nodes)){
9353
			dojo.lang.forEach(nodes, dojo.html.show);
9354
		}else{
9355
			dojo.html.show(nodes);
9356
		}
9357
	});
9358
 
9359
	return anim; // dojo.lfx.Animation
9360
}
9361
 
9362
dojo.lfx.html.fadeHide = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
9363
	// summary: Returns an animation that will fade "nodes" from its current opacity to opaque and hides
9364
	//			"nodes" at the end.
9365
	// nodes: An array of DOMNodes or one DOMNode.
9366
	// duration: Duration of the animation in milliseconds.
9367
	// easing: An easing function.
9368
	// callback: Function to run at the end of the animation.
9369
	var anim = dojo.lfx.html.fadeOut(nodes, duration, easing, function(){
9370
		if(dojo.lang.isArrayLike(nodes)){
9371
			dojo.lang.forEach(nodes, dojo.html.hide);
9372
		}else{
9373
			dojo.html.hide(nodes);
9374
		}
9375
		if(callback){ callback(nodes, anim); }
9376
	});
9377
 
9378
	return anim; // dojo.lfx.Animation
9379
}
9380
 
9381
dojo.lfx.html.wipeIn = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
9382
	// summary: Returns an animation that will show and wipe in "nodes".
9383
	// nodes: An array of DOMNodes or one DOMNode.
9384
	// duration: Duration of the animation in milliseconds.
9385
	// easing: An easing function.
9386
	// callback: Function to run at the end of the animation.
9387
	nodes = dojo.lfx.html._byId(nodes);
9388
	var anims = [];
9389
 
9390
	dojo.lang.forEach(nodes, function(node){
9391
		var oprop = {  };	// old properties of node (before we mucked w/them)
9392
 
9393
		// get node height, either it's natural height or it's height specified via style or class attributes
9394
		// (for FF, the node has to be (temporarily) rendered to measure height)
9395
		// TODO: should this offscreen code be part of dojo.html, so that getBorderBox() works on hidden nodes?
9396
		var origTop, origLeft, origPosition;
9397
		with(node.style){
9398
			origTop=top; origLeft=left; origPosition=position;
9399
			top="-9999px"; left="-9999px"; position="absolute";
9400
			display="";
9401
		}
9402
		var nodeHeight = dojo.html.getBorderBox(node).height;
9403
		with(node.style){
9404
			top=origTop; left=origLeft; position=origPosition;
9405
			display="none";
9406
		}
9407
 
9408
		var anim = dojo.lfx.propertyAnimation(node,
9409
			{	"height": {
9410
					start: 1, // 0 causes IE to display the whole panel
9411
					end: function(){ return nodeHeight; }
9412
				}
9413
			},
9414
			duration,
9415
			easing);
9416
 
9417
		anim.connect("beforeBegin", function(){
9418
			oprop.overflow = node.style.overflow;
9419
			oprop.height = node.style.height;
9420
			with(node.style){
9421
				overflow = "hidden";
9422
				height = "1px"; // 0 causes IE to display the whole panel
9423
			}
9424
			dojo.html.show(node);
9425
		});
9426
 
9427
		anim.connect("onEnd", function(){
9428
			with(node.style){
9429
				overflow = oprop.overflow;
9430
				height = oprop.height;
9431
			}
9432
			if(callback){ callback(node, anim); }
9433
		});
9434
		anims.push(anim);
9435
	});
9436
 
9437
	return dojo.lfx.combine(anims); // dojo.lfx.Combine
9438
}
9439
 
9440
dojo.lfx.html.wipeOut = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
9441
	// summary: Returns an animation that will wipe out and hide "nodes".
9442
	// nodes: An array of DOMNodes or one DOMNode.
9443
	// duration: Duration of the animation in milliseconds.
9444
	// easing: An easing function.
9445
	// callback: Function to run at the end of the animation.
9446
	nodes = dojo.lfx.html._byId(nodes);
9447
	var anims = [];
9448
 
9449
	dojo.lang.forEach(nodes, function(node){
9450
		var oprop = {  };	// old properties of node (before we mucked w/them)
9451
		var anim = dojo.lfx.propertyAnimation(node,
9452
			{	"height": {
9453
					start: function(){ return dojo.html.getContentBox(node).height; },
9454
					end: 1 // 0 causes IE to display the whole panel
9455
				}
9456
			},
9457
			duration,
9458
			easing,
9459
			{
9460
				"beforeBegin": function(){
9461
					oprop.overflow = node.style.overflow;
9462
					oprop.height = node.style.height;
9463
					with(node.style){
9464
						overflow = "hidden";
9465
					}
9466
					dojo.html.show(node);
9467
				},
9468
 
9469
				"onEnd": function(){
9470
					dojo.html.hide(node);
9471
					with(node.style){
9472
						overflow = oprop.overflow;
9473
						height = oprop.height;
9474
					}
9475
					if(callback){ callback(node, anim); }
9476
				}
9477
			}
9478
		);
9479
		anims.push(anim);
9480
	});
9481
 
9482
	return dojo.lfx.combine(anims); // dojo.lfx.Combine
9483
}
9484
 
9485
dojo.lfx.html.slideTo = function(/*DOMNode*/ nodes,
9486
								 /*Object*/ coords,
9487
								 /*int?*/ duration,
9488
								 /*Function?*/ easing,
9489
								 /*Function?*/ callback){
9490
	// summary: Returns an animation that will slide "nodes" from its current position to
9491
	//			the position defined in "coords".
9492
	// nodes: An array of DOMNodes or one DOMNode.
9493
	// coords: { top: Decimal?, left: Decimal? }
9494
	// duration: Duration of the animation in milliseconds.
9495
	// easing: An easing function.
9496
	// callback: Function to run at the end of the animation.
9497
	nodes = dojo.lfx.html._byId(nodes);
9498
	var anims = [];
9499
	var compute = dojo.html.getComputedStyle;
9500
 
9501
	if(dojo.lang.isArray(coords)){
9502
		/* coords: Array
9503
		   pId: a */
9504
		dojo.deprecated('dojo.lfx.html.slideTo(node, array)', 'use dojo.lfx.html.slideTo(node, {top: value, left: value});', '0.5');
9505
		coords = { top: coords[0], left: coords[1] };
9506
	}
9507
	dojo.lang.forEach(nodes, function(node){
9508
		var top = null;
9509
		var left = null;
9510
 
9511
		var init = (function(){
9512
			var innerNode = node;
9513
			return function(){
9514
				var pos = compute(innerNode, 'position');
9515
				top = (pos == 'absolute' ? node.offsetTop : parseInt(compute(node, 'top')) || 0);
9516
				left = (pos == 'absolute' ? node.offsetLeft : parseInt(compute(node, 'left')) || 0);
9517
 
9518
				if (!dojo.lang.inArray(['absolute', 'relative'], pos)) {
9519
					var ret = dojo.html.abs(innerNode, true);
9520
					dojo.html.setStyleAttributes(innerNode, "position:absolute;top:"+ret.y+"px;left:"+ret.x+"px;");
9521
					top = ret.y;
9522
					left = ret.x;
9523
				}
9524
			}
9525
		})();
9526
		init();
9527
 
9528
		var anim = dojo.lfx.propertyAnimation(node,
9529
			{	"top": { start: top, end: (coords.top||0) },
9530
				"left": { start: left, end: (coords.left||0)  }
9531
			},
9532
			duration,
9533
			easing,
9534
			{ "beforeBegin": init }
9535
		);
9536
 
9537
		if(callback){
9538
			anim.connect("onEnd", function(){ callback(nodes, anim); });
9539
		}
9540
 
9541
		anims.push(anim);
9542
	});
9543
 
9544
	return dojo.lfx.combine(anims); // dojo.lfx.Combine
9545
}
9546
 
9547
dojo.lfx.html.slideBy = function(/*DOMNode*/ nodes, /*Object*/ coords, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
9548
	// summary: Returns an animation that will slide "nodes" from its current position
9549
	//			to its current position plus the numbers defined in "coords".
9550
	// nodes: An array of DOMNodes or one DOMNode.
9551
	// coords: { top: Decimal?, left: Decimal? }
9552
	// duration: Duration of the animation in milliseconds.
9553
	// easing: An easing function.
9554
	// callback: Function to run at the end of the animation.
9555
	nodes = dojo.lfx.html._byId(nodes);
9556
	var anims = [];
9557
	var compute = dojo.html.getComputedStyle;
9558
 
9559
	if(dojo.lang.isArray(coords)){
9560
		/* coords: Array
9561
		   pId: a */
9562
		dojo.deprecated('dojo.lfx.html.slideBy(node, array)', 'use dojo.lfx.html.slideBy(node, {top: value, left: value});', '0.5');
9563
		coords = { top: coords[0], left: coords[1] };
9564
	}
9565
 
9566
	dojo.lang.forEach(nodes, function(node){
9567
		var top = null;
9568
		var left = null;
9569
 
9570
		var init = (function(){
9571
			var innerNode = node;
9572
			return function(){
9573
				var pos = compute(innerNode, 'position');
9574
				top = (pos == 'absolute' ? node.offsetTop : parseInt(compute(node, 'top')) || 0);
9575
				left = (pos == 'absolute' ? node.offsetLeft : parseInt(compute(node, 'left')) || 0);
9576
 
9577
				if (!dojo.lang.inArray(['absolute', 'relative'], pos)) {
9578
					var ret = dojo.html.abs(innerNode, true);
9579
					dojo.html.setStyleAttributes(innerNode, "position:absolute;top:"+ret.y+"px;left:"+ret.x+"px;");
9580
					top = ret.y;
9581
					left = ret.x;
9582
				}
9583
			}
9584
		})();
9585
		init();
9586
 
9587
		var anim = dojo.lfx.propertyAnimation(node,
9588
			{
9589
				"top": { start: top, end: top+(coords.top||0) },
9590
				"left": { start: left, end: left+(coords.left||0) }
9591
			},
9592
			duration,
9593
			easing).connect("beforeBegin", init);
9594
 
9595
		if(callback){
9596
			anim.connect("onEnd", function(){ callback(nodes, anim); });
9597
		}
9598
 
9599
		anims.push(anim);
9600
	});
9601
 
9602
	return dojo.lfx.combine(anims); // dojo.lfx.Combine
9603
}
9604
 
9605
dojo.lfx.html.explode = function(/*DOMNode*/ start,
9606
								 /*DOMNode*/ endNode,
9607
								 /*int?*/ duration,
9608
								 /*Function?*/ easing,
9609
								 /*Function?*/ callback){
9610
	// summary: Returns an animation that will
9611
	// start:
9612
	// endNode:
9613
	// duration: Duration of the animation in milliseconds.
9614
	// easing: An easing function.
9615
	// callback: Function to run at the end of the animation.
9616
	var h = dojo.html;
9617
	start = dojo.byId(start);
9618
	endNode = dojo.byId(endNode);
9619
	var startCoords = h.toCoordinateObject(start, true);
9620
	var outline = document.createElement("div");
9621
	h.copyStyle(outline, endNode);
9622
	if(endNode.explodeClassName){ outline.className = endNode.explodeClassName; }
9623
	with(outline.style){
9624
		position = "absolute";
9625
		display = "none";
9626
		// border = "1px solid black";
9627
		var backgroundStyle = h.getStyle(start, "background-color");
9628
		backgroundColor = backgroundStyle ? backgroundStyle.toLowerCase() : "transparent";
9629
		backgroundColor = (backgroundColor == "transparent") ? "rgb(221, 221, 221)" : backgroundColor;
9630
	}
9631
	dojo.body().appendChild(outline);
9632
 
9633
	with(endNode.style){
9634
		visibility = "hidden";
9635
		display = "block";
9636
	}
9637
	var endCoords = h.toCoordinateObject(endNode, true);
9638
	with(endNode.style){
9639
		display = "none";
9640
		visibility = "visible";
9641
	}
9642
 
9643
	var props = { opacity: { start: 0.5, end: 1.0 } };
9644
	dojo.lang.forEach(["height", "width", "top", "left"], function(type){
9645
		props[type] = { start: startCoords[type], end: endCoords[type] }
9646
	});
9647
 
9648
	var anim = new dojo.lfx.propertyAnimation(outline,
9649
		props,
9650
		duration,
9651
		easing,
9652
		{
9653
			"beforeBegin": function(){
9654
				h.setDisplay(outline, "block");
9655
			},
9656
			"onEnd": function(){
9657
				h.setDisplay(endNode, "block");
9658
				outline.parentNode.removeChild(outline);
9659
			}
9660
		}
9661
	);
9662
 
9663
	if(callback){
9664
		anim.connect("onEnd", function(){ callback(endNode, anim); });
9665
	}
9666
	return anim; // dojo.lfx.Animation
9667
}
9668
 
9669
dojo.lfx.html.implode = function(/*DOMNode*/ startNode,
9670
								 /*DOMNode*/ end,
9671
								 /*int?*/ duration,
9672
								 /*Function?*/ easing,
9673
								 /*Function?*/ callback){
9674
	// summary: Returns an animation that will
9675
	// startNode:
9676
	// end:
9677
	// duration: Duration of the animation in milliseconds.
9678
	// easing: An easing function.
9679
	// callback: Function to run at the end of the animation.
9680
	var h = dojo.html;
9681
	startNode = dojo.byId(startNode);
9682
	end = dojo.byId(end);
9683
	var startCoords = dojo.html.toCoordinateObject(startNode, true);
9684
	var endCoords = dojo.html.toCoordinateObject(end, true);
9685
 
9686
	var outline = document.createElement("div");
9687
	dojo.html.copyStyle(outline, startNode);
9688
	if (startNode.explodeClassName) { outline.className = startNode.explodeClassName; }
9689
	dojo.html.setOpacity(outline, 0.3);
9690
	with(outline.style){
9691
		position = "absolute";
9692
		display = "none";
9693
		backgroundColor = h.getStyle(startNode, "background-color").toLowerCase();
9694
	}
9695
	dojo.body().appendChild(outline);
9696
 
9697
	var props = { opacity: { start: 1.0, end: 0.5 } };
9698
	dojo.lang.forEach(["height", "width", "top", "left"], function(type){
9699
		props[type] = { start: startCoords[type], end: endCoords[type] }
9700
	});
9701
 
9702
	var anim = new dojo.lfx.propertyAnimation(outline,
9703
		props,
9704
		duration,
9705
		easing,
9706
		{
9707
			"beforeBegin": function(){
9708
				dojo.html.hide(startNode);
9709
				dojo.html.show(outline);
9710
			},
9711
			"onEnd": function(){
9712
				outline.parentNode.removeChild(outline);
9713
			}
9714
		}
9715
	);
9716
 
9717
	if(callback){
9718
		anim.connect("onEnd", function(){ callback(startNode, anim); });
9719
	}
9720
	return anim; // dojo.lfx.Animation
9721
}
9722
 
9723
dojo.lfx.html.highlight = function(/*DOMNode[]*/ nodes,
9724
								   /*dojo.gfx.color.Color*/ startColor,
9725
								   /*int?*/ duration,
9726
								   /*Function?*/ easing,
9727
								   /*Function?*/ callback){
9728
	// summary: Returns an animation that will set the background color
9729
	//			of "nodes" to startColor and transition it to "nodes"
9730
	//			original color.
9731
	// startColor: Color to transition from.
9732
	// duration: Duration of the animation in milliseconds.
9733
	// easing: An easing function.
9734
	// callback: Function to run at the end of the animation.
9735
	nodes = dojo.lfx.html._byId(nodes);
9736
	var anims = [];
9737
 
9738
	dojo.lang.forEach(nodes, function(node){
9739
		var color = dojo.html.getBackgroundColor(node);
9740
		var bg = dojo.html.getStyle(node, "background-color").toLowerCase();
9741
		var bgImage = dojo.html.getStyle(node, "background-image");
9742
		var wasTransparent = (bg == "transparent" || bg == "rgba(0, 0, 0, 0)");
9743
		while(color.length > 3) { color.pop(); }
9744
 
9745
		var rgb = new dojo.gfx.color.Color(startColor);
9746
		var endRgb = new dojo.gfx.color.Color(color);
9747
 
9748
		var anim = dojo.lfx.propertyAnimation(node,
9749
			{ "background-color": { start: rgb, end: endRgb } },
9750
			duration,
9751
			easing,
9752
			{
9753
				"beforeBegin": function(){
9754
					if(bgImage){
9755
						node.style.backgroundImage = "none";
9756
					}
9757
					node.style.backgroundColor = "rgb(" + rgb.toRgb().join(",") + ")";
9758
				},
9759
				"onEnd": function(){
9760
					if(bgImage){
9761
						node.style.backgroundImage = bgImage;
9762
					}
9763
					if(wasTransparent){
9764
						node.style.backgroundColor = "transparent";
9765
					}
9766
					if(callback){
9767
						callback(node, anim);
9768
					}
9769
				}
9770
			}
9771
		);
9772
 
9773
		anims.push(anim);
9774
	});
9775
	return dojo.lfx.combine(anims); // dojo.lfx.Combine
9776
}
9777
 
9778
dojo.lfx.html.unhighlight = function(/*DOMNode[]*/ nodes,
9779
									 /*dojo.gfx.color.Color*/ endColor,
9780
									 /*int?*/ duration,
9781
									 /*Function?*/ easing,
9782
									 /*Function?*/ callback){
9783
	// summary: Returns an animation that will transition "nodes" background color
9784
	//			from its current color to "endColor".
9785
	// endColor: Color to transition to.
9786
	// duration: Duration of the animation in milliseconds.
9787
	// easing: An easing function.
9788
	// callback: Function to run at the end of the animation.
9789
	nodes = dojo.lfx.html._byId(nodes);
9790
	var anims = [];
9791
 
9792
	dojo.lang.forEach(nodes, function(node){
9793
		var color = new dojo.gfx.color.Color(dojo.html.getBackgroundColor(node));
9794
		var rgb = new dojo.gfx.color.Color(endColor);
9795
 
9796
		var bgImage = dojo.html.getStyle(node, "background-image");
9797
 
9798
		var anim = dojo.lfx.propertyAnimation(node,
9799
			{ "background-color": { start: color, end: rgb } },
9800
			duration,
9801
			easing,
9802
			{
9803
				"beforeBegin": function(){
9804
					if(bgImage){
9805
						node.style.backgroundImage = "none";
9806
					}
9807
					node.style.backgroundColor = "rgb(" + color.toRgb().join(",") + ")";
9808
				},
9809
				"onEnd": function(){
9810
					if(callback){
9811
						callback(node, anim);
9812
					}
9813
				}
9814
			}
9815
		);
9816
		anims.push(anim);
9817
	});
9818
	return dojo.lfx.combine(anims); // dojo.lfx.Combine
9819
}
9820
 
9821
dojo.lang.mixin(dojo.lfx, dojo.lfx.html);
9822
 
9823
dojo.kwCompoundRequire({
9824
	browser: ["dojo.lfx.html"],
9825
	dashboard: ["dojo.lfx.html"]
9826
});
9827
dojo.provide("dojo.lfx.*");
9828