Subversion Repositories Applications.papyrus

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2150 mathias 1
/*
2
	Copyright (c) 2004-2007, 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/book/dojo-book-0-9/introduction/licensing
9
*/
10
 
11
/*
12
	This is a compiled version of Dojo, built for deployment and not for
13
	development. To get an editable version, please visit:
14
 
15
		http://dojotoolkit.org
16
 
17
	for documentation and information on getting the source.
18
*/
19
 
20
if(!dojo._hasResource["dojo.colors"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21
dojo._hasResource["dojo.colors"] = true;
22
dojo.provide("dojo.colors");
23
 
24
(function(){
25
	// this is a standard convertion prescribed by the CSS3 Color Module
26
	var hue2rgb = function(m1, m2, h){
27
		if(h < 0){ ++h; }
28
		if(h > 1){ --h; }
29
		var h6 = 6 * h;
30
		if(h6 < 1){ return m1 + (m2 - m1) * h6; }
31
		if(2 * h < 1){ return m2; }
32
		if(3 * h < 2){ return m1 + (m2 - m1) * (2 / 3 - h) * 6; }
33
		return m1;
34
	};
35
 
36
	dojo.colorFromRgb = function(/*String*/ color, /*dojo.Color?*/ obj){
37
		// summary:
38
		//		get rgb(a) array from css-style color declarations
39
		// description:
40
		//		this function can handle all 4 CSS3 Color Module formats: rgb,
41
		//		rgba, hsl, hsla, including rgb(a) with percentage values.
42
		var m = color.toLowerCase().match(/^(rgba?|hsla?)\(([\s\.\-,%0-9]+)\)/);
43
		if(m){
44
			var c = m[2].split(/\s*,\s*/), l = c.length, t = m[1];
45
			if((t == "rgb" && l == 3) || (t == "rgba" && l == 4)){
46
				var r = c[0];
47
				if(r.charAt(r.length - 1) == "%"){
48
					// 3 rgb percentage values
49
					var a = dojo.map(c, function(x){
50
						return parseFloat(x) * 2.56;
51
					});
52
					if(l == 4){ a[3] = c[3]; }
53
					return dojo.colorFromArray(a, obj);	// dojo.Color
54
				}
55
				return dojo.colorFromArray(c, obj);	// dojo.Color
56
			}
57
			if((t == "hsl" && l == 3) || (t == "hsla" && l == 4)){
58
				// normalize hsl values
59
				var H = ((parseFloat(c[0]) % 360) + 360) % 360 / 360,
60
					S = parseFloat(c[1]) / 100,
61
					L = parseFloat(c[2]) / 100,
62
					// calculate rgb according to the algorithm
63
					// recommended by the CSS3 Color Module
64
					m2 = L <= 0.5 ? L * (S + 1) : L + S - L * S,
65
					m1 = 2 * L - m2,
66
					a = [hue2rgb(m1, m2, H + 1 / 3) * 256,
67
						hue2rgb(m1, m2, H) * 256, hue2rgb(m1, m2, H - 1 / 3) * 256, 1];
68
				if(l == 4){ a[3] = c[3]; }
69
				return dojo.colorFromArray(a, obj);	// dojo.Color
70
			}
71
		}
72
		return null;	// dojo.Color
73
	};
74
 
75
	var confine = function(c, low, high){
76
		// summary:
77
		//		sanitize a color component by making sure it is a number,
78
		//		and clamping it to valid values
79
		c = Number(c);
80
		return isNaN(c) ? high : c < low ? low : c > high ? high : c;	// Number
81
	};
82
 
83
	dojo.Color.prototype.sanitize = function(){
84
		// summary: makes sure that the object has correct attributes
85
		var t = this;
86
		t.r = Math.round(confine(t.r, 0, 255));
87
		t.g = Math.round(confine(t.g, 0, 255));
88
		t.b = Math.round(confine(t.b, 0, 255));
89
		t.a = confine(t.a, 0, 1);
90
		return this;	// dojo.Color
91
	};
92
})();
93
 
94
 
95
dojo.colors.makeGrey = function(/*Number*/ g, /*Number?*/ a){
96
	// summary: creates a greyscale color with an optional alpha
97
	return dojo.colorFromArray([g, g, g, a]);
98
};
99
 
100
 
101
// mixin all CSS3 named colors not already in _base, along with SVG 1.0 variant spellings
102
dojo.Color.named = dojo.mixin({
103
	aliceblue:	[240,248,255],
104
	antiquewhite:	[250,235,215],
105
	aquamarine:	[127,255,212],
106
	azure:	[240,255,255],
107
	beige:	[245,245,220],
108
	bisque:	[255,228,196],
109
	blanchedalmond:	[255,235,205],
110
	blueviolet:	[138,43,226],
111
	brown:	[165,42,42],
112
	burlywood:	[222,184,135],
113
	cadetblue:	[95,158,160],
114
	chartreuse:	[127,255,0],
115
	chocolate:	[210,105,30],
116
	coral:	[255,127,80],
117
	cornflowerblue:	[100,149,237],
118
	cornsilk:	[255,248,220],
119
	crimson:	[220,20,60],
120
	cyan:	[0,255,255],
121
	darkblue:	[0,0,139],
122
	darkcyan:	[0,139,139],
123
	darkgoldenrod:	[184,134,11],
124
	darkgray:	[169,169,169],
125
	darkgreen:	[0,100,0],
126
	darkgrey:	[169,169,169],
127
	darkkhaki:	[189,183,107],
128
	darkmagenta:	[139,0,139],
129
	darkolivegreen:	[85,107,47],
130
	darkorange:	[255,140,0],
131
	darkorchid:	[153,50,204],
132
	darkred:	[139,0,0],
133
	darksalmon:	[233,150,122],
134
	darkseagreen:	[143,188,143],
135
	darkslateblue:	[72,61,139],
136
	darkslategray:	[47,79,79],
137
	darkslategrey:	[47,79,79],
138
	darkturquoise:	[0,206,209],
139
	darkviolet:	[148,0,211],
140
	deeppink:	[255,20,147],
141
	deepskyblue:	[0,191,255],
142
	dimgray:	[105,105,105],
143
	dimgrey:	[105,105,105],
144
	dodgerblue:	[30,144,255],
145
	firebrick:	[178,34,34],
146
	floralwhite:	[255,250,240],
147
	forestgreen:	[34,139,34],
148
	gainsboro:	[220,220,220],
149
	ghostwhite:	[248,248,255],
150
	gold:	[255,215,0],
151
	goldenrod:	[218,165,32],
152
	greenyellow:	[173,255,47],
153
	grey:	[128,128,128],
154
	honeydew:	[240,255,240],
155
	hotpink:	[255,105,180],
156
	indianred:	[205,92,92],
157
	indigo:	[75,0,130],
158
	ivory:	[255,255,240],
159
	khaki:	[240,230,140],
160
	lavender:	[230,230,250],
161
	lavenderblush:	[255,240,245],
162
	lawngreen:	[124,252,0],
163
	lemonchiffon:	[255,250,205],
164
	lightblue:	[173,216,230],
165
	lightcoral:	[240,128,128],
166
	lightcyan:	[224,255,255],
167
	lightgoldenrodyellow:	[250,250,210],
168
	lightgray:	[211,211,211],
169
	lightgreen:	[144,238,144],
170
	lightgrey:	[211,211,211],
171
	lightpink:	[255,182,193],
172
	lightsalmon:	[255,160,122],
173
	lightseagreen:	[32,178,170],
174
	lightskyblue:	[135,206,250],
175
	lightslategray:	[119,136,153],
176
	lightslategrey:	[119,136,153],
177
	lightsteelblue:	[176,196,222],
178
	lightyellow:	[255,255,224],
179
	limegreen:	[50,205,50],
180
	linen:	[250,240,230],
181
	magenta:	[255,0,255],
182
	mediumaquamarine:	[102,205,170],
183
	mediumblue:	[0,0,205],
184
	mediumorchid:	[186,85,211],
185
	mediumpurple:	[147,112,219],
186
	mediumseagreen:	[60,179,113],
187
	mediumslateblue:	[123,104,238],
188
	mediumspringgreen:	[0,250,154],
189
	mediumturquoise:	[72,209,204],
190
	mediumvioletred:	[199,21,133],
191
	midnightblue:	[25,25,112],
192
	mintcream:	[245,255,250],
193
	mistyrose:	[255,228,225],
194
	moccasin:	[255,228,181],
195
	navajowhite:	[255,222,173],
196
	oldlace:	[253,245,230],
197
	olivedrab:	[107,142,35],
198
	orange:	[255,165,0],
199
	orangered:	[255,69,0],
200
	orchid:	[218,112,214],
201
	palegoldenrod:	[238,232,170],
202
	palegreen:	[152,251,152],
203
	paleturquoise:	[175,238,238],
204
	palevioletred:	[219,112,147],
205
	papayawhip:	[255,239,213],
206
	peachpuff:	[255,218,185],
207
	peru:	[205,133,63],
208
	pink:	[255,192,203],
209
	plum:	[221,160,221],
210
	powderblue:	[176,224,230],
211
	rosybrown:	[188,143,143],
212
	royalblue:	[65,105,225],
213
	saddlebrown:	[139,69,19],
214
	salmon:	[250,128,114],
215
	sandybrown:	[244,164,96],
216
	seagreen:	[46,139,87],
217
	seashell:	[255,245,238],
218
	sienna:	[160,82,45],
219
	skyblue:	[135,206,235],
220
	slateblue:	[106,90,205],
221
	slategray:	[112,128,144],
222
	slategrey:	[112,128,144],
223
	snow:	[255,250,250],
224
	springgreen:	[0,255,127],
225
	steelblue:	[70,130,180],
226
	tan:	[210,180,140],
227
	thistle:	[216,191,216],
228
	tomato:	[255,99,71],
229
	transparent: [0, 0, 0, 0],
230
	turquoise:	[64,224,208],
231
	violet:	[238,130,238],
232
	wheat:	[245,222,179],
233
	whitesmoke:	[245,245,245],
234
	yellowgreen:	[154,205,50]
235
}, dojo.Color.named);
236
 
237
}
238
 
239
if(!dojo._hasResource["dojo.i18n"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
240
dojo._hasResource["dojo.i18n"] = true;
241
dojo.provide("dojo.i18n");
242
 
243
dojo.i18n.getLocalization = function(/*String*/packageName, /*String*/bundleName, /*String?*/locale){
244
	//	summary:
245
	//		Returns an Object containing the localization for a given resource
246
	//		bundle in a package, matching the specified locale.
247
	//	description:
248
	//		Returns a hash containing name/value pairs in its prototypesuch
249
	//		that values can be easily overridden.  Throws an exception if the
250
	//		bundle is not found.  Bundle must have already been loaded by
251
	//		dojo.requireLocalization() or by a build optimization step.  NOTE:
252
	//		try not to call this method as part of an object property
253
	//		definition (var foo = { bar: dojo.i18n.getLocalization() }).  In
254
	//		some loading situations, the bundle may not be available in time
255
	//		for the object definition.  Instead, call this method inside a
256
	//		function that is run after all modules load or the page loads (like
257
	//		in dojo.adOnLoad()), or in a widget lifecycle method.
258
	//	packageName:
259
	//		package which is associated with this resource
260
	//	bundleName:
261
	//		the base filename of the resource bundle (without the ".js" suffix)
262
	//	locale:
263
	//		the variant to load (optional).  By default, the locale defined by
264
	//		the host environment: dojo.locale
265
 
266
	locale = dojo.i18n.normalizeLocale(locale);
267
 
268
	// look for nearest locale match
269
	var elements = locale.split('-');
270
	var module = [packageName,"nls",bundleName].join('.');
271
	var bundle = dojo._loadedModules[module];
272
	if(bundle){
273
		var localization;
274
		for(var i = elements.length; i > 0; i--){
275
			var loc = elements.slice(0, i).join('_');
276
			if(bundle[loc]){
277
				localization = bundle[loc];
278
				break;
279
			}
280
		}
281
		if(!localization){
282
			localization = bundle.ROOT;
283
		}
284
 
285
		// make a singleton prototype so that the caller won't accidentally change the values globally
286
		if(localization){
287
			var clazz = function(){};
288
			clazz.prototype = localization;
289
			return new clazz(); // Object
290
		}
291
	}
292
 
293
	throw new Error("Bundle not found: " + bundleName + " in " + packageName+" , locale=" + locale);
294
};
295
 
296
dojo.i18n.normalizeLocale = function(/*String?*/locale){
297
	//	summary:
298
	//		Returns canonical form of locale, as used by Dojo.
299
	//
300
	//  description:
301
	//		All variants are case-insensitive and are separated by '-' as specified in RFC 3066.
302
	//		If no locale is specified, the dojo.locale is returned.  dojo.locale is defined by
303
	//		the user agent's locale unless overridden by djConfig.
304
 
305
	var result = locale ? locale.toLowerCase() : dojo.locale;
306
	if(result == "root"){
307
		result = "ROOT";
308
	}
309
	return result; // String
310
};
311
 
312
dojo.i18n._requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String?*/availableFlatLocales){
313
	//	summary:
314
	//		See dojo.requireLocalization()
315
	//	description:
316
	// 		Called by the bootstrap, but factored out so that it is only
317
	// 		included in the build when needed.
318
 
319
	var targetLocale = dojo.i18n.normalizeLocale(locale);
320
 	var bundlePackage = [moduleName, "nls", bundleName].join(".");
321
	// NOTE:
322
	//		When loading these resources, the packaging does not match what is
323
	//		on disk.  This is an implementation detail, as this is just a
324
	//		private data structure to hold the loaded resources.  e.g.
325
	//		tests/hello/nls/en-us/salutations.js is loaded as the object
326
	//		tests.hello.nls.salutations.en_us={...} The structure on disk is
327
	//		intended to be most convenient for developers and translators, but
328
	//		in memory it is more logical and efficient to store in a different
329
	//		order.  Locales cannot use dashes, since the resulting path will
330
	//		not evaluate as valid JS, so we translate them to underscores.
331
 
332
	//Find the best-match locale to load if we have available flat locales.
333
	var bestLocale = "";
334
	if(availableFlatLocales){
335
		var flatLocales = availableFlatLocales.split(",");
336
		for(var i = 0; i < flatLocales.length; i++){
337
			//Locale must match from start of string.
338
			if(targetLocale.indexOf(flatLocales[i]) == 0){
339
				if(flatLocales[i].length > bestLocale.length){
340
					bestLocale = flatLocales[i];
341
				}
342
			}
343
		}
344
		if(!bestLocale){
345
			bestLocale = "ROOT";
346
		}
347
	}
348
 
349
	//See if the desired locale is already loaded.
350
	var tempLocale = availableFlatLocales ? bestLocale : targetLocale;
351
	var bundle = dojo._loadedModules[bundlePackage];
352
	var localizedBundle = null;
353
	if(bundle){
354
		if(djConfig.localizationComplete && bundle._built){return;}
355
		var jsLoc = tempLocale.replace(/-/g, '_');
356
		var translationPackage = bundlePackage+"."+jsLoc;
357
		localizedBundle = dojo._loadedModules[translationPackage];
358
	}
359
 
360
	if(!localizedBundle){
361
		bundle = dojo["provide"](bundlePackage);
362
		var syms = dojo._getModuleSymbols(moduleName);
363
		var modpath = syms.concat("nls").join("/");
364
		var parent;
365
 
366
		dojo.i18n._searchLocalePath(tempLocale, availableFlatLocales, function(loc){
367
			var jsLoc = loc.replace(/-/g, '_');
368
			var translationPackage = bundlePackage + "." + jsLoc;
369
			var loaded = false;
370
			if(!dojo._loadedModules[translationPackage]){
371
				// Mark loaded whether it's found or not, so that further load attempts will not be made
372
				dojo["provide"](translationPackage);
373
				var module = [modpath];
374
				if(loc != "ROOT"){module.push(loc);}
375
				module.push(bundleName);
376
				var filespec = module.join("/") + '.js';
377
				loaded = dojo._loadPath(filespec, null, function(hash){
378
					// Use singleton with prototype to point to parent bundle, then mix-in result from loadPath
379
					var clazz = function(){};
380
					clazz.prototype = parent;
381
					bundle[jsLoc] = new clazz();
382
					for(var j in hash){ bundle[jsLoc][j] = hash[j]; }
383
				});
384
			}else{
385
				loaded = true;
386
			}
387
			if(loaded && bundle[jsLoc]){
388
				parent = bundle[jsLoc];
389
			}else{
390
				bundle[jsLoc] = parent;
391
			}
392
 
393
			if(availableFlatLocales){
394
				//Stop the locale path searching if we know the availableFlatLocales, since
395
				//the first call to this function will load the only bundle that is needed.
396
				return true;
397
			}
398
		});
399
	}
400
 
401
	//Save the best locale bundle as the target locale bundle when we know the
402
	//the available bundles.
403
	if(availableFlatLocales && targetLocale != bestLocale){
404
		bundle[targetLocale.replace(/-/g, '_')] = bundle[bestLocale.replace(/-/g, '_')];
405
	}
406
};
407
 
408
(function(){
409
	// If other locales are used, dojo.requireLocalization should load them as
410
	// well, by default.
411
	//
412
	// Override dojo.requireLocalization to do load the default bundle, then
413
	// iterate through the extraLocale list and load those translations as
414
	// well, unless a particular locale was requested.
415
 
416
	var extra = djConfig.extraLocale;
417
	if(extra){
418
		if(!extra instanceof Array){
419
			extra = [extra];
420
		}
421
 
422
		var req = dojo.i18n._requireLocalization;
423
		dojo.i18n._requireLocalization = function(m, b, locale, availableFlatLocales){
424
			req(m,b,locale, availableFlatLocales);
425
			if(locale){return;}
426
			for(var i=0; i<extra.length; i++){
427
				req(m,b,extra[i], availableFlatLocales);
428
			}
429
		};
430
	}
431
})();
432
 
433
dojo.i18n._searchLocalePath = function(/*String*/locale, /*Boolean*/down, /*Function*/searchFunc){
434
	//	summary:
435
	//		A helper method to assist in searching for locale-based resources.
436
	//		Will iterate through the variants of a particular locale, either up
437
	//		or down, executing a callback function.  For example, "en-us" and
438
	//		true will try "en-us" followed by "en" and finally "ROOT".
439
 
440
	locale = dojo.i18n.normalizeLocale(locale);
441
 
442
	var elements = locale.split('-');
443
	var searchlist = [];
444
	for(var i = elements.length; i > 0; i--){
445
		searchlist.push(elements.slice(0, i).join('-'));
446
	}
447
	searchlist.push(false);
448
	if(down){searchlist.reverse();}
449
 
450
	for(var j = searchlist.length - 1; j >= 0; j--){
451
		var loc = searchlist[j] || "ROOT";
452
		var stop = searchFunc(loc);
453
		if(stop){ break; }
454
	}
455
};
456
 
457
dojo.i18n._preloadLocalizations = function(/*String*/bundlePrefix, /*Array*/localesGenerated){
458
	//	summary:
459
	//		Load built, flattened resource bundles, if available for all
460
	//		locales used in the page. Only called by built layer files.
461
 
462
	function preload(locale){
463
		locale = dojo.i18n.normalizeLocale(locale);
464
		dojo.i18n._searchLocalePath(locale, true, function(loc){
465
			for(var i=0; i<localesGenerated.length;i++){
466
				if(localesGenerated[i] == loc){
467
					dojo["require"](bundlePrefix+"_"+loc);
468
					return true; // Boolean
469
				}
470
			}
471
			return false; // Boolean
472
		});
473
	}
474
	preload();
475
	var extra = djConfig.extraLocale||[];
476
	for(var i=0; i<extra.length; i++){
477
		preload(extra[i]);
478
	}
479
};
480
 
481
}
482
 
483
if(!dojo._hasResource["dijit.ColorPalette"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
484
dojo._hasResource["dijit.ColorPalette"] = true;
485
dojo.provide("dijit.ColorPalette");
486
 
487
 
488
 
489
 
490
 
491
 
492
 
493
dojo.declare(
494
		"dijit.ColorPalette",
495
		[dijit._Widget, dijit._Templated],
496
{
497
	// summary
498
	//		Grid showing various colors, so the user can pick a certain color
499
 
500
	// defaultTimeout: Number
501
	//		number of milliseconds before a held key or button becomes typematic
502
	defaultTimeout: 500,
503
 
504
	// timeoutChangeRate: Number
505
	//		fraction of time used to change the typematic timer between events
506
	//		1.0 means that each typematic event fires at defaultTimeout intervals
507
	//		< 1.0 means that each typematic event fires at an increasing faster rate
508
	timeoutChangeRate: 0.90,
509
 
510
	// palette: String
511
	//		Size of grid, either "7x10" or "3x4".
512
	palette: "7x10",
513
 
514
	//_value: String
515
	//		The value of the selected color.
516
	value: null,
517
 
518
	//_currentFocus: Integer
519
	//		Index of the currently focused color.
520
	_currentFocus: 0,
521
 
522
	// _xDim: Integer
523
	//		This is the number of colors horizontally across.
524
	_xDim: null,
525
 
526
	// _yDim: Integer
527
	///		This is the number of colors vertically down.
528
	_yDim: null,
529
 
530
	// _palettes: Map
531
	// 		This represents the value of the colors.
532
	//		The first level is a hashmap of the different arrays available
533
	//		The next two dimensions represent the columns and rows of colors.
534
	_palettes: {
535
 
536
		"7x10":	[["white", "seashell", "cornsilk", "lemonchiffon","lightyellow", "palegreen", "paleturquoise", "lightcyan",	"lavender", "plum"],
537
				["lightgray", "pink", "bisque", "moccasin", "khaki", "lightgreen", "lightseagreen", "lightskyblue", "cornflowerblue", "violet"],
538
				["silver", "lightcoral", "sandybrown", "orange", "palegoldenrod", "chartreuse", "mediumturquoise", 	"skyblue", "mediumslateblue","orchid"],
539
				["gray", "red", "orangered", "darkorange", "yellow", "limegreen", 	"darkseagreen", "royalblue", "slateblue", "mediumorchid"],
540
				["dimgray", "crimson", 	"chocolate", "coral", "gold", "forestgreen", "seagreen", "blue", "blueviolet", "darkorchid"],
541
				["darkslategray","firebrick","saddlebrown", "sienna", "olive", "green", "darkcyan", "mediumblue","darkslateblue", "darkmagenta" ],
542
				["black", "darkred", "maroon", "brown", "darkolivegreen", "darkgreen", "midnightblue", "navy", "indigo", 	"purple"]],
543
 
544
		"3x4": [["white", "lime", "green", "blue"],
545
			["silver", "yellow", "fuchsia", "navy"],
546
			["gray", "red", "purple", "black"]]
547
 
548
	},
549
 
550
	// _imagePaths: Map
551
	//		This is stores the path to the palette images
552
	_imagePaths: {
553
		"7x10": dojo.moduleUrl("dijit", "templates/colors7x10.png"),
554
		"3x4": dojo.moduleUrl("dijit", "templates/colors3x4.png")
555
	},
556
 
557
	// _paletteCoords: Map
558
	//		This is a map that is used to calculate the coordinates of the
559
	//		images that make up the palette.
560
	_paletteCoords: {
561
		"leftOffset": 4, "topOffset": 4,
562
		"cWidth": 20, "cHeight": 20
563
 
564
	},
565
 
566
	// templatePath: String
567
	//		Path to the template of this widget.
568
	templateString:"<div class=\"dijitInline dijitColorPalette\">\n\t<div class=\"dijitColorPaletteInner\" dojoAttachPoint=\"divNode\" waiRole=\"grid\" tabIndex=\"-1\">\n\t\t<img class=\"dijitColorPaletteUnder\" dojoAttachPoint=\"imageNode\" waiRole=\"presentation\">\n\t</div>\t\n</div>\n",
569
 
570
	// _paletteDims: Object
571
	//		Size of the supported palettes for alignment purposes.
572
	_paletteDims: {
573
		"7x10": {"width": "206px", "height": "145px"},
574
		"3x4": {"width": "86px", "height": "64px"}
575
	},
576
 
577
 
578
	postCreate: function(){
579
		// A name has to be given to the colorMap, this needs to be unique per Palette.
580
		dojo.mixin(this.divNode.style, this._paletteDims[this.palette]);
581
		this.imageNode.setAttribute("src", this._imagePaths[this.palette]);
582
		var choices = this._palettes[this.palette];
583
		this.domNode.style.position = "relative";
584
		this._highlightNodes = [];
585
		this.colorNames = dojo.i18n.getLocalization("dojo", "colors", this.lang);
586
		var url= dojo.moduleUrl("dijit", "templates/blank.gif");
587
		var colorObject = new dojo.Color(),
588
		    coords = this._paletteCoords;
589
		for(var row=0; row < choices.length; row++){
590
			for(var col=0; col < choices[row].length; col++) {
591
                var highlightNode = document.createElement("img");
592
                highlightNode.src = url;
593
                dojo.addClass(highlightNode, "dijitPaletteImg");
594
                var color = choices[row][col],
595
                        colorValue = colorObject.setColor(dojo.Color.named[color]);
596
                highlightNode.alt = this.colorNames[color];
597
                highlightNode.color = colorValue.toHex();
598
                var highlightStyle = highlightNode.style;
599
                highlightStyle.color = highlightStyle.backgroundColor = highlightNode.color;
600
                dojo.forEach(["Dijitclick", "MouseOut", "MouseOver", "Blur", "Focus"], function(handler) {
601
                    this.connect(highlightNode, "on" + handler.toLowerCase(), "_onColor" + handler);
602
                }, this);
603
                this.divNode.appendChild(highlightNode);
604
                highlightStyle.top = coords.topOffset + (row * coords.cHeight) + "px";
605
                highlightStyle.left = coords.leftOffset + (col * coords.cWidth) + "px";
606
                highlightNode.setAttribute("tabIndex", "-1");
607
                highlightNode.title = this.colorNames[color];
608
                dijit.setWaiRole(highlightNode, "gridcell");
609
                highlightNode.index = this._highlightNodes.length;
610
                this._highlightNodes.push(highlightNode);
611
            }
612
		}
613
		this._highlightNodes[this._currentFocus].tabIndex = 0;
614
		this._xDim = choices[0].length;
615
		this._yDim = choices.length;
616
 
617
		// Now set all events
618
		// The palette itself is navigated to with the tab key on the keyboard
619
		// Keyboard navigation within the Palette is with the arrow keys
620
		// Spacebar selects the color.
621
		// For the up key the index is changed by negative the x dimension.
622
 
623
		var keyIncrementMap = {
624
			UP_ARROW: -this._xDim,
625
			// The down key the index is increase by the x dimension.
626
			DOWN_ARROW: this._xDim,
627
			// Right and left move the index by 1.
628
			RIGHT_ARROW: 1,
629
			LEFT_ARROW: -1
630
		};
631
		for(var key in keyIncrementMap){
632
			this._connects.push(dijit.typematic.addKeyListener(this.domNode,
633
				{keyCode:dojo.keys[key], ctrlKey:false, altKey:false, shiftKey:false},
634
				this,
635
				function(){
636
					var increment = keyIncrementMap[key];
637
					return function(count){ this._navigateByKey(increment, count); };
638
				}(),
639
				this.timeoutChangeRate, this.defaultTimeout));
640
		}
641
	},
642
 
643
	focus: function(){
644
		// summary:
645
		//		Focus this ColorPalette.
646
		dijit.focus(this._highlightNodes[this._currentFocus]);
647
	},
648
 
649
	onChange: function(color){
650
		// summary:
651
		//		Callback when a color is selected.
652
		// color: String
653
		//		Hex value corresponding to color.
654
//		console.debug("Color selected is: "+color);
655
	},
656
 
657
	_onColorDijitclick: function(/*Event*/ evt){
658
		// summary:
659
		//		Handler for click, enter key & space key. Selects the color.
660
		// evt:
661
		//		The event.
662
		var target = evt.currentTarget;
663
		if (this._currentFocus != target.index){
664
			this._currentFocus = target.index;
665
			dijit.focus(target);
666
		}
667
		this._selectColor(target);
668
		dojo.stopEvent(evt);
669
	},
670
 
671
	_onColorMouseOut: function(/*Event*/ evt){
672
		// summary:
673
		//		Handler for onMouseOut. Removes highlight.
674
		// evt:
675
		//		The mouse event.
676
		dojo.removeClass(evt.currentTarget, "dijitPaletteImgHighlight");
677
	},
678
 
679
	_onColorMouseOver: function(/*Event*/ evt){
680
		// summary:
681
		//		Handler for onMouseOver. Highlights the color.
682
		// evt:
683
		//		The mouse event.
684
		var target = evt.currentTarget;
685
		target.tabIndex = 0;
686
		target.focus();
687
	},
688
 
689
	_onColorBlur: function(/*Event*/ evt){
690
		// summary:
691
		//		Handler for onBlur. Removes highlight and sets
692
		//		the first color as the palette's tab point.
693
		// evt:
694
		//		The blur event.
695
		dojo.removeClass(evt.currentTarget, "dijitPaletteImgHighlight");
696
		evt.currentTarget.tabIndex = -1;
697
		this._currentFocus = 0;
698
		this._highlightNodes[0].tabIndex = 0;
699
	},
700
 
701
	_onColorFocus: function(/*Event*/ evt){
702
		// summary:
703
		//		Handler for onFocus. Highlights the color.
704
		// evt:
705
		//		The focus event.
706
		if(this._currentFocus != evt.currentTarget.index){
707
			this._highlightNodes[this._currentFocus].tabIndex = -1;
708
		}
709
		this._currentFocus = evt.currentTarget.index;
710
		dojo.addClass(evt.currentTarget, "dijitPaletteImgHighlight");
711
 
712
	},
713
 
714
	_selectColor: function(selectNode){
715
		// summary:
716
		// 		This selects a color. It triggers the onChange event
717
		// area:
718
		//		The area node that covers the color being selected.
719
		this.onChange(this.value = selectNode.color);
720
	},
721
 
722
	_navigateByKey: function(increment, typeCount){
723
		// summary:we
724
		// 	  	This is the callback for typematic.
725
		// 		It changes the focus and the highlighed color.
726
		// increment:
727
		// 		How much the key is navigated.
728
		// typeCount:
729
		//		How many times typematic has fired.
730
 
731
		// typecount == -1 means the key is released.
732
		if(typeCount == -1){ return; }
733
 
734
		var newFocusIndex = this._currentFocus + increment;
735
		if(newFocusIndex < this._highlightNodes.length && newFocusIndex > -1)
736
		{
737
			var focusNode = this._highlightNodes[newFocusIndex];
738
			focusNode.tabIndex = 0;
739
			focusNode.focus();
740
		}
741
	}
742
});
743
 
744
}
745
 
746
if(!dojo._hasResource["dijit.Declaration"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
747
dojo._hasResource["dijit.Declaration"] = true;
748
dojo.provide("dijit.Declaration");
749
 
750
 
751
 
752
dojo.declare(
753
	"dijit.Declaration",
754
	dijit._Widget,
755
	{
756
		// summary:
757
		//		The Declaration widget allows a user to declare new widget
758
		//		classes directly from a snippet of markup.
759
 
760
		_noScript: true,
761
		widgetClass: "",
762
		replaceVars: true,
763
		defaults: null,
764
		mixins: [],
765
		buildRendering: function(){
766
			var src = this.srcNodeRef.parentNode.removeChild(this.srcNodeRef);
767
			var preambles = dojo.query("> script[type='dojo/method'][event='preamble']", src).orphan();
768
			var scripts = dojo.query("> script[type^='dojo/']", src).orphan();
769
			var srcType = src.nodeName;
770
 
771
			var propList = this.defaults||{};
772
 
773
			// map array of strings like [ "dijit.form.Button" ] to array of mixin objects
774
			// (note that dojo.map(this.mixins, dojo.getObject) doesn't work because it passes
775
			// a bogus third argument to getObject(), confusing it)
776
			this.mixins = this.mixins.length ?
777
				dojo.map(this.mixins, function(name){ return dojo.getObject(name); } ) :
778
				[ dijit._Widget, dijit._Templated ];
779
 
780
			if(preambles.length){
781
				// we only support one preamble. So be it.
782
				propList.preamble = dojo.parser._functionFromScript(preambles[0]);
783
			}
784
 
785
			var parsedScripts = dojo.map(scripts, function(s){
786
				var evt = s.getAttribute("event")||"postscript";
787
				return {
788
					event: evt,
789
					func: dojo.parser._functionFromScript(s)
790
				};
791
			});
792
 
793
			// do the connects for each <script type="dojo/connect" event="foo"> block and make
794
			// all <script type="dojo/method"> tags execute right after construction
795
			this.mixins.push(function(){
796
				dojo.forEach(parsedScripts, function(s){
797
					dojo.connect(this, s.event, this, s.func);
798
				}, this);
799
			});
800
 
801
			propList.widgetsInTemplate = true;
802
			propList._skipNodeCache = true;
803
			propList.templateString = "<"+srcType+" class='"+src.className+"' dojoAttachPoint='"+(src.getAttribute("dojoAttachPoint")||'')+"' dojoAttachEvent='"+(src.getAttribute("dojoAttachEvent")||'')+"' >"+src.innerHTML.replace(/\%7B/g,"{").replace(/\%7D/g,"}")+"</"+srcType+">";
804
			// console.debug(propList.templateString);
805
 
806
			// strip things so we don't create stuff under us in the initial setup phase
807
			dojo.query("[dojoType]", src).forEach(function(node){
808
				node.removeAttribute("dojoType");
809
			});
810
 
811
			// create the new widget class
812
			dojo.declare(
813
				this.widgetClass,
814
				this.mixins,
815
				propList
816
			);
817
		}
818
	}
819
);
820
 
821
}
822
 
823
if(!dojo._hasResource["dojo.dnd.common"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
824
dojo._hasResource["dojo.dnd.common"] = true;
825
dojo.provide("dojo.dnd.common");
826
 
827
dojo.dnd._copyKey = navigator.appVersion.indexOf("Macintosh") < 0 ? "ctrlKey" : "metaKey";
828
 
829
dojo.dnd.getCopyKeyState = function(e) {
830
	// summary: abstracts away the difference between selection on Mac and PC,
831
	//	and returns the state of the "copy" key to be pressed.
832
	// e: Event: mouse event
833
	return e[dojo.dnd._copyKey];	// Boolean
834
};
835
 
836
dojo.dnd._uniqueId = 0;
837
dojo.dnd.getUniqueId = function(){
838
	// summary: returns a unique string for use with any DOM element
839
	var id;
840
	do{
841
		id = "dojoUnique" + (++dojo.dnd._uniqueId);
842
	}while(dojo.byId(id));
843
	return id;
844
};
845
 
846
dojo.dnd._empty = {};
847
 
848
dojo.dnd.isFormElement = function(/*Event*/ e){
849
	// summary: returns true, if user clicked on a form element
850
	var t = e.target;
851
	if(t.nodeType == 3 /*TEXT_NODE*/){
852
		t = t.parentNode;
853
	}
854
	return " button textarea input select option ".indexOf(" " + t.tagName.toLowerCase() + " ") >= 0;	// Boolean
855
};
856
 
857
}
858
 
859
if(!dojo._hasResource["dojo.dnd.autoscroll"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
860
dojo._hasResource["dojo.dnd.autoscroll"] = true;
861
dojo.provide("dojo.dnd.autoscroll");
862
 
863
dojo.dnd.getViewport = function(){
864
	// summary: returns a viewport size (visible part of the window)
865
 
866
	// FIXME: need more docs!!
867
	var d = dojo.doc, dd = d.documentElement, w = window, b = dojo.body();
868
	if(dojo.isMozilla){
869
		return {w: dd.clientWidth, h: w.innerHeight};	// Object
870
	}else if(!dojo.isOpera && w.innerWidth){
871
		return {w: w.innerWidth, h: w.innerHeight};		// Object
872
	}else if (!dojo.isOpera && dd && dd.clientWidth){
873
		return {w: dd.clientWidth, h: dd.clientHeight};	// Object
874
	}else if (b.clientWidth){
875
		return {w: b.clientWidth, h: b.clientHeight};	// Object
876
	}
877
	return null;	// Object
878
};
879
 
880
dojo.dnd.V_TRIGGER_AUTOSCROLL = 32;
881
dojo.dnd.H_TRIGGER_AUTOSCROLL = 32;
882
 
883
dojo.dnd.V_AUTOSCROLL_VALUE = 16;
884
dojo.dnd.H_AUTOSCROLL_VALUE = 16;
885
 
886
dojo.dnd.autoScroll = function(e){
887
	// summary:
888
	//		a handler for onmousemove event, which scrolls the window, if
889
	//		necesary
890
	// e: Event:
891
	//		onmousemove event
892
 
893
	// FIXME: needs more docs!
894
	var v = dojo.dnd.getViewport(), dx = 0, dy = 0;
895
	if(e.clientX < dojo.dnd.H_TRIGGER_AUTOSCROLL){
896
		dx = -dojo.dnd.H_AUTOSCROLL_VALUE;
897
	}else if(e.clientX > v.w - dojo.dnd.H_TRIGGER_AUTOSCROLL){
898
		dx = dojo.dnd.H_AUTOSCROLL_VALUE;
899
	}
900
	if(e.clientY < dojo.dnd.V_TRIGGER_AUTOSCROLL){
901
		dy = -dojo.dnd.V_AUTOSCROLL_VALUE;
902
	}else if(e.clientY > v.h - dojo.dnd.V_TRIGGER_AUTOSCROLL){
903
		dy = dojo.dnd.V_AUTOSCROLL_VALUE;
904
	}
905
	window.scrollBy(dx, dy);
906
};
907
 
908
dojo.dnd._validNodes = {"div": 1, "p": 1, "td": 1};
909
dojo.dnd._validOverflow = {"auto": 1, "scroll": 1};
910
 
911
dojo.dnd.autoScrollNodes = function(e){
912
	// summary:
913
	//		a handler for onmousemove event, which scrolls the first avaialble
914
	//		Dom element, it falls back to dojo.dnd.autoScroll()
915
	// e: Event:
916
	//		onmousemove event
917
 
918
	// FIXME: needs more docs!
919
	for(var n = e.target; n;){
920
		if(n.nodeType == 1 && (n.tagName.toLowerCase() in dojo.dnd._validNodes)){
921
			var s = dojo.getComputedStyle(n);
922
			if(s.overflow.toLowerCase() in dojo.dnd._validOverflow){
923
				var b = dojo._getContentBox(n, s), t = dojo._abs(n, true);
924
				// console.debug(b.l, b.t, t.x, t.y, n.scrollLeft, n.scrollTop);
925
				b.l += t.x + n.scrollLeft;
926
				b.t += t.y + n.scrollTop;
927
				var w = Math.min(dojo.dnd.H_TRIGGER_AUTOSCROLL, b.w / 2),
928
					h = Math.min(dojo.dnd.V_TRIGGER_AUTOSCROLL, b.h / 2),
929
					rx = e.pageX - b.l, ry = e.pageY - b.t, dx = 0, dy = 0;
930
				if(rx > 0 && rx < b.w){
931
					if(rx < w){
932
						dx = -dojo.dnd.H_AUTOSCROLL_VALUE;
933
					}else if(rx > b.w - w){
934
						dx = dojo.dnd.H_AUTOSCROLL_VALUE;
935
					}
936
				}
937
				//console.debug("ry =", ry, "b.h =", b.h, "h =", h);
938
				if(ry > 0 && ry < b.h){
939
					if(ry < h){
940
						dy = -dojo.dnd.V_AUTOSCROLL_VALUE;
941
					}else if(ry > b.h - h){
942
						dy = dojo.dnd.V_AUTOSCROLL_VALUE;
943
					}
944
				}
945
				var oldLeft = n.scrollLeft, oldTop = n.scrollTop;
946
				n.scrollLeft = n.scrollLeft + dx;
947
				n.scrollTop  = n.scrollTop  + dy;
948
				// if(dx || dy){ console.debug(oldLeft + ", " + oldTop + "\n" + dx + ", " + dy + "\n" + n.scrollLeft + ", " + n.scrollTop); }
949
				if(oldLeft != n.scrollLeft || oldTop != n.scrollTop){ return; }
950
			}
951
		}
952
		try{
953
			n = n.parentNode;
954
		}catch(x){
955
			n = null;
956
		}
957
	}
958
	dojo.dnd.autoScroll(e);
959
};
960
 
961
}
962
 
963
if(!dojo._hasResource["dojo.dnd.Mover"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
964
dojo._hasResource["dojo.dnd.Mover"] = true;
965
dojo.provide("dojo.dnd.Mover");
966
 
967
 
968
 
969
 
970
dojo.declare("dojo.dnd.Mover", null, {
971
	constructor: function(node, e, host){
972
		// summary: an object, which makes a node follow the mouse,
973
		//	used as a default mover, and as a base class for custom movers
974
		// node: Node: a node (or node's id) to be moved
975
		// e: Event: a mouse event, which started the move;
976
		//	only pageX and pageY properties are used
977
		// host: Object?: object which implements the functionality of the move,
978
		//	 and defines proper events (onMoveStart and onMoveStop)
979
		this.node = dojo.byId(node);
980
		this.marginBox = {l: e.pageX, t: e.pageY};
981
		this.mouseButton = e.button;
982
		var h = this.host = host, d = node.ownerDocument,
983
			firstEvent = dojo.connect(d, "onmousemove", this, "onFirstMove");
984
		this.events = [
985
			dojo.connect(d, "onmousemove", this, "onMouseMove"),
986
			dojo.connect(d, "onmouseup",   this, "onMouseUp"),
987
			// cancel text selection and text dragging
988
			dojo.connect(d, "ondragstart",   dojo, "stopEvent"),
989
			dojo.connect(d, "onselectstart", dojo, "stopEvent"),
990
			firstEvent
991
		];
992
		// notify that the move has started
993
		if(h && h.onMoveStart){
994
			h.onMoveStart(this);
995
		}
996
	},
997
	// mouse event processors
998
	onMouseMove: function(e){
999
		// summary: event processor for onmousemove
1000
		// e: Event: mouse event
1001
		dojo.dnd.autoScroll(e);
1002
		var m = this.marginBox;
1003
		this.host.onMove(this, {l: m.l + e.pageX, t: m.t + e.pageY});
1004
	},
1005
	onMouseUp: function(e){
1006
		if(this.mouseButton == e.button){
1007
			this.destroy();
1008
		}
1009
	},
1010
	// utilities
1011
	onFirstMove: function(){
1012
		// summary: makes the node absolute; it is meant to be called only once
1013
		this.node.style.position = "absolute";	// enforcing the absolute mode
1014
		var m = dojo.marginBox(this.node);
1015
		m.l -= this.marginBox.l;
1016
		m.t -= this.marginBox.t;
1017
		this.marginBox = m;
1018
		this.host.onFirstMove(this);
1019
		dojo.disconnect(this.events.pop());
1020
	},
1021
	destroy: function(){
1022
		// summary: stops the move, deletes all references, so the object can be garbage-collected
1023
		dojo.forEach(this.events, dojo.disconnect);
1024
		// undo global settings
1025
		var h = this.host;
1026
		if(h && h.onMoveStop){
1027
			h.onMoveStop(this);
1028
		}
1029
		// destroy objects
1030
		this.events = this.node = null;
1031
	}
1032
});
1033
 
1034
}
1035
 
1036
if(!dojo._hasResource["dojo.dnd.Moveable"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1037
dojo._hasResource["dojo.dnd.Moveable"] = true;
1038
dojo.provide("dojo.dnd.Moveable");
1039
 
1040
 
1041
 
1042
dojo.declare("dojo.dnd.Moveable", null, {
1043
	// object attributes (for markup)
1044
	handle: "",
1045
	delay: 0,
1046
	skip: false,
1047
 
1048
	constructor: function(node, params){
1049
		// summary: an object, which makes a node moveable
1050
		// node: Node: a node (or node's id) to be moved
1051
		// params: Object: an optional object with additional parameters;
1052
		//	following parameters are recognized:
1053
		//		handle: Node: a node (or node's id), which is used as a mouse handle
1054
		//			if omitted, the node itself is used as a handle
1055
		//		delay: Number: delay move by this number of pixels
1056
		//		skip: Boolean: skip move of form elements
1057
		//		mover: Object: a constructor of custom Mover
1058
		this.node = dojo.byId(node);
1059
		if(!params){ params = {}; }
1060
		this.handle = params.handle ? dojo.byId(params.handle) : null;
1061
		if(!this.handle){ this.handle = this.node; }
1062
		this.delay = params.delay > 0 ? params.delay : 0;
1063
		this.skip  = params.skip;
1064
		this.mover = params.mover ? params.mover : dojo.dnd.Mover;
1065
		this.events = [
1066
			dojo.connect(this.handle, "onmousedown", this, "onMouseDown"),
1067
			// cancel text selection and text dragging
1068
			dojo.connect(this.handle, "ondragstart",   this, "onSelectStart"),
1069
			dojo.connect(this.handle, "onselectstart", this, "onSelectStart")
1070
		];
1071
	},
1072
 
1073
	// markup methods
1074
	markupFactory: function(params, node){
1075
		return new dojo.dnd.Moveable(node, params);
1076
	},
1077
 
1078
	// methods
1079
	destroy: function(){
1080
		// summary: stops watching for possible move, deletes all references, so the object can be garbage-collected
1081
		dojo.forEach(this.events, dojo.disconnect);
1082
		this.events = this.node = this.handle = null;
1083
	},
1084
 
1085
	// mouse event processors
1086
	onMouseDown: function(e){
1087
		// summary: event processor for onmousedown, creates a Mover for the node
1088
		// e: Event: mouse event
1089
		if(this.skip && dojo.dnd.isFormElement(e)){ return; }
1090
		if(this.delay){
1091
			this.events.push(dojo.connect(this.handle, "onmousemove", this, "onMouseMove"));
1092
			this.events.push(dojo.connect(this.handle, "onmouseup", this, "onMouseUp"));
1093
			this._lastX = e.pageX;
1094
			this._lastY = e.pageY;
1095
		}else{
1096
			new this.mover(this.node, e, this);
1097
		}
1098
		dojo.stopEvent(e);
1099
	},
1100
	onMouseMove: function(e){
1101
		// summary: event processor for onmousemove, used only for delayed drags
1102
		// e: Event: mouse event
1103
		if(Math.abs(e.pageX - this._lastX) > this.delay || Math.abs(e.pageY - this._lastY) > this.delay){
1104
			this.onMouseUp(e);
1105
			new this.mover(this.node, e, this);
1106
		}
1107
		dojo.stopEvent(e);
1108
	},
1109
	onMouseUp: function(e){
1110
		// summary: event processor for onmouseup, used only for delayed delayed drags
1111
		// e: Event: mouse event
1112
		dojo.disconnect(this.events.pop());
1113
		dojo.disconnect(this.events.pop());
1114
	},
1115
	onSelectStart: function(e){
1116
		// summary: event processor for onselectevent and ondragevent
1117
		// e: Event: mouse event
1118
		if(!this.skip || !dojo.dnd.isFormElement(e)){
1119
			dojo.stopEvent(e);
1120
		}
1121
	},
1122
 
1123
	// local events
1124
	onMoveStart: function(/* dojo.dnd.Mover */ mover){
1125
		// summary: called before every move operation
1126
		dojo.publish("/dnd/move/start", [mover]);
1127
		dojo.addClass(dojo.body(), "dojoMove");
1128
		dojo.addClass(this.node, "dojoMoveItem");
1129
	},
1130
	onMoveStop: function(/* dojo.dnd.Mover */ mover){
1131
		// summary: called after every move operation
1132
		dojo.publish("/dnd/move/stop", [mover]);
1133
		dojo.removeClass(dojo.body(), "dojoMove");
1134
		dojo.removeClass(this.node, "dojoMoveItem");
1135
	},
1136
	onFirstMove: function(/* dojo.dnd.Mover */ mover){
1137
		// summary: called during the very first move notification,
1138
		//	can be used to initialize coordinates, can be overwritten.
1139
 
1140
		// default implementation does nothing
1141
	},
1142
	onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
1143
		// summary: called during every move notification,
1144
		//	should actually move the node, can be overwritten.
1145
		this.onMoving(mover, leftTop);
1146
		dojo.marginBox(mover.node, leftTop);
1147
		this.onMoved(mover, leftTop);
1148
	},
1149
	onMoving: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
1150
		// summary: called before every incremental move,
1151
		//	can be overwritten.
1152
 
1153
		// default implementation does nothing
1154
	},
1155
	onMoved: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
1156
		// summary: called after every incremental move,
1157
		//	can be overwritten.
1158
 
1159
		// default implementation does nothing
1160
	}
1161
});
1162
 
1163
}
1164
 
1165
if(!dojo._hasResource["dojo.dnd.move"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1166
dojo._hasResource["dojo.dnd.move"] = true;
1167
dojo.provide("dojo.dnd.move");
1168
 
1169
 
1170
 
1171
 
1172
dojo.declare("dojo.dnd.move.constrainedMoveable", dojo.dnd.Moveable, {
1173
	// object attributes (for markup)
1174
	constraints: function(){},
1175
	within: false,
1176
 
1177
	// markup methods
1178
	markupFactory: function(params, node){
1179
		return new dojo.dnd.move.constrainedMoveable(node, params);
1180
	},
1181
 
1182
	constructor: function(node, params){
1183
		// summary: an object, which makes a node moveable
1184
		// node: Node: a node (or node's id) to be moved
1185
		// params: Object: an optional object with additional parameters;
1186
		//	following parameters are recognized:
1187
		//		constraints: Function: a function, which calculates a constraint box,
1188
		//			it is called in a context of the moveable object.
1189
		//		within: Boolean: restrict move within boundaries.
1190
		//	the rest is passed to the base class
1191
		if(!params){ params = {}; }
1192
		this.constraints = params.constraints;
1193
		this.within = params.within;
1194
	},
1195
	onFirstMove: function(/* dojo.dnd.Mover */ mover){
1196
		// summary: called during the very first move notification,
1197
		//	can be used to initialize coordinates, can be overwritten.
1198
		var c = this.constraintBox = this.constraints.call(this, mover), m = mover.marginBox;
1199
		c.r = c.l + c.w - (this.within ? m.w : 0);
1200
		c.b = c.t + c.h - (this.within ? m.h : 0);
1201
	},
1202
	onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
1203
		// summary: called during every move notification,
1204
		//	should actually move the node, can be overwritten.
1205
		var c = this.constraintBox;
1206
		leftTop.l = leftTop.l < c.l ? c.l : c.r < leftTop.l ? c.r : leftTop.l;
1207
		leftTop.t = leftTop.t < c.t ? c.t : c.b < leftTop.t ? c.b : leftTop.t;
1208
		dojo.marginBox(mover.node, leftTop);
1209
	}
1210
});
1211
 
1212
dojo.declare("dojo.dnd.move.boxConstrainedMoveable", dojo.dnd.move.constrainedMoveable, {
1213
	// object attributes (for markup)
1214
	box: {},
1215
 
1216
	// markup methods
1217
	markupFactory: function(params, node){
1218
		return new dojo.dnd.move.boxConstrainedMoveable(node, params);
1219
	},
1220
 
1221
	constructor: function(node, params){
1222
		// summary: an object, which makes a node moveable
1223
		// node: Node: a node (or node's id) to be moved
1224
		// params: Object: an optional object with additional parameters;
1225
		//	following parameters are recognized:
1226
		//		box: Object: a constraint box
1227
		//	the rest is passed to the base class
1228
		var box = params && params.box;
1229
		this.constraints = function(){ return box; };
1230
	}
1231
});
1232
 
1233
dojo.declare("dojo.dnd.move.parentConstrainedMoveable", dojo.dnd.move.constrainedMoveable, {
1234
	// object attributes (for markup)
1235
	area: "content",
1236
 
1237
	// markup methods
1238
	markupFactory: function(params, node){
1239
		return new dojo.dnd.move.parentConstrainedMoveable(node, params);
1240
	},
1241
 
1242
	constructor: function(node, params){
1243
		// summary: an object, which makes a node moveable
1244
		// node: Node: a node (or node's id) to be moved
1245
		// params: Object: an optional object with additional parameters;
1246
		//	following parameters are recognized:
1247
		//		area: String: a parent's area to restrict the move,
1248
		//			can be "margin", "border", "padding", or "content".
1249
		//	the rest is passed to the base class
1250
		var area = params && params.area;
1251
		this.constraints = function(){
1252
			var n = this.node.parentNode,
1253
				s = dojo.getComputedStyle(n),
1254
				mb = dojo._getMarginBox(n, s);
1255
			if(area == "margin"){
1256
				return mb;	// Object
1257
			}
1258
			var t = dojo._getMarginExtents(n, s);
1259
			mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
1260
			if(area == "border"){
1261
				return mb;	// Object
1262
			}
1263
			t = dojo._getBorderExtents(n, s);
1264
			mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
1265
			if(area == "padding"){
1266
				return mb;	// Object
1267
			}
1268
			t = dojo._getPadExtents(n, s);
1269
			mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
1270
			return mb;	// Object
1271
		};
1272
	}
1273
});
1274
 
1275
// WARNING: below are obsolete objects, instead of custom movers use custom moveables (above)
1276
 
1277
dojo.dnd.move.constrainedMover = function(fun, within){
1278
	// summary: returns a constrained version of dojo.dnd.Mover
1279
	// description: this function produces n object, which will put a constraint on
1280
	//	the margin box of dragged object in absolute coordinates
1281
	// fun: Function: called on drag, and returns a constraint box
1282
	// within: Boolean: if true, constraints the whole dragged object withtin the rectangle,
1283
	//	otherwise the constraint is applied to the left-top corner
1284
	var mover = function(node, e, notifier){
1285
		dojo.dnd.Mover.call(this, node, e, notifier);
1286
	};
1287
	dojo.extend(mover, dojo.dnd.Mover.prototype);
1288
	dojo.extend(mover, {
1289
		onMouseMove: function(e){
1290
			// summary: event processor for onmousemove
1291
			// e: Event: mouse event
1292
			dojo.dnd.autoScroll(e);
1293
			var m = this.marginBox, c = this.constraintBox,
1294
				l = m.l + e.pageX, t = m.t + e.pageY;
1295
			l = l < c.l ? c.l : c.r < l ? c.r : l;
1296
			t = t < c.t ? c.t : c.b < t ? c.b : t;
1297
			this.host.onMove(this, {l: l, t: t});
1298
		},
1299
		onFirstMove: function(){
1300
			// summary: called once to initialize things; it is meant to be called only once
1301
			dojo.dnd.Mover.prototype.onFirstMove.call(this);
1302
			var c = this.constraintBox = fun.call(this), m = this.marginBox;
1303
			c.r = c.l + c.w - (within ? m.w : 0);
1304
			c.b = c.t + c.h - (within ? m.h : 0);
1305
		}
1306
	});
1307
	return mover;	// Object
1308
};
1309
 
1310
dojo.dnd.move.boxConstrainedMover = function(box, within){
1311
	// summary: a specialization of dojo.dnd.constrainedMover, which constrains to the specified box
1312
	// box: Object: a constraint box (l, t, w, h)
1313
	// within: Boolean: if true, constraints the whole dragged object withtin the rectangle,
1314
	//	otherwise the constraint is applied to the left-top corner
1315
	return dojo.dnd.move.constrainedMover(function(){ return box; }, within);	// Object
1316
};
1317
 
1318
dojo.dnd.move.parentConstrainedMover = function(area, within){
1319
	// summary: a specialization of dojo.dnd.constrainedMover, which constrains to the parent node
1320
	// area: String: "margin" to constrain within the parent's margin box, "border" for the border box,
1321
	//	"padding" for the padding box, and "content" for the content box; "content" is the default value.
1322
	// within: Boolean: if true, constraints the whole dragged object withtin the rectangle,
1323
	//	otherwise the constraint is applied to the left-top corner
1324
	var fun = function(){
1325
		var n = this.node.parentNode,
1326
			s = dojo.getComputedStyle(n),
1327
			mb = dojo._getMarginBox(n, s);
1328
		if(area == "margin"){
1329
			return mb;	// Object
1330
		}
1331
		var t = dojo._getMarginExtents(n, s);
1332
		mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
1333
		if(area == "border"){
1334
			return mb;	// Object
1335
		}
1336
		t = dojo._getBorderExtents(n, s);
1337
		mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
1338
		if(area == "padding"){
1339
			return mb;	// Object
1340
		}
1341
		t = dojo._getPadExtents(n, s);
1342
		mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
1343
		return mb;	// Object
1344
	};
1345
	return dojo.dnd.move.constrainedMover(fun, within);	// Object
1346
};
1347
 
1348
// patching functions one level up for compatibility
1349
 
1350
dojo.dnd.constrainedMover = dojo.dnd.move.constrainedMover;
1351
dojo.dnd.boxConstrainedMover = dojo.dnd.move.boxConstrainedMover;
1352
dojo.dnd.parentConstrainedMover = dojo.dnd.move.parentConstrainedMover;
1353
 
1354
}
1355
 
1356
if(!dojo._hasResource["dojo.fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1357
dojo._hasResource["dojo.fx"] = true;
1358
dojo.provide("dojo.fx");
1359
dojo.provide("dojo.fx.Toggler");
1360
 
1361
dojo.fx.chain = function(/*dojo._Animation[]*/ animations){
1362
	// summary: Chain a list of dojo._Animation s to run in sequence
1363
	// example:
1364
	//	|	dojo.fx.chain([
1365
	//	|		dojo.fadeIn({ node:node }),
1366
	//	|		dojo.fadeOut({ node:otherNode })
1367
	//	|	]).play();
1368
	//
1369
	var first = animations.shift();
1370
	var previous = first;
1371
	dojo.forEach(animations, function(current){
1372
		dojo.connect(previous, "onEnd", current, "play");
1373
		previous = current;
1374
	});
1375
	return first; // dojo._Animation
1376
};
1377
 
1378
dojo.fx.combine = function(/*dojo._Animation[]*/ animations){
1379
	// summary: Combine a list of dojo._Animation s to run in parallel
1380
	// example:
1381
	//	|	dojo.fx.combine([
1382
	//	|		dojo.fadeIn({ node:node }),
1383
	//	|		dojo.fadeOut({ node:otherNode })
1384
	//	|	]).play();
1385
	var ctr = new dojo._Animation({ curve: [0, 1] });
1386
	if(!animations.length){ return ctr; }
1387
	// animations.sort(function(a, b){ return a.duration-b.duration; });
1388
	ctr.duration = animations[0].duration;
1389
	dojo.forEach(animations, function(current){
1390
		dojo.forEach([ "play", "pause", "stop" ],
1391
			function(e){
1392
				if(current[e]){
1393
					dojo.connect(ctr, e, current, e);
1394
				}
1395
			}
1396
		);
1397
	});
1398
	return ctr; // dojo._Animation
1399
};
1400
 
1401
dojo.declare("dojo.fx.Toggler", null, {
1402
	// summary:
1403
	//		class constructor for an animation toggler. It accepts a packed
1404
	//		set of arguments about what type of animation to use in each
1405
	//		direction, duration, etc.
1406
	//
1407
	// example:
1408
	//	|	var t = new dojo.fx.Toggler({
1409
	//	|		node: "nodeId",
1410
	//	|		showDuration: 500,
1411
	//	|		// hideDuration will default to "200"
1412
	//	|		showFunc: dojo.wipeIn,
1413
	//	|		// hideFunc will default to "fadeOut"
1414
	//	|	});
1415
	//	|	t.show(100); // delay showing for 100ms
1416
	//	|	// ...time passes...
1417
	//	|	t.hide();
1418
 
1419
	// FIXME: need a policy for where the toggler should "be" the next
1420
	// time show/hide are called if we're stopped somewhere in the
1421
	// middle.
1422
 
1423
	constructor: function(args){
1424
		var _t = this;
1425
 
1426
		dojo.mixin(_t, args);
1427
		_t.node = args.node;
1428
		_t._showArgs = dojo.mixin({}, args);
1429
		_t._showArgs.node = _t.node;
1430
		_t._showArgs.duration = _t.showDuration;
1431
		_t.showAnim = _t.showFunc(_t._showArgs);
1432
 
1433
		_t._hideArgs = dojo.mixin({}, args);
1434
		_t._hideArgs.node = _t.node;
1435
		_t._hideArgs.duration = _t.hideDuration;
1436
		_t.hideAnim = _t.hideFunc(_t._hideArgs);
1437
 
1438
		dojo.connect(_t.showAnim, "beforeBegin", dojo.hitch(_t.hideAnim, "stop", true));
1439
		dojo.connect(_t.hideAnim, "beforeBegin", dojo.hitch(_t.showAnim, "stop", true));
1440
	},
1441
 
1442
	// node: DomNode
1443
	//	the node to toggle
1444
	node: null,
1445
 
1446
	// showFunc: Function
1447
	//	The function that returns the dojo._Animation to show the node
1448
	showFunc: dojo.fadeIn,
1449
 
1450
	// hideFunc: Function
1451
	//	The function that returns the dojo._Animation to hide the node
1452
	hideFunc: dojo.fadeOut,
1453
 
1454
	// showDuration:
1455
	//	Time in milliseconds to run the show Animation
1456
	showDuration: 200,
1457
 
1458
	// hideDuration:
1459
	//	Time in milliseconds to run the hide Animation
1460
	hideDuration: 200,
1461
 
1462
	/*=====
1463
	_showArgs: null,
1464
	_showAnim: null,
1465
 
1466
	_hideArgs: null,
1467
	_hideAnim: null,
1468
 
1469
	_isShowing: false,
1470
	_isHiding: false,
1471
	=====*/
1472
 
1473
	show: function(delay){
1474
		// summary: Toggle the node to showing
1475
		return this.showAnim.play(delay || 0);
1476
	},
1477
 
1478
	hide: function(delay){
1479
		// summary: Toggle the node to hidden
1480
		return this.hideAnim.play(delay || 0);
1481
	}
1482
});
1483
 
1484
dojo.fx.wipeIn = function(/*Object*/ args){
1485
	// summary
1486
	//		Returns an animation that will expand the
1487
	//		node defined in 'args' object from it's current height to
1488
	//		it's natural height (with no scrollbar).
1489
	//		Node must have no margin/border/padding.
1490
	args.node = dojo.byId(args.node);
1491
	var node = args.node, s = node.style;
1492
 
1493
	var anim = dojo.animateProperty(dojo.mixin({
1494
		properties: {
1495
			height: {
1496
				// wrapped in functions so we wait till the last second to query (in case value has changed)
1497
				start: function(){
1498
					// start at current [computed] height, but use 1px rather than 0
1499
					// because 0 causes IE to display the whole panel
1500
					s.overflow="hidden";
1501
					if(s.visibility=="hidden"||s.display=="none"){
1502
						s.height="1px";
1503
						s.display="";
1504
						s.visibility="";
1505
						return 1;
1506
					}else{
1507
						var height = dojo.style(node, "height");
1508
						return Math.max(height, 1);
1509
					}
1510
				},
1511
				end: function(){
1512
					return node.scrollHeight;
1513
				}
1514
			}
1515
		}
1516
	}, args));
1517
 
1518
	dojo.connect(anim, "onEnd", function(){
1519
		s.height = "auto";
1520
	});
1521
 
1522
	return anim; // dojo._Animation
1523
}
1524
 
1525
dojo.fx.wipeOut = function(/*Object*/ args){
1526
	// summary
1527
	//		Returns an animation that will shrink node defined in "args"
1528
	//		from it's current height to 1px, and then hide it.
1529
	var node = args.node = dojo.byId(args.node);
1530
	var s = node.style;
1531
 
1532
	var anim = dojo.animateProperty(dojo.mixin({
1533
		properties: {
1534
			height: {
1535
				end: 1 // 0 causes IE to display the whole panel
1536
			}
1537
		}
1538
	}, args));
1539
 
1540
	dojo.connect(anim, "beforeBegin", function(){
1541
		s.overflow = "hidden";
1542
		s.display = "";
1543
	});
1544
	dojo.connect(anim, "onEnd", function(){
1545
		s.height = "auto";
1546
		s.display = "none";
1547
	});
1548
 
1549
	return anim; // dojo._Animation
1550
}
1551
 
1552
dojo.fx.slideTo = function(/*Object?*/ args){
1553
	// summary
1554
	//		Returns an animation that will slide "node"
1555
	//		defined in args Object from its current position to
1556
	//		the position defined by (args.left, args.top).
1557
	// example:
1558
	//	|	dojo.fx.slideTo({ node: node, left:"40", top:"50", unit:"px" }).play()
1559
 
1560
	var node = (args.node = dojo.byId(args.node));
1561
 
1562
	var top = null;
1563
	var left = null;
1564
 
1565
	var init = (function(n){
1566
		return function(){
1567
			var cs = dojo.getComputedStyle(n);
1568
			var pos = cs.position;
1569
			top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0);
1570
			left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0);
1571
			if(pos != 'absolute' && pos != 'relative'){
1572
				var ret = dojo.coords(n, true);
1573
				top = ret.y;
1574
				left = ret.x;
1575
				n.style.position="absolute";
1576
				n.style.top=top+"px";
1577
				n.style.left=left+"px";
1578
			}
1579
		};
1580
	})(node);
1581
	init();
1582
 
1583
	var anim = dojo.animateProperty(dojo.mixin({
1584
		properties: {
1585
			top: { end: args.top||0 },
1586
			left: { end: args.left||0 }
1587
		}
1588
	}, args));
1589
	dojo.connect(anim, "beforeBegin", anim, init);
1590
 
1591
	return anim; // dojo._Animation
1592
}
1593
 
1594
}
1595
 
1596
if(!dojo._hasResource["dijit.layout.ContentPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1597
dojo._hasResource["dijit.layout.ContentPane"] = true;
1598
dojo.provide("dijit.layout.ContentPane");
1599
 
1600
 
1601
 
1602
 
1603
 
1604
 
1605
 
1606
 
1607
dojo.declare(
1608
	"dijit.layout.ContentPane",
1609
	dijit._Widget,
1610
{
1611
	// summary:
1612
	//		A widget that acts as a Container for other widgets, and includes a ajax interface
1613
	// description:
1614
	//		A widget that can be used as a standalone widget
1615
	//		or as a baseclass for other widgets
1616
	//		Handles replacement of document fragment using either external uri or javascript
1617
	//		generated markup or DOM content, instantiating widgets within that content.
1618
	//		Don't confuse it with an iframe, it only needs/wants document fragments.
1619
	//		It's useful as a child of LayoutContainer, SplitContainer, or TabContainer.
1620
	//		But note that those classes can contain any widget as a child.
1621
	// example:
1622
	//		Some quick samples:
1623
	//		To change the innerHTML use .setContent('<b>new content</b>')
1624
	//
1625
	//		Or you can send it a NodeList, .setContent(dojo.query('div [class=selected]', userSelection))
1626
	//		please note that the nodes in NodeList will copied, not moved
1627
	//
1628
	//		To do a ajax update use .setHref('url')
1629
	//
1630
	// href: String
1631
	//		The href of the content that displays now.
1632
	//		Set this at construction if you want to load data externally when the
1633
	//		pane is shown.  (Set preload=true to load it immediately.)
1634
	//		Changing href after creation doesn't have any effect; see setHref();
1635
	href: "",
1636
 
1637
	// extractContent: Boolean
1638
	//	Extract visible content from inside of <body> .... </body>
1639
	extractContent: false,
1640
 
1641
	// parseOnLoad: Boolean
1642
	//	parse content and create the widgets, if any
1643
	parseOnLoad:	true,
1644
 
1645
	// preventCache: Boolean
1646
	//		Cache content retreived externally
1647
	preventCache:	false,
1648
 
1649
	// preload: Boolean
1650
	//	Force load of data even if pane is hidden.
1651
	preload: false,
1652
 
1653
	// refreshOnShow: Boolean
1654
	//		Refresh (re-download) content when pane goes from hidden to shown
1655
	refreshOnShow: false,
1656
 
1657
	// loadingMessage: String
1658
	//	Message that shows while downloading
1659
	loadingMessage: "<span class='dijitContentPaneLoading'>${loadingState}</span>",
1660
 
1661
	// errorMessage: String
1662
	//	Message that shows if an error occurs
1663
	errorMessage: "<span class='dijitContentPaneError'>${errorState}</span>",
1664
 
1665
	// isLoaded: Boolean
1666
	//	Tells loading status see onLoad|onUnload for event hooks
1667
	isLoaded: false,
1668
 
1669
	// class: String
1670
	//	Class name to apply to ContentPane dom nodes
1671
	"class": "dijitContentPane",
1672
 
1673
	postCreate: function(){
1674
		// remove the title attribute so it doesn't show up when i hover
1675
		// over a node
1676
		this.domNode.title = "";
1677
 
1678
		if(this.preload){
1679
			this._loadCheck();
1680
		}
1681
 
1682
		var messages = dojo.i18n.getLocalization("dijit", "loading", this.lang);
1683
		this.loadingMessage = dojo.string.substitute(this.loadingMessage, messages);
1684
		this.errorMessage = dojo.string.substitute(this.errorMessage, messages);
1685
 
1686
		// for programatically created ContentPane (with <span> tag), need to muck w/CSS
1687
		// or it's as though overflow:visible is set
1688
		dojo.addClass(this.domNode, this["class"]);
1689
	},
1690
 
1691
	startup: function(){
1692
		if(this._started){ return; }
1693
		this._checkIfSingleChild();
1694
		if(this._singleChild){
1695
			this._singleChild.startup();
1696
		}
1697
		this._loadCheck();
1698
		this._started = true;
1699
	},
1700
 
1701
	_checkIfSingleChild: function(){
1702
		// summary:
1703
		// 	Test if we have exactly one widget as a child, and if so assume that we are a container for that widget,
1704
		//	and should propogate startup() and resize() calls to it.
1705
		var childNodes = dojo.query(">", this.containerNode || this.domNode),
1706
			childWidgets = childNodes.filter("[widgetId]");
1707
 
1708
		if(childNodes.length == 1 && childWidgets.length == 1){
1709
			this.isContainer = true;
1710
			this._singleChild = dijit.byNode(childWidgets[0]);
1711
		}else{
1712
			delete this.isContainer;
1713
			delete this._singleChild;
1714
		}
1715
	},
1716
 
1717
	refresh: function(){
1718
		// summary:
1719
		//	Force a refresh (re-download) of content, be sure to turn off cache
1720
 
1721
		// we return result of _prepareLoad here to avoid code dup. in dojox.layout.ContentPane
1722
		return this._prepareLoad(true);
1723
	},
1724
 
1725
	setHref: function(/*String|Uri*/ href){
1726
		// summary:
1727
		//		Reset the (external defined) content of this pane and replace with new url
1728
		//		Note: It delays the download until widget is shown if preload is false
1729
		//	href:
1730
		//		url to the page you want to get, must be within the same domain as your mainpage
1731
		this.href = href;
1732
 
1733
		// we return result of _prepareLoad here to avoid code dup. in dojox.layout.ContentPane
1734
		return this._prepareLoad();
1735
	},
1736
 
1737
	setContent: function(/*String|DomNode|Nodelist*/data){
1738
		// summary:
1739
		//		Replaces old content with data content, include style classes from old content
1740
		//	data:
1741
		//		the new Content may be String, DomNode or NodeList
1742
		//
1743
		//		if data is a NodeList (or an array of nodes) nodes are copied
1744
		//		so you can import nodes from another document implicitly
1745
 
1746
		// clear href so we cant run refresh and clear content
1747
		// refresh should only work if we downloaded the content
1748
		if(!this._isDownloaded){
1749
			this.href = "";
1750
			this._onUnloadHandler();
1751
		}
1752
 
1753
		this._setContent(data || "");
1754
 
1755
		this._isDownloaded = false; // must be set after _setContent(..), pathadjust in dojox.layout.ContentPane
1756
 
1757
		if(this.parseOnLoad){
1758
			this._createSubWidgets();
1759
		}
1760
 
1761
		this._checkIfSingleChild();
1762
		if(this._singleChild && this._singleChild.resize){
1763
			this._singleChild.resize(this._contentBox);
1764
		}
1765
 
1766
		this._onLoadHandler();
1767
	},
1768
 
1769
	cancel: function(){
1770
		// summary:
1771
		//		Cancels a inflight download of content
1772
		if(this._xhrDfd && (this._xhrDfd.fired == -1)){
1773
			this._xhrDfd.cancel();
1774
		}
1775
		delete this._xhrDfd; // garbage collect
1776
	},
1777
 
1778
	destroy: function(){
1779
		// if we have multiple controllers destroying us, bail after the first
1780
		if(this._beingDestroyed){
1781
			return;
1782
		}
1783
		// make sure we call onUnload
1784
		this._onUnloadHandler();
1785
		this._beingDestroyed = true;
1786
		this.inherited("destroy",arguments);
1787
	},
1788
 
1789
	resize: function(size){
1790
		dojo.marginBox(this.domNode, size);
1791
 
1792
		// Compute content box size in case we [later] need to size child
1793
		// If either height or width wasn't specified by the user, then query node for it.
1794
		// But note that setting the margin box and then immediately querying dimensions may return
1795
		// inaccurate results, so try not to depend on it.
1796
		var node = this.containerNode || this.domNode,
1797
			mb = dojo.mixin(dojo.marginBox(node), size||{});
1798
 
1799
		this._contentBox = dijit.layout.marginBox2contentBox(node, mb);
1800
 
1801
		// If we have a single widget child then size it to fit snugly within my borders
1802
		if(this._singleChild && this._singleChild.resize){
1803
			this._singleChild.resize(this._contentBox);
1804
		}
1805
	},
1806
 
1807
	_prepareLoad: function(forceLoad){
1808
		// sets up for a xhrLoad, load is deferred until widget onShow
1809
		// cancels a inflight download
1810
		this.cancel();
1811
		this.isLoaded = false;
1812
		this._loadCheck(forceLoad);
1813
	},
1814
 
1815
	_loadCheck: function(forceLoad){
1816
		// call this when you change onShow (onSelected) status when selected in parent container
1817
		// it's used as a trigger for href download when this.domNode.display != 'none'
1818
 
1819
		// sequence:
1820
		// if no href -> bail
1821
		// forceLoad -> always load
1822
		// this.preload -> load when download not in progress, domNode display doesn't matter
1823
		// this.refreshOnShow -> load when download in progress bails, domNode display !='none' AND
1824
		//						this.open !== false (undefined is ok), isLoaded doesn't matter
1825
		// else -> load when download not in progress, if this.open !== false (undefined is ok) AND
1826
		//						domNode display != 'none', isLoaded must be false
1827
 
1828
		var displayState = ((this.open !== false) && (this.domNode.style.display != 'none'));
1829
 
1830
		if(this.href &&
1831
			(forceLoad ||
1832
				(this.preload && !this._xhrDfd) ||
1833
				(this.refreshOnShow && displayState && !this._xhrDfd) ||
1834
				(!this.isLoaded && displayState && !this._xhrDfd)
1835
			)
1836
		){
1837
			this._downloadExternalContent();
1838
		}
1839
	},
1840
 
1841
	_downloadExternalContent: function(){
1842
		this._onUnloadHandler();
1843
 
1844
		// display loading message
1845
		this._setContent(
1846
			this.onDownloadStart.call(this)
1847
		);
1848
 
1849
		var self = this;
1850
		var getArgs = {
1851
			preventCache: (this.preventCache || this.refreshOnShow),
1852
			url: this.href,
1853
			handleAs: "text"
1854
		};
1855
		if(dojo.isObject(this.ioArgs)){
1856
			dojo.mixin(getArgs, this.ioArgs);
1857
		}
1858
 
1859
		var hand = this._xhrDfd = (this.ioMethod || dojo.xhrGet)(getArgs);
1860
 
1861
		hand.addCallback(function(html){
1862
			try{
1863
				self.onDownloadEnd.call(self);
1864
				self._isDownloaded = true;
1865
				self.setContent.call(self, html); // onload event is called from here
1866
			}catch(err){
1867
				self._onError.call(self, 'Content', err); // onContentError
1868
			}
1869
			delete self._xhrDfd;
1870
			return html;
1871
		});
1872
 
1873
		hand.addErrback(function(err){
1874
			if(!hand.cancelled){
1875
				// show error message in the pane
1876
				self._onError.call(self, 'Download', err); // onDownloadError
1877
			}
1878
			delete self._xhrDfd;
1879
			return err;
1880
		});
1881
	},
1882
 
1883
	_onLoadHandler: function(){
1884
		this.isLoaded = true;
1885
		try{
1886
			this.onLoad.call(this);
1887
		}catch(e){
1888
			console.error('Error '+this.widgetId+' running custom onLoad code');
1889
		}
1890
	},
1891
 
1892
	_onUnloadHandler: function(){
1893
		this.isLoaded = false;
1894
		this.cancel();
1895
		try{
1896
			this.onUnload.call(this);
1897
		}catch(e){
1898
			console.error('Error '+this.widgetId+' running custom onUnload code');
1899
		}
1900
	},
1901
 
1902
	_setContent: function(cont){
1903
		this.destroyDescendants();
1904
 
1905
		try{
1906
			var node = this.containerNode || this.domNode;
1907
			while(node.firstChild){
1908
				dojo._destroyElement(node.firstChild);
1909
			}
1910
			if(typeof cont == "string"){
1911
				// dijit.ContentPane does only minimal fixes,
1912
				// No pathAdjustments, script retrieval, style clean etc
1913
				// some of these should be available in the dojox.layout.ContentPane
1914
				if(this.extractContent){
1915
					match = cont.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
1916
					if(match){ cont = match[1]; }
1917
				}
1918
				node.innerHTML = cont;
1919
			}else{
1920
				// domNode or NodeList
1921
				if(cont.nodeType){ // domNode (htmlNode 1 or textNode 3)
1922
					node.appendChild(cont);
1923
				}else{// nodelist or array such as dojo.Nodelist
1924
					dojo.forEach(cont, function(n){
1925
						node.appendChild(n.cloneNode(true));
1926
					});
1927
				}
1928
			}
1929
		}catch(e){
1930
			// check if a domfault occurs when we are appending this.errorMessage
1931
			// like for instance if domNode is a UL and we try append a DIV
1932
			var errMess = this.onContentError(e);
1933
			try{
1934
				node.innerHTML = errMess;
1935
			}catch(e){
1936
				console.error('Fatal '+this.id+' could not change content due to '+e.message, e);
1937
			}
1938
		}
1939
	},
1940
 
1941
	_onError: function(type, err, consoleText){
1942
		// shows user the string that is returned by on[type]Error
1943
		// overide on[type]Error and return your own string to customize
1944
		var errText = this['on' + type + 'Error'].call(this, err);
1945
		if(consoleText){
1946
			console.error(consoleText, err);
1947
		}else if(errText){// a empty string won't change current content
1948
			this._setContent.call(this, errText);
1949
		}
1950
	},
1951
 
1952
	_createSubWidgets: function(){
1953
		// summary: scan my contents and create subwidgets
1954
		var rootNode = this.containerNode || this.domNode;
1955
		try{
1956
			dojo.parser.parse(rootNode, true);
1957
		}catch(e){
1958
			this._onError('Content', e, "Couldn't create widgets in "+this.id
1959
				+(this.href ? " from "+this.href : ""));
1960
		}
1961
	},
1962
 
1963
	// EVENT's, should be overide-able
1964
	onLoad: function(e){
1965
		// summary:
1966
		//		Event hook, is called after everything is loaded and widgetified
1967
	},
1968
 
1969
	onUnload: function(e){
1970
		// summary:
1971
		//		Event hook, is called before old content is cleared
1972
	},
1973
 
1974
	onDownloadStart: function(){
1975
		// summary:
1976
		//		called before download starts
1977
		//		the string returned by this function will be the html
1978
		//		that tells the user we are loading something
1979
		//		override with your own function if you want to change text
1980
		return this.loadingMessage;
1981
	},
1982
 
1983
	onContentError: function(/*Error*/ error){
1984
		// summary:
1985
		//		called on DOM faults, require fault etc in content
1986
		//		default is to display errormessage inside pane
1987
	},
1988
 
1989
	onDownloadError: function(/*Error*/ error){
1990
		// summary:
1991
		//		Called when download error occurs, default is to display
1992
		//		errormessage inside pane. Overide function to change that.
1993
		//		The string returned by this function will be the html
1994
		//		that tells the user a error happend
1995
		return this.errorMessage;
1996
	},
1997
 
1998
	onDownloadEnd: function(){
1999
		// summary:
2000
		//		called when download is finished
2001
	}
2002
});
2003
 
2004
}
2005
 
2006
if(!dojo._hasResource["dijit.form.Form"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2007
dojo._hasResource["dijit.form.Form"] = true;
2008
dojo.provide("dijit.form.Form");
2009
 
2010
 
2011
 
2012
 
2013
dojo.declare("dijit.form._FormMixin", null,
2014
	{
2015
		/*
2016
		summary:
2017
			Widget corresponding to <form> tag, for validation and serialization
2018
 
2019
		usage:
2020
			<form dojoType="dijit.form.Form" id="myForm">
2021
				Name: <input type="text" name="name" />
2022
			</form>
2023
			myObj={name: "John Doe"};
2024
			dijit.byId('myForm').setValues(myObj);
2025
 
2026
			myObj=dijit.byId('myForm').getValues();
2027
		TODO:
2028
		* Repeater
2029
		* better handling for arrays.  Often form elements have names with [] like
2030
		* people[3].sex (for a list of people [{name: Bill, sex: M}, ...])
2031
 
2032
		*/
2033
 
2034
		// HTML <FORM> attributes
2035
 
2036
		action: "",
2037
		method: "",
2038
		enctype: "",
2039
		name: "",
2040
		"accept-charset": "",
2041
		accept: "",
2042
		target: "",
2043
 
2044
		attributeMap: dojo.mixin(dojo.clone(dijit._Widget.prototype.attributeMap),
2045
			{action: "", method: "", enctype: "", "accept-charset": "", accept: "", target: ""}),
2046
 
2047
		// execute: Function
2048
		//	User defined function to do stuff when the user hits the submit button
2049
		execute: function(/*Object*/ formContents){},
2050
 
2051
		// onCancel: Function
2052
		//	Callback when user has canceled dialog, to notify container
2053
		//	(user shouldn't override)
2054
		onCancel: function(){},
2055
 
2056
		// onExecute: Function
2057
		//	Callback when user is about to execute dialog, to notify container
2058
		//	(user shouldn't override)
2059
		onExecute: function(){},
2060
 
2061
		templateString: "<form dojoAttachPoint='containerNode' dojoAttachEvent='onsubmit:_onSubmit' name='${name}' enctype='multipart/form-data'></form>",
2062
 
2063
		_onSubmit: function(/*event*/e) {
2064
			// summary: callback when user hits submit button
2065
			dojo.stopEvent(e);
2066
			this.onExecute();	// notify container that we are about to execute
2067
			this.execute(this.getValues());
2068
		},
2069
 
2070
		submit: function() {
2071
			// summary: programatically submit form
2072
			this.containerNode.submit();
2073
		},
2074
 
2075
		setValues: function(/*object*/obj) {
2076
			// summary: fill in form values from a JSON structure
2077
 
2078
			// generate map from name --> [list of widgets with that name]
2079
			var map = {};
2080
			dojo.forEach(this.getDescendants(), function(widget){
2081
				if(!widget.name){ return; }
2082
				var entry = map[widget.name] || (map[widget.name] = [] );
2083
				entry.push(widget);
2084
			});
2085
 
2086
			// call setValue() or setChecked() for each widget, according to obj
2087
			for(var name in map){
2088
				var widgets = map[name],						// array of widgets w/this name
2089
					values = dojo.getObject(name, false, obj);	// list of values for those widgets
2090
				if(!dojo.isArray(values)){
2091
					values = [ values ];
2092
				}
2093
				if(widgets[0].setChecked){
2094
					// for checkbox/radio, values is a list of which widgets should be checked
2095
					dojo.forEach(widgets, function(w, i){
2096
						w.setChecked(dojo.indexOf(values, w.value) != -1);
2097
					});
2098
				}else{
2099
					// otherwise, values is a list of values to be assigned sequentially to each widget
2100
					dojo.forEach(widgets, function(w, i){
2101
						w.setValue(values[i]);
2102
					});
2103
				}
2104
			}
2105
 
2106
			/***
2107
			 * 	TODO: code for plain input boxes (this shouldn't run for inputs that are part of widgets
2108
 
2109
			dojo.forEach(this.containerNode.elements, function(element){
2110
				if (element.name == ''){return};	// like "continue"
2111
				var namePath = element.name.split(".");
2112
				var myObj=obj;
2113
				var name=namePath[namePath.length-1];
2114
				for(var j=1,len2=namePath.length;j<len2;++j) {
2115
					var p=namePath[j - 1];
2116
					// repeater support block
2117
					var nameA=p.split("[");
2118
					if (nameA.length > 1) {
2119
						if(typeof(myObj[nameA[0]]) == "undefined") {
2120
							myObj[nameA[0]]=[ ];
2121
						} // if
2122
 
2123
						nameIndex=parseInt(nameA[1]);
2124
						if(typeof(myObj[nameA[0]][nameIndex]) == "undefined") {
2125
							myObj[nameA[0]][nameIndex]={};
2126
						}
2127
						myObj=myObj[nameA[0]][nameIndex];
2128
						continue;
2129
					} // repeater support ends
2130
 
2131
					if(typeof(myObj[p]) == "undefined") {
2132
						myObj=undefined;
2133
						break;
2134
					};
2135
					myObj=myObj[p];
2136
				}
2137
 
2138
				if (typeof(myObj) == "undefined") {
2139
					return;		// like "continue"
2140
				}
2141
				if (typeof(myObj[name]) == "undefined" && this.ignoreNullValues) {
2142
					return;		// like "continue"
2143
				}
2144
 
2145
				// TODO: widget values (just call setValue() on the widget)
2146
 
2147
				switch(element.type) {
2148
					case "checkbox":
2149
						element.checked = (name in myObj) &&
2150
							dojo.some(myObj[name], function(val){ return val==element.value; });
2151
						break;
2152
					case "radio":
2153
						element.checked = (name in myObj) && myObj[name]==element.value;
2154
						break;
2155
					case "select-multiple":
2156
						element.selectedIndex=-1;
2157
						dojo.forEach(element.options, function(option){
2158
							option.selected = dojo.some(myObj[name], function(val){ return option.value == val; });
2159
						});
2160
						break;
2161
					case "select-one":
2162
						element.selectedIndex="0";
2163
						dojo.forEach(element.options, function(option){
2164
							option.selected = option.value == myObj[name];
2165
						});
2166
						break;
2167
					case "hidden":
2168
					case "text":
2169
					case "textarea":
2170
					case "password":
2171
						element.value = myObj[name] || "";
2172
						break;
2173
				}
2174
	  		});
2175
	  		*/
2176
		},
2177
 
2178
		getValues: function() {
2179
			// summary: generate JSON structure from form values
2180
 
2181
			// get widget values
2182
			var obj = {};
2183
			dojo.forEach(this.getDescendants(), function(widget){
2184
				var value = widget.getValue ? widget.getValue() : widget.value;
2185
				var name = widget.name;
2186
				if(!name){ return; }
2187
 
2188
				// Store widget's value(s) as a scalar, except for checkboxes which are automatically arrays
2189
				if(widget.setChecked){
2190
					if(/Radio/.test(widget.declaredClass)){
2191
						// radio button
2192
						if(widget.checked){
2193
							dojo.setObject(name, value, obj);
2194
						}
2195
					}else{
2196
						// checkbox/toggle button
2197
						var ary=dojo.getObject(name, false, obj);
2198
						if(!ary){
2199
							ary=[];
2200
							dojo.setObject(name, ary, obj);
2201
						}
2202
						if(widget.checked){
2203
							ary.push(value);
2204
						}
2205
					}
2206
				}else{
2207
					// plain input
2208
					dojo.setObject(name, value, obj);
2209
				}
2210
			});
2211
 
2212
			/***
2213
			 * code for plain input boxes (see also dojo.formToObject, can we use that instead of this code?
2214
			 * but it doesn't understand [] notation, presumably)
2215
			var obj = { };
2216
			dojo.forEach(this.containerNode.elements, function(elm){
2217
				if (!elm.name)	{
2218
					return;		// like "continue"
2219
				}
2220
				var namePath = elm.name.split(".");
2221
				var myObj=obj;
2222
				var name=namePath[namePath.length-1];
2223
				for(var j=1,len2=namePath.length;j<len2;++j) {
2224
					var nameIndex = null;
2225
					var p=namePath[j - 1];
2226
					var nameA=p.split("[");
2227
					if (nameA.length > 1) {
2228
						if(typeof(myObj[nameA[0]]) == "undefined") {
2229
							myObj[nameA[0]]=[ ];
2230
						} // if
2231
						nameIndex=parseInt(nameA[1]);
2232
						if(typeof(myObj[nameA[0]][nameIndex]) == "undefined") {
2233
							myObj[nameA[0]][nameIndex]={};
2234
						}
2235
					} else if(typeof(myObj[nameA[0]]) == "undefined") {
2236
						myObj[nameA[0]]={}
2237
					} // if
2238
 
2239
					if (nameA.length == 1) {
2240
						myObj=myObj[nameA[0]];
2241
					} else {
2242
						myObj=myObj[nameA[0]][nameIndex];
2243
					} // if
2244
				} // for
2245
 
2246
				if ((elm.type != "select-multiple" && elm.type != "checkbox" && elm.type != "radio") || (elm.type=="radio" && elm.checked)) {
2247
					if(name == name.split("[")[0]) {
2248
						myObj[name]=elm.value;
2249
					} else {
2250
						// can not set value when there is no name
2251
					}
2252
				} else if (elm.type == "checkbox" && elm.checked) {
2253
					if(typeof(myObj[name]) == 'undefined') {
2254
						myObj[name]=[ ];
2255
					}
2256
					myObj[name].push(elm.value);
2257
				} else if (elm.type == "select-multiple") {
2258
					if(typeof(myObj[name]) == 'undefined') {
2259
						myObj[name]=[ ];
2260
					}
2261
					for (var jdx=0,len3=elm.options.length; jdx<len3; ++jdx) {
2262
						if (elm.options[jdx].selected) {
2263
							myObj[name].push(elm.options[jdx].value);
2264
						}
2265
					}
2266
				} // if
2267
				name=undefined;
2268
			}); // forEach
2269
			***/
2270
			return obj;
2271
		},
2272
 
2273
	 	isValid: function() {
2274
	 		// TODO: ComboBox might need time to process a recently input value.  This should be async?
2275
	 		// make sure that every widget that has a validator function returns true
2276
	 		return dojo.every(this.getDescendants(), function(widget){
2277
	 			return !widget.isValid || widget.isValid();
2278
	 		});
2279
		}
2280
	});
2281
 
2282
dojo.declare(
2283
	"dijit.form.Form",
2284
	[dijit._Widget, dijit._Templated, dijit.form._FormMixin],
2285
	null
2286
);
2287
 
2288
}
2289
 
2290
if(!dojo._hasResource["dijit.Dialog"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2291
dojo._hasResource["dijit.Dialog"] = true;
2292
dojo.provide("dijit.Dialog");
2293
 
2294
 
2295
 
2296
 
2297
 
2298
 
2299
 
2300
 
2301
 
2302
dojo.declare(
2303
	"dijit.DialogUnderlay",
2304
	[dijit._Widget, dijit._Templated],
2305
	{
2306
		// summary: the thing that grays out the screen behind the dialog
2307
 
2308
		// Template has two divs; outer div is used for fade-in/fade-out, and also to hold background iframe.
2309
		// Inner div has opacity specified in CSS file.
2310
		templateString: "<div class=dijitDialogUnderlayWrapper id='${id}_underlay'><div class=dijitDialogUnderlay dojoAttachPoint='node'></div></div>",
2311
 
2312
		postCreate: function(){
2313
			dojo.body().appendChild(this.domNode);
2314
			this.bgIframe = new dijit.BackgroundIframe(this.domNode);
2315
		},
2316
 
2317
		layout: function(){
2318
			// summary
2319
			//		Sets the background to the size of the viewport (rather than the size
2320
			//		of the document) since we need to cover the whole browser window, even
2321
			//		if the document is only a few lines long.
2322
 
2323
			var viewport = dijit.getViewport();
2324
			var is = this.node.style,
2325
				os = this.domNode.style;
2326
 
2327
			os.top = viewport.t + "px";
2328
			os.left = viewport.l + "px";
2329
			is.width = viewport.w + "px";
2330
			is.height = viewport.h + "px";
2331
 
2332
			// process twice since the scroll bar may have been removed
2333
			// by the previous resizing
2334
			var viewport2 = dijit.getViewport();
2335
			if(viewport.w != viewport2.w){ is.width = viewport2.w + "px"; }
2336
			if(viewport.h != viewport2.h){ is.height = viewport2.h + "px"; }
2337
		},
2338
 
2339
		show: function(){
2340
			this.domNode.style.display = "block";
2341
			this.layout();
2342
			if(this.bgIframe.iframe){
2343
				this.bgIframe.iframe.style.display = "block";
2344
			}
2345
			this._resizeHandler = this.connect(window, "onresize", "layout");
2346
		},
2347
 
2348
		hide: function(){
2349
			this.domNode.style.display = "none";
2350
			if(this.bgIframe.iframe){
2351
				this.bgIframe.iframe.style.display = "none";
2352
			}
2353
			this.disconnect(this._resizeHandler);
2354
		},
2355
 
2356
		uninitialize: function(){
2357
			if(this.bgIframe){
2358
				this.bgIframe.destroy();
2359
			}
2360
		}
2361
	}
2362
);
2363
 
2364
dojo.declare(
2365
	"dijit.Dialog",
2366
	[dijit.layout.ContentPane, dijit._Templated, dijit.form._FormMixin],
2367
	{
2368
		// summary:
2369
		//		Pops up a modal dialog window, blocking access to the screen
2370
		//		and also graying out the screen Dialog is extended from
2371
		//		ContentPane so it supports all the same parameters (href, etc.)
2372
 
2373
		templateString: null,
2374
		templateString:"<div class=\"dijitDialog\">\n\t<div dojoAttachPoint=\"titleBar\" class=\"dijitDialogTitleBar\" tabindex=\"0\" waiRole=\"dialog\">\n\t<span dojoAttachPoint=\"titleNode\" class=\"dijitDialogTitle\">${title}</span>\n\t<span dojoAttachPoint=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" dojoAttachEvent=\"onclick: hide\">\n\t\t<span dojoAttachPoint=\"closeText\" class=\"closeText\">x</span>\n\t</span>\n\t</div>\n\t\t<div dojoAttachPoint=\"containerNode\" class=\"dijitDialogPaneContent\"></div>\n\t<span dojoAttachPoint=\"tabEnd\" dojoAttachEvent=\"onfocus:_cycleFocus\" tabindex=\"0\"></span>\n</div>\n",
2375
 
2376
		// open: Boolean
2377
		//		is True or False depending on state of dialog
2378
		open: false,
2379
 
2380
		// duration: Integer
2381
		//		The time in milliseconds it takes the dialog to fade in and out
2382
		duration: 400,
2383
 
2384
		_lastFocusItem:null,
2385
 
2386
		attributeMap: dojo.mixin(dojo.clone(dijit._Widget.prototype.attributeMap),
2387
			{title: "titleBar"}),
2388
 
2389
		postCreate: function(){
2390
			dojo.body().appendChild(this.domNode);
2391
			this.inherited("postCreate",arguments);
2392
			this.domNode.style.display="none";
2393
			this.connect(this, "onExecute", "hide");
2394
			this.connect(this, "onCancel", "hide");
2395
		},
2396
 
2397
		onLoad: function(){
2398
			// summary:
2399
			//		when href is specified we need to reposition the dialog after the data is loaded
2400
			this._position();
2401
			this.inherited("onLoad",arguments);
2402
		},
2403
 
2404
		_setup: function(){
2405
			// summary:
2406
			//		stuff we need to do before showing the Dialog for the first
2407
			//		time (but we defer it until right beforehand, for
2408
			//		performance reasons)
2409
 
2410
			this._modalconnects = [];
2411
 
2412
			if(this.titleBar){
2413
				this._moveable = new dojo.dnd.Moveable(this.domNode, { handle: this.titleBar });
2414
			}
2415
 
2416
			this._underlay = new dijit.DialogUnderlay();
2417
 
2418
			var node = this.domNode;
2419
			this._fadeIn = dojo.fx.combine(
2420
				[dojo.fadeIn({
2421
					node: node,
2422
					duration: this.duration
2423
				 }),
2424
				 dojo.fadeIn({
2425
					node: this._underlay.domNode,
2426
					duration: this.duration,
2427
					onBegin: dojo.hitch(this._underlay, "show")
2428
				 })
2429
				]
2430
			);
2431
 
2432
			this._fadeOut = dojo.fx.combine(
2433
				[dojo.fadeOut({
2434
					node: node,
2435
					duration: this.duration,
2436
					onEnd: function(){
2437
						node.style.display="none";
2438
					}
2439
				 }),
2440
				 dojo.fadeOut({
2441
					node: this._underlay.domNode,
2442
					duration: this.duration,
2443
					onEnd: dojo.hitch(this._underlay, "hide")
2444
				 })
2445
				]
2446
			);
2447
		},
2448
 
2449
		uninitialize: function(){
2450
			if(this._underlay){
2451
				this._underlay.destroy();
2452
			}
2453
		},
2454
 
2455
		_position: function(){
2456
			// summary: position modal dialog in center of screen
2457
 
2458
			if(dojo.hasClass(dojo.body(),"dojoMove")){ return; }
2459
			var viewport = dijit.getViewport();
2460
			var mb = dojo.marginBox(this.domNode);
2461
 
2462
			var style = this.domNode.style;
2463
			style.left = Math.floor((viewport.l + (viewport.w - mb.w)/2)) + "px";
2464
			style.top = Math.floor((viewport.t + (viewport.h - mb.h)/2)) + "px";
2465
		},
2466
 
2467
		_findLastFocus: function(/*Event*/ evt){
2468
			// summary:  called from onblur of dialog container to determine the last focusable item
2469
			this._lastFocused = evt.target;
2470
		},
2471
 
2472
		_cycleFocus: function(/*Event*/ evt){
2473
			// summary: when tabEnd receives focus, advance focus around to titleBar
2474
 
2475
			// on first focus to tabEnd, store the last focused item in dialog
2476
			if(!this._lastFocusItem){
2477
				this._lastFocusItem = this._lastFocused;
2478
			}
2479
			this.titleBar.focus();
2480
		},
2481
 
2482
		_onKey: function(/*Event*/ evt){
2483
			if(evt.keyCode){
2484
				var node = evt.target;
2485
				// see if we are shift-tabbing from titleBar
2486
				if(node == this.titleBar && evt.shiftKey && evt.keyCode == dojo.keys.TAB){
2487
					if(this._lastFocusItem){
2488
						this._lastFocusItem.focus(); // send focus to last item in dialog if known
2489
					}
2490
					dojo.stopEvent(evt);
2491
				}else{
2492
					// see if the key is for the dialog
2493
					while(node){
2494
						if(node == this.domNode){
2495
							if(evt.keyCode == dojo.keys.ESCAPE){
2496
								this.hide();
2497
							}else{
2498
								return; // just let it go
2499
							}
2500
						}
2501
						node = node.parentNode;
2502
					}
2503
					// this key is for the disabled document window
2504
					if(evt.keyCode != dojo.keys.TAB){ // allow tabbing into the dialog for a11y
2505
						dojo.stopEvent(evt);
2506
					// opera won't tab to a div
2507
					}else if (!dojo.isOpera){
2508
						try{
2509
							this.titleBar.focus();
2510
						}catch(e){/*squelch*/}
2511
					}
2512
				}
2513
			}
2514
		},
2515
 
2516
		show: function(){
2517
			// summary: display the dialog
2518
 
2519
			// first time we show the dialog, there's some initialization stuff to do
2520
			if(!this._alreadyInitialized){
2521
				this._setup();
2522
				this._alreadyInitialized=true;
2523
			}
2524
 
2525
			if(this._fadeOut.status() == "playing"){
2526
				this._fadeOut.stop();
2527
			}
2528
 
2529
			this._modalconnects.push(dojo.connect(window, "onscroll", this, "layout"));
2530
			this._modalconnects.push(dojo.connect(document.documentElement, "onkeypress", this, "_onKey"));
2531
 
2532
			// IE doesn't bubble onblur events - use ondeactivate instead
2533
			var ev = typeof(document.ondeactivate) == "object" ? "ondeactivate" : "onblur";
2534
			this._modalconnects.push(dojo.connect(this.containerNode, ev, this, "_findLastFocus"));
2535
 
2536
			dojo.style(this.domNode, "opacity", 0);
2537
			this.domNode.style.display="block";
2538
			this.open = true;
2539
			this._loadCheck(); // lazy load trigger
2540
 
2541
			this._position();
2542
 
2543
			this._fadeIn.play();
2544
 
2545
			this._savedFocus = dijit.getFocus(this);
2546
 
2547
			// set timeout to allow the browser to render dialog
2548
			setTimeout(dojo.hitch(this, function(){
2549
				dijit.focus(this.titleBar);
2550
			}), 50);
2551
		},
2552
 
2553
		hide: function(){
2554
			// summary
2555
			//		Hide the dialog
2556
 
2557
			// if we haven't been initialized yet then we aren't showing and we can just return
2558
			if(!this._alreadyInitialized){
2559
				return;
2560
			}
2561
 
2562
			if(this._fadeIn.status() == "playing"){
2563
				this._fadeIn.stop();
2564
			}
2565
			this._fadeOut.play();
2566
 
2567
			if (this._scrollConnected){
2568
				this._scrollConnected = false;
2569
			}
2570
			dojo.forEach(this._modalconnects, dojo.disconnect);
2571
			this._modalconnects = [];
2572
 
2573
			this.connect(this._fadeOut,"onEnd",dojo.hitch(this,function(){
2574
				dijit.focus(this._savedFocus);
2575
			}));
2576
			this.open = false;
2577
		},
2578
 
2579
		layout: function() {
2580
			// summary: position the Dialog and the underlay
2581
			if(this.domNode.style.display == "block"){
2582
				this._underlay.layout();
2583
				this._position();
2584
			}
2585
		}
2586
	}
2587
);
2588
 
2589
dojo.declare(
2590
	"dijit.TooltipDialog",
2591
	[dijit.layout.ContentPane, dijit._Templated, dijit.form._FormMixin],
2592
	{
2593
		// summary:
2594
		//		Pops up a dialog that appears like a Tooltip
2595
		// title: String
2596
		// 		Description of tooltip dialog (required for a11Y)
2597
		title: "",
2598
 
2599
		_lastFocusItem: null,
2600
 
2601
		templateString: null,
2602
		templateString:"<div class=\"dijitTooltipDialog\" >\n\t<div class=\"dijitTooltipContainer\">\n\t\t<div class =\"dijitTooltipContents dijitTooltipFocusNode\" dojoAttachPoint=\"containerNode\" tabindex=\"0\" waiRole=\"dialog\"></div>\n\t</div>\n\t<span dojoAttachPoint=\"tabEnd\" tabindex=\"0\" dojoAttachEvent=\"focus:_cycleFocus\"></span>\n\t<div class=\"dijitTooltipConnector\" ></div>\n</div>\n",
2603
 
2604
		postCreate: function(){
2605
			this.inherited("postCreate",arguments);
2606
			this.connect(this.containerNode, "onkeypress", "_onKey");
2607
 
2608
			// IE doesn't bubble onblur events - use ondeactivate instead
2609
			var ev = typeof(document.ondeactivate) == "object" ? "ondeactivate" : "onblur";
2610
			this.connect(this.containerNode, ev, "_findLastFocus");
2611
			this.containerNode.title=this.title;
2612
		},
2613
 
2614
		orient: function(/*Object*/ corner){
2615
			// summary: configure widget to be displayed in given position relative to the button
2616
			this.domNode.className="dijitTooltipDialog " +" dijitTooltipAB"+(corner.charAt(1)=='L'?"Left":"Right")+" dijitTooltip"+(corner.charAt(0)=='T' ? "Below" : "Above");
2617
		},
2618
 
2619
		onOpen: function(/*Object*/ pos){
2620
			// summary: called when dialog is displayed
2621
			this.orient(pos.corner);
2622
			this._loadCheck(); // lazy load trigger
2623
			this.containerNode.focus();
2624
		},
2625
 
2626
		_onKey: function(/*Event*/ evt){
2627
			// summary: keep keyboard focus in dialog; close dialog on escape key
2628
			if(evt.keyCode == dojo.keys.ESCAPE){
2629
				this.onCancel();
2630
			}else if(evt.target == this.containerNode && evt.shiftKey && evt.keyCode == dojo.keys.TAB){
2631
				if (this._lastFocusItem){
2632
					this._lastFocusItem.focus();
2633
				}
2634
				dojo.stopEvent(evt);
2635
			}else if(evt.keyCode == dojo.keys.TAB){
2636
				// we want the browser's default tab handling to move focus
2637
				// but we don't want the tab to propagate upwards
2638
				evt.stopPropagation();
2639
			}
2640
		},
2641
 
2642
		_findLastFocus: function(/*Event*/ evt){
2643
			// summary: called from onblur of dialog container to determine the last focusable item
2644
			this._lastFocused = evt.target;
2645
		},
2646
 
2647
		_cycleFocus: function(/*Event*/ evt){
2648
			// summary: when tabEnd receives focus, advance focus around to containerNode
2649
 
2650
			// on first focus to tabEnd, store the last focused item in dialog
2651
			if(!this._lastFocusItem){
2652
				this._lastFocusItem = this._lastFocused;
2653
			}
2654
			this.containerNode.focus();
2655
		}
2656
	}
2657
);
2658
 
2659
 
2660
}
2661
 
2662
if(!dojo._hasResource["dijit._editor.selection"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2663
dojo._hasResource["dijit._editor.selection"] = true;
2664
dojo.provide("dijit._editor.selection");
2665
 
2666
// FIXME:
2667
//		all of these methods branch internally for IE. This is probably
2668
//		sub-optimal in terms of runtime performance. We should investigate the
2669
//		size difference for differentiating at definition time.
2670
 
2671
dojo.mixin(dijit._editor.selection, {
2672
	getType: function(){
2673
		// summary: Get the selection type (like document.select.type in IE).
2674
		if(dojo.doc["selection"]){ //IE
2675
			return dojo.doc.selection.type.toLowerCase();
2676
		}else{
2677
			var stype = "text";
2678
 
2679
			// Check if the actual selection is a CONTROL (IMG, TABLE, HR, etc...).
2680
			var oSel;
2681
			try{
2682
				oSel = dojo.global.getSelection();
2683
			}catch(e){ /*squelch*/ }
2684
 
2685
			if(oSel && oSel.rangeCount==1){
2686
				var oRange = oSel.getRangeAt(0);
2687
				if(	(oRange.startContainer == oRange.endContainer) &&
2688
					((oRange.endOffset - oRange.startOffset) == 1) &&
2689
					(oRange.startContainer.nodeType != 3 /* text node*/)
2690
				){
2691
					stype = "control";
2692
				}
2693
			}
2694
			return stype;
2695
		}
2696
	},
2697
 
2698
	getSelectedText: function(){
2699
		// summary:
2700
		//		Return the text (no html tags) included in the current selection or null if no text is selected
2701
		if(dojo.doc["selection"]){ //IE
2702
			if(dijit._editor.selection.getType() == 'control'){
2703
				return null;
2704
			}
2705
			return dojo.doc.selection.createRange().text;
2706
		}else{
2707
			var selection = dojo.global.getSelection();
2708
			if(selection){
2709
				return selection.toString();
2710
			}
2711
		}
2712
	},
2713
 
2714
	getSelectedHtml: function(){
2715
		// summary:
2716
		//		Return the html of the current selection or null if unavailable
2717
		if(dojo.doc["selection"]){ //IE
2718
			if(dijit._editor.selection.getType() == 'control'){
2719
				return null;
2720
			}
2721
			return dojo.doc.selection.createRange().htmlText;
2722
		}else{
2723
			var selection = dojo.global.getSelection();
2724
			if(selection && selection.rangeCount){
2725
				var frag = selection.getRangeAt(0).cloneContents();
2726
				var div = document.createElement("div");
2727
				div.appendChild(frag);
2728
				return div.innerHTML;
2729
			}
2730
			return null;
2731
		}
2732
	},
2733
 
2734
	getSelectedElement: function(){
2735
		// summary:
2736
		//		Retrieves the selected element (if any), just in the case that
2737
		//		a single element (object like and image or a table) is
2738
		//		selected.
2739
		if(this.getType() == "control"){
2740
			if(dojo.doc["selection"]){ //IE
2741
				var range = dojo.doc.selection.createRange();
2742
				if(range && range.item){
2743
					return dojo.doc.selection.createRange().item(0);
2744
				}
2745
			}else{
2746
				var selection = dojo.global.getSelection();
2747
				return selection.anchorNode.childNodes[ selection.anchorOffset ];
2748
			}
2749
		}
2750
	},
2751
 
2752
	getParentElement: function(){
2753
		// summary:
2754
		//		Get the parent element of the current selection
2755
		if(this.getType() == "control"){
2756
			var p = this.getSelectedElement();
2757
			if(p){ return p.parentNode; }
2758
		}else{
2759
			if(dojo.doc["selection"]){ //IE
2760
				return dojo.doc.selection.createRange().parentElement();
2761
			}else{
2762
				var selection = dojo.global.getSelection();
2763
				if(selection){
2764
					var node = selection.anchorNode;
2765
 
2766
					while(node && (node.nodeType != 1)){ // not an element
2767
						node = node.parentNode;
2768
					}
2769
 
2770
					return node;
2771
				}
2772
			}
2773
		}
2774
	},
2775
 
2776
	hasAncestorElement: function(/*String*/tagName /* ... */){
2777
		// summary:
2778
		// 		Check whether current selection has a  parent element which is
2779
		// 		of type tagName (or one of the other specified tagName)
2780
		return (this.getAncestorElement.apply(this, arguments) != null);
2781
	},
2782
 
2783
	getAncestorElement: function(/*String*/tagName /* ... */){
2784
		// summary:
2785
		//		Return the parent element of the current selection which is of
2786
		//		type tagName (or one of the other specified tagName)
2787
 
2788
		var node = this.getSelectedElement() || this.getParentElement();
2789
		return this.getParentOfType(node, arguments);
2790
	},
2791
 
2792
	isTag: function(/*DomNode*/node, /*Array*/tags){
2793
		if(node && node.tagName){
2794
			var _nlc = node.tagName.toLowerCase();
2795
			for(var i=0; i<tags.length; i++){
2796
				var _tlc = String(tags[i]).toLowerCase();
2797
				if(_nlc == _tlc){
2798
					return _tlc;
2799
				}
2800
			}
2801
		}
2802
		return "";
2803
	},
2804
 
2805
	getParentOfType: function(/*DomNode*/node, /*Array*/tags){
2806
		while(node){
2807
			if(this.isTag(node, tags).length){
2808
				return node;
2809
			}
2810
			node = node.parentNode;
2811
		}
2812
		return null;
2813
	},
2814
 
2815
	remove: function(){
2816
		// summary: delete current selection
2817
		var _s = dojo.doc.selection;
2818
		if(_s){ //IE
2819
			if(_s.type.toLowerCase() != "none"){
2820
				_s.clear();
2821
			}
2822
			return _s;
2823
		}else{
2824
			_s = dojo.global.getSelection();
2825
			_s.deleteFromDocument();
2826
			return _s;
2827
		}
2828
	},
2829
 
2830
	selectElementChildren: function(/*DomNode*/element,/*Boolean?*/nochangefocus){
2831
		// summary:
2832
		//		clear previous selection and select the content of the node
2833
		//		(excluding the node itself)
2834
		var _window = dojo.global;
2835
		var _document = dojo.doc;
2836
		element = dojo.byId(element);
2837
		if(_document.selection && dojo.body().createTextRange){ // IE
2838
			var range = element.ownerDocument.body.createTextRange();
2839
			range.moveToElementText(element);
2840
			if(!nochangefocus){
2841
				range.select();
2842
			}
2843
		}else if(_window["getSelection"]){
2844
			var selection = _window.getSelection();
2845
			if(selection["setBaseAndExtent"]){ // Safari
2846
				selection.setBaseAndExtent(element, 0, element, element.innerText.length - 1);
2847
			}else if(selection["selectAllChildren"]){ // Mozilla
2848
				selection.selectAllChildren(element);
2849
			}
2850
		}
2851
	},
2852
 
2853
	selectElement: function(/*DomNode*/element,/*Boolean?*/nochangefocus){
2854
		// summary:
2855
		//		clear previous selection and select element (including all its children)
2856
		var _document = dojo.doc;
2857
		element = dojo.byId(element);
2858
		if(_document.selection && dojo.body().createTextRange){ // IE
2859
			try{
2860
				var range = dojo.body().createControlRange();
2861
				range.addElement(element);
2862
				if(!nochangefocus){
2863
					range.select();
2864
				}
2865
			}catch(e){
2866
				this.selectElementChildren(element,nochangefocus);
2867
			}
2868
		}else if(dojo.global["getSelection"]){
2869
			var selection = dojo.global.getSelection();
2870
			// FIXME: does this work on Safari?
2871
			if(selection["removeAllRanges"]){ // Mozilla
2872
				var range = _document.createRange() ;
2873
				range.selectNode(element) ;
2874
				selection.removeAllRanges() ;
2875
				selection.addRange(range) ;
2876
			}
2877
		}
2878
	}
2879
});
2880
 
2881
}
2882
 
2883
if(!dojo._hasResource["dijit._editor.RichText"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2884
dojo._hasResource["dijit._editor.RichText"] = true;
2885
dojo.provide("dijit._editor.RichText");
2886
 
2887
 
2888
 
2889
 
2890
 
2891
 
2892
// used to restore content when user leaves this page then comes back
2893
// but do not try doing document.write if we are using xd loading.
2894
// document.write will only work if RichText.js is included in the dojo.js
2895
// file. If it is included in dojo.js and you want to allow rich text saving
2896
// for back/forward actions, then set djConfig.allowXdRichTextSave = true.
2897
if(!djConfig["useXDomain"] || djConfig["allowXdRichTextSave"]){
2898
	if(dojo._postLoad){
2899
		(function(){
2900
			var savetextarea = dojo.doc.createElement('textarea');
2901
			savetextarea.id = "dijit._editor.RichText.savedContent";
2902
			var s = savetextarea.style;
2903
			s.display='none';
2904
			s.position='absolute';
2905
			s.top="-100px";
2906
			s.left="-100px"
2907
			s.height="3px";
2908
			s.width="3px";
2909
			dojo.body().appendChild(savetextarea);
2910
		})();
2911
	}else{
2912
		//dojo.body() is not available before onLoad is fired
2913
		try {
2914
			dojo.doc.write('<textarea id="dijit._editor.RichText.savedContent" ' +
2915
				'style="display:none;position:absolute;top:-100px;left:-100px;height:3px;width:3px;overflow:hidden;"></textarea>');
2916
		}catch(e){ }
2917
	}
2918
}
2919
dojo.declare("dijit._editor.RichText", [ dijit._Widget ], {
2920
	constructor: function(){
2921
		// summary:
2922
		//		dijit._editor.RichText is the core of the WYSIWYG editor in dojo, which
2923
		//		provides the basic editing features. It also encapsulates the differences
2924
		//		of different js engines for various browsers
2925
		//
2926
		// contentPreFilters: Array
2927
		//		pre content filter function register array.
2928
		//		these filters will be executed before the actual
2929
		//		editing area get the html content
2930
		this.contentPreFilters = [];
2931
 
2932
		// contentPostFilters: Array
2933
		//		post content filter function register array.
2934
		//		these will be used on the resulting html
2935
		//		from contentDomPostFilters. The resuling
2936
		//		content is the final html (returned by getValue())
2937
		this.contentPostFilters = [];
2938
 
2939
		// contentDomPreFilters: Array
2940
		//		pre content dom filter function register array.
2941
		//		these filters are applied after the result from
2942
		//		contentPreFilters are set to the editing area
2943
		this.contentDomPreFilters = [];
2944
 
2945
		// contentDomPostFilters: Array
2946
		//		post content dom filter function register array.
2947
		//		these filters are executed on the editing area dom
2948
		//		the result from these will be passed to contentPostFilters
2949
		this.contentDomPostFilters = [];
2950
 
2951
		// editingAreaStyleSheets: Array
2952
		//		array to store all the stylesheets applied to the editing area
2953
		this.editingAreaStyleSheets=[];
2954
 
2955
		this._keyHandlers = {};
2956
		this.contentPreFilters.push(dojo.hitch(this, "_preFixUrlAttributes"));
2957
		if(dojo.isMoz){
2958
			this.contentPreFilters.push(this._fixContentForMoz);
2959
		}
2960
		//this.contentDomPostFilters.push(this._postDomFixUrlAttributes);
2961
 
2962
		this.onLoadDeferred = new dojo.Deferred();
2963
	},
2964
 
2965
	// inheritWidth: Boolean
2966
	//		whether to inherit the parent's width or simply use 100%
2967
	inheritWidth: false,
2968
 
2969
	// focusOnLoad: Boolean
2970
	//		whether focusing into this instance of richtext when page onload
2971
	focusOnLoad: false,
2972
 
2973
	// name: String
2974
	//		If a save name is specified the content is saved and restored when the user
2975
	//		leave this page can come back, or if the editor is not properly closed after
2976
	//		editing has started.
2977
	name: "",
2978
 
2979
	// styleSheets: String
2980
	//		semicolon (";") separated list of css files for the editing area
2981
	styleSheets: "",
2982
 
2983
	// _content: String
2984
	//		temporary content storage
2985
	_content: "",
2986
 
2987
	// height: String
2988
	//		set height to fix the editor at a specific height, with scrolling.
2989
	//		By default, this is 300px. If you want to have the editor always
2990
	//		resizes to accommodate the content, use AlwaysShowToolbar plugin
2991
	//		and set height=""
2992
	height: "300px",
2993
 
2994
	// minHeight: String
2995
	//		The minimum height that the editor should have
2996
	minHeight: "1em",
2997
 
2998
	// isClosed: Boolean
2999
	isClosed: true,
3000
 
3001
	// isLoaded: Boolean
3002
	isLoaded: false,
3003
 
3004
	// _SEPARATOR: String
3005
	//		used to concat contents from multiple textareas into a single string
3006
	_SEPARATOR: "@@**%%__RICHTEXTBOUNDRY__%%**@@",
3007
 
3008
	// onLoadDeferred: dojo.Deferred
3009
	//		deferred which is fired when the editor finishes loading
3010
	onLoadDeferred: null,
3011
 
3012
	postCreate: function(){
3013
		// summary: init
3014
		dojo.publish("dijit._editor.RichText::init", [this]);
3015
		this.open();
3016
		this.setupDefaultShortcuts();
3017
	},
3018
 
3019
	setupDefaultShortcuts: function(){
3020
		// summary: add some default key handlers
3021
		// description:
3022
		// 		Overwrite this to setup your own handlers. The default
3023
		// 		implementation does not use Editor commands, but directly
3024
		//		executes the builtin commands within the underlying browser
3025
		//		support.
3026
		var ctrl = this.KEY_CTRL;
3027
		var exec = function(cmd, arg){
3028
			return arguments.length == 1 ? function(){ this.execCommand(cmd); } :
3029
				function(){ this.execCommand(cmd, arg); }
3030
		}
3031
		this.addKeyHandler("b", ctrl, exec("bold"));
3032
		this.addKeyHandler("i", ctrl, exec("italic"));
3033
		this.addKeyHandler("u", ctrl, exec("underline"));
3034
		this.addKeyHandler("a", ctrl, exec("selectall"));
3035
		this.addKeyHandler("s", ctrl, function () { this.save(true); });
3036
 
3037
		this.addKeyHandler("1", ctrl, exec("formatblock", "h1"));
3038
		this.addKeyHandler("2", ctrl, exec("formatblock", "h2"));
3039
		this.addKeyHandler("3", ctrl, exec("formatblock", "h3"));
3040
		this.addKeyHandler("4", ctrl, exec("formatblock", "h4"));
3041
 
3042
		this.addKeyHandler("\\", ctrl, exec("insertunorderedlist"));
3043
		if(!dojo.isIE){
3044
			this.addKeyHandler("Z", ctrl, exec("redo"));
3045
		}
3046
	},
3047
 
3048
	// events: Array
3049
	//		 events which should be connected to the underlying editing area
3050
	events: ["onKeyPress", "onKeyDown", "onKeyUp", "onClick"],
3051
 
3052
	// events: Array
3053
	//		 events which should be connected to the underlying editing
3054
	//		 area, events in this array will be addListener with
3055
	//		 capture=true
3056
	captureEvents: [],
3057
 
3058
	_editorCommandsLocalized: false,
3059
	_localizeEditorCommands: function(){
3060
		if(this._editorCommandsLocalized){
3061
			return;
3062
		}
3063
		this._editorCommandsLocalized = true;
3064
 
3065
		//in IE, names for blockformat is locale dependent, so we cache the values here
3066
 
3067
		//if the normal way fails, we try the hard way to get the list
3068
 
3069
		//do not use _cacheLocalBlockFormatNames here, as it will
3070
		//trigger security warning in IE7
3071
 
3072
		//in the array below, ul can not come directly after ol,
3073
		//otherwise the queryCommandValue returns Normal for it
3074
		var formats = ['p', 'pre', 'address', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'div', 'ul'];
3075
		var localhtml = "", format, i=0;
3076
		while((format=formats[i++])){
3077
			if(format.charAt(1) != 'l'){
3078
				localhtml += "<"+format+"><span>content</span></"+format+">";
3079
			}else{
3080
				localhtml += "<"+format+"><li>content</li></"+format+">";
3081
			}
3082
		}
3083
		//queryCommandValue returns empty if we hide editNode, so move it out of screen temporary
3084
		var div=document.createElement('div');
3085
		div.style.position = "absolute";
3086
		div.style.left = "-2000px";
3087
		div.style.top = "-2000px";
3088
		document.body.appendChild(div);
3089
		div.innerHTML = localhtml;
3090
		var node = div.firstChild;
3091
		while(node){
3092
			dijit._editor.selection.selectElement(node.firstChild);
3093
			dojo.withGlobal(this.window, "selectElement", dijit._editor.selection, [node.firstChild]);
3094
			var nativename = node.tagName.toLowerCase();
3095
			this._local2NativeFormatNames[nativename] = document.queryCommandValue("formatblock");//this.queryCommandValue("formatblock");
3096
			this._native2LocalFormatNames[this._local2NativeFormatNames[nativename]] = nativename;
3097
			node = node.nextSibling;
3098
		}
3099
		document.body.removeChild(div);
3100
	},
3101
 
3102
	open: function(/*DomNode?*/element){
3103
		// summary:
3104
		//		Transforms the node referenced in this.domNode into a rich text editing
3105
		//		node. This will result in the creation and replacement with an <iframe>
3106
		//		if designMode(FF)/contentEditable(IE) is used.
3107
 
3108
		if((!this.onLoadDeferred)||(this.onLoadDeferred.fired >= 0)){
3109
			this.onLoadDeferred = new dojo.Deferred();
3110
		}
3111
 
3112
		if(!this.isClosed){ this.close(); }
3113
		dojo.publish("dijit._editor.RichText::open", [ this ]);
3114
 
3115
		this._content = "";
3116
		if((arguments.length == 1)&&(element["nodeName"])){ this.domNode = element; } // else unchanged
3117
 
3118
		if(	(this.domNode["nodeName"])&&
3119
			(this.domNode.nodeName.toLowerCase() == "textarea")){
3120
			// if we were created from a textarea, then we need to create a
3121
			// new editing harness node.
3122
			this.textarea = this.domNode;
3123
			this.name=this.textarea.name;
3124
			var html = this._preFilterContent(this.textarea.value);
3125
			this.domNode = dojo.doc.createElement("div");
3126
			this.domNode.setAttribute('widgetId',this.id);
3127
			this.textarea.removeAttribute('widgetId');
3128
			this.domNode.cssText = this.textarea.cssText;
3129
			this.domNode.className += " "+this.textarea.className;
3130
			dojo.place(this.domNode, this.textarea, "before");
3131
			var tmpFunc = dojo.hitch(this, function(){
3132
				//some browsers refuse to submit display=none textarea, so
3133
				//move the textarea out of screen instead
3134
				with(this.textarea.style){
3135
					display = "block";
3136
					position = "absolute";
3137
					left = top = "-1000px";
3138
 
3139
					if(dojo.isIE){ //nasty IE bug: abnormal formatting if overflow is not hidden
3140
						this.__overflow = overflow;
3141
						overflow = "hidden";
3142
					}
3143
				}
3144
			});
3145
			if(dojo.isIE){
3146
				setTimeout(tmpFunc, 10);
3147
			}else{
3148
				tmpFunc();
3149
			}
3150
 
3151
			// this.domNode.innerHTML = html;
3152
 
3153
//				if(this.textarea.form){
3154
//					// FIXME: port: this used to be before advice!!!
3155
//					dojo.connect(this.textarea.form, "onsubmit", this, function(){
3156
//						// FIXME: should we be calling close() here instead?
3157
//						this.textarea.value = this.getValue();
3158
//					});
3159
//				}
3160
		}else{
3161
			var html = this._preFilterContent(this.getNodeChildrenHtml(this.domNode));
3162
			this.domNode.innerHTML = '';
3163
		}
3164
		if(html == ""){ html = "&nbsp;"; }
3165
 
3166
		var content = dojo.contentBox(this.domNode);
3167
		// var content = dojo.contentBox(this.srcNodeRef);
3168
		this._oldHeight = content.h;
3169
		this._oldWidth = content.w;
3170
 
3171
		this.savedContent = html;
3172
 
3173
		// If we're a list item we have to put in a blank line to force the
3174
		// bullet to nicely align at the top of text
3175
		if(	(this.domNode["nodeName"]) &&
3176
			(this.domNode.nodeName == "LI") ){
3177
			this.domNode.innerHTML = " <br>";
3178
		}
3179
 
3180
		this.editingArea = dojo.doc.createElement("div");
3181
		this.domNode.appendChild(this.editingArea);
3182
 
3183
		if(this.name != "" && (!djConfig["useXDomain"] || djConfig["allowXdRichTextSave"])){
3184
			var saveTextarea = dojo.byId("dijit._editor.RichText.savedContent");
3185
			if(saveTextarea.value != ""){
3186
				var datas = saveTextarea.value.split(this._SEPARATOR), i=0, dat;
3187
				while((dat=datas[i++])){
3188
					var data = dat.split(":");
3189
					if(data[0] == this.name){
3190
						html = data[1];
3191
						datas.splice(i, 1);
3192
						break;
3193
					}
3194
				}
3195
			}
3196
 
3197
			// FIXME: need to do something different for Opera/Safari
3198
			dojo.connect(window, "onbeforeunload", this, "_saveContent");
3199
			// dojo.connect(window, "onunload", this, "_saveContent");
3200
		}
3201
 
3202
		this.isClosed = false;
3203
		// Safari's selections go all out of whack if we do it inline,
3204
		// so for now IE is our only hero
3205
		//if (typeof document.body.contentEditable != "undefined") {
3206
		if(dojo.isIE || dojo.isSafari || dojo.isOpera){ // contentEditable, easy
3207
			var ifr = this.iframe = dojo.doc.createElement('iframe');
3208
			ifr.src = 'javascript:void(0)';
3209
			this.editorObject = ifr;
3210
			ifr.style.border = "none";
3211
			ifr.style.width = "100%";
3212
			ifr.frameBorder = 0;
3213
//			ifr.style.scrolling = this.height ? "auto" : "vertical";
3214
			this.editingArea.appendChild(ifr);
3215
			this.window = ifr.contentWindow;
3216
			this.document = this.window.document;
3217
			this.document.open();
3218
			this.document.write(this._getIframeDocTxt(html));
3219
			this.document.close();
3220
 
3221
			if(dojo.isIE >= 7){
3222
				if(this.height){
3223
					ifr.style.height = this.height;
3224
				}
3225
				if(this.minHeight){
3226
					ifr.style.minHeight = this.minHeight;
3227
				}
3228
			}else{
3229
				ifr.style.height = this.height ? this.height : this.minHeight;
3230
			}
3231
 
3232
			if(dojo.isIE){
3233
				this._localizeEditorCommands();
3234
			}
3235
 
3236
			this.onLoad();
3237
		}else{ // designMode in iframe
3238
			this._drawIframe(html);
3239
		}
3240
 
3241
		// TODO: this is a guess at the default line-height, kinda works
3242
		if(this.domNode.nodeName == "LI"){ this.domNode.lastChild.style.marginTop = "-1.2em"; }
3243
		this.domNode.className += " RichTextEditable";
3244
	},
3245
 
3246
	//static cache variables shared among all instance of this class
3247
	_local2NativeFormatNames: {},
3248
	_native2LocalFormatNames: {},
3249
	_localizedIframeTitles: null,
3250
 
3251
	_getIframeDocTxt: function(/* String */ html){
3252
		var _cs = dojo.getComputedStyle(this.domNode);
3253
		if(!this.height && !dojo.isMoz){
3254
			html="<div>"+html+"</div>";
3255
		}
3256
		var font = [ _cs.fontWeight, _cs.fontSize, _cs.fontFamily ].join(" ");
3257
 
3258
		// line height is tricky - applying a units value will mess things up.
3259
		// if we can't get a non-units value, bail out.
3260
		var lineHeight = _cs.lineHeight;
3261
		if(lineHeight.indexOf("px") >= 0){
3262
			lineHeight = parseFloat(lineHeight)/parseFloat(_cs.fontSize);
3263
			// console.debug(lineHeight);
3264
		}else if(lineHeight.indexOf("em")>=0){
3265
			lineHeight = parseFloat(lineHeight);
3266
		}else{
3267
			lineHeight = "1.0";
3268
		}
3269
		return [
3270
			this.isLeftToRight() ? "<html><head>" : "<html dir='rtl'><head>",
3271
			(dojo.isMoz ? "<title>" + this._localizedIframeTitles.iframeEditTitle + "</title>" : ""),
3272
			"<style>",
3273
			"body,html {",
3274
			"	background:transparent;",
3275
			"	padding: 0;",
3276
			"	margin: 0;",
3277
			"}",
3278
			// TODO: left positioning will cause contents to disappear out of view
3279
			//	   if it gets too wide for the visible area
3280
			"body{",
3281
			"	top:0px; left:0px; right:0px;",
3282
				((this.height||dojo.isOpera) ? "" : "position: fixed;"),
3283
			"	font:", font, ";",
3284
			// FIXME: IE 6 won't understand min-height?
3285
			"	min-height:", this.minHeight, ";",
3286
			"	line-height:", lineHeight,
3287
			"}",
3288
			"p{ margin: 1em 0 !important; }",
3289
			(this.height ?
3290
				"" : "body,html{overflow-y:hidden;/*for IE*/} body > div {overflow-x:auto;/*for FF to show vertical scrollbar*/}"
3291
			),
3292
			"li > ul:-moz-first-node, li > ol:-moz-first-node{ padding-top: 1.2em; } ",
3293
			"li{ min-height:1.2em; }",
3294
			"</style>",
3295
			this._applyEditingAreaStyleSheets(),
3296
			"</head><body>"+html+"</body></html>"
3297
		].join(""); // String
3298
	},
3299
 
3300
	_drawIframe: function(/*String*/html){
3301
		// summary:
3302
		//		Draws an iFrame using the existing one if one exists.
3303
		//		Used by Mozilla, Safari, and Opera
3304
 
3305
		if(!this.iframe){
3306
			var ifr = this.iframe = dojo.doc.createElement("iframe");
3307
			// this.iframe.src = "about:blank";
3308
			// document.body.appendChild(this.iframe);
3309
			// console.debug(this.iframe.contentDocument.open());
3310
			// dojo.body().appendChild(this.iframe);
3311
			var ifrs = ifr.style;
3312
			// ifrs.border = "1px solid black";
3313
			ifrs.border = "none";
3314
			ifrs.lineHeight = "0"; // squash line height
3315
			ifrs.verticalAlign = "bottom";
3316
//			ifrs.scrolling = this.height ? "auto" : "vertical";
3317
			this.editorObject = this.iframe;
3318
			// get screen reader text for mozilla here, too
3319
			this._localizedIframeTitles = dojo.i18n.getLocalization("dijit", "Textarea");
3320
			// need to find any associated label element and update iframe document title
3321
			var label=dojo.query('label[for="'+this.id+'"]');
3322
			if(label.length){
3323
				this._localizedIframeTitles.iframeEditTitle = label[0].innerHTML + " " + this._localizedIframeTitles.iframeEditTitle;
3324
			}
3325
		}
3326
		// opera likes this to be outside the with block
3327
		//	this.iframe.src = "javascript:void(0)";//dojo.uri.dojoUri("src/widget/templates/richtextframe.html") + ((dojo.doc.domain != currentDomain) ? ("#"+dojo.doc.domain) : "");
3328
		this.iframe.style.width = this.inheritWidth ? this._oldWidth : "100%";
3329
 
3330
		if(this.height){
3331
			this.iframe.style.height = this.height;
3332
		}else{
3333
			this.iframe.height = this._oldHeight;
3334
		}
3335
 
3336
		if(this.textarea){
3337
			var tmpContent = this.srcNodeRef;
3338
		}else{
3339
			var tmpContent = dojo.doc.createElement('div');
3340
			tmpContent.style.display="none";
3341
			tmpContent.innerHTML = html;
3342
			//append tmpContent to under the current domNode so that the margin
3343
			//calculation below is correct
3344
			this.editingArea.appendChild(tmpContent);
3345
		}
3346
 
3347
		this.editingArea.appendChild(this.iframe);
3348
 
3349
		//do we want to show the content before the editing area finish loading here?
3350
		//if external style sheets are used for the editing area, the appearance now
3351
		//and after loading of the editing area won't be the same (and padding/margin
3352
		//calculation above may not be accurate)
3353
		//	tmpContent.style.display = "none";
3354
		//	this.editingArea.appendChild(this.iframe);
3355
 
3356
		var _iframeInitialized = false;
3357
		// console.debug(this.iframe);
3358
		// var contentDoc = this.iframe.contentWindow.document;
3359
 
3360
 
3361
		// note that on Safari lower than 420+, we have to get the iframe
3362
		// by ID in order to get something w/ a contentDocument property
3363
 
3364
		var contentDoc = this.iframe.contentDocument;
3365
		contentDoc.open();
3366
		contentDoc.write(this._getIframeDocTxt(html));
3367
		contentDoc.close();
3368
 
3369
		// now we wait for onload. Janky hack!
3370
		var ifrFunc = dojo.hitch(this, function(){
3371
			if(!_iframeInitialized){
3372
				_iframeInitialized = true;
3373
			}else{ return; }
3374
			if(!this.editNode){
3375
				try{
3376
					if(this.iframe.contentWindow){
3377
						this.window = this.iframe.contentWindow;
3378
						this.document = this.iframe.contentWindow.document
3379
					}else if(this.iframe.contentDocument){
3380
						// for opera
3381
						this.window = this.iframe.contentDocument.window;
3382
						this.document = this.iframe.contentDocument;
3383
					}
3384
					if(!this.document.body){
3385
						throw 'Error';
3386
					}
3387
				}catch(e){
3388
					setTimeout(ifrFunc,500);
3389
					_iframeInitialized = false;
3390
					return;
3391
				}
3392
 
3393
				dojo._destroyElement(tmpContent);
3394
				this.document.designMode = "on";
3395
				//	try{
3396
				//	this.document.designMode = "on";
3397
				// }catch(e){
3398
				//	this._tryDesignModeOnClick=true;
3399
				// }
3400
 
3401
				this.onLoad();
3402
			}else{
3403
				dojo._destroyElement(tmpContent);
3404
				this.editNode.innerHTML = html;
3405
				this.onDisplayChanged();
3406
			}
3407
			this._preDomFilterContent(this.editNode);
3408
		});
3409
 
3410
		ifrFunc();
3411
	},
3412
 
3413
	_applyEditingAreaStyleSheets: function(){
3414
		// summary:
3415
		//		apply the specified css files in styleSheets
3416
		var files = [];
3417
		if(this.styleSheets){
3418
			files = this.styleSheets.split(';');
3419
			this.styleSheets = '';
3420
		}
3421
 
3422
		//empty this.editingAreaStyleSheets here, as it will be filled in addStyleSheet
3423
		files = files.concat(this.editingAreaStyleSheets);
3424
		this.editingAreaStyleSheets = [];
3425
 
3426
		var text='', i=0, url;
3427
		while((url=files[i++])){
3428
			var abstring = (new dojo._Url(dojo.global.location, url)).toString();
3429
			this.editingAreaStyleSheets.push(abstring);
3430
			text += '<link rel="stylesheet" type="text/css" href="'+abstring+'"/>'
3431
		}
3432
		return text;
3433
	},
3434
 
3435
	addStyleSheet: function(/*dojo._Url*/uri){
3436
		// summary:
3437
		//		add an external stylesheet for the editing area
3438
		// uri:	a dojo.uri.Uri pointing to the url of the external css file
3439
		var url=uri.toString();
3440
 
3441
		//if uri is relative, then convert it to absolute so that it can be resolved correctly in iframe
3442
		if(url.charAt(0) == '.' || (url.charAt(0) != '/' && !uri.host)){
3443
			url = (new dojo._Url(dojo.global.location, url)).toString();
3444
		}
3445
 
3446
		if(dojo.indexOf(this.editingAreaStyleSheets, url) > -1){
3447
			console.debug("dijit._editor.RichText.addStyleSheet: Style sheet "+url+" is already applied to the editing area!");
3448
			return;
3449
		}
3450
 
3451
		this.editingAreaStyleSheets.push(url);
3452
		if(this.document.createStyleSheet){ //IE
3453
			this.document.createStyleSheet(url);
3454
		}else{ //other browser
3455
			var head = this.document.getElementsByTagName("head")[0];
3456
			var stylesheet = this.document.createElement("link");
3457
			with(stylesheet){
3458
				rel="stylesheet";
3459
				type="text/css";
3460
				href=url;
3461
			}
3462
			head.appendChild(stylesheet);
3463
		}
3464
	},
3465
 
3466
	removeStyleSheet: function(/*dojo._Url*/uri){
3467
		// summary:
3468
		//		remove an external stylesheet for the editing area
3469
		var url=uri.toString();
3470
		//if uri is relative, then convert it to absolute so that it can be resolved correctly in iframe
3471
		if(url.charAt(0) == '.' || (url.charAt(0) != '/' && !uri.host)){
3472
			url = (new dojo._Url(dojo.global.location, url)).toString();
3473
		}
3474
		var index = dojo.indexOf(this.editingAreaStyleSheets, url);
3475
		if(index == -1){
3476
			console.debug("dijit._editor.RichText.removeStyleSheet: Style sheet "+url+" is not applied to the editing area so it can not be removed!");
3477
			return;
3478
		}
3479
		delete this.editingAreaStyleSheets[index];
3480
		dojo.withGlobal(this.window,'query', dojo, ['link:[href="'+url+'"]']).orphan()
3481
	},
3482
 
3483
	disabled: false,
3484
	_mozSettingProps: ['styleWithCSS','insertBrOnReturn'],
3485
	setDisabled: function(/*Boolean*/ disabled){
3486
		if(dojo.isIE || dojo.isSafari || dojo.isOpera){
3487
			this.editNode.contentEditable=!disabled;
3488
		}else{ //moz
3489
			if(disabled){
3490
				this._mozSettings=[false,this.blockNodeForEnter==='BR'];
3491
			}
3492
			this.document.designMode=(disabled?'off':'on');
3493
			if(!disabled){
3494
				dojo.forEach(this._mozSettingProps, function(s,i){
3495
					this.document.execCommand(s,false,this._mozSettings[i]);
3496
				},this);
3497
			}
3498
//			this.document.execCommand('contentReadOnly', false, disabled);
3499
//				if(disabled){
3500
//					this.blur(); //to remove the blinking caret
3501
//				}
3502
//
3503
		}
3504
		this.disabled=disabled;
3505
	},
3506
 
3507
/* Event handlers
3508
 *****************/
3509
 
3510
	_isResized: function(){ return false; },
3511
 
3512
	onLoad: function(/* Event */ e){
3513
		// summary: handler after the content of the document finishes loading
3514
		this.isLoaded = true;
3515
		if(this.height || dojo.isMoz){
3516
			this.editNode=this.document.body;
3517
		}else{
3518
			this.editNode=this.document.body.firstChild;
3519
		}
3520
		this.editNode.contentEditable = true; //should do no harm in FF
3521
		this._preDomFilterContent(this.editNode);
3522
 
3523
		var events=this.events.concat(this.captureEvents),i=0,et;
3524
		while((et=events[i++])){
3525
			this.connect(this.document, et.toLowerCase(), et);
3526
		}
3527
		if(!dojo.isIE){
3528
			try{ // sanity check for Mozilla
3529
//					this.document.execCommand("useCSS", false, true); // old moz call
3530
				this.document.execCommand("styleWithCSS", false, false); // new moz call
3531
				//this.document.execCommand("insertBrOnReturn", false, false); // new moz call
3532
			}catch(e2){ }
3533
			// FIXME: when scrollbars appear/disappear this needs to be fired
3534
		}else{ // IE contentEditable
3535
			// give the node Layout on IE
3536
			this.editNode.style.zoom = 1.0;
3537
		}
3538
 
3539
		if(this.focusOnLoad){
3540
			this.focus();
3541
		}
3542
 
3543
		this.onDisplayChanged(e);
3544
		if(this.onLoadDeferred){
3545
			this.onLoadDeferred.callback(true);
3546
		}
3547
	},
3548
 
3549
	onKeyDown: function(/* Event */ e){
3550
		// summary: Fired on keydown
3551
 
3552
//		 console.info("onkeydown:", e.keyCode);
3553
 
3554
		// we need this event at the moment to get the events from control keys
3555
		// such as the backspace. It might be possible to add this to Dojo, so that
3556
		// keyPress events can be emulated by the keyDown and keyUp detection.
3557
		if(dojo.isIE){
3558
			if(e.keyCode === dojo.keys.BACKSPACE && this.document.selection.type === "Control"){
3559
				// IE has a bug where if a non-text object is selected in the editor,
3560
		  // hitting backspace would act as if the browser's back button was
3561
		  // clicked instead of deleting the object. see #1069
3562
				dojo.stopEvent(e);
3563
				this.execCommand("delete");
3564
			}else if(	(65 <= e.keyCode&&e.keyCode <= 90) ||
3565
				(e.keyCode>=37&&e.keyCode<=40) // FIXME: get this from connect() instead!
3566
			){ //arrow keys
3567
				e.charCode = e.keyCode;
3568
				this.onKeyPress(e);
3569
			}
3570
		}
3571
		else if (dojo.isMoz){
3572
			if(e.keyCode == dojo.keys.TAB && !e.shiftKey && !e.ctrlKey && !e.altKey && this.iframe){
3573
				// update iframe document title for screen reader
3574
				this.iframe.contentDocument.title = this._localizedIframeTitles.iframeFocusTitle;
3575
 
3576
				// Place focus on the iframe. A subsequent tab or shift tab will put focus
3577
				// on the correct control.
3578
				this.iframe.focus();  // this.focus(); won't work
3579
				dojo.stopEvent(e);
3580
			}else if (e.keyCode == dojo.keys.TAB && e.shiftKey){
3581
				// if there is a toolbar, set focus to it, otherwise ignore
3582
				if (this.toolbar){
3583
					this.toolbar.focus();
3584
				}
3585
				dojo.stopEvent(e);
3586
			}
3587
		}
3588
	},
3589
 
3590
	onKeyUp: function(e){
3591
		// summary: Fired on keyup
3592
		return;
3593
	},
3594
 
3595
	KEY_CTRL: 1,
3596
	KEY_SHIFT: 2,
3597
 
3598
	onKeyPress: function(e){
3599
		// summary: Fired on keypress
3600
 
3601
//		 console.info("onkeypress:", e.keyCode);
3602
 
3603
		// handle the various key events
3604
		var modifiers = e.ctrlKey ? this.KEY_CTRL : 0 | e.shiftKey?this.KEY_SHIFT : 0;
3605
 
3606
		var key = e.keyChar||e.keyCode;
3607
		if(this._keyHandlers[key]){
3608
			// console.debug("char:", e.key);
3609
			var handlers = this._keyHandlers[key], i = 0, h;
3610
			while((h = handlers[i++])){
3611
				if(modifiers == h.modifiers){
3612
					if(!h.handler.apply(this,arguments)){
3613
						e.preventDefault();
3614
					}
3615
					break;
3616
				}
3617
			}
3618
		}
3619
 
3620
		// function call after the character has been inserted
3621
		setTimeout(dojo.hitch(this, function(){
3622
			this.onKeyPressed(e);
3623
		}), 1);
3624
	},
3625
 
3626
	addKeyHandler: function(/*String*/key, /*Int*/modifiers, /*Function*/handler){
3627
		// summary: add a handler for a keyboard shortcut
3628
		if(!dojo.isArray(this._keyHandlers[key])){ this._keyHandlers[key] = []; }
3629
		this._keyHandlers[key].push({
3630
			modifiers: modifiers || 0,
3631
			handler: handler
3632
		});
3633
	},
3634
 
3635
	onKeyPressed: function(/*Event*/e){
3636
		this.onDisplayChanged(/*e*/); // can't pass in e
3637
	},
3638
 
3639
	onClick: function(/*Event*/e){
3640
//			console.debug('onClick',this._tryDesignModeOnClick);
3641
//			if(this._tryDesignModeOnClick){
3642
//				try{
3643
//					this.document.designMode='on';
3644
//					this._tryDesignModeOnClick=false;
3645
//				}catch(e){}
3646
//			}
3647
		this.onDisplayChanged(e); },
3648
	_onBlur: function(e){
3649
		var _c=this.getValue(true);
3650
		if(_c!=this.savedContent){
3651
			this.onChange(_c);
3652
			this.savedContent=_c;
3653
		}
3654
		if (dojo.isMoz && this.iframe){
3655
			this.iframe.contentDocument.title = this._localizedIframeTitles.iframeEditTitle;
3656
		}
3657
//			console.info('_onBlur')
3658
	},
3659
	_initialFocus: true,
3660
	_onFocus: function(/*Event*/e){
3661
//			console.info('_onFocus')
3662
		// summary: Fired on focus
3663
		if( (dojo.isMoz)&&(this._initialFocus) ){
3664
			this._initialFocus = false;
3665
			if(this.editNode.innerHTML.replace(/^\s+|\s+$/g, "") == "&nbsp;"){
3666
				this.placeCursorAtStart();
3667
//					this.execCommand("selectall");
3668
//					this.window.getSelection().collapseToStart();
3669
			}
3670
		}
3671
	},
3672
 
3673
	blur: function(){
3674
		// summary: remove focus from this instance
3675
		if(this.iframe){
3676
			this.window.blur();
3677
		}else if(this.editNode){
3678
			this.editNode.blur();
3679
		}
3680
	},
3681
 
3682
	focus: function(){
3683
		// summary: move focus to this instance
3684
		if(this.iframe && !dojo.isIE){
3685
			dijit.focus(this.iframe);
3686
		}else if(this.editNode && this.editNode.focus){
3687
			// editNode may be hidden in display:none div, lets just punt in this case
3688
			dijit.focus(this.editNode);
3689
		}else{
3690
			console.debug("Have no idea how to focus into the editor!");
3691
		}
3692
	},
3693
 
3694
//		_lastUpdate: 0,
3695
	updateInterval: 200,
3696
	_updateTimer: null,
3697
	onDisplayChanged: function(/*Event*/e){
3698
		// summary:
3699
		//		This event will be fired everytime the display context
3700
		//		changes and the result needs to be reflected in the UI.
3701
		// description:
3702
		//		If you don't want to have update too often,
3703
		//		onNormalizedDisplayChanged should be used instead
3704
 
3705
//			var _t=new Date();
3706
		if(!this._updateTimer){
3707
//				this._lastUpdate=_t;
3708
			if(this._updateTimer){
3709
				clearTimeout(this._updateTimer);
3710
			}
3711
			this._updateTimer=setTimeout(dojo.hitch(this,this.onNormalizedDisplayChanged),this.updateInterval);
3712
		}
3713
	},
3714
	onNormalizedDisplayChanged: function(){
3715
		// summary:
3716
		//		This event is fired every updateInterval ms or more
3717
		// description:
3718
		//		If something needs to happen immidiately after a
3719
		//		user change, please use onDisplayChanged instead
3720
		this._updateTimer=null;
3721
	},
3722
	onChange: function(newContent){
3723
		// summary:
3724
		//		this is fired if and only if the editor loses focus and
3725
		//		the content is changed
3726
 
3727
//			console.log('onChange',newContent);
3728
	},
3729
	_normalizeCommand: function(/*String*/cmd){
3730
		// summary:
3731
		//		Used as the advice function by dojo.connect to map our
3732
		//		normalized set of commands to those supported by the target
3733
		//		browser
3734
 
3735
		var command = cmd.toLowerCase();
3736
		if(command == "formatblock"){
3737
			if(dojo.isSafari){ command = "heading"; }
3738
		}else if(command == "hilitecolor" && !dojo.isMoz){
3739
			command = "backcolor";
3740
		}
3741
 
3742
		return command;
3743
	},
3744
 
3745
	queryCommandAvailable: function(/*String*/command){
3746
		// summary:
3747
		//		Tests whether a command is supported by the host. Clients SHOULD check
3748
		//		whether a command is supported before attempting to use it, behaviour
3749
		//		for unsupported commands is undefined.
3750
		// command: The command to test for
3751
		var ie = 1;
3752
		var mozilla = 1 << 1;
3753
		var safari = 1 << 2;
3754
		var opera = 1 << 3;
3755
		var safari420 = 1 << 4;
3756
 
3757
		var gt420 = dojo.isSafari;
3758
 
3759
		function isSupportedBy(browsers){
3760
			return {
3761
				ie: Boolean(browsers & ie),
3762
				mozilla: Boolean(browsers & mozilla),
3763
				safari: Boolean(browsers & safari),
3764
				safari420: Boolean(browsers & safari420),
3765
				opera: Boolean(browsers & opera)
3766
			}
3767
		}
3768
 
3769
		var supportedBy = null;
3770
 
3771
		switch(command.toLowerCase()){
3772
			case "bold": case "italic": case "underline":
3773
			case "subscript": case "superscript":
3774
			case "fontname": case "fontsize":
3775
			case "forecolor": case "hilitecolor":
3776
			case "justifycenter": case "justifyfull": case "justifyleft":
3777
			case "justifyright": case "delete": case "selectall":
3778
				supportedBy = isSupportedBy(mozilla | ie | safari | opera);
3779
				break;
3780
 
3781
			case "createlink": case "unlink": case "removeformat":
3782
			case "inserthorizontalrule": case "insertimage":
3783
			case "insertorderedlist": case "insertunorderedlist":
3784
			case "indent": case "outdent": case "formatblock":
3785
			case "inserthtml": case "undo": case "redo": case "strikethrough":
3786
				supportedBy = isSupportedBy(mozilla | ie | opera | safari420);
3787
				break;
3788
 
3789
			case "blockdirltr": case "blockdirrtl":
3790
			case "dirltr": case "dirrtl":
3791
			case "inlinedirltr": case "inlinedirrtl":
3792
				supportedBy = isSupportedBy(ie);
3793
				break;
3794
			case "cut": case "copy": case "paste":
3795
				supportedBy = isSupportedBy( ie | mozilla | safari420);
3796
				break;
3797
 
3798
			case "inserttable":
3799
				supportedBy = isSupportedBy(mozilla | ie);
3800
				break;
3801
 
3802
			case "insertcell": case "insertcol": case "insertrow":
3803
			case "deletecells": case "deletecols": case "deleterows":
3804
			case "mergecells": case "splitcell":
3805
				supportedBy = isSupportedBy(ie | mozilla);
3806
				break;
3807
 
3808
			default: return false;
3809
		}
3810
 
3811
		return (dojo.isIE && supportedBy.ie) ||
3812
			(dojo.isMoz && supportedBy.mozilla) ||
3813
			(dojo.isSafari && supportedBy.safari) ||
3814
			(gt420 && supportedBy.safari420) ||
3815
			(dojo.isOpera && supportedBy.opera);  // Boolean return true if the command is supported, false otherwise
3816
	},
3817
 
3818
	execCommand: function(/*String*/command, argument){
3819
		// summary: Executes a command in the Rich Text area
3820
		// command: The command to execute
3821
		// argument: An optional argument to the command
3822
		var returnValue;
3823
 
3824
		//focus() is required for IE to work
3825
		//In addition, focus() makes sure after the execution of
3826
		//the command, the editor receives the focus as expected
3827
		this.focus();
3828
 
3829
		command = this._normalizeCommand(command);
3830
		if(argument != undefined){
3831
			if(command == "heading"){
3832
				throw new Error("unimplemented");
3833
			}else if((command == "formatblock") && dojo.isIE){
3834
				argument = '<'+argument+'>';
3835
			}
3836
		}
3837
		if(command == "inserthtml"){
3838
			//TODO: we shall probably call _preDomFilterContent here as well
3839
			argument=this._preFilterContent(argument);
3840
			if(dojo.isIE){
3841
				var insertRange = this.document.selection.createRange();
3842
				insertRange.pasteHTML(argument);
3843
				insertRange.select();
3844
				//insertRange.collapse(true);
3845
				returnValue=true;
3846
			}else if(dojo.isMoz && !argument.length){
3847
				//mozilla can not inserthtml an empty html to delete current selection
3848
				//so we delete the selection instead in this case
3849
				dojo.withGlobal(this.window,'remove',dijit._editor.selection); // FIXME
3850
				returnValue=true;
3851
			}else{
3852
				returnValue=this.document.execCommand(command, false, argument);
3853
			}
3854
		}else if(
3855
			(command == "unlink")&&
3856
			(this.queryCommandEnabled("unlink"))&&
3857
			(dojo.isMoz || dojo.isSafari)
3858
		){
3859
			// fix up unlink in Mozilla to unlink the link and not just the selection
3860
 
3861
			// grab selection
3862
			// Mozilla gets upset if we just store the range so we have to
3863
			// get the basic properties and recreate to save the selection
3864
			var selection = this.window.getSelection();
3865
			//	var selectionRange = selection.getRangeAt(0);
3866
			//	var selectionStartContainer = selectionRange.startContainer;
3867
			//	var selectionStartOffset = selectionRange.startOffset;
3868
			//	var selectionEndContainer = selectionRange.endContainer;
3869
			//	var selectionEndOffset = selectionRange.endOffset;
3870
 
3871
			// select our link and unlink
3872
			var a = dojo.withGlobal(this.window, "getAncestorElement",dijit._editor.selection, ['a']);
3873
			dojo.withGlobal(this.window, "selectElement", dijit._editor.selection, [a]);
3874
 
3875
			returnValue=this.document.execCommand("unlink", false, null);
3876
		}else if((command == "hilitecolor")&&(dojo.isMoz)){
3877
//				// mozilla doesn't support hilitecolor properly when useCSS is
3878
//				// set to false (bugzilla #279330)
3879
 
3880
			this.document.execCommand("styleWithCSS", false, true);
3881
			returnValue = this.document.execCommand(command, false, argument);
3882
			this.document.execCommand("styleWithCSS", false, false);
3883
 
3884
		}else if((dojo.isIE)&&( (command == "backcolor")||(command == "forecolor") )){
3885
			// Tested under IE 6 XP2, no problem here, comment out
3886
			// IE weirdly collapses ranges when we exec these commands, so prevent it
3887
//				var tr = this.document.selection.createRange();
3888
			argument = arguments.length > 1 ? argument : null;
3889
			returnValue = this.document.execCommand(command, false, argument);
3890
 
3891
			// timeout is workaround for weird IE behavior were the text
3892
			// selection gets correctly re-created, but subsequent input
3893
			// apparently isn't bound to it
3894
//				setTimeout(function(){tr.select();}, 1);
3895
		}else{
3896
			argument = arguments.length > 1 ? argument : null;
3897
//				if(dojo.isMoz){
3898
//					this.document = this.iframe.contentWindow.document
3899
//				}
3900
 
3901
			if(argument || command!="createlink"){
3902
				returnValue = this.document.execCommand(command, false, argument);
3903
			}
3904
		}
3905
 
3906
		this.onDisplayChanged();
3907
		return returnValue;
3908
	},
3909
 
3910
	queryCommandEnabled: function(/*String*/command){
3911
		// summary: check whether a command is enabled or not
3912
		command = this._normalizeCommand(command);
3913
		if(dojo.isMoz || dojo.isSafari){
3914
			if(command == "unlink"){ // mozilla returns true always
3915
				// console.debug(dojo.withGlobal(this.window, "hasAncestorElement",dijit._editor.selection, ['a']));
3916
				return dojo.withGlobal(this.window, "hasAncestorElement",dijit._editor.selection, ['a']);
3917
			}else if (command == "inserttable"){
3918
				return true;
3919
			}
3920
		}
3921
		//see #4109
3922
		if(dojo.isSafari)
3923
			if(command == "copy"){
3924
				command="cut";
3925
			}else if(command == "paste"){
3926
				return true;
3927
		}
3928
 
3929
		// return this.document.queryCommandEnabled(command);
3930
		var elem = (dojo.isIE) ? this.document.selection.createRange() : this.document;
3931
		return elem.queryCommandEnabled(command);
3932
	},
3933
 
3934
	queryCommandState: function(command){
3935
		// summary: check the state of a given command
3936
		command = this._normalizeCommand(command);
3937
		return this.document.queryCommandState(command);
3938
	},
3939
 
3940
	queryCommandValue: function(command){
3941
		// summary: check the value of a given command
3942
		command = this._normalizeCommand(command);
3943
		if(dojo.isIE && command == "formatblock"){
3944
			return this._local2NativeFormatNames[this.document.queryCommandValue(command)];
3945
		}
3946
		return this.document.queryCommandValue(command);
3947
	},
3948
 
3949
	// Misc.
3950
 
3951
	placeCursorAtStart: function(){
3952
		// summary:
3953
		//		place the cursor at the start of the editing area
3954
		this.focus();
3955
 
3956
		//see comments in placeCursorAtEnd
3957
		var isvalid=false;
3958
		if(dojo.isMoz){
3959
			var first=this.editNode.firstChild;
3960
			while(first){
3961
				if(first.nodeType == 3){
3962
					if(first.nodeValue.replace(/^\s+|\s+$/g, "").length>0){
3963
						isvalid=true;
3964
						dojo.withGlobal(this.window, "selectElement", dijit._editor.selection, [first]);
3965
						break;
3966
					}
3967
				}else if(first.nodeType == 1){
3968
					isvalid=true;
3969
					dojo.withGlobal(this.window, "selectElementChildren",dijit._editor.selection, [first]);
3970
					break;
3971
				}
3972
				first = first.nextSibling;
3973
			}
3974
		}else{
3975
			isvalid=true;
3976
			dojo.withGlobal(this.window, "selectElementChildren",dijit._editor.selection, [this.editNode]);
3977
		}
3978
		if(isvalid){
3979
			dojo.withGlobal(this.window, "collapse", dijit._editor.selection, [true]);
3980
		}
3981
	},
3982
 
3983
	placeCursorAtEnd: function(){
3984
		// summary:
3985
		//		place the cursor at the end of the editing area
3986
		this.focus();
3987
 
3988
		//In mozilla, if last child is not a text node, we have to use selectElementChildren on this.editNode.lastChild
3989
		//otherwise the cursor would be placed at the end of the closing tag of this.editNode.lastChild
3990
		var isvalid=false;
3991
		if(dojo.isMoz){
3992
			var last=this.editNode.lastChild;
3993
			while(last){
3994
				if(last.nodeType == 3){
3995
					if(last.nodeValue.replace(/^\s+|\s+$/g, "").length>0){
3996
						isvalid=true;
3997
						dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [last]);
3998
						break;
3999
					}
4000
				}else if(last.nodeType == 1){
4001
					isvalid=true;
4002
					if(last.lastChild){
4003
						dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [last.lastChild]);
4004
					}else{
4005
						dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [last]);
4006
					}
4007
					break;
4008
				}
4009
				last = last.previousSibling;
4010
			}
4011
		}else{
4012
			isvalid=true;
4013
			dojo.withGlobal(this.window, "selectElementChildren",dijit._editor.selection, [this.editNode]);
4014
		}
4015
		if(isvalid){
4016
			dojo.withGlobal(this.window, "collapse", dijit._editor.selection, [false]);
4017
		}
4018
	},
4019
 
4020
	getValue: function(/*Boolean?*/nonDestructive){
4021
		// summary:
4022
		//		return the current content of the editing area (post filters are applied)
4023
		if(this.textarea){
4024
			if(this.isClosed || !this.isLoaded){
4025
				return this.textarea.value;
4026
			}
4027
		}
4028
 
4029
		return this._postFilterContent(null, nonDestructive);
4030
	},
4031
 
4032
	setValue: function(/*String*/html){
4033
		// summary:
4034
		//		this function set the content. No undo history is preserved
4035
		if(this.textarea && (this.isClosed || !this.isLoaded)){
4036
			this.textarea.value=html;
4037
		}else{
4038
			html = this._preFilterContent(html);
4039
			if(this.isClosed){
4040
				this.domNode.innerHTML = html;
4041
				this._preDomFilterContent(this.domNode);
4042
			}else{
4043
				this.editNode.innerHTML = html;
4044
				this._preDomFilterContent(this.editNode);
4045
			}
4046
		}
4047
	},
4048
 
4049
	replaceValue: function(/*String*/html){
4050
		// summary:
4051
		//		this function set the content while trying to maintain the undo stack
4052
		//		(now only works fine with Moz, this is identical to setValue in all
4053
		//		other browsers)
4054
		if(this.isClosed){
4055
			this.setValue(html);
4056
		}else if(this.window && this.window.getSelection && !dojo.isMoz){ // Safari
4057
			// look ma! it's a totally f'd browser!
4058
			this.setValue(html);
4059
		}else if(this.window && this.window.getSelection){ // Moz
4060
			html = this._preFilterContent(html);
4061
			this.execCommand("selectall");
4062
			if(dojo.isMoz && !html){ html = "&nbsp;" }
4063
			this.execCommand("inserthtml", html);
4064
			this._preDomFilterContent(this.editNode);
4065
		}else if(this.document && this.document.selection){//IE
4066
			//In IE, when the first element is not a text node, say
4067
			//an <a> tag, when replacing the content of the editing
4068
			//area, the <a> tag will be around all the content
4069
			//so for now, use setValue for IE too
4070
			this.setValue(html);
4071
		}
4072
	},
4073
 
4074
	_preFilterContent: function(/*String*/html){
4075
		// summary:
4076
		//		filter the input before setting the content of the editing area
4077
		var ec = html;
4078
		dojo.forEach(this.contentPreFilters, function(ef){ if(ef){ ec = ef(ec); } });
4079
		return ec;
4080
	},
4081
	_preDomFilterContent: function(/*DomNode*/dom){
4082
		// summary:
4083
		//		filter the input
4084
		dom = dom || this.editNode;
4085
		dojo.forEach(this.contentDomPreFilters, function(ef){
4086
			if(ef && dojo.isFunction(ef)){
4087
				ef(dom);
4088
			}
4089
		}, this);
4090
	},
4091
 
4092
	_postFilterContent: function(/*DomNode|DomNode[]?*/dom,/*Boolean?*/nonDestructive){
4093
		// summary:
4094
		//		filter the output after getting the content of the editing area
4095
		dom = dom || this.editNode;
4096
		if(this.contentDomPostFilters.length){
4097
			if(nonDestructive && dom['cloneNode']){
4098
				dom = dom.cloneNode(true);
4099
			}
4100
			dojo.forEach(this.contentDomPostFilters, function(ef){
4101
				dom = ef(dom);
4102
			});
4103
		}
4104
		var ec = this.getNodeChildrenHtml(dom);
4105
		if(!ec.replace(/^(?:\s|\xA0)+/g, "").replace(/(?:\s|\xA0)+$/g,"").length){ ec = ""; }
4106
 
4107
		//	if(dojo.isIE){
4108
		//		//removing appended <P>&nbsp;</P> for IE
4109
		//		ec = ec.replace(/(?:<p>&nbsp;</p>[\n\r]*)+$/i,"");
4110
		//	}
4111
		dojo.forEach(this.contentPostFilters, function(ef){
4112
			ec = ef(ec);
4113
		});
4114
 
4115
		return ec;
4116
	},
4117
 
4118
	_saveContent: function(/*Event*/e){
4119
		// summary:
4120
		//		Saves the content in an onunload event if the editor has not been closed
4121
		var saveTextarea = dojo.byId("dijit._editor.RichText.savedContent");
4122
		saveTextarea.value += this._SEPARATOR + this.name + ":" + this.getValue();
4123
	},
4124
 
4125
 
4126
	escapeXml: function(/*String*/str, /*Boolean*/noSingleQuotes){
4127
		//summary:
4128
		//		Adds escape sequences for special characters in XML: &<>"'
4129
		//		Optionally skips escapes for single quotes
4130
		str = str.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
4131
		if(!noSingleQuotes){
4132
			str = str.replace(/'/gm, "&#39;");
4133
		}
4134
		return str; // string
4135
	},
4136
 
4137
	getNodeHtml: function(/* DomNode */node){
4138
		switch(node.nodeType){
4139
			case 1: //element node
4140
				var output = '<'+node.tagName.toLowerCase();
4141
				if(dojo.isMoz){
4142
					if(node.getAttribute('type')=='_moz'){
4143
						node.removeAttribute('type');
4144
					}
4145
					if(node.getAttribute('_moz_dirty') != undefined){
4146
						node.removeAttribute('_moz_dirty');
4147
					}
4148
				}
4149
				//store the list of attributes and sort it to have the
4150
				//attributes appear in the dictionary order
4151
				var attrarray = [];
4152
				if(dojo.isIE){
4153
					var s = node.outerHTML;
4154
					s = s.substr(0,s.indexOf('>'));
4155
					s = s.replace(/(?:['"])[^"']*\1/g, '');//to make the following regexp safe
4156
					var reg = /([^\s=]+)=/g;
4157
					var m, key;
4158
					while((m = reg.exec(s)) != undefined){
4159
						key=m[1];
4160
						if(key.substr(0,3) != '_dj'){
4161
							if(key == 'src' || key == 'href'){
4162
								if(node.getAttribute('_djrealurl')){
4163
									attrarray.push([key,node.getAttribute('_djrealurl')]);
4164
									continue;
4165
								}
4166
							}
4167
							if(key == 'class'){
4168
								attrarray.push([key,node.className]);
4169
							}else{
4170
								attrarray.push([key,node.getAttribute(key)]);
4171
							}
4172
						}
4173
					}
4174
				}else{
4175
					var attr, i=0, attrs = node.attributes;
4176
					while((attr=attrs[i++])){
4177
						//ignore all attributes starting with _dj which are
4178
						//internal temporary attributes used by the editor
4179
						if(attr.name.substr(0,3) != '_dj' /*&&
4180
							(attr.specified == undefined || attr.specified)*/){
4181
							var v = attr.value;
4182
							if(attr.name == 'src' || attr.name == 'href'){
4183
								if(node.getAttribute('_djrealurl')){
4184
									v = node.getAttribute('_djrealurl');
4185
								}
4186
							}
4187
							attrarray.push([attr.name,v]);
4188
						}
4189
					}
4190
				}
4191
				attrarray.sort(function(a,b){
4192
					return a[0]<b[0]?-1:(a[0]==b[0]?0:1);
4193
				});
4194
				i=0;
4195
				while((attr=attrarray[i++])){
4196
					output += ' '+attr[0]+'="'+attr[1]+'"';
4197
				}
4198
				if(node.childNodes.length){
4199
					output += '>' + this.getNodeChildrenHtml(node)+'</'+node.tagName.toLowerCase()+'>';
4200
				}else{
4201
					output += ' />';
4202
				}
4203
				break;
4204
			case 3: //text
4205
				// FIXME:
4206
				var output = this.escapeXml(node.nodeValue,true);
4207
				break;
4208
			case 8: //comment
4209
				// FIXME:
4210
				var output = '<!--'+this.escapeXml(node.nodeValue,true)+'-->';
4211
				break;
4212
			default:
4213
				var output = "Element not recognized - Type: " + node.nodeType + " Name: " + node.nodeName;
4214
		}
4215
		return output;
4216
	},
4217
 
4218
	getNodeChildrenHtml: function(/* DomNode */dom){
4219
		// summary: Returns the html content of a DomNode and children
4220
		var out = "";
4221
		if(!dom){ return out; }
4222
		var nodes = dom["childNodes"]||dom;
4223
		var i=0;
4224
		var node;
4225
		while((node=nodes[i++])){
4226
			out += this.getNodeHtml(node);
4227
		}
4228
		return out; // String
4229
	},
4230
 
4231
	close: function(/*Boolean*/save, /*Boolean*/force){
4232
		// summary:
4233
		//		Kills the editor and optionally writes back the modified contents to the
4234
		//		element from which it originated.
4235
		// save:
4236
		//		Whether or not to save the changes. If false, the changes are discarded.
4237
		// force:
4238
		if(this.isClosed){return false; }
4239
 
4240
		if(!arguments.length){ save = true; }
4241
		this._content = this.getValue();
4242
		var changed = (this.savedContent != this._content);
4243
 
4244
		// line height is squashed for iframes
4245
		// FIXME: why was this here? if (this.iframe){ this.domNode.style.lineHeight = null; }
4246
 
4247
		if(this.interval){ clearInterval(this.interval); }
4248
 
4249
		if(this.textarea){
4250
			with(this.textarea.style){
4251
				position = "";
4252
				left = top = "";
4253
				if(dojo.isIE){
4254
					overflow = this.__overflow;
4255
					this.__overflow = null;
4256
				}
4257
			}
4258
			if(save){
4259
				this.textarea.value = this._content;
4260
			}else{
4261
				this.textarea.value = this.savedContent;
4262
			}
4263
			dojo._destroyElement(this.domNode);
4264
			this.domNode = this.textarea;
4265
		}else{
4266
			if(save){
4267
				//why we treat moz differently? comment out to fix #1061
4268
//					if(dojo.isMoz){
4269
//						var nc = dojo.doc.createElement("span");
4270
//						this.domNode.appendChild(nc);
4271
//						nc.innerHTML = this.editNode.innerHTML;
4272
//					}else{
4273
//						this.domNode.innerHTML = this._content;
4274
//					}
4275
				this.domNode.innerHTML = this._content;
4276
			}else{
4277
				this.domNode.innerHTML = this.savedContent;
4278
			}
4279
		}
4280
 
4281
		dojo.removeClass(this.domNode, "RichTextEditable");
4282
		this.isClosed = true;
4283
		this.isLoaded = false;
4284
		// FIXME: is this always the right thing to do?
4285
		delete this.editNode;
4286
 
4287
		if(this.window && this.window._frameElement){
4288
			this.window._frameElement = null;
4289
		}
4290
 
4291
		this.window = null;
4292
		this.document = null;
4293
		this.editingArea = null;
4294
		this.editorObject = null;
4295
 
4296
		return changed; // Boolean: whether the content has been modified
4297
	},
4298
 
4299
	destroyRendering: function(){
4300
		// summary: stub
4301
	},
4302
 
4303
	destroy: function(){
4304
		this.destroyRendering();
4305
		if(!this.isClosed){ this.close(false); }
4306
		this.inherited("destroy",arguments);
4307
		//dijit._editor.RichText.superclass.destroy.call(this);
4308
	},
4309
 
4310
	_fixContentForMoz: function(/* String */ html){
4311
		// summary:
4312
		//		Moz can not handle strong/em tags correctly, convert them to b/i
4313
		html = html.replace(/<(\/)?strong([ \>])/gi, '<$1b$2' );
4314
		html = html.replace(/<(\/)?em([ \>])/gi, '<$1i$2' );
4315
		return html; // String
4316
	},
4317
 
4318
	_srcInImgRegex	: /(?:(<img(?=\s).*?\ssrc=)("|')(.*?)\2)|(?:(<img\s.*?src=)([^"'][^ >]+))/gi ,
4319
	_hrefInARegex	: /(?:(<a(?=\s).*?\shref=)("|')(.*?)\2)|(?:(<a\s.*?href=)([^"'][^ >]+))/gi ,
4320
 
4321
	_preFixUrlAttributes: function(/* String */ html){
4322
		html = html.replace(this._hrefInARegex, '$1$4$2$3$5$2 _djrealurl=$2$3$5$2') ;
4323
		html = html.replace(this._srcInImgRegex, '$1$4$2$3$5$2 _djrealurl=$2$3$5$2') ;
4324
		return html; // String
4325
	}
4326
});
4327
 
4328
}
4329
 
4330
if(!dojo._hasResource["dijit.Toolbar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
4331
dojo._hasResource["dijit.Toolbar"] = true;
4332
dojo.provide("dijit.Toolbar");
4333
 
4334
 
4335
 
4336
 
4337
 
4338
dojo.declare(
4339
	"dijit.Toolbar",
4340
	[dijit._Widget, dijit._Templated, dijit._KeyNavContainer],
4341
{
4342
	templateString:
4343
		'<div class="dijit dijitToolbar" waiRole="toolbar" tabIndex="${tabIndex}" dojoAttachPoint="containerNode">' +
4344
//			'<table style="table-layout: fixed" class="dijitReset dijitToolbarTable">' + // factor out style
4345
//				'<tr class="dijitReset" dojoAttachPoint="containerNode"></tr>'+
4346
//			'</table>' +
4347
		'</div>',
4348
 
4349
	tabIndex: "0",
4350
 
4351
	postCreate: function(){
4352
		this.connectKeyNavHandlers(
4353
			this.isLeftToRight() ? [dojo.keys.LEFT_ARROW] : [dojo.keys.RIGHT_ARROW],
4354
			this.isLeftToRight() ? [dojo.keys.RIGHT_ARROW] : [dojo.keys.LEFT_ARROW]
4355
		);
4356
	},
4357
 
4358
	startup: function(){
4359
		this.startupKeyNavChildren();
4360
	}
4361
}
4362
);
4363
 
4364
// Combine with dijit.MenuSeparator??
4365
dojo.declare(
4366
	"dijit.ToolbarSeparator",
4367
	[ dijit._Widget, dijit._Templated ],
4368
{
4369
	// summary
4370
	//	A line between two menu items
4371
	templateString: '<div class="dijitToolbarSeparator dijitInline"></div>',
4372
	postCreate: function(){ dojo.setSelectable(this.domNode, false); },
4373
	isFocusable: function(){ return false; }
4374
});
4375
 
4376
}
4377
 
4378
if(!dojo._hasResource["dijit.form.Button"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
4379
dojo._hasResource["dijit.form.Button"] = true;
4380
dojo.provide("dijit.form.Button");
4381
 
4382
 
4383
 
4384
 
4385
dojo.declare("dijit.form.Button", dijit.form._FormWidget, {
4386
/*
4387
 * usage
4388
 *	<button dojoType="button" onClick="...">Hello world</button>
4389
 *
4390
 *  var button1 = new dijit.form.Button({label: "hello world", onClick: foo});
4391
 *	dojo.body().appendChild(button1.domNode);
4392
 */
4393
	// summary
4394
	//	Basically the same thing as a normal HTML button, but with special styling.
4395
 
4396
	// label: String
4397
	//	text to display in button
4398
	label: "",
4399
 
4400
	// showLabel: Boolean
4401
	//	whether or not to display the text label in button
4402
	showLabel: true,
4403
 
4404
	// iconClass: String
4405
	//	class to apply to div in button to make it display an icon
4406
	iconClass: "",
4407
 
4408
	type: "button",
4409
	baseClass: "dijitButton",
4410
	templateString:"<div class=\"dijit dijitLeft dijitInline dijitButton\"\n\tdojoAttachEvent=\"onclick:_onButtonClick,onmouseenter:_onMouse,onmouseleave:_onMouse,onmousedown:_onMouse\"\n\t><div class='dijitRight'\n\t\t><button class=\"dijitStretch dijitButtonNode dijitButtonContents\" dojoAttachPoint=\"focusNode,titleNode\"\n\t\t\ttype=\"${type}\" waiRole=\"button\" waiState=\"labelledby-${id}_label\"\n\t\t\t><span class=\"dijitInline ${iconClass}\" dojoAttachPoint=\"iconNode\" \n \t\t\t\t><span class=\"dijitToggleButtonIconChar\">&#10003</span \n\t\t\t></span\n\t\t\t><span class=\"dijitButtonText\" id=\"${id}_label\" dojoAttachPoint=\"containerNode\">${label}</span\n\t\t></button\n\t></div\n></div>\n",
4411
 
4412
	// TODO: set button's title to this.containerNode.innerText
4413
 
4414
	_onClick: function(/*Event*/ e){
4415
		// summary: internal function to handle click actions
4416
		if(this.disabled){ return false; }
4417
		this._clicked(); // widget click actions
4418
		return this.onClick(e); // user click actions
4419
	},
4420
 
4421
	_onButtonClick: function(/*Event*/ e){
4422
		// summary: callback when the user mouse clicks the button portion
4423
		dojo.stopEvent(e);
4424
		var okToSubmit = this._onClick(e) !== false; // returning nothing is same as true
4425
 
4426
		// for some reason type=submit buttons don't automatically submit the form; do it manually
4427
		if(this.type=="submit" && okToSubmit){
4428
			for(var node=this.domNode; node; node=node.parentNode){
4429
				var widget=dijit.byNode(node);
4430
				if(widget && widget._onSubmit){
4431
					widget._onSubmit(e);
4432
					break;
4433
				}
4434
				if(node.tagName.toLowerCase() == "form"){
4435
					if(!node.onsubmit || node.onsubmit()){ node.submit(); }
4436
					break;
4437
				}
4438
			}
4439
		}
4440
	},
4441
 
4442
	postCreate: function(){
4443
		// summary:
4444
		//	get label and set as title on button icon if necessary
4445
		if (this.showLabel == false){
4446
			var labelText = "";
4447
			this.label = this.containerNode.innerHTML;
4448
			labelText = dojo.trim(this.containerNode.innerText || this.containerNode.textContent);
4449
			// set title attrib on iconNode
4450
			this.titleNode.title=labelText;
4451
			dojo.addClass(this.containerNode,"dijitDisplayNone");
4452
		}
4453
		this.inherited(arguments);
4454
	},
4455
 
4456
	onClick: function(/*Event*/ e){
4457
		// summary: user callback for when button is clicked
4458
		//      if type="submit", return value != false to perform submit
4459
		return true;
4460
	},
4461
 
4462
	_clicked: function(/*Event*/ e){
4463
		// summary: internal replaceable function for when the button is clicked
4464
	},
4465
 
4466
	setLabel: function(/*String*/ content){
4467
		// summary: reset the label (text) of the button; takes an HTML string
4468
		this.containerNode.innerHTML = this.label = content;
4469
		if(dojo.isMozilla){ // Firefox has re-render issues with tables
4470
			var oldDisplay = dojo.getComputedStyle(this.domNode).display;
4471
			this.domNode.style.display="none";
4472
			var _this = this;
4473
			setTimeout(function(){_this.domNode.style.display=oldDisplay;},1);
4474
		}
4475
		if (this.showLabel == false){
4476
				this.titleNode.title=dojo.trim(this.containerNode.innerText || this.containerNode.textContent);
4477
		}
4478
	}
4479
});
4480
 
4481
/*
4482
 * usage
4483
 *	<button dojoType="DropDownButton" label="Hello world"><div dojotype=dijit.Menu>...</div></button>
4484
 *
4485
 *  var button1 = new dijit.form.DropDownButton({ label: "hi", dropDown: new dijit.Menu(...) });
4486
 *	dojo.body().appendChild(button1);
4487
 */
4488
dojo.declare("dijit.form.DropDownButton", [dijit.form.Button, dijit._Container], {
4489
	// summary
4490
	//		push the button and a menu shows up
4491
 
4492
	baseClass : "dijitDropDownButton",
4493
 
4494
	templateString:"<div class=\"dijit dijitLeft dijitInline\"\n\tdojoAttachEvent=\"onmouseenter:_onMouse,onmouseleave:_onMouse,onmousedown:_onMouse,onclick:_onDropDownClick,onkeydown:_onDropDownKeydown,onblur:_onDropDownBlur,onkeypress:_onKey\"\n\t><div class='dijitRight'>\n\t<button class=\"dijitStretch dijitButtonNode dijitButtonContents\" type=\"${type}\"\n\t\tdojoAttachPoint=\"focusNode,titleNode\" waiRole=\"button\" waiState=\"haspopup-true,labelledby-${id}_label\"\n\t\t><div class=\"dijitInline ${iconClass}\" dojoAttachPoint=\"iconNode\"></div\n\t\t><span class=\"dijitButtonText\" \tdojoAttachPoint=\"containerNode,popupStateNode\"\n\t\tid=\"${id}_label\">${label}</span\n\t\t><span class='dijitA11yDownArrow'>&#9660;</span>\n\t</button>\n</div></div>\n",
4495
 
4496
	_fillContent: function(){
4497
		// my inner HTML contains both the button contents and a drop down widget, like
4498
		// <DropDownButton>  <span>push me</span>  <Menu> ... </Menu> </DropDownButton>
4499
		// The first node is assumed to be the button content. The widget is the popup.
4500
		if(this.srcNodeRef){ // programatically created buttons might not define srcNodeRef
4501
			//FIXME: figure out how to filter out the widget and use all remaining nodes as button
4502
			//	content, not just nodes[0]
4503
			var nodes = dojo.query("*", this.srcNodeRef);
4504
			dijit.form.DropDownButton.superclass._fillContent.call(this, nodes[0]);
4505
 
4506
			// save pointer to srcNode so we can grab the drop down widget after it's instantiated
4507
			this.dropDownContainer = this.srcNodeRef;
4508
		}
4509
	},
4510
 
4511
	startup: function(){
4512
		// the child widget from srcNodeRef is the dropdown widget.  Insert it in the page DOM,
4513
		// make it invisible, and store a reference to pass to the popup code.
4514
		if(!this.dropDown){
4515
			var dropDownNode = dojo.query("[widgetId]", this.dropDownContainer)[0];
4516
			this.dropDown = dijit.byNode(dropDownNode);
4517
			delete this.dropDownContainer;
4518
		}
4519
		dojo.body().appendChild(this.dropDown.domNode);
4520
		this.dropDown.domNode.style.display="none";
4521
	},
4522
 
4523
	_onArrowClick: function(/*Event*/ e){
4524
		// summary: callback when the user mouse clicks on menu popup node
4525
		if(this.disabled){ return; }
4526
		this._toggleDropDown();
4527
	},
4528
 
4529
	_onDropDownClick: function(/*Event*/ e){
4530
		// on Firefox 2 on the Mac it is possible to fire onclick
4531
		// by pressing enter down on a second element and transferring
4532
		// focus to the DropDownButton;
4533
		// we want to prevent opening our menu in this situation
4534
		// and only do so if we have seen a keydown on this button;
4535
		// e.detail != 0 means that we were fired by mouse
4536
		var isMacFFlessThan3 = dojo.isFF && dojo.isFF < 3
4537
			&& navigator.appVersion.indexOf("Macintosh") != -1;
4538
		if(!isMacFFlessThan3 || e.detail != 0 || this._seenKeydown){
4539
			this._onArrowClick(e);
4540
		}
4541
		this._seenKeydown = false;
4542
	},
4543
 
4544
	_onDropDownKeydown: function(/*Event*/ e){
4545
		this._seenKeydown = true;
4546
	},
4547
 
4548
	_onDropDownBlur: function(/*Event*/ e){
4549
		this._seenKeydown = false;
4550
	},
4551
 
4552
	_onKey: function(/*Event*/ e){
4553
		// summary: callback when the user presses a key on menu popup node
4554
		if(this.disabled){ return; }
4555
		if(e.keyCode == dojo.keys.DOWN_ARROW){
4556
			if(!this.dropDown || this.dropDown.domNode.style.display=="none"){
4557
				dojo.stopEvent(e);
4558
				return this._toggleDropDown();
4559
			}
4560
		}
4561
	},
4562
 
4563
	_onBlur: function(){
4564
		// summary: called magically when focus has shifted away from this widget and it's dropdown
4565
		this._closeDropDown();
4566
		// don't focus on button.  the user has explicitly focused on something else.
4567
	},
4568
 
4569
	_toggleDropDown: function(){
4570
		// summary: toggle the drop-down widget; if it is up, close it, if not, open it
4571
		if(this.disabled){ return; }
4572
		dijit.focus(this.popupStateNode);
4573
		var dropDown = this.dropDown;
4574
		if(!dropDown){ return false; }
4575
		if(!dropDown.isShowingNow){
4576
			// If there's an href, then load that first, so we don't get a flicker
4577
			if(dropDown.href && !dropDown.isLoaded){
4578
				var self = this;
4579
				var handler = dojo.connect(dropDown, "onLoad", function(){
4580
					dojo.disconnect(handler);
4581
					self._openDropDown();
4582
				});
4583
				dropDown._loadCheck(true);
4584
				return;
4585
			}else{
4586
				this._openDropDown();
4587
			}
4588
		}else{
4589
			this._closeDropDown();
4590
		}
4591
	},
4592
 
4593
	_openDropDown: function(){
4594
		var dropDown = this.dropDown;
4595
		var oldWidth=dropDown.domNode.style.width;
4596
		var self = this;
4597
 
4598
		dijit.popup.open({
4599
			parent: this,
4600
			popup: dropDown,
4601
			around: this.domNode,
4602
			orient: this.isLeftToRight() ? {'BL':'TL', 'BR':'TR', 'TL':'BL', 'TR':'BR'}
4603
				: {'BR':'TR', 'BL':'TL', 'TR':'BR', 'TL':'BL'},
4604
			onExecute: function(){
4605
				self._closeDropDown(true);
4606
			},
4607
			onCancel: function(){
4608
				self._closeDropDown(true);
4609
			},
4610
			onClose: function(){
4611
				dropDown.domNode.style.width = oldWidth;
4612
				self.popupStateNode.removeAttribute("popupActive");
4613
				this._opened = false;
4614
			}
4615
		});
4616
		if(this.domNode.offsetWidth > dropDown.domNode.offsetWidth){
4617
			var adjustNode = null;
4618
			if(!this.isLeftToRight()){
4619
				adjustNode = dropDown.domNode.parentNode;
4620
				var oldRight = adjustNode.offsetLeft + adjustNode.offsetWidth;
4621
			}
4622
			// make menu at least as wide as the button
4623
			dojo.marginBox(dropDown.domNode, {w: this.domNode.offsetWidth});
4624
			if(adjustNode){
4625
				adjustNode.style.left = oldRight - this.domNode.offsetWidth + "px";
4626
			}
4627
		}
4628
		this.popupStateNode.setAttribute("popupActive", "true");
4629
		this._opened=true;
4630
		if(dropDown.focus){
4631
			dropDown.focus();
4632
		}
4633
		// TODO: set this.checked and call setStateClass(), to affect button look while drop down is shown
4634
	},
4635
 
4636
	_closeDropDown: function(/*Boolean*/ focus){
4637
		if(this._opened){
4638
			dijit.popup.close(this.dropDown);
4639
			if(focus){ this.focus(); }
4640
			this._opened = false;
4641
		}
4642
	}
4643
});
4644
 
4645
/*
4646
 * usage
4647
 *	<button dojoType="ComboButton" onClick="..."><span>Hello world</span><div dojoType=dijit.Menu>...</div></button>
4648
 *
4649
 *  var button1 = new dijit.form.ComboButton({label: "hello world", onClick: foo, dropDown: "myMenu"});
4650
 *	dojo.body().appendChild(button1.domNode);
4651
 */
4652
dojo.declare("dijit.form.ComboButton", dijit.form.DropDownButton, {
4653
	// summary
4654
	//		left side is normal button, right side displays menu
4655
	templateString:"<table class='dijit dijitReset dijitInline dijitLeft'\n\tcellspacing='0' cellpadding='0'\n\tdojoAttachEvent=\"onmouseenter:_onMouse,onmouseleave:_onMouse,onmousedown:_onMouse\">\n\t<tr>\n\t\t<td\tclass=\"dijitStretch dijitButtonContents dijitButtonNode\"\n\t\t\ttabIndex=\"${tabIndex}\"\n\t\t\tdojoAttachEvent=\"ondijitclick:_onButtonClick\"  dojoAttachPoint=\"titleNode\"\n\t\t\twaiRole=\"button\" waiState=\"labelledby-${id}_label\">\n\t\t\t<div class=\"dijitInline ${iconClass}\" dojoAttachPoint=\"iconNode\"></div>\n\t\t\t<span class=\"dijitButtonText\" id=\"${id}_label\" dojoAttachPoint=\"containerNode\">${label}</span>\n\t\t</td>\n\t\t<td class='dijitReset dijitRight dijitButtonNode dijitDownArrowButton'\n\t\t\tdojoAttachPoint=\"popupStateNode,focusNode\"\n\t\t\tdojoAttachEvent=\"ondijitclick:_onArrowClick, onkeypress:_onKey\"\n\t\t\tstateModifier=\"DownArrow\"\n\t\t\ttitle=\"${optionsTitle}\" name=\"${name}\"\n\t\t\twaiRole=\"button\" waiState=\"haspopup-true\"\n\t\t><div waiRole=\"presentation\">&#9660;</div>\n\t</td></tr>\n</table>\n",
4656
 
4657
	attributeMap: dojo.mixin(dojo.clone(dijit.form._FormWidget.prototype.attributeMap),
4658
		{id:"", name:""}),
4659
 
4660
	// optionsTitle: String
4661
	//  text that describes the options menu (accessibility)
4662
	optionsTitle: "",
4663
 
4664
	baseClass: "dijitComboButton",
4665
 
4666
	_focusedNode: null,
4667
 
4668
	postCreate: function(){
4669
		this.inherited(arguments);
4670
		this._focalNodes = [this.titleNode, this.popupStateNode];
4671
		dojo.forEach(this._focalNodes, dojo.hitch(this, function(node){
4672
			if(dojo.isIE){
4673
				this.connect(node, "onactivate", this._onNodeFocus);
4674
			}else{
4675
				this.connect(node, "onfocus", this._onNodeFocus);
4676
			}
4677
		}));
4678
	},
4679
 
4680
	focusFocalNode: function(node){
4681
		// summary: Focus the focal node node.
4682
		this._focusedNode = node;
4683
		dijit.focus(node);
4684
	},
4685
 
4686
	hasNextFocalNode: function(){
4687
		// summary: Returns true if this widget has no node currently
4688
		//		focused or if there is a node following the focused one.
4689
		//		False is returned if the last node has focus.
4690
		return this._focusedNode !== this.getFocalNodes()[1];
4691
	},
4692
 
4693
	focusNext: function(){
4694
		// summary: Focus the focal node following the current node with focus
4695
		//		or the first one if no node currently has focus.
4696
		this._focusedNode = this.getFocalNodes()[this._focusedNode ? 1 : 0];
4697
		dijit.focus(this._focusedNode);
4698
	},
4699
 
4700
	hasPrevFocalNode: function(){
4701
		// summary: Returns true if this widget has no node currently
4702
		//		focused or if there is a node before the focused one.
4703
		//		False is returned if the first node has focus.
4704
		return this._focusedNode !== this.getFocalNodes()[0];
4705
	},
4706
 
4707
	focusPrev: function(){
4708
		// summary: Focus the focal node before the current node with focus
4709
		//		or the last one if no node currently has focus.
4710
		this._focusedNode = this.getFocalNodes()[this._focusedNode ? 0 : 1];
4711
		dijit.focus(this._focusedNode);
4712
	},
4713
 
4714
	getFocalNodes: function(){
4715
		// summary: Returns an array of focal nodes for this widget.
4716
		return this._focalNodes;
4717
	},
4718
 
4719
	_onNodeFocus: function(evt){
4720
		this._focusedNode = evt.currentTarget;
4721
	},
4722
 
4723
	_onBlur: function(evt){
4724
		this.inherited(arguments);
4725
		this._focusedNode = null;
4726
	}
4727
});
4728
 
4729
dojo.declare("dijit.form.ToggleButton", dijit.form.Button, {
4730
	// summary
4731
	//	A button that can be in two states (checked or not).
4732
	//	Can be base class for things like tabs or checkbox or radio buttons
4733
 
4734
	baseClass: "dijitToggleButton",
4735
 
4736
	// checked: Boolean
4737
	//		Corresponds to the native HTML <input> element's attribute.
4738
	//		In markup, specified as "checked='checked'" or just "checked".
4739
	//		True if the button is depressed, or the checkbox is checked,
4740
	//		or the radio button is selected, etc.
4741
	checked: false,
4742
 
4743
	_clicked: function(/*Event*/ evt){
4744
		this.setChecked(!this.checked);
4745
	},
4746
 
4747
	setChecked: function(/*Boolean*/ checked){
4748
		// summary
4749
		//	Programatically deselect the button
4750
		this.checked = checked;
4751
		dijit.setWaiState(this.focusNode || this.domNode, "pressed", this.checked);
4752
		this._setStateClass();
4753
		this.onChange(checked);
4754
	}
4755
});
4756
 
4757
}
4758
 
4759
if(!dojo._hasResource["dijit._editor._Plugin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
4760
dojo._hasResource["dijit._editor._Plugin"] = true;
4761
dojo.provide("dijit._editor._Plugin");
4762
 
4763
 
4764
 
4765
 
4766
dojo.declare("dijit._editor._Plugin", null, {
4767
	// summary
4768
	//		This represents a "plugin" to the editor, which is basically
4769
	//		a single button on the Toolbar and some associated code
4770
	constructor: function(/*Object?*/args, /*DomNode?*/node){
4771
		if(args){
4772
			dojo.mixin(this, args);
4773
		}
4774
	},
4775
 
4776
	editor: null,
4777
	iconClassPrefix: "dijitEditorIcon",
4778
	button: null,
4779
	queryCommand: null,
4780
	command: "",
4781
	commandArg: null,
4782
	useDefaultCommand: true,
4783
	buttonClass: dijit.form.Button,
4784
	updateInterval: 200, // only allow updates every two tenths of a second
4785
	_initButton: function(){
4786
		if(this.command.length){
4787
			var label = this.editor.commands[this.command];
4788
			var className = "dijitEditorIcon "+this.iconClassPrefix + this.command.charAt(0).toUpperCase() + this.command.substr(1);
4789
			if(!this.button){
4790
				var props = {
4791
					label: label,
4792
					showLabel: false,
4793
					iconClass: className,
4794
					dropDown: this.dropDown
4795
				};
4796
				this.button = new this.buttonClass(props);
4797
			}
4798
		}
4799
	},
4800
	updateState: function(){
4801
		var _e = this.editor;
4802
		var _c = this.command;
4803
		if(!_e){ return; }
4804
		if(!_e.isLoaded){ return; }
4805
		if(!_c.length){ return; }
4806
		if(this.button){
4807
			try{
4808
				var enabled = _e.queryCommandEnabled(_c);
4809
				this.button.setDisabled(!enabled);
4810
				if(this.button.setChecked){
4811
					this.button.setChecked(_e.queryCommandState(_c));
4812
				}
4813
			}catch(e){
4814
				console.debug(e);
4815
			}
4816
		}
4817
	},
4818
	setEditor: function(/*Widget*/editor){
4819
		// FIXME: detatch from previous editor!!
4820
		this.editor = editor;
4821
 
4822
		// FIXME: prevent creating this if we don't need to (i.e., editor can't handle our command)
4823
		this._initButton();
4824
 
4825
		// FIXME: wire up editor to button here!
4826
		if(	(this.command.length) &&
4827
			(!this.editor.queryCommandAvailable(this.command))
4828
		){
4829
			// console.debug("hiding:", this.command);
4830
			if(this.button){
4831
				this.button.domNode.style.display = "none";
4832
			}
4833
		}
4834
		if(this.button && this.useDefaultCommand){
4835
			dojo.connect(this.button, "onClick",
4836
				dojo.hitch(this.editor, "execCommand", this.command, this.commandArg)
4837
			);
4838
		}
4839
		dojo.connect(this.editor, "onNormalizedDisplayChanged", this, "updateState");
4840
	},
4841
	setToolbar: function(/*Widget*/toolbar){
4842
		if(this.button){
4843
			toolbar.addChild(this.button);
4844
		}
4845
		// console.debug("adding", this.button, "to:", toolbar);
4846
	}
4847
});
4848
 
4849
}
4850
 
4851
if(!dojo._hasResource["dijit.Editor"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
4852
dojo._hasResource["dijit.Editor"] = true;
4853
dojo.provide("dijit.Editor");
4854
 
4855
 
4856
 
4857
 
4858
 
4859
 
4860
 
4861
dojo.declare(
4862
	"dijit.Editor",
4863
	dijit._editor.RichText,
4864
	{
4865
	// summary: A rich-text Editing widget
4866
 
4867
		// plugins: Array
4868
		//		a list of plugin names (as strings) or instances (as objects)
4869
		//		for this widget.
4870
		plugins: null,
4871
 
4872
		// extraPlugins: Array
4873
		//		a list of extra plugin names which will be appended to plugins array
4874
		extraPlugins: null,
4875
 
4876
		constructor: function(){
4877
			this.plugins=["undo","redo","|","cut","copy","paste","|","bold","italic","underline","strikethrough","|",
4878
			"insertOrderedList","insertUnorderedList","indent","outdent","|","justifyLeft","justifyRight","justifyCenter","justifyFull"/*"createLink"*/];
4879
 
4880
			this._plugins=[];
4881
			this._editInterval = this.editActionInterval * 1000;
4882
		},
4883
 
4884
		postCreate: function(){
4885
			//for custom undo/redo
4886
			if(this.customUndo){
4887
				dojo['require']("dijit._editor.range");
4888
				this._steps=this._steps.slice(0);
4889
				this._undoedSteps=this._undoedSteps.slice(0);
4890
//				this.addKeyHandler('z',this.KEY_CTRL,this.undo);
4891
//				this.addKeyHandler('y',this.KEY_CTRL,this.redo);
4892
			}
4893
			if(dojo.isArray(this.extraPlugins)){
4894
				this.plugins=this.plugins.concat(this.extraPlugins);
4895
			}
4896
 
4897
//			try{
4898
			dijit.Editor.superclass.postCreate.apply(this, arguments);
4899
 
4900
			this.commands = dojo.i18n.getLocalization("dijit._editor", "commands", this.lang);
4901
 
4902
			if(!this.toolbar){
4903
				// if we haven't been assigned a toolbar, create one
4904
				var toolbarNode = dojo.doc.createElement("div");
4905
				dojo.place(toolbarNode, this.editingArea, "before");
4906
				this.toolbar = new dijit.Toolbar({}, toolbarNode);
4907
			}
4908
 
4909
			dojo.forEach(this.plugins, this.addPlugin, this);
4910
			this.onNormalizedDisplayChanged(); //update toolbar button status
4911
//			}catch(e){ console.debug(e); }
4912
		},
4913
		destroy: function(){
4914
			dojo.forEach(this._plugins, function(p){
4915
				if(p.destroy){
4916
					p.destroy();
4917
				}
4918
			});
4919
			this._plugins=[];
4920
			this.toolbar.destroy(); delete this.toolbar;
4921
			this.inherited('destroy',arguments);
4922
		},
4923
		addPlugin: function(/*String||Object*/plugin, /*Integer?*/index){
4924
			//	summary:
4925
			//		takes a plugin name as a string or a plugin instance and
4926
			//		adds it to the toolbar and associates it with this editor
4927
			//		instance. The resulting plugin is added to the Editor's
4928
			//		plugins array. If index is passed, it's placed in the plugins
4929
			//		array at that index. No big magic, but a nice helper for
4930
			//		passing in plugin names via markup.
4931
			//	plugin: String, args object or plugin instance. Required.
4932
			//	args: This object will be passed to the plugin constructor.
4933
			//	index:
4934
			//		Integer, optional. Used when creating an instance from
4935
			//		something already in this.plugins. Ensures that the new
4936
			//		instance is assigned to this.plugins at that index.
4937
			var args=dojo.isString(plugin)?{name:plugin}:plugin;
4938
			if(!args.setEditor){
4939
				var o={"args":args,"plugin":null,"editor":this};
4940
				dojo.publish("dijit.Editor.getPlugin",[o]);
4941
				if(!o.plugin){
4942
					var pc = dojo.getObject(args.name);
4943
					if(pc){
4944
						o.plugin=new pc(args);
4945
					}
4946
				}
4947
				if(!o.plugin){
4948
					console.debug('Cannot find plugin',plugin);
4949
					return;
4950
				}
4951
				plugin=o.plugin;
4952
			}
4953
			if(arguments.length > 1){
4954
				this._plugins[index] = plugin;
4955
			}else{
4956
				this._plugins.push(plugin);
4957
			}
4958
			plugin.setEditor(this);
4959
			if(dojo.isFunction(plugin.setToolbar)){
4960
				plugin.setToolbar(this.toolbar);
4961
			}
4962
		},
4963
		/* beginning of custom undo/redo support */
4964
 
4965
		// customUndo: Boolean
4966
		//		Whether we shall use custom undo/redo support instead of the native
4967
		//		browser support. By default, we only enable customUndo for IE, as it
4968
		//		has broken native undo/redo support. Note: the implementation does
4969
		//		support other browsers which have W3C DOM2 Range API.
4970
		customUndo: dojo.isIE,
4971
 
4972
		//	editActionInterval: Integer
4973
		//		When using customUndo, not every keystroke will be saved as a step.
4974
		//		Instead typing (including delete) will be grouped together: after
4975
		//		a user stop typing for editActionInterval seconds, a step will be
4976
		//		saved; if a user resume typing within editActionInterval seconds,
4977
		//		the timeout will be restarted. By default, editActionInterval is 3
4978
		//		seconds.
4979
		editActionInterval: 3,
4980
		beginEditing: function(cmd){
4981
			if(!this._inEditing){
4982
				this._inEditing=true;
4983
				this._beginEditing(cmd);
4984
			}
4985
			if(this.editActionInterval>0){
4986
				if(this._editTimer){
4987
					clearTimeout(this._editTimer);
4988
				}
4989
				this._editTimer = setTimeout(dojo.hitch(this, this.endEditing), this._editInterval);
4990
			}
4991
		},
4992
		_steps:[],
4993
		_undoedSteps:[],
4994
		execCommand: function(cmd){
4995
			if(this.customUndo && (cmd=='undo' || cmd=='redo')){
4996
				return this[cmd]();
4997
			}else{
4998
				try{
4999
					if(this.customUndo){
5000
						this.endEditing();
5001
						this._beginEditing();
5002
					}
5003
					var r = this.inherited('execCommand',arguments);
5004
					if(this.customUndo){
5005
						this._endEditing();
5006
					}
5007
					return r;
5008
				}catch(e){
5009
					if(dojo.isMoz && /copy|cut|paste/.test(cmd)){
5010
						// Warn user of platform limitation.  Cannot programmatically access keyboard. See ticket #4136
5011
						var sub = dojo.string.substitute,
5012
							accel = {cut:'X', copy:'C', paste:'V'},
5013
							isMac = navigator.userAgent.indexOf("Macintosh") != -1;
5014
						alert(sub(this.commands.systemShortcutFF,
5015
							[this.commands[cmd], sub(this.commands[isMac ? 'appleKey' : 'ctrlKey'], [accel[cmd]])]));
5016
					}
5017
					return false;
5018
				}
5019
			}
5020
		},
5021
		queryCommandEnabled: function(cmd){
5022
			if(this.customUndo && (cmd=='undo' || cmd=='redo')){
5023
				return cmd=='undo'?(this._steps.length>1):(this._undoedSteps.length>0);
5024
			}else{
5025
				return this.inherited('queryCommandEnabled',arguments);
5026
			}
5027
		},
5028
		_changeToStep: function(from,to){
5029
			this.setValue(to.text);
5030
			var b=to.bookmark;
5031
			if(!b){ return; }
5032
			if(dojo.isIE){
5033
				if(dojo.isArray(b)){//IE CONTROL
5034
					var tmp=[];
5035
					dojo.forEach(b,function(n){
5036
						tmp.push(dijit.range.getNode(n,this.editNode));
5037
					},this);
5038
					b=tmp;
5039
				}
5040
			}else{//w3c range
5041
				var r=dijit.range.create();
5042
				r.setStart(dijit.range.getNode(b.startContainer,this.editNode),b.startOffset);
5043
				r.setEnd(dijit.range.getNode(b.endContainer,this.editNode),b.endOffset);
5044
				b=r;
5045
			}
5046
			dojo.withGlobal(this.window,'moveToBookmark',dijit,[b]);
5047
		},
5048
		undo: function(){
5049
//			console.log('undo');
5050
			this.endEditing(true);
5051
			var s=this._steps.pop();
5052
			if(this._steps.length>0){
5053
				this.focus();
5054
				this._changeToStep(s,this._steps[this._steps.length-1]);
5055
				this._undoedSteps.push(s);
5056
				this.onDisplayChanged();
5057
				return true;
5058
			}
5059
			return false;
5060
		},
5061
		redo: function(){
5062
//			console.log('redo');
5063
			this.endEditing(true);
5064
			var s=this._undoedSteps.pop();
5065
			if(s && this._steps.length>0){
5066
				this.focus();
5067
				this._changeToStep(this._steps[this._steps.length-1],s);
5068
				this._steps.push(s);
5069
				this.onDisplayChanged();
5070
				return true;
5071
			}
5072
			return false;
5073
		},
5074
		endEditing: function(ignore_caret){
5075
			if(this._editTimer){
5076
				clearTimeout(this._editTimer);
5077
			}
5078
			if(this._inEditing){
5079
				this._endEditing(ignore_caret);
5080
				this._inEditing=false;
5081
			}
5082
		},
5083
		_getBookmark: function(){
5084
			var b=dojo.withGlobal(this.window,dijit.getBookmark);
5085
			if(dojo.isIE){
5086
				if(dojo.isArray(b)){//CONTROL
5087
					var tmp=[];
5088
					dojo.forEach(b,function(n){
5089
						tmp.push(dijit.range.getIndex(n,this.editNode).o);
5090
					},this);
5091
					b=tmp;
5092
				}
5093
			}else{//w3c range
5094
				var tmp=dijit.range.getIndex(b.startContainer,this.editNode).o
5095
				b={startContainer:tmp,
5096
					startOffset:b.startOffset,
5097
					endContainer:b.endContainer===b.startContainer?tmp:dijit.range.getIndex(b.endContainer,this.editNode).o,
5098
					endOffset:b.endOffset};
5099
			}
5100
			return b;
5101
		},
5102
		_beginEditing: function(cmd){
5103
			if(this._steps.length===0){
5104
				this._steps.push({'text':this.savedContent,'bookmark':this._getBookmark()});
5105
			}
5106
		},
5107
		_endEditing: function(ignore_caret){
5108
			var v=this.getValue(true);
5109
 
5110
			this._undoedSteps=[];//clear undoed steps
5111
			this._steps.push({'text':v,'bookmark':this._getBookmark()});
5112
		},
5113
		onKeyDown: function(e){
5114
			if(!this.customUndo){
5115
				this.inherited('onKeyDown',arguments);
5116
				return;
5117
			}
5118
			var k=e.keyCode,ks=dojo.keys;
5119
			if(e.ctrlKey){
5120
				if(k===90||k===122){ //z
5121
					dojo.stopEvent(e);
5122
					this.undo();
5123
					return;
5124
				}else if(k===89||k===121){ //y
5125
					dojo.stopEvent(e);
5126
					this.redo();
5127
					return;
5128
				}
5129
			}
5130
			this.inherited('onKeyDown',arguments);
5131
 
5132
			switch(k){
5133
					case ks.ENTER:
5134
						this.beginEditing();
5135
						break;
5136
					case ks.BACKSPACE:
5137
					case ks.DELETE:
5138
						this.beginEditing();
5139
						break;
5140
					case 88: //x
5141
					case 86: //v
5142
						if(e.ctrlKey && !e.altKey && !e.metaKey){
5143
							this.endEditing();//end current typing step if any
5144
							if(e.keyCode == 88){
5145
								this.beginEditing('cut');
5146
								//use timeout to trigger after the cut is complete
5147
								setTimeout(dojo.hitch(this, this.endEditing), 1);
5148
							}else{
5149
								this.beginEditing('paste');
5150
								//use timeout to trigger after the paste is complete
5151
								setTimeout(dojo.hitch(this, this.endEditing), 1);
5152
							}
5153
							break;
5154
						}
5155
						//pass through
5156
					default:
5157
						if(!e.ctrlKey && !e.altKey && !e.metaKey && (e.keyCode<dojo.keys.F1 || e.keyCode>dojo.keys.F15)){
5158
							this.beginEditing();
5159
							break;
5160
						}
5161
						//pass through
5162
					case ks.ALT:
5163
						this.endEditing();
5164
						break;
5165
					case ks.UP_ARROW:
5166
					case ks.DOWN_ARROW:
5167
					case ks.LEFT_ARROW:
5168
					case ks.RIGHT_ARROW:
5169
					case ks.HOME:
5170
					case ks.END:
5171
					case ks.PAGE_UP:
5172
					case ks.PAGE_DOWN:
5173
						this.endEditing(true);
5174
						break;
5175
					//maybe ctrl+backspace/delete, so don't endEditing when ctrl is pressed
5176
					case ks.CTRL:
5177
					case ks.SHIFT:
5178
					case ks.TAB:
5179
						break;
5180
				}
5181
		},
5182
		_onBlur: function(){
5183
			this.inherited('_onBlur',arguments);
5184
			this.endEditing(true);
5185
		},
5186
		onClick: function(){
5187
			this.endEditing(true);
5188
			this.inherited('onClick',arguments);
5189
		}
5190
		/* end of custom undo/redo support */
5191
	}
5192
);
5193
 
5194
/* the following code is to registered a handler to get default plugins */
5195
dojo.subscribe("dijit.Editor.getPlugin",null,function(o){
5196
	if(o.plugin){ return; }
5197
	var args=o.args, p;
5198
	var _p = dijit._editor._Plugin;
5199
	var name=args.name;
5200
	switch(name){
5201
		case "undo": case "redo": case "cut": case "copy": case "paste": case "insertOrderedList":
5202
		case "insertUnorderedList": case "indent": case "outdent": case "justifyCenter":
5203
		case "justifyFull": case "justifyLeft": case "justifyRight": case "delete":
5204
		case "selectAll": case "removeFormat":
5205
			p = new _p({ command: name });
5206
			break;
5207
 
5208
		case "bold": case "italic": case "underline": case "strikethrough":
5209
		case "subscript": case "superscript":
5210
			p = new _p({ buttonClass: dijit.form.ToggleButton, command: name });
5211
			break;
5212
		case "|":
5213
			p = new _p({ button: new dijit.ToolbarSeparator() });
5214
			break;
5215
		case "createLink":
5216
//					dojo['require']('dijit._editor.plugins.LinkDialog');
5217
			p = new dijit._editor.plugins.LinkDialog({ command: name });
5218
			break;
5219
		case "foreColor": case "hiliteColor":
5220
			p = new dijit._editor.plugins.TextColor({ command: name });
5221
			break;
5222
		case "fontName": case "fontSize": case "formatBlock":
5223
			p = new dijit._editor.plugins.FontChoice({ command: name });
5224
	}
5225
//	console.log('name',name,p);
5226
	o.plugin=p;
5227
});
5228
 
5229
}
5230
 
5231
if(!dojo._hasResource["dijit.Menu"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
5232
dojo._hasResource["dijit.Menu"] = true;
5233
dojo.provide("dijit.Menu");
5234
 
5235
 
5236
 
5237
 
5238
 
5239
dojo.declare(
5240
	"dijit.Menu",
5241
	[dijit._Widget, dijit._Templated, dijit._KeyNavContainer],
5242
{
5243
	constructor: function() {
5244
		this._bindings = [];
5245
	},
5246
 
5247
	templateString:
5248
			'<table class="dijit dijitMenu dijitReset dijitMenuTable" waiRole="menu" dojoAttachEvent="onkeypress:_onKeyPress">' +
5249
				'<tbody class="dijitReset" dojoAttachPoint="containerNode"></tbody>'+
5250
			'</table>',
5251
 
5252
	// targetNodeIds: String[]
5253
	//	Array of dom node ids of nodes to attach to.
5254
	//	Fill this with nodeIds upon widget creation and it becomes context menu for those nodes.
5255
	targetNodeIds: [],
5256
 
5257
	// contextMenuForWindow: Boolean
5258
	//	if true, right clicking anywhere on the window will cause this context menu to open;
5259
	//	if false, must specify targetNodeIds
5260
	contextMenuForWindow: false,
5261
 
5262
	// parentMenu: Widget
5263
	// pointer to menu that displayed me
5264
	parentMenu: null,
5265
 
5266
	// popupDelay: Integer
5267
	//	number of milliseconds before hovering (without clicking) causes the popup to automatically open
5268
	popupDelay: 500,
5269
 
5270
	// _contextMenuWithMouse: Boolean
5271
	//	used to record mouse and keyboard events to determine if a context
5272
	//	menu is being opened with the keyboard or the mouse
5273
	_contextMenuWithMouse: false,
5274
 
5275
	postCreate: function(){
5276
		if(this.contextMenuForWindow){
5277
			this.bindDomNode(dojo.body());
5278
		}else{
5279
			dojo.forEach(this.targetNodeIds, this.bindDomNode, this);
5280
		}
5281
		this.connectKeyNavHandlers([dojo.keys.UP_ARROW], [dojo.keys.DOWN_ARROW]);
5282
	},
5283
 
5284
	startup: function(){
5285
		dojo.forEach(this.getChildren(), function(child){ child.startup(); });
5286
		this.startupKeyNavChildren();
5287
	},
5288
 
5289
	onExecute: function(){
5290
		// summary: attach point for notification about when a menu item has been executed
5291
	},
5292
 
5293
	onCancel: function(/*Boolean*/ closeAll){
5294
		// summary: attach point for notification about when the user cancels the current menu
5295
	},
5296
 
5297
	_moveToPopup: function(/*Event*/ evt){
5298
		if(this.focusedChild && this.focusedChild.popup && !this.focusedChild.disabled){
5299
			this.focusedChild._onClick(evt);
5300
		}
5301
	},
5302
 
5303
	_onKeyPress: function(/*Event*/ evt){
5304
		// summary
5305
		//	Handle keyboard based menu navigation.
5306
		if(evt.ctrlKey || evt.altKey){ return; }
5307
 
5308
		switch(evt.keyCode){
5309
			case dojo.keys.RIGHT_ARROW:
5310
				this._moveToPopup(evt);
5311
				dojo.stopEvent(evt);
5312
				break;
5313
			case dojo.keys.LEFT_ARROW:
5314
				if(this.parentMenu){
5315
					this.onCancel(false);
5316
				}else{
5317
					dojo.stopEvent(evt);
5318
				}
5319
				break;
5320
		}
5321
	},
5322
 
5323
	onItemHover: function(/*MenuItem*/ item){
5324
		this.focusChild(item);
5325
 
5326
		if(this.focusedChild.popup && !this.focusedChild.disabled && !this.hover_timer){
5327
			this.hover_timer = setTimeout(dojo.hitch(this, "_openPopup"), this.popupDelay);
5328
		}
5329
	},
5330
 
5331
	_onChildBlur: function(item){
5332
		// Close all popups that are open and descendants of this menu
5333
		dijit.popup.close(item.popup);
5334
		item._blur();
5335
		this._stopPopupTimer();
5336
	},
5337
 
5338
	onItemUnhover: function(/*MenuItem*/ item){
5339
	},
5340
 
5341
	_stopPopupTimer: function(){
5342
		if(this.hover_timer){
5343
			clearTimeout(this.hover_timer);
5344
			this.hover_timer = null;
5345
		}
5346
	},
5347
 
5348
	_getTopMenu: function(){
5349
		for(var top=this; top.parentMenu; top=top.parentMenu);
5350
		return top;
5351
	},
5352
 
5353
	onItemClick: function(/*Widget*/ item){
5354
		// summary: user defined function to handle clicks on an item
5355
		// summary: internal function for clicks
5356
		if(item.disabled){ return false; }
5357
 
5358
		if(item.popup){
5359
			if(!this.is_open){
5360
				this._openPopup();
5361
			}
5362
		}else{
5363
			// before calling user defined handler, close hierarchy of menus
5364
			// and restore focus to place it was when menu was opened
5365
			this.onExecute();
5366
 
5367
			// user defined handler for click
5368
			item.onClick();
5369
		}
5370
	},
5371
 
5372
	// thanks burstlib!
5373
	_iframeContentWindow: function(/* HTMLIFrameElement */iframe_el) {
5374
		//	summary
5375
		//	returns the window reference of the passed iframe
5376
		var win = dijit.getDocumentWindow(dijit.Menu._iframeContentDocument(iframe_el)) ||
5377
			// Moz. TODO: is this available when defaultView isn't?
5378
			dijit.Menu._iframeContentDocument(iframe_el)['__parent__'] ||
5379
			(iframe_el.name && document.frames[iframe_el.name]) || null;
5380
		return win;	//	Window
5381
	},
5382
 
5383
	_iframeContentDocument: function(/* HTMLIFrameElement */iframe_el){
5384
		//	summary
5385
		//	returns a reference to the document object inside iframe_el
5386
		var doc = iframe_el.contentDocument // W3
5387
			|| (iframe_el.contentWindow && iframe_el.contentWindow.document) // IE
5388
			|| (iframe_el.name && document.frames[iframe_el.name] && document.frames[iframe_el.name].document)
5389
			|| null;
5390
		return doc;	//	HTMLDocument
5391
	},
5392
 
5393
	bindDomNode: function(/*String|DomNode*/ node){
5394
		// summary: attach menu to given node
5395
		node = dojo.byId(node);
5396
 
5397
		//TODO: this is to support context popups in Editor.  Maybe this shouldn't be in dijit.Menu
5398
		var win = dijit.getDocumentWindow(node.ownerDocument);
5399
		if(node.tagName.toLowerCase()=="iframe"){
5400
			win = this._iframeContentWindow(node);
5401
			node = dojo.withGlobal(win, dojo.body);
5402
		}
5403
 
5404
		// to capture these events at the top level,
5405
		// attach to document, not body
5406
		var cn = (node == dojo.body() ? dojo.doc : node);
5407
 
5408
		node[this.id] = this._bindings.push([
5409
			dojo.connect(cn, "oncontextmenu", this, "_openMyself"),
5410
			dojo.connect(cn, "onkeydown", this, "_contextKey"),
5411
			dojo.connect(cn, "onmousedown", this, "_contextMouse")
5412
		]);
5413
	},
5414
 
5415
	unBindDomNode: function(/*String|DomNode*/ nodeName){
5416
		// summary: detach menu from given node
5417
		var node = dojo.byId(nodeName);
5418
		var bid = node[this.id]-1, b = this._bindings[bid];
5419
		dojo.forEach(b, dojo.disconnect);
5420
		delete this._bindings[bid];
5421
	},
5422
 
5423
	_contextKey: function(e){
5424
		this._contextMenuWithMouse = false;
5425
		if (e.keyCode == dojo.keys.F10) {
5426
			dojo.stopEvent(e);
5427
			if (e.shiftKey && e.type=="keydown") {
5428
				// FF: copying the wrong property from e will cause the system
5429
				// context menu to appear in spite of stopEvent. Don't know
5430
				// exactly which properties cause this effect.
5431
				var _e = { target: e.target, pageX: e.pageX, pageY: e.pageY };
5432
				_e.preventDefault = _e.stopPropagation = function(){};
5433
				// IE: without the delay, focus work in "open" causes the system
5434
				// context menu to appear in spite of stopEvent.
5435
				window.setTimeout(dojo.hitch(this, function(){ this._openMyself(_e); }), 1);
5436
			}
5437
		}
5438
	},
5439
 
5440
	_contextMouse: function(e){
5441
		this._contextMenuWithMouse = true;
5442
	},
5443
 
5444
	_openMyself: function(/*Event*/ e){
5445
		// summary:
5446
		//		Internal function for opening myself when the user
5447
		//		does a right-click or something similar
5448
 
5449
		dojo.stopEvent(e);
5450
 
5451
		// Get coordinates.
5452
		// if we are opening the menu with the mouse or on safari open
5453
		// the menu at the mouse cursor
5454
		// (Safari does not have a keyboard command to open the context menu
5455
		// and we don't currently have a reliable way to determine
5456
		// _contextMenuWithMouse on Safari)
5457
		var x,y;
5458
		if(dojo.isSafari || this._contextMenuWithMouse){
5459
			x=e.pageX;
5460
			y=e.pageY;
5461
		}else{
5462
			// otherwise open near e.target
5463
			var coords = dojo.coords(e.target, true);
5464
			x = coords.x + 10;
5465
			y = coords.y + 10;
5466
		}
5467
 
5468
		var self=this;
5469
		var savedFocus = dijit.getFocus(this);
5470
		function closeAndRestoreFocus(){
5471
			// user has clicked on a menu or popup
5472
			dijit.focus(savedFocus);
5473
			dijit.popup.close(self);
5474
		}
5475
		dijit.popup.open({
5476
			popup: this,
5477
			x: x,
5478
			y: y,
5479
			onExecute: closeAndRestoreFocus,
5480
			onCancel: closeAndRestoreFocus,
5481
			orient: this.isLeftToRight() ? 'L' : 'R'
5482
		});
5483
		this.focus();
5484
 
5485
		this._onBlur = function(){
5486
			// Usually the parent closes the child widget but if this is a context
5487
			// menu then there is no parent
5488
			dijit.popup.close(this);
5489
			// don't try to restore focus; user has clicked another part of the screen
5490
			// and set focus there
5491
		}
5492
	},
5493
 
5494
	onOpen: function(/*Event*/ e){
5495
		// summary
5496
		//		Open menu relative to the mouse
5497
		this.isShowingNow = true;
5498
	},
5499
 
5500
	onClose: function(){
5501
		// summary: callback when this menu is closed
5502
		this._stopPopupTimer();
5503
		this.parentMenu = null;
5504
		this.isShowingNow = false;
5505
		this.currentPopup = null;
5506
		if(this.focusedChild){
5507
			this._onChildBlur(this.focusedChild);
5508
			this.focusedChild = null;
5509
		}
5510
	},
5511
 
5512
	_openPopup: function(){
5513
		// summary: open the popup to the side of the current menu item
5514
		this._stopPopupTimer();
5515
		var from_item = this.focusedChild;
5516
		var popup = from_item.popup;
5517
 
5518
		if(popup.isShowingNow){ return; }
5519
		popup.parentMenu = this;
5520
		var self = this;
5521
		dijit.popup.open({
5522
			parent: this,
5523
			popup: popup,
5524
			around: from_item.arrowCell,
5525
			orient: this.isLeftToRight() ? {'TR': 'TL', 'TL': 'TR'} : {'TL': 'TR', 'TR': 'TL'},
5526
			onCancel: function(){
5527
				// called when the child menu is canceled
5528
				dijit.popup.close(popup);
5529
				from_item.focus();	// put focus back on my node
5530
				self.currentPopup = null;
5531
			}
5532
		});
5533
 
5534
		this.currentPopup = popup;
5535
 
5536
		if(popup.focus){
5537
			popup.focus();
5538
		}
5539
	}
5540
}
5541
);
5542
 
5543
dojo.declare(
5544
	"dijit.MenuItem",
5545
	[dijit._Widget, dijit._Templated, dijit._Contained],
5546
{
5547
	// summary
5548
	//	A line item in a Menu2
5549
 
5550
	// Make 3 columns
5551
	//   icon, label, and expand arrow (BiDi-dependent) indicating sub-menu
5552
	templateString:
5553
		 '<tr class="dijitReset dijitMenuItem"'
5554
		+'dojoAttachEvent="onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick">'
5555
		+'<td class="dijitReset"><div class="dijitMenuItemIcon ${iconClass}" dojoAttachPoint="iconNode" ></div></td>'
5556
		+'<td tabIndex="-1" class="dijitReset dijitMenuItemLabel" dojoAttachPoint="containerNode" waiRole="menuitem"></td>'
5557
		+'<td class="dijitReset" dojoAttachPoint="arrowCell">'
5558
			+'<div class="dijitMenuExpand" dojoAttachPoint="expand" style="display:none">'
5559
			+'<span class="dijitInline dijitArrowNode dijitMenuExpandInner">+</span>'
5560
			+'</div>'
5561
		+'</td>'
5562
		+'</tr>',
5563
 
5564
	// label: String
5565
	//	menu text
5566
	label: '',
5567
 
5568
	// iconClass: String
5569
	//	class to apply to div in button to make it display an icon
5570
	iconClass: "",
5571
 
5572
	// disabled: Boolean
5573
	//  if true, the menu item is disabled
5574
	//  if false, the menu item is enabled
5575
	disabled: false,
5576
 
5577
	postCreate: function(){
5578
		dojo.setSelectable(this.domNode, false);
5579
		this.setDisabled(this.disabled);
5580
		if(this.label){
5581
			this.containerNode.innerHTML=this.label;
5582
		}
5583
	},
5584
 
5585
	_onHover: function(){
5586
		// summary: callback when mouse is moved onto menu item
5587
		this.getParent().onItemHover(this);
5588
	},
5589
 
5590
	_onUnhover: function(){
5591
		// summary: callback when mouse is moved off of menu item
5592
		// if we are unhovering the currently selected item
5593
		// then unselect it
5594
		this.getParent().onItemUnhover(this);
5595
	},
5596
 
5597
	_onClick: function(evt){
5598
		this.getParent().onItemClick(this);
5599
		dojo.stopEvent(evt);
5600
	},
5601
 
5602
	onClick: function() {
5603
		// summary
5604
		//	User defined function to handle clicks
5605
	},
5606
 
5607
	focus: function(){
5608
		dojo.addClass(this.domNode, 'dijitMenuItemHover');
5609
		try{
5610
			dijit.focus(this.containerNode);
5611
		}catch(e){
5612
			// this throws on IE (at least) in some scenarios
5613
		}
5614
	},
5615
 
5616
	_blur: function(){
5617
		dojo.removeClass(this.domNode, 'dijitMenuItemHover');
5618
	},
5619
 
5620
	setDisabled: function(/*Boolean*/ value){
5621
		// summary: enable or disable this menu item
5622
		this.disabled = value;
5623
		dojo[value ? "addClass" : "removeClass"](this.domNode, 'dijitMenuItemDisabled');
5624
		dijit.setWaiState(this.containerNode, 'disabled', value ? 'true' : 'false');
5625
	}
5626
});
5627
 
5628
dojo.declare(
5629
	"dijit.PopupMenuItem",
5630
	dijit.MenuItem,
5631
{
5632
	_fillContent: function(){
5633
		// my inner HTML contains both the menu item text and a popup widget, like
5634
		// <div dojoType="dijit.PopupMenuItem">
5635
		//		<span>pick me</span>
5636
		//		<popup> ... </popup>
5637
		// </div>
5638
		// the first part holds the menu item text and the second part is the popup
5639
		if(this.srcNodeRef){
5640
			var nodes = dojo.query("*", this.srcNodeRef);
5641
			dijit.PopupMenuItem.superclass._fillContent.call(this, nodes[0]);
5642
 
5643
			// save pointer to srcNode so we can grab the drop down widget after it's instantiated
5644
			this.dropDownContainer = this.srcNodeRef;
5645
		}
5646
	},
5647
 
5648
	startup: function(){
5649
		// we didn't copy the dropdown widget from the this.srcNodeRef, so it's in no-man's
5650
		// land now.  move it to document.body.
5651
		if(!this.popup){
5652
			var node = dojo.query("[widgetId]", this.dropDownContainer)[0];
5653
			this.popup = dijit.byNode(node);
5654
		}
5655
		dojo.body().appendChild(this.popup.domNode);
5656
 
5657
		this.popup.domNode.style.display="none";
5658
		dojo.addClass(this.expand, "dijitMenuExpandEnabled");
5659
		dojo.style(this.expand, "display", "");
5660
		dijit.setWaiState(this.containerNode, "haspopup", "true");
5661
	}
5662
});
5663
 
5664
dojo.declare(
5665
	"dijit.MenuSeparator",
5666
	[dijit._Widget, dijit._Templated, dijit._Contained],
5667
{
5668
	// summary
5669
	//	A line between two menu items
5670
 
5671
	templateString: '<tr class="dijitMenuSeparator"><td colspan=3>'
5672
			+'<div class="dijitMenuSeparatorTop"></div>'
5673
			+'<div class="dijitMenuSeparatorBottom"></div>'
5674
			+'</td></tr>',
5675
 
5676
	postCreate: function(){
5677
		dojo.setSelectable(this.domNode, false);
5678
	},
5679
 
5680
	isFocusable: function(){
5681
		// summary:
5682
		//		over ride to always return false
5683
		return false;
5684
	}
5685
});
5686
 
5687
}
5688
 
5689
if(!dojo._hasResource["dojo.regexp"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
5690
dojo._hasResource["dojo.regexp"] = true;
5691
dojo.provide("dojo.regexp");
5692
 
5693
dojo.regexp.escapeString = function(/*String*/str, /*String?*/except){
5694
	//	summary:
5695
	//		Adds escape sequences for special characters in regular expressions
5696
	// except:
5697
	//		a String with special characters to be left unescaped
5698
 
5699
//	return str.replace(/([\f\b\n\t\r[\^$|?*+(){}])/gm, "\\$1"); // string
5700
	return str.replace(/([\.$?*!=:|{}\(\)\[\]\\\/^])/g, function(ch){
5701
		if(except && except.indexOf(ch) != -1){
5702
			return ch;
5703
		}
5704
		return "\\" + ch;
5705
	}); // String
5706
}
5707
 
5708
dojo.regexp.buildGroupRE = function(/*Object|Array*/arr, /*Function*/re, /*Boolean?*/nonCapture){
5709
	//	summary:
5710
	//		Builds a regular expression that groups subexpressions
5711
	//	description:
5712
	//		A utility function used by some of the RE generators. The
5713
	//		subexpressions are constructed by the function, re, in the second
5714
	//		parameter.  re builds one subexpression for each elem in the array
5715
	//		a, in the first parameter. Returns a string for a regular
5716
	//		expression that groups all the subexpressions.
5717
	// arr:
5718
	//		A single value or an array of values.
5719
	// re:
5720
	//		A function. Takes one parameter and converts it to a regular
5721
	//		expression.
5722
	// nonCapture:
5723
	//		If true, uses non-capturing match, otherwise matches are retained
5724
	//		by regular expression. Defaults to false
5725
 
5726
	// case 1: a is a single value.
5727
	if(!(arr instanceof Array)){
5728
		return re(arr); // String
5729
	}
5730
 
5731
	// case 2: a is an array
5732
	var b = [];
5733
	for(var i = 0; i < arr.length; i++){
5734
		// convert each elem to a RE
5735
		b.push(re(arr[i]));
5736
	}
5737
 
5738
	 // join the REs as alternatives in a RE group.
5739
	return dojo.regexp.group(b.join("|"), nonCapture); // String
5740
}
5741
 
5742
dojo.regexp.group = function(/*String*/expression, /*Boolean?*/nonCapture){
5743
	// summary:
5744
	//		adds group match to expression
5745
	// nonCapture:
5746
	//		If true, uses non-capturing match, otherwise matches are retained
5747
	//		by regular expression.
5748
	return "(" + (nonCapture ? "?:":"") + expression + ")"; // String
5749
}
5750
 
5751
}
5752
 
5753
if(!dojo._hasResource["dojo.number"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
5754
dojo._hasResource["dojo.number"] = true;
5755
dojo.provide("dojo.number");
5756
 
5757
 
5758
 
5759
 
5760
 
5761
 
5762
 
5763
/*=====
5764
dojo.number.__formatOptions = function(kwArgs){
5765
	//	pattern: String?
5766
	//		override formatting pattern with this string (see
5767
	//		dojo.number._applyPattern)
5768
	//	type: String?
5769
	//		choose a format type based on the locale from the following:
5770
	//		decimal, scientific, percent, currency. decimal by default.
5771
	//	places: Number?
5772
	//		fixed number of decimal places to show.  This overrides any
5773
	//		information in the provided pattern.
5774
	//	round: NUmber?
5775
	//		5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
5776
	//		means don't round.
5777
	//	currency: String?
5778
	//		iso4217 currency code
5779
	//	symbol: String?
5780
	//		localized currency symbol
5781
	//	locale: String?
5782
	//		override the locale used to determine formatting rules
5783
}
5784
=====*/
5785
 
5786
dojo.number.format = function(/*Number*/value, /*dojo.number.__formatOptions?*/options){
5787
	// summary:
5788
	//		Format a Number as a String, using locale-specific settings
5789
	// description:
5790
	//		Create a string from a Number using a known localized pattern.
5791
	//		Formatting patterns appropriate to the locale are chosen from the
5792
	//		CLDR http://unicode.org/cldr as well as the appropriate symbols and
5793
	//		delimiters.  See http://www.unicode.org/reports/tr35/#Number_Elements
5794
	// value:
5795
	//		the number to be formatted.  If not a valid JavaScript number,
5796
	//		return null.
5797
 
5798
	options = dojo.mixin({}, options || {});
5799
	var locale = dojo.i18n.normalizeLocale(options.locale);
5800
	var bundle = dojo.i18n.getLocalization("dojo.cldr", "number", locale);
5801
	options.customs = bundle;
5802
	var pattern = options.pattern || bundle[(options.type || "decimal") + "Format"];
5803
	if(isNaN(value)){ return null; } // null
5804
	return dojo.number._applyPattern(value, pattern, options); // String
5805
};
5806
 
5807
//dojo.number._numberPatternRE = /(?:[#0]*,?)*[#0](?:\.0*#*)?/; // not precise, but good enough
5808
dojo.number._numberPatternRE = /[#0,]*[#0](?:\.0*#*)?/; // not precise, but good enough
5809
 
5810
dojo.number._applyPattern = function(/*Number*/value, /*String*/pattern, /*dojo.number.__formatOptions?*/options){
5811
	// summary:
5812
	//		Apply pattern to format value as a string using options. Gives no
5813
	//		consideration to local customs.
5814
	// value:
5815
	//		the number to be formatted.
5816
	// pattern:
5817
	//		a pattern string as described in
5818
	//		http://www.unicode.org/reports/tr35/#Number_Format_Patterns
5819
	// options: dojo.number.__formatOptions?
5820
	//		_applyPattern is usually called via dojo.number.format() which
5821
	//		populates an extra property in the options parameter, "customs".
5822
	//		The customs object specifies group and decimal parameters if set.
5823
 
5824
	//TODO: support escapes
5825
	options = options || {};
5826
	var group = options.customs.group;
5827
	var decimal = options.customs.decimal;
5828
 
5829
	var patternList = pattern.split(';');
5830
	var positivePattern = patternList[0];
5831
	pattern = patternList[(value < 0) ? 1 : 0] || ("-" + positivePattern);
5832
 
5833
	//TODO: only test against unescaped
5834
	if(pattern.indexOf('%') != -1){
5835
		value *= 100;
5836
	}else if(pattern.indexOf('\u2030') != -1){
5837
		value *= 1000; // per mille
5838
	}else if(pattern.indexOf('\u00a4') != -1){
5839
		group = options.customs.currencyGroup || group;//mixins instead?
5840
		decimal = options.customs.currencyDecimal || decimal;// Should these be mixins instead?
5841
		pattern = pattern.replace(/\u00a4{1,3}/, function(match){
5842
			var prop = ["symbol", "currency", "displayName"][match.length-1];
5843
			return options[prop] || options.currency || "";
5844
		});
5845
	}else if(pattern.indexOf('E') != -1){
5846
		throw new Error("exponential notation not supported");
5847
	}
5848
 
5849
	//TODO: support @ sig figs?
5850
	var numberPatternRE = dojo.number._numberPatternRE;
5851
	var numberPattern = positivePattern.match(numberPatternRE);
5852
	if(!numberPattern){
5853
		throw new Error("unable to find a number expression in pattern: "+pattern);
5854
	}
5855
	return pattern.replace(numberPatternRE,
5856
		dojo.number._formatAbsolute(value, numberPattern[0], {decimal: decimal, group: group, places: options.places}));
5857
}
5858
 
5859
dojo.number.round = function(/*Number*/value, /*Number*/places, /*Number?*/multiple){
5860
	//	summary:
5861
	//		Rounds the number at the given number of places
5862
	//	value:
5863
	//		the number to round
5864
	//	places:
5865
	//		the number of decimal places where rounding takes place
5866
	//	multiple:
5867
	//		rounds next place to nearest multiple
5868
 
5869
	var pieces = String(value).split(".");
5870
	var length = (pieces[1] && pieces[1].length) || 0;
5871
	if(length > places){
5872
		var factor = Math.pow(10, places);
5873
		if(multiple > 0){factor *= 10/multiple;places++;} //FIXME
5874
		value = Math.round(value * factor)/factor;
5875
 
5876
		// truncate to remove any residual floating point values
5877
		pieces = String(value).split(".");
5878
		length = (pieces[1] && pieces[1].length) || 0;
5879
		if(length > places){
5880
			pieces[1] = pieces[1].substr(0, places);
5881
			value = Number(pieces.join("."));
5882
		}
5883
	}
5884
	return value; //Number
5885
}
5886
 
5887
/*=====
5888
dojo.number.__formatAbsoluteOptions = function(kwArgs){
5889
	//	decimal: String?
5890
	//		the decimal separator
5891
	//	group: String?
5892
	//		the group separator
5893
	//	places: Integer?
5894
	//		number of decimal places
5895
	//	round: Number?
5896
	//		5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
5897
	//		means don't round.
5898
}
5899
=====*/
5900
 
5901
dojo.number._formatAbsolute = function(/*Number*/value, /*String*/pattern, /*dojo.number.__formatAbsoluteOptions?*/options){
5902
	// summary:
5903
	//		Apply numeric pattern to absolute value using options. Gives no
5904
	//		consideration to local customs.
5905
	// value:
5906
	//		the number to be formatted, ignores sign
5907
	// pattern:
5908
	//		the number portion of a pattern (e.g. #,##0.00)
5909
	options = options || {};
5910
	if(options.places === true){options.places=0;}
5911
	if(options.places === Infinity){options.places=6;} // avoid a loop; pick a limit
5912
 
5913
	var patternParts = pattern.split(".");
5914
	var maxPlaces = (options.places >= 0) ? options.places : (patternParts[1] && patternParts[1].length) || 0;
5915
	if(!(options.round < 0)){
5916
		value = dojo.number.round(value, maxPlaces, options.round);
5917
	}
5918
 
5919
	var valueParts = String(Math.abs(value)).split(".");
5920
	var fractional = valueParts[1] || "";
5921
	if(options.places){
5922
		valueParts[1] = dojo.string.pad(fractional.substr(0, options.places), options.places, '0', true);
5923
	}else if(patternParts[1] && options.places !== 0){
5924
		// Pad fractional with trailing zeros
5925
		var pad = patternParts[1].lastIndexOf("0") + 1;
5926
		if(pad > fractional.length){
5927
			valueParts[1] = dojo.string.pad(fractional, pad, '0', true);
5928
		}
5929
 
5930
		// Truncate fractional
5931
		var places = patternParts[1].length;
5932
		if(places < fractional.length){
5933
			valueParts[1] = fractional.substr(0, places);
5934
		}
5935
	}else{
5936
		if(valueParts[1]){ valueParts.pop(); }
5937
	}
5938
 
5939
	// Pad whole with leading zeros
5940
	var patternDigits = patternParts[0].replace(',', '');
5941
	pad = patternDigits.indexOf("0");
5942
	if(pad != -1){
5943
		pad = patternDigits.length - pad;
5944
		if(pad > valueParts[0].length){
5945
			valueParts[0] = dojo.string.pad(valueParts[0], pad);
5946
		}
5947
 
5948
		// Truncate whole
5949
		if(patternDigits.indexOf("#") == -1){
5950
			valueParts[0] = valueParts[0].substr(valueParts[0].length - pad);
5951
		}
5952
	}
5953
 
5954
	// Add group separators
5955
	var index = patternParts[0].lastIndexOf(',');
5956
	var groupSize, groupSize2;
5957
	if(index != -1){
5958
		groupSize = patternParts[0].length - index - 1;
5959
		var remainder = patternParts[0].substr(0, index);
5960
		index = remainder.lastIndexOf(',');
5961
		if(index != -1){
5962
			groupSize2 = remainder.length - index - 1;
5963
		}
5964
	}
5965
	var pieces = [];
5966
	for(var whole = valueParts[0]; whole;){
5967
		var off = whole.length - groupSize;
5968
		pieces.push((off > 0) ? whole.substr(off) : whole);
5969
		whole = (off > 0) ? whole.slice(0, off) : "";
5970
		if(groupSize2){
5971
			groupSize = groupSize2;
5972
			delete groupSize2;
5973
		}
5974
	}
5975
	valueParts[0] = pieces.reverse().join(options.group || ",");
5976
 
5977
	return valueParts.join(options.decimal || ".");
5978
};
5979
 
5980
/*=====
5981
dojo.number.__regexpOptions = function(kwArgs){
5982
	//	pattern: String?
5983
	//		override pattern with this string.  Default is provided based on
5984
	//		locale.
5985
	//	type: String?
5986
	//		choose a format type based on the locale from the following:
5987
	//		decimal, scientific, percent, currency. decimal by default.
5988
	//	locale: String?
5989
	//		override the locale used to determine formatting rules
5990
	//	strict: Boolean?
5991
	//		strict parsing, false by default
5992
	//	places: Number|String?
5993
	//		number of decimal places to accept: Infinity, a positive number, or
5994
	//		a range "n,m".  By default, defined by pattern.
5995
}
5996
=====*/
5997
dojo.number.regexp = function(/*dojo.number.__regexpOptions?*/options){
5998
	//	summary:
5999
	//		Builds the regular needed to parse a number
6000
	//	description:
6001
	//		Returns regular expression with positive and negative match, group
6002
	//		and decimal separators
6003
	return dojo.number._parseInfo(options).regexp; // String
6004
}
6005
 
6006
dojo.number._parseInfo = function(/*Object?*/options){
6007
	options = options || {};
6008
	var locale = dojo.i18n.normalizeLocale(options.locale);
6009
	var bundle = dojo.i18n.getLocalization("dojo.cldr", "number", locale);
6010
	var pattern = options.pattern || bundle[(options.type || "decimal") + "Format"];
6011
//TODO: memoize?
6012
	var group = bundle.group;
6013
	var decimal = bundle.decimal;
6014
	var factor = 1;
6015
 
6016
	if(pattern.indexOf('%') != -1){
6017
		factor /= 100;
6018
	}else if(pattern.indexOf('\u2030') != -1){
6019
		factor /= 1000; // per mille
6020
	}else{
6021
		var isCurrency = pattern.indexOf('\u00a4') != -1;
6022
		if(isCurrency){
6023
			group = bundle.currencyGroup || group;
6024
			decimal = bundle.currencyDecimal || decimal;
6025
		}
6026
	}
6027
 
6028
	//TODO: handle quoted escapes
6029
	var patternList = pattern.split(';');
6030
	if(patternList.length == 1){
6031
		patternList.push("-" + patternList[0]);
6032
	}
6033
 
6034
	var re = dojo.regexp.buildGroupRE(patternList, function(pattern){
6035
		pattern = "(?:"+dojo.regexp.escapeString(pattern, '.')+")";
6036
		return pattern.replace(dojo.number._numberPatternRE, function(format){
6037
			var flags = {
6038
				signed: false,
6039
				separator: options.strict ? group : [group,""],
6040
				fractional: options.fractional,
6041
				decimal: decimal,
6042
				exponent: false};
6043
			var parts = format.split('.');
6044
			var places = options.places;
6045
			if(parts.length == 1 || places === 0){flags.fractional = false;}
6046
			else{
6047
				if(typeof places == "undefined"){ places = parts[1].lastIndexOf('0')+1; }
6048
				if(places && options.fractional == undefined){flags.fractional = true;} // required fractional, unless otherwise specified
6049
				if(!options.places && (places < parts[1].length)){ places += "," + parts[1].length; }
6050
				flags.places = places;
6051
			}
6052
			var groups = parts[0].split(',');
6053
			if(groups.length>1){
6054
				flags.groupSize = groups.pop().length;
6055
				if(groups.length>1){
6056
					flags.groupSize2 = groups.pop().length;
6057
				}
6058
			}
6059
			return "("+dojo.number._realNumberRegexp(flags)+")";
6060
		});
6061
	}, true);
6062
 
6063
	if(isCurrency){
6064
		// substitute the currency symbol for the placeholder in the pattern
6065
		re = re.replace(/(\s*)(\u00a4{1,3})(\s*)/g, function(match, before, target, after){
6066
			var prop = ["symbol", "currency", "displayName"][target.length-1];
6067
			var symbol = dojo.regexp.escapeString(options[prop] || options.currency || "");
6068
			before = before ? "\\s" : "";
6069
			after = after ? "\\s" : "";
6070
			if(!options.strict){
6071
				if(before){before += "*";}
6072
				if(after){after += "*";}
6073
				return "(?:"+before+symbol+after+")?";
6074
			}
6075
			return before+symbol+after;
6076
		});
6077
	}
6078
 
6079
//TODO: substitute localized sign/percent/permille/etc.?
6080
 
6081
	// normalize whitespace and return
6082
	return {regexp: re.replace(/[\xa0 ]/g, "[\\s\\xa0]"), group: group, decimal: decimal, factor: factor}; // Object
6083
}
6084
 
6085
/*=====
6086
dojo.number.__parseOptions = function(kwArgs){
6087
	//	pattern: String
6088
	//		override pattern with this string.  Default is provided based on
6089
	//		locale.
6090
	//	type: String?
6091
	//		choose a format type based on the locale from the following:
6092
	//		decimal, scientific, percent, currency. decimal by default.
6093
	//	locale: String
6094
	//		override the locale used to determine formatting rules
6095
	//	strict: Boolean?
6096
	//		strict parsing, false by default
6097
	//	currency: Object
6098
	//		object with currency information
6099
}
6100
=====*/
6101
dojo.number.parse = function(/*String*/expression, /*dojo.number.__parseOptions?*/options){
6102
	// summary:
6103
	//		Convert a properly formatted string to a primitive Number, using
6104
	//		locale-specific settings.
6105
	// description:
6106
	//		Create a Number from a string using a known localized pattern.
6107
	//		Formatting patterns are chosen appropriate to the locale.
6108
	//		Formatting patterns are implemented using the syntax described at
6109
	//		*URL*
6110
	// expression:
6111
	//		A string representation of a Number
6112
	var info = dojo.number._parseInfo(options);
6113
	var results = (new RegExp("^"+info.regexp+"$")).exec(expression);
6114
	if(!results){
6115
		return NaN; //NaN
6116
	}
6117
	var absoluteMatch = results[1]; // match for the positive expression
6118
	if(!results[1]){
6119
		if(!results[2]){
6120
			return NaN; //NaN
6121
		}
6122
		// matched the negative pattern
6123
		absoluteMatch =results[2];
6124
		info.factor *= -1;
6125
	}
6126
 
6127
	// Transform it to something Javascript can parse as a number.  Normalize
6128
	// decimal point and strip out group separators or alternate forms of whitespace
6129
	absoluteMatch = absoluteMatch.
6130
		replace(new RegExp("["+info.group + "\\s\\xa0"+"]", "g"), "").
6131
		replace(info.decimal, ".");
6132
	// Adjust for negative sign, percent, etc. as necessary
6133
	return Number(absoluteMatch) * info.factor; //Number
6134
};
6135
 
6136
/*=====
6137
dojo.number.__realNumberRegexpFlags = function(kwArgs){
6138
	//	places: Number?
6139
	//		The integer number of decimal places or a range given as "n,m".  If
6140
	//		not given, the decimal part is optional and the number of places is
6141
	//		unlimited.
6142
	//	decimal: String?
6143
	//		A string for the character used as the decimal point.  Default
6144
	//		is ".".
6145
	//	fractional: Boolean|Array?
6146
	//		Whether decimal places are allowed.  Can be true, false, or [true,
6147
	//		false].  Default is [true, false]
6148
	//	exponent: Boolean|Array?
6149
	//		Express in exponential notation.  Can be true, false, or [true,
6150
	//		false]. Default is [true, false], (i.e. will match if the
6151
	//		exponential part is present are not).
6152
	//	eSigned: Boolean|Array?
6153
	//		The leading plus-or-minus sign on the exponent.  Can be true,
6154
	//		false, or [true, false].  Default is [true, false], (i.e. will
6155
	//		match if it is signed or unsigned).  flags in regexp.integer can be
6156
	//		applied.
6157
}
6158
=====*/
6159
 
6160
dojo.number._realNumberRegexp = function(/*dojo.number.__realNumberRegexpFlags?*/flags){
6161
	// summary:
6162
	//		Builds a regular expression to match a real number in exponential
6163
	//		notation
6164
	// flags:
6165
	//		An object
6166
 
6167
	// assign default values to missing paramters
6168
	flags = flags || {};
6169
	if(typeof flags.places == "undefined"){ flags.places = Infinity; }
6170
	if(typeof flags.decimal != "string"){ flags.decimal = "."; }
6171
	if(typeof flags.fractional == "undefined" || /^0/.test(flags.places)){ flags.fractional = [true, false]; }
6172
	if(typeof flags.exponent == "undefined"){ flags.exponent = [true, false]; }
6173
	if(typeof flags.eSigned == "undefined"){ flags.eSigned = [true, false]; }
6174
 
6175
	// integer RE
6176
	var integerRE = dojo.number._integerRegexp(flags);
6177
 
6178
	// decimal RE
6179
	var decimalRE = dojo.regexp.buildGroupRE(flags.fractional,
6180
		function(q){
6181
			var re = "";
6182
			if(q && (flags.places!==0)){
6183
				re = "\\" + flags.decimal;
6184
				if(flags.places == Infinity){
6185
					re = "(?:" + re + "\\d+)?";
6186
				}else{
6187
					re += "\\d{" + flags.places + "}";
6188
				}
6189
			}
6190
			return re;
6191
		},
6192
		true
6193
	);
6194
 
6195
	// exponent RE
6196
	var exponentRE = dojo.regexp.buildGroupRE(flags.exponent,
6197
		function(q){
6198
			if(q){ return "([eE]" + dojo.number._integerRegexp({ signed: flags.eSigned}) + ")"; }
6199
			return "";
6200
		}
6201
	);
6202
 
6203
	// real number RE
6204
	var realRE = integerRE + decimalRE;
6205
	// allow for decimals without integers, e.g. .25
6206
	if(decimalRE){realRE = "(?:(?:"+ realRE + ")|(?:" + decimalRE + "))";}
6207
	return realRE + exponentRE; // String
6208
};
6209
 
6210
/*=====
6211
dojo.number.__integerRegexpFlags = function(kwArgs){
6212
	//	signed: Boolean?
6213
	//		The leading plus-or-minus sign. Can be true, false, or [true,
6214
	//		false]. Default is [true, false], (i.e. will match if it is signed
6215
	//		or unsigned).
6216
	//	separator: String?
6217
	//		The character used as the thousands separator. Default is no
6218
	//		separator. For more than one symbol use an array, e.g. [",", ""],
6219
	//		makes ',' optional.
6220
	//	groupSize: Number?
6221
	//		group size between separators
6222
	//	flags.groupSize2: Number?
6223
	//		second grouping (for India)
6224
}
6225
=====*/
6226
 
6227
dojo.number._integerRegexp = function(/*dojo.number.__integerRegexpFlags?*/flags){
6228
	// summary:
6229
	//		Builds a regular expression that matches an integer
6230
	// flags:
6231
	//		An object
6232
 
6233
	// assign default values to missing paramters
6234
	flags = flags || {};
6235
	if(typeof flags.signed == "undefined"){ flags.signed = [true, false]; }
6236
	if(typeof flags.separator == "undefined"){
6237
		flags.separator = "";
6238
	}else if(typeof flags.groupSize == "undefined"){
6239
		flags.groupSize = 3;
6240
	}
6241
	// build sign RE
6242
	var signRE = dojo.regexp.buildGroupRE(flags.signed,
6243
		function(q) { return q ? "[-+]" : ""; },
6244
		true
6245
	);
6246
 
6247
	// number RE
6248
	var numberRE = dojo.regexp.buildGroupRE(flags.separator,
6249
		function(sep){
6250
			if(!sep){
6251
				return "(?:0|[1-9]\\d*)";
6252
			}
6253
 
6254
			sep = dojo.regexp.escapeString(sep);
6255
			if(sep == " "){ sep = "\\s"; }
6256
			else if(sep == "\xa0"){ sep = "\\s\\xa0"; }
6257
 
6258
			var grp = flags.groupSize, grp2 = flags.groupSize2;
6259
			if(grp2){
6260
				var grp2RE = "(?:0|[1-9]\\d{0," + (grp2-1) + "}(?:[" + sep + "]\\d{" + grp2 + "})*[" + sep + "]\\d{" + grp + "})";
6261
				return ((grp-grp2) > 0) ? "(?:" + grp2RE + "|(?:0|[1-9]\\d{0," + (grp-1) + "}))" : grp2RE;
6262
			}
6263
			return "(?:0|[1-9]\\d{0," + (grp-1) + "}(?:[" + sep + "]\\d{" + grp + "})*)";
6264
		},
6265
		true
6266
	);
6267
 
6268
	// integer RE
6269
	return signRE + numberRE; // String
6270
}
6271
 
6272
}
6273
 
6274
if(!dojo._hasResource["dijit.ProgressBar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
6275
dojo._hasResource["dijit.ProgressBar"] = true;
6276
dojo.provide("dijit.ProgressBar");
6277
 
6278
 
6279
 
6280
 
6281
 
6282
 
6283
 
6284
dojo.declare("dijit.ProgressBar", [dijit._Widget, dijit._Templated], {
6285
	// summary:
6286
	// a progress widget
6287
	//
6288
	// usage:
6289
	// <div dojoType="ProgressBar"
6290
	//   places="0"
6291
	//   progress="..." maximum="..."></div>
6292
 
6293
	// progress: String (Percentage or Number)
6294
	// initial progress value.
6295
	// with "%": percentage value, 0% <= progress <= 100%
6296
	// or without "%": absolute value, 0 <= progress <= maximum
6297
	progress: "0",
6298
 
6299
	// maximum: Float
6300
	// max sample number
6301
	maximum: 100,
6302
 
6303
	// places: Number
6304
	// number of places to show in values; 0 by default
6305
	places: 0,
6306
 
6307
	// indeterminate: Boolean
6308
	// false: show progress
6309
	// true: show that a process is underway but that the progress is unknown
6310
	indeterminate: false,
6311
 
6312
	templateString:"<div class=\"dijitProgressBar dijitProgressBarEmpty\"\n\t><div waiRole=\"progressbar\" tabindex=\"0\" dojoAttachPoint=\"internalProgress\" class=\"dijitProgressBarFull\"\n\t\t><div class=\"dijitProgressBarTile\"></div\n\t\t><span style=\"visibility:hidden\">&nbsp;</span\n\t></div\n\t><div dojoAttachPoint=\"label\" class=\"dijitProgressBarLabel\" id=\"${id}_label\">&nbsp;</div\n\t><img dojoAttachPoint=\"inteterminateHighContrastImage\" class=\"dijitProgressBarIndeterminateHighContrastImage\"\n\t></img\n></div>\n",
6313
 
6314
	_indeterminateHighContrastImagePath:
6315
		dojo.moduleUrl("dijit", "themes/a11y/indeterminate_progress.gif"),
6316
 
6317
	// public functions
6318
	postCreate: function(){
6319
		this.inherited("postCreate",arguments);
6320
		this.inteterminateHighContrastImage.setAttribute("src",
6321
			this._indeterminateHighContrastImagePath);
6322
		this.update();
6323
	},
6324
 
6325
	update: function(/*Object?*/attributes){
6326
		// summary: update progress information
6327
		//
6328
		// attributes: may provide progress and/or maximum properties on this parameter,
6329
		//	see attribute specs for details.
6330
		dojo.mixin(this, attributes||{});
6331
		var percent = 1, classFunc;
6332
		if(this.indeterminate){
6333
			classFunc = "addClass";
6334
			dijit.removeWaiState(this.internalProgress, "valuenow");
6335
			dijit.removeWaiState(this.internalProgress, "valuemin");
6336
			dijit.removeWaiState(this.internalProgress, "valuemax");
6337
		}else{
6338
			classFunc = "removeClass";
6339
			if(String(this.progress).indexOf("%") != -1){
6340
				percent = Math.min(parseFloat(this.progress)/100, 1);
6341
				this.progress = percent * this.maximum;
6342
			}else{
6343
				this.progress = Math.min(this.progress, this.maximum);
6344
				percent = this.progress / this.maximum;
6345
			}
6346
			var text = this.report(percent);
6347
			this.label.firstChild.nodeValue = text;
6348
			dijit.setWaiState(this.internalProgress, "describedby", this.label.id);
6349
			dijit.setWaiState(this.internalProgress, "valuenow", this.progress);
6350
			dijit.setWaiState(this.internalProgress, "valuemin", 0);
6351
			dijit.setWaiState(this.internalProgress, "valuemax", this.maximum);
6352
		}
6353
		dojo[classFunc](this.domNode, "dijitProgressBarIndeterminate");
6354
		this.internalProgress.style.width = (percent * 100) + "%";
6355
		this.onChange();
6356
	},
6357
 
6358
	report: function(/*float*/percent){
6359
		// Generates message to show; may be overridden by user
6360
		return dojo.number.format(percent, {type: "percent", places: this.places, locale: this.lang});
6361
	},
6362
 
6363
	onChange: function(){}
6364
});
6365
 
6366
}
6367
 
6368
if(!dojo._hasResource["dijit.TitlePane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
6369
dojo._hasResource["dijit.TitlePane"] = true;
6370
dojo.provide("dijit.TitlePane");
6371
 
6372
 
6373
 
6374
 
6375
 
6376
 
6377
dojo.declare(
6378
	"dijit.TitlePane",
6379
	[dijit.layout.ContentPane, dijit._Templated],
6380
{
6381
	// summary
6382
	//		A pane with a title on top, that can be opened or collapsed.
6383
	//
6384
	// title: String
6385
	//		Title of the pane
6386
	title: "",
6387
 
6388
	// open: Boolean
6389
	//		Whether pane is opened or closed.
6390
	open: true,
6391
 
6392
	// duration: Integer
6393
	//		Time in milliseconds to fade in/fade out
6394
	duration: 250,
6395
 
6396
	// baseClass: String
6397
	//	the root className to use for the various states of this widget
6398
	baseClass: "dijitTitlePane",
6399
 
6400
	templateString:"<div class=\"dijitTitlePane\">\n\t<div dojoAttachEvent=\"onclick:toggle,onkeypress: _onTitleKey,onfocus:_handleFocus,onblur:_handleFocus\" tabindex=\"0\"\n\t\t\twaiRole=\"button\" class=\"dijitTitlePaneTitle\" dojoAttachPoint=\"focusNode\">\n\t\t<div dojoAttachPoint=\"arrowNode\" class=\"dijitInline dijitArrowNode\"><span dojoAttachPoint=\"arrowNodeInner\" class=\"dijitArrowNodeInner\"></span></div>\n\t\t<div dojoAttachPoint=\"titleNode\" class=\"dijitTitlePaneTextNode\"></div>\n\t</div>\n\t<div class=\"dijitTitlePaneContentOuter\" dojoAttachPoint=\"hideNode\">\n\t\t<div class=\"dijitReset\" dojoAttachPoint=\"wipeNode\">\n\t\t\t<div class=\"dijitTitlePaneContentInner\" dojoAttachPoint=\"containerNode\" waiRole=\"region\" tabindex=\"-1\">\n\t\t\t\t<!-- nested divs because wipeIn()/wipeOut() doesn't work right on node w/padding etc.  Put padding on inner div. -->\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n",
6401
 
6402
	postCreate: function(){
6403
		this.setTitle(this.title);
6404
		if(!this.open){
6405
			this.hideNode.style.display = this.wipeNode.style.display = "none";
6406
		}
6407
		this._setCss();
6408
		dojo.setSelectable(this.titleNode, false);
6409
		this.inherited("postCreate",arguments);
6410
		dijit.setWaiState(this.containerNode, "labelledby", this.titleNode.id);
6411
		dijit.setWaiState(this.focusNode, "haspopup", "true");
6412
 
6413
		// setup open/close animations
6414
		var hideNode = this.hideNode, wipeNode = this.wipeNode;
6415
		this._wipeIn = dojo.fx.wipeIn({
6416
			node: this.wipeNode,
6417
			duration: this.duration,
6418
			beforeBegin: function(){
6419
				hideNode.style.display="";
6420
			}
6421
		});
6422
		this._wipeOut = dojo.fx.wipeOut({
6423
			node: this.wipeNode,
6424
			duration: this.duration,
6425
			onEnd: function(){
6426
				hideNode.style.display="none";
6427
			}
6428
		});
6429
	},
6430
 
6431
	setContent: function(content){
6432
		// summary
6433
		// 		Typically called when an href is loaded.  Our job is to make the animation smooth
6434
		if(this._wipeOut.status() == "playing"){
6435
			// we are currently *closing* the pane, so just let that continue
6436
			this.inherited("setContent",arguments);
6437
		}else{
6438
			if(this._wipeIn.status() == "playing"){
6439
				this._wipeIn.stop();
6440
			}
6441
 
6442
			// freeze container at current height so that adding new content doesn't make it jump
6443
			dojo.marginBox(this.wipeNode, {h: dojo.marginBox(this.wipeNode).h});
6444
 
6445
			// add the new content (erasing the old content, if any)
6446
			this.inherited("setContent",arguments);
6447
 
6448
			// call _wipeIn.play() to animate from current height to new height
6449
			this._wipeIn.play();
6450
		}
6451
	},
6452
 
6453
	toggle: function(){
6454
		// summary: switches between opened and closed state
6455
		dojo.forEach([this._wipeIn, this._wipeOut], function(animation){
6456
			if(animation.status() == "playing"){
6457
				animation.stop();
6458
			}
6459
		});
6460
 
6461
		this[this.open ? "_wipeOut" : "_wipeIn"].play();
6462
		this.open =! this.open;
6463
 
6464
		// load content (if this is the first time we are opening the TitlePane
6465
		// and content is specified as an href, or we have setHref when hidden)
6466
		this._loadCheck();
6467
 
6468
		this._setCss();
6469
	},
6470
 
6471
	_setCss: function(){
6472
		// summary: set the open/close css state for the TitlePane
6473
		var classes = ["dijitClosed", "dijitOpen"];
6474
		var boolIndex = this.open;
6475
		dojo.removeClass(this.focusNode, classes[!boolIndex+0]);
6476
		this.focusNode.className += " " + classes[boolIndex+0];
6477
 
6478
		// provide a character based indicator for images-off mode
6479
		this.arrowNodeInner.innerHTML = this.open ? "-" : "+";
6480
	},
6481
 
6482
	_onTitleKey: function(/*Event*/ e){
6483
		// summary: callback when user hits a key
6484
		if(e.keyCode == dojo.keys.ENTER || e.charCode == dojo.keys.SPACE){
6485
			this.toggle();
6486
		}
6487
		else if(e.keyCode == dojo.keys.DOWN_ARROW){
6488
			if(this.open){
6489
				this.containerNode.focus();
6490
				e.preventDefault();
6491
			}
6492
	 	}
6493
	},
6494
 
6495
	_handleFocus: function(/*Event*/ e){
6496
		// summary: handle blur and focus for this widget
6497
 
6498
		// add/removeClass is safe to call without hasClass in this case
6499
		dojo[(e.type=="focus" ? "addClass" : "removeClass")](this.focusNode,this.baseClass+"Focused");
6500
	},
6501
 
6502
	setTitle: function(/*String*/ title){
6503
		// summary: sets the text of the title
6504
		this.titleNode.innerHTML=title;
6505
	}
6506
});
6507
 
6508
}
6509
 
6510
if(!dojo._hasResource["dijit.Tooltip"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
6511
dojo._hasResource["dijit.Tooltip"] = true;
6512
dojo.provide("dijit.Tooltip");
6513
 
6514
 
6515
 
6516
 
6517
dojo.declare(
6518
	"dijit._MasterTooltip",
6519
	[dijit._Widget, dijit._Templated],
6520
	{
6521
		// summary
6522
		//		Internal widget that holds the actual tooltip markup,
6523
		//		which occurs once per page.
6524
		//		Called by Tooltip widgets which are just containers to hold
6525
		//		the markup
6526
 
6527
		// duration: Integer
6528
		//		Milliseconds to fade in/fade out
6529
		duration: 200,
6530
 
6531
		templateString:"<div class=\"dijitTooltip dijitTooltipLeft\" id=\"dojoTooltip\">\n\t<div class=\"dijitTooltipContainer dijitTooltipContents\" dojoAttachPoint=\"containerNode\" waiRole='alert'></div>\n\t<div class=\"dijitTooltipConnector\"></div>\n</div>\n",
6532
 
6533
		postCreate: function(){
6534
			dojo.body().appendChild(this.domNode);
6535
 
6536
			this.bgIframe = new dijit.BackgroundIframe(this.domNode);
6537
 
6538
			// Setup fade-in and fade-out functions.
6539
			this.fadeIn = dojo.fadeIn({ node: this.domNode, duration: this.duration, onEnd: dojo.hitch(this, "_onShow") });
6540
			this.fadeOut = dojo.fadeOut({ node: this.domNode, duration: this.duration, onEnd: dojo.hitch(this, "_onHide") });
6541
 
6542
		},
6543
 
6544
		show: function(/*String*/ innerHTML, /*DomNode*/ aroundNode){
6545
			// summary:
6546
			//	Display tooltip w/specified contents to right specified node
6547
			//	(To left if there's no space on the right, or if LTR==right)
6548
 
6549
			if(this.aroundNode && this.aroundNode === aroundNode){
6550
				return;
6551
			}
6552
 
6553
			if(this.fadeOut.status() == "playing"){
6554
				// previous tooltip is being hidden; wait until the hide completes then show new one
6555
				this._onDeck=arguments;
6556
				return;
6557
			}
6558
			this.containerNode.innerHTML=innerHTML;
6559
 
6560
			// Firefox bug. when innerHTML changes to be shorter than previous
6561
			// one, the node size will not be updated until it moves.
6562
			this.domNode.style.top = (this.domNode.offsetTop + 1) + "px";
6563
 
6564
			// position the element and change CSS according to position
6565
			var align = this.isLeftToRight() ? {'BR': 'BL', 'BL': 'BR'} : {'BL': 'BR', 'BR': 'BL'};
6566
			var pos = dijit.placeOnScreenAroundElement(this.domNode, aroundNode, align);
6567
			this.domNode.className="dijitTooltip dijitTooltip" + (pos.corner=='BL' ? "Right" : "Left");//FIXME: might overwrite class
6568
 
6569
			// show it
6570
			dojo.style(this.domNode, "opacity", 0);
6571
			this.fadeIn.play();
6572
			this.isShowingNow = true;
6573
			this.aroundNode = aroundNode;
6574
		},
6575
 
6576
		_onShow: function(){
6577
			if(dojo.isIE){
6578
				// the arrow won't show up on a node w/an opacity filter
6579
				this.domNode.style.filter="";
6580
			}
6581
		},
6582
 
6583
		hide: function(aroundNode){
6584
			// summary: hide the tooltip
6585
			if(!this.aroundNode || this.aroundNode !== aroundNode){
6586
				return;
6587
			}
6588
			if(this._onDeck){
6589
				// this hide request is for a show() that hasn't even started yet;
6590
				// just cancel the pending show()
6591
				this._onDeck=null;
6592
				return;
6593
			}
6594
			this.fadeIn.stop();
6595
			this.isShowingNow = false;
6596
			this.aroundNode = null;
6597
			this.fadeOut.play();
6598
		},
6599
 
6600
		_onHide: function(){
6601
			this.domNode.style.cssText="";	// to position offscreen again
6602
			if(this._onDeck){
6603
				// a show request has been queued up; do it now
6604
				this.show.apply(this, this._onDeck);
6605
				this._onDeck=null;
6606
			}
6607
		}
6608
 
6609
	}
6610
);
6611
 
6612
dijit.showTooltip = function(/*String*/ innerHTML, /*DomNode*/ aroundNode){
6613
	// summary:
6614
	//	Display tooltip w/specified contents to right specified node
6615
	//	(To left if there's no space on the right, or if LTR==right)
6616
	if(!dijit._masterTT){ dijit._masterTT = new dijit._MasterTooltip(); }
6617
	return dijit._masterTT.show(innerHTML, aroundNode);
6618
};
6619
 
6620
dijit.hideTooltip = function(aroundNode){
6621
	// summary: hide the tooltip
6622
	if(!dijit._masterTT){ dijit._masterTT = new dijit._MasterTooltip(); }
6623
	return dijit._masterTT.hide(aroundNode);
6624
};
6625
 
6626
dojo.declare(
6627
	"dijit.Tooltip",
6628
	dijit._Widget,
6629
	{
6630
		// summary
6631
		//		Pops up a tooltip (a help message) when you hover over a node.
6632
 
6633
		// label: String
6634
		//		Text to display in the tooltip.
6635
		//		Specified as innerHTML when creating the widget from markup.
6636
		label: "",
6637
 
6638
		// showDelay: Integer
6639
		//		Number of milliseconds to wait after hovering over/focusing on the object, before
6640
		//		the tooltip is displayed.
6641
		showDelay: 400,
6642
 
6643
		// connectId: String[]
6644
		//		Id(s) of domNodes to attach the tooltip to.
6645
		//		When user hovers over any of the specified dom nodes, the tooltip will appear.
6646
		connectId: [],
6647
 
6648
		postCreate: function(){
6649
			if(this.srcNodeRef){
6650
				this.srcNodeRef.style.display = "none";
6651
			}
6652
 
6653
			this._connectNodes = [];
6654
 
6655
			dojo.forEach(this.connectId, function(id) {
6656
				var node = dojo.byId(id);
6657
				if (node) {
6658
					this._connectNodes.push(node);
6659
					dojo.forEach(["onMouseOver", "onMouseOut", "onFocus", "onBlur", "onHover", "onUnHover"], function(event){
6660
						this.connect(node, event.toLowerCase(), "_"+event);
6661
					}, this);
6662
					if(dojo.isIE){
6663
						// BiDi workaround
6664
						node.style.zoom = 1;
6665
					}
6666
				}
6667
			}, this);
6668
		},
6669
 
6670
		_onMouseOver: function(/*Event*/ e){
6671
			this._onHover(e);
6672
		},
6673
 
6674
		_onMouseOut: function(/*Event*/ e){
6675
			if(dojo.isDescendant(e.relatedTarget, e.target)){
6676
				// false event; just moved from target to target child; ignore.
6677
				return;
6678
			}
6679
			this._onUnHover(e);
6680
		},
6681
 
6682
		_onFocus: function(/*Event*/ e){
6683
			this._focus = true;
6684
			this._onHover(e);
6685
		},
6686
 
6687
		_onBlur: function(/*Event*/ e){
6688
			this._focus = false;
6689
			this._onUnHover(e);
6690
		},
6691
 
6692
		_onHover: function(/*Event*/ e){
6693
			if(!this._showTimer){
6694
				var target = e.target;
6695
				this._showTimer = setTimeout(dojo.hitch(this, function(){this.open(target)}), this.showDelay);
6696
			}
6697
		},
6698
 
6699
		_onUnHover: function(/*Event*/ e){
6700
			// keep a tooltip open if the associated element has focus
6701
			if(this._focus){ return; }
6702
			if(this._showTimer){
6703
				clearTimeout(this._showTimer);
6704
				delete this._showTimer;
6705
			}
6706
			this.close();
6707
		},
6708
 
6709
		open: function(/*DomNode*/ target){
6710
 			// summary: display the tooltip; usually not called directly.
6711
			target = target || this._connectNodes[0];
6712
			if(!target){ return; }
6713
 
6714
			if(this._showTimer){
6715
				clearTimeout(this._showTimer);
6716
				delete this._showTimer;
6717
			}
6718
			dijit.showTooltip(this.label || this.domNode.innerHTML, target);
6719
 
6720
			this._connectNode = target;
6721
		},
6722
 
6723
		close: function(){
6724
			// summary: hide the tooltip; usually not called directly.
6725
			dijit.hideTooltip(this._connectNode);
6726
			delete this._connectNode;
6727
			if(this._showTimer){
6728
				clearTimeout(this._showTimer);
6729
				delete this._showTimer;
6730
			}
6731
		},
6732
 
6733
		uninitialize: function(){
6734
			this.close();
6735
		}
6736
	}
6737
);
6738
 
6739
}
6740
 
6741
if(!dojo._hasResource["dojo.cookie"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
6742
dojo._hasResource["dojo.cookie"] = true;
6743
dojo.provide("dojo.cookie");
6744
 
6745
/*=====
6746
dojo.__cookieProps = function(kwArgs){
6747
	//	expires: Date|Number?
6748
	//		If a number, seen as the number of days from today. If a date, the
6749
	//		date past which the cookie is invalid. If expires is in the past,
6750
	//		the cookie will be deleted If expires is left out or is 0, the
6751
	//		cookie will expire when the browser closes.
6752
	//	path: String?
6753
	//		The path to use for the cookie.
6754
	//	domain: String?
6755
	//		The domain to use for the cookie.
6756
	//	secure: Boolean?
6757
	//		Whether to only send the cookie on secure connections
6758
}
6759
=====*/
6760
 
6761
 
6762
dojo.cookie = function(/*String*/name, /*String?*/value, /*dojo.__cookieProps?*/props){
6763
	//	summary:
6764
	//		Get or set a cookie.
6765
	//	description:
6766
	// 		If you pass in one argument, the the value of the cookie is returned
6767
	//
6768
	// 		If you pass in two arguments, the cookie value is set to the second
6769
	// 		argument.
6770
	//
6771
	// 		If you pass in three arguments, the cookie value is set to the
6772
	// 		second argument, and the options on the third argument are used for
6773
	// 		extended properties on the cookie
6774
	//	name:
6775
	//		The name of the cookie
6776
	//	value:
6777
	//		Optional. The value for the cookie.
6778
	//	props:
6779
	//		Optional additional properties for the cookie
6780
	//	example:
6781
	//		set a cookie with the JSON-serialized contents of an object which
6782
	//		will expire 5 days from now:
6783
	//	|	dojo.cookie("configObj", dojo.toJson(config), { expires: 5 });
6784
	//
6785
	//	example:
6786
	//		de-serialize a cookie back into a JavaScript object:
6787
	//	|	var config = dojo.fromJson(dojo.cookie("configObj"));
6788
	//
6789
	//	example:
6790
	//		delete a cookie:
6791
	//	|	dojo.cookie("configObj", null);
6792
	var c = document.cookie;
6793
	if(arguments.length == 1){
6794
		var idx = c.lastIndexOf(name+'=');
6795
		if(idx == -1){ return null; }
6796
		var start = idx+name.length+1;
6797
		var end = c.indexOf(';', idx+name.length+1);
6798
		if(end == -1){ end = c.length; }
6799
		return decodeURIComponent(c.substring(start, end));
6800
	}else{
6801
		props = props || {};
6802
		value = encodeURIComponent(value);
6803
		if(typeof(props.expires) == "number"){
6804
			var d = new Date();
6805
			d.setTime(d.getTime()+(props.expires*24*60*60*1000));
6806
			props.expires = d;
6807
		}
6808
		document.cookie = name + "=" + value
6809
			+ (props.expires ? "; expires=" + props.expires.toUTCString() : "")
6810
			+ (props.path ? "; path=" + props.path : "")
6811
			+ (props.domain ? "; domain=" + props.domain : "")
6812
			+ (props.secure ? "; secure" : "");
6813
		return null;
6814
	}
6815
};
6816
 
6817
}
6818
 
6819
if(!dojo._hasResource["dijit.Tree"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
6820
dojo._hasResource["dijit.Tree"] = true;
6821
dojo.provide("dijit.Tree");
6822
 
6823
 
6824
 
6825
 
6826
 
6827
 
6828
 
6829
 
6830
dojo.declare(
6831
	"dijit._TreeNode",
6832
	[dijit._Widget, dijit._Templated, dijit._Container, dijit._Contained],
6833
{
6834
	// summary
6835
	//		Single node within a tree
6836
 
6837
	// item: dojo.data.Item
6838
	//		the dojo.data entry this tree represents
6839
	item: null,
6840
 
6841
	isTreeNode: true,
6842
 
6843
	// label: String
6844
	//		Text of this tree node
6845
	label: "",
6846
 
6847
	isExpandable: null, // show expando node
6848
 
6849
	isExpanded: false,
6850
 
6851
	// state: String
6852
	//		dynamic loading-related stuff.
6853
	//		When an empty folder node appears, it is "UNCHECKED" first,
6854
	//		then after dojo.data query it becomes "LOADING" and, finally "LOADED"
6855
	state: "UNCHECKED",
6856
 
6857
	templateString:"<div class=\"dijitTreeNode dijitTreeExpandLeaf dijitTreeChildrenNo\" waiRole=\"presentation\"\n\t><span dojoAttachPoint=\"expandoNode\" class=\"dijitTreeExpando\" waiRole=\"presentation\"\n\t></span\n\t><span dojoAttachPoint=\"expandoNodeText\" class=\"dijitExpandoText\" waiRole=\"presentation\"\n\t></span\n\t>\n\t<div dojoAttachPoint=\"contentNode\" class=\"dijitTreeContent\" waiRole=\"presentation\">\n\t\t<div dojoAttachPoint=\"iconNode\" class=\"dijitInline dijitTreeIcon\" waiRole=\"presentation\"></div>\n\t\t<span dojoAttachPoint=\"labelNode\" class=\"dijitTreeLabel\" wairole=\"treeitem\" tabindex=\"-1\"></span>\n\t</div>\n</div>\n",
6858
 
6859
	postCreate: function(){
6860
		// set label, escaping special characters
6861
		this.setLabelNode(this.label);
6862
 
6863
		// set expand icon for leaf
6864
		this._setExpando();
6865
 
6866
		// set icon and label class based on item
6867
		this._updateItemClasses(this.item);
6868
 
6869
		if(this.isExpandable){
6870
			dijit.setWaiState(this.labelNode, "expanded", this.isExpanded);
6871
		}
6872
	},
6873
 
6874
	markProcessing: function(){
6875
		// summary: visually denote that tree is loading data, etc.
6876
		this.state = "LOADING";
6877
		this._setExpando(true);
6878
	},
6879
 
6880
	unmarkProcessing: function(){
6881
		// summary: clear markup from markProcessing() call
6882
		this._setExpando(false);
6883
	},
6884
 
6885
	_updateItemClasses: function(item){
6886
		// summary: set appropriate CSS classes for item (used to allow for item updates to change respective CSS)
6887
		this.iconNode.className = "dijitInline dijitTreeIcon " + this.tree.getIconClass(item);
6888
		this.labelNode.className = "dijitTreeLabel " + this.tree.getLabelClass(item);
6889
	},
6890
 
6891
	_updateLayout: function(){
6892
		// summary: set appropriate CSS classes for this.domNode
6893
		var parent = this.getParent();
6894
		if(parent && parent.isTree && parent._hideRoot){
6895
			/* if we are hiding the root node then make every first level child look like a root node */
6896
			dojo.addClass(this.domNode, "dijitTreeIsRoot");
6897
		}else{
6898
			dojo.toggleClass(this.domNode, "dijitTreeIsLast", !this.getNextSibling());
6899
		}
6900
	},
6901
 
6902
	_setExpando: function(/*Boolean*/ processing){
6903
		// summary: set the right image for the expando node
6904
 
6905
		// apply the appropriate class to the expando node
6906
		var styles = ["dijitTreeExpandoLoading", "dijitTreeExpandoOpened",
6907
			"dijitTreeExpandoClosed", "dijitTreeExpandoLeaf"];
6908
		var idx = processing ? 0 : (this.isExpandable ?	(this.isExpanded ? 1 : 2) : 3);
6909
		dojo.forEach(styles,
6910
			function(s){
6911
				dojo.removeClass(this.expandoNode, s);
6912
			}, this
6913
		);
6914
		dojo.addClass(this.expandoNode, styles[idx]);
6915
 
6916
		// provide a non-image based indicator for images-off mode
6917
		this.expandoNodeText.innerHTML =
6918
			processing ? "*" :
6919
				(this.isExpandable ?
6920
					(this.isExpanded ? "-" : "+") : "*");
6921
	},
6922
 
6923
	expand: function(){
6924
		// summary: show my children
6925
		if(this.isExpanded){ return; }
6926
		// cancel in progress collapse operation
6927
		if(this._wipeOut.status() == "playing"){
6928
			this._wipeOut.stop();
6929
		}
6930
 
6931
		this.isExpanded = true;
6932
		dijit.setWaiState(this.labelNode, "expanded", "true");
6933
		dijit.setWaiRole(this.containerNode, "group");
6934
 
6935
		this._setExpando();
6936
 
6937
		this._wipeIn.play();
6938
	},
6939
 
6940
	collapse: function(){
6941
		if(!this.isExpanded){ return; }
6942
 
6943
		// cancel in progress expand operation
6944
		if(this._wipeIn.status() == "playing"){
6945
			this._wipeIn.stop();
6946
		}
6947
 
6948
		this.isExpanded = false;
6949
		dijit.setWaiState(this.labelNode, "expanded", "false");
6950
		this._setExpando();
6951
 
6952
		this._wipeOut.play();
6953
	},
6954
 
6955
	setLabelNode: function(label){
6956
		this.labelNode.innerHTML="";
6957
		this.labelNode.appendChild(document.createTextNode(label));
6958
	},
6959
 
6960
	_setChildren: function(/* Object[] */ childrenArray){
6961
		// summary:
6962
		//		Sets the children of this node.
6963
		//		Sets this.isExpandable based on whether or not there are children
6964
		// 		Takes array of objects like: {label: ...} (_TreeNode options basically)
6965
		//		See parameters of _TreeNode for details.
6966
 
6967
		this.destroyDescendants();
6968
 
6969
		this.state = "LOADED";
6970
		var nodeMap= {};
6971
		if(childrenArray && childrenArray.length > 0){
6972
			this.isExpandable = true;
6973
			if(!this.containerNode){ // maybe this node was unfolderized and still has container
6974
				this.containerNode = this.tree.containerNodeTemplate.cloneNode(true);
6975
				this.domNode.appendChild(this.containerNode);
6976
			}
6977
 
6978
			// Create _TreeNode widget for each specified tree node
6979
			dojo.forEach(childrenArray, function(childParams){
6980
				var child = new dijit._TreeNode(dojo.mixin({
6981
					tree: this.tree,
6982
					label: this.tree.getLabel(childParams.item)
6983
				}, childParams));
6984
				this.addChild(child);
6985
				var identity = this.tree.store.getIdentity(childParams.item);
6986
				nodeMap[identity] = child;
6987
				if(this.tree.persist){
6988
					if(this.tree._openedItemIds[identity]){
6989
						this.tree._expandNode(child);
6990
					}
6991
				}
6992
			}, this);
6993
 
6994
			// note that updateLayout() needs to be called on each child after
6995
			// _all_ the children exist
6996
			dojo.forEach(this.getChildren(), function(child, idx){
6997
				child._updateLayout();
6998
			});
6999
		}else{
7000
			this.isExpandable=false;
7001
		}
7002
 
7003
		if(this._setExpando){
7004
			// change expando to/form dot or + icon, as appropriate
7005
			this._setExpando(false);
7006
		}
7007
 
7008
		if(this.isTree && this._hideRoot){
7009
			// put first child in tab index if one exists.
7010
			var fc = this.getChildren()[0];
7011
			var tabnode = fc ? fc.labelNode : this.domNode;
7012
			tabnode.setAttribute("tabIndex", "0");
7013
		}
7014
 
7015
		// create animations for showing/hiding the children (if children exist)
7016
		if(this.containerNode && !this._wipeIn){
7017
			this._wipeIn = dojo.fx.wipeIn({node: this.containerNode, duration: 150});
7018
			this._wipeOut = dojo.fx.wipeOut({node: this.containerNode, duration: 150});
7019
		}
7020
 
7021
		return nodeMap;
7022
	},
7023
 
7024
	_addChildren: function(/* object[] */ childrenArray){
7025
		// summary:
7026
		//		adds the children to this node.
7027
		// 		Takes array of objects like: {label: ...}  (_TreeNode options basically)
7028
 
7029
		//		See parameters of _TreeNode for details.
7030
		var nodeMap = {};
7031
		if(childrenArray && childrenArray.length > 0){
7032
			dojo.forEach(childrenArray, function(childParams){
7033
				var child = new dijit._TreeNode(
7034
					dojo.mixin({
7035
						tree: this.tree,
7036
						label: this.tree.getLabel(childParams.item)
7037
					}, childParams)
7038
				);
7039
				this.addChild(child);
7040
				nodeMap[this.tree.store.getIdentity(childParams.item)] = child;
7041
			}, this);
7042
 
7043
			dojo.forEach(this.getChildren(), function(child, idx){
7044
				child._updateLayout();
7045
			});
7046
		}
7047
 
7048
		return nodeMap;
7049
	},
7050
 
7051
	deleteNode: function(/* treeNode */ node){
7052
		node.destroy();
7053
 
7054
		var children = this.getChildren();
7055
		if(children.length == 0){
7056
			this.isExpandable = false;
7057
			this.collapse();
7058
		}
7059
 
7060
		dojo.forEach(children, function(child){
7061
				child._updateLayout();
7062
		});
7063
	},
7064
 
7065
	makeExpandable: function(){
7066
		//summary
7067
		//		if this node wasn't already showing the expando node,
7068
		//		turn it into one and call _setExpando()
7069
		this.isExpandable = true;
7070
		this._setExpando(false);
7071
	}
7072
});
7073
 
7074
dojo.declare(
7075
	"dijit.Tree",
7076
	dijit._TreeNode,
7077
{
7078
	// summary
7079
	//	This widget displays hierarchical data from a store.  A query is specified
7080
	//	to get the "top level children" from a data store, and then those items are
7081
	//	queried for their children and so on (but lazily, as the user clicks the expand node).
7082
	//
7083
	//	Thus in the default mode of operation this widget is technically a forest, not a tree,
7084
	//	in that there can be multiple "top level children".  However, if you specify label,
7085
	//	then a special top level node (not corresponding to any item in the datastore) is
7086
	//	created, to father all the top level children.
7087
 
7088
	// store: String||dojo.data.Store
7089
	//	The store to get data to display in the tree
7090
	store: null,
7091
 
7092
	// query: String
7093
	//	query to get top level node(s) of tree (ex: {type:'continent'})
7094
	query: null,
7095
 
7096
	// childrenAttr: String
7097
	//		one ore more attributes that holds children of a tree node
7098
	childrenAttr: ["children"],
7099
 
7100
	templateString:"<div class=\"dijitTreeContainer\" style=\"\" waiRole=\"tree\"\n\tdojoAttachEvent=\"onclick:_onClick,onkeypress:_onKeyPress\">\n\t<div class=\"dijitTreeNode  dijitTreeIsRoot dijitTreeExpandLeaf dijitTreeChildrenNo\" waiRole=\"presentation\"\n\t\tdojoAttachPoint=\"rowNode\"\n\t\t><span dojoAttachPoint=\"expandoNode\" class=\"dijitTreeExpando\" waiRole=\"presentation\"\n\t\t></span\n\t\t><span dojoAttachPoint=\"expandoNodeText\" class=\"dijitExpandoText\" waiRole=\"presentation\"\n\t\t></span\n\t\t>\n\t\t<div dojoAttachPoint=\"contentNode\" class=\"dijitTreeContent\" waiRole=\"presentation\">\n\t\t\t<div dojoAttachPoint=\"iconNode\" class=\"dijitInline dijitTreeIcon\" waiRole=\"presentation\"></div>\n\t\t\t<span dojoAttachPoint=\"labelNode\" class=\"dijitTreeLabel\" wairole=\"treeitem\" tabindex=\"0\"></span>\n\t\t</div>\n\t</div>\n</div>\n",
7101
 
7102
	isExpandable: true,
7103
 
7104
	isTree: true,
7105
 
7106
	// persist: Boolean
7107
	//	enables/disables use of cookies for state saving.
7108
	persist: true,
7109
 
7110
	// dndController: String
7111
	//	class name to use as as the dnd controller
7112
	dndController: null,
7113
 
7114
	//parameters to pull off of the tree and pass on to the dndController as its params
7115
	dndParams: ["onDndDrop","itemCreator","onDndCancel","checkAcceptance", "checkItemAcceptance"],
7116
 
7117
	//declare the above items so they can be pulled from the tree's markup
7118
	onDndDrop:null,
7119
	itemCreator:null,
7120
	onDndCancel:null,
7121
	checkAcceptance:null,
7122
	checkItemAcceptance:null,
7123
 
7124
	_publish: function(/*String*/ topicName, /*Object*/ message){
7125
		// summary:
7126
		//		Publish a message for this widget/topic
7127
		dojo.publish(this.id, [dojo.mixin({tree: this, event: topicName}, message||{})]);
7128
	},
7129
 
7130
	postMixInProperties: function(){
7131
		this.tree = this;
7132
		this.lastFocused = this.labelNode;
7133
 
7134
		this._itemNodeMap={};
7135
 
7136
		this._hideRoot = !this.label;
7137
 
7138
		if(!this.store.getFeatures()['dojo.data.api.Identity']){
7139
			throw new Error("dijit.tree requires access to a store supporting the dojo.data Identity api");
7140
		}
7141
 
7142
		if(!this.cookieName){
7143
			this.cookieName = this.id + "SaveStateCookie";
7144
		}
7145
 
7146
		// if the store supports Notification, subscribe to the notification events
7147
		if(this.store.getFeatures()['dojo.data.api.Notification']){
7148
			this.connect(this.store, "onNew", "_onNewItem");
7149
			this.connect(this.store, "onDelete", "_onDeleteItem");
7150
			this.connect(this.store, "onSet", "_onSetItem");
7151
		}
7152
	},
7153
 
7154
	postCreate: function(){
7155
		// load in which nodes should be opened automatically
7156
		if(this.persist){
7157
			var cookie = dojo.cookie(this.cookieName);
7158
			this._openedItemIds = {};
7159
			if(cookie){
7160
				dojo.forEach(cookie.split(','), function(item){
7161
					this._openedItemIds[item] = true;
7162
				}, this);
7163
			}
7164
		}
7165
 
7166
		// make template for container node (we will clone this and insert it into
7167
		// any nodes that have children)
7168
		var div = document.createElement('div');
7169
		div.style.display = 'none';
7170
		div.className = "dijitTreeContainer";
7171
		dijit.setWaiRole(div, "presentation");
7172
		this.containerNodeTemplate = div;
7173
 
7174
		if(this._hideRoot){
7175
			this.rowNode.style.display="none";
7176
		}
7177
 
7178
		this.inherited("postCreate", arguments);
7179
 
7180
		// load top level children
7181
		this._expandNode(this);
7182
 
7183
		if(this.dndController){
7184
			if(dojo.isString(this.dndController)){
7185
				this.dndController= dojo.getObject(this.dndController);
7186
			}
7187
			var params={};
7188
			for (var i=0; i<this.dndParams.length;i++){
7189
				if(this[this.dndParams[i]]){
7190
					params[this.dndParams[i]]=this[this.dndParams[i]];
7191
				}
7192
			}
7193
			this.dndController= new this.dndController(this, params);
7194
		}
7195
 
7196
		this.connect(this.domNode,
7197
			dojo.isIE ? "onactivate" : "onfocus",
7198
			"_onTreeFocus");
7199
	},
7200
 
7201
	////////////// Data store related functions //////////////////////
7202
 
7203
	mayHaveChildren: function(/*dojo.data.Item*/ item){
7204
		// summary
7205
		//		User overridable function to tell if an item has or may have children.
7206
		//		Controls whether or not +/- expando icon is shown.
7207
		//		(For efficiency reasons we may not want to check if an element has
7208
		//		children until user clicks the expando node)
7209
 
7210
		return dojo.some(this.childrenAttr, function(attr){
7211
			return this.store.hasAttribute(item, attr);
7212
		}, this);
7213
	},
7214
 
7215
	getItemChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete){
7216
		// summary
7217
		// 		User overridable function that return array of child items of given parent item,
7218
		//		or if parentItem==null then return top items in tree
7219
		var store = this.store;
7220
		if(parentItem == null){
7221
			// get top level nodes
7222
			store.fetch({ query: this.query, onComplete: onComplete});
7223
		}else{
7224
			// get children of specified node
7225
			var childItems = [];
7226
			for (var i=0; i<this.childrenAttr.length; i++){
7227
				childItems= childItems.concat(store.getValues(parentItem, this.childrenAttr[i]));
7228
			}
7229
			// count how many items need to be loaded
7230
			var _waitCount = 0;
7231
			dojo.forEach(childItems, function(item){ if(!store.isItemLoaded(item)){ _waitCount++; } });
7232
 
7233
			if(_waitCount == 0){
7234
				// all items are already loaded.  proceed..
7235
				onComplete(childItems);
7236
			}else{
7237
				// still waiting for some or all of the items to load
7238
				function onItem(item){
7239
					if(--_waitCount == 0){
7240
						// all nodes have been loaded, send them to the tree
7241
						onComplete(childItems);
7242
					}
7243
				}
7244
				dojo.forEach(childItems, function(item){
7245
					if(!store.isItemLoaded(item)){
7246
						store.loadItem({item: item, onItem: onItem});
7247
					}
7248
				});
7249
			}
7250
		}
7251
	},
7252
 
7253
	getItemParentIdentity: function(/*dojo.data.Item*/ item, /*Object*/ parentInfo){
7254
		// summary
7255
		//		User overridable function, to return id of parent (or null if top level).
7256
		//		It's called with args from dojo.store.onNew
7257
		return this.store.getIdentity(parentInfo.item);		// String
7258
	},
7259
 
7260
	getLabel: function(/*dojo.data.Item*/ item){
7261
		// summary: user overridable function to get the label for a tree node (given the item)
7262
		return this.store.getLabel(item);	// String
7263
	},
7264
 
7265
	getIconClass: function(/*dojo.data.Item*/ item){
7266
		// summary: user overridable function to return CSS class name to display icon
7267
	},
7268
 
7269
	getLabelClass: function(/*dojo.data.Item*/ item){
7270
		// summary: user overridable function to return CSS class name to display label
7271
	},
7272
 
7273
	_onLoadAllItems: function(/*_TreeNode*/ node, /*dojo.data.Item[]*/ items){
7274
		// sumary: callback when all the children of a given node have been loaded
7275
		var childParams=dojo.map(items, function(item){
7276
			return {
7277
				item: item,
7278
				isExpandable: this.mayHaveChildren(item)
7279
			};
7280
		}, this);
7281
 
7282
		dojo.mixin(this._itemNodeMap,node._setChildren(childParams));
7283
 
7284
		this._expandNode(node);
7285
	},
7286
 
7287
	/////////// Keyboard and Mouse handlers ////////////////////
7288
 
7289
	_onKeyPress: function(/*Event*/ e){
7290
		// summary: translates keypress events into commands for the controller
7291
		if(e.altKey){ return; }
7292
		var treeNode = dijit.getEnclosingWidget(e.target);
7293
		if(!treeNode){ return; }
7294
 
7295
		// Note: On IE e.keyCode is not 0 for printables so check e.charCode.
7296
		// In dojo charCode is universally 0 for non-printables.
7297
		if(e.charCode){  // handle printables (letter navigation)
7298
			// Check for key navigation.
7299
			var navKey = e.charCode;
7300
			if(!e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey){
7301
				navKey = (String.fromCharCode(navKey)).toLowerCase();
7302
				this._onLetterKeyNav( { node: treeNode, key: navKey } );
7303
				dojo.stopEvent(e);
7304
			}
7305
		}else{  // handle non-printables (arrow keys)
7306
			var map = this._keyHandlerMap;
7307
			if(!map){
7308
				// setup table mapping keys to events
7309
				map = {};
7310
				map[dojo.keys.ENTER]="_onEnterKey";
7311
				map[dojo.keys.LEFT_ARROW]="_onLeftArrow";
7312
				map[dojo.keys.RIGHT_ARROW]="_onRightArrow";
7313
				map[dojo.keys.UP_ARROW]="_onUpArrow";
7314
				map[dojo.keys.DOWN_ARROW]="_onDownArrow";
7315
				map[dojo.keys.HOME]="_onHomeKey";
7316
				map[dojo.keys.END]="_onEndKey";
7317
				this._keyHandlerMap = map;
7318
			}
7319
			if(this._keyHandlerMap[e.keyCode]){
7320
				this[this._keyHandlerMap[e.keyCode]]( { node: treeNode, item: treeNode.item } );
7321
				dojo.stopEvent(e);
7322
			}
7323
		}
7324
	},
7325
 
7326
	_onEnterKey: function(/*Object*/ message){
7327
		this._publish("execute", { item: message.item, node: message.node} );
7328
		this.onClick(message.item, message.node);
7329
	},
7330
 
7331
	_onDownArrow: function(/*Object*/ message){
7332
		// summary: down arrow pressed; get next visible node, set focus there
7333
		var returnNode = this._navToNextNode(message.node);
7334
		if(returnNode && returnNode.isTreeNode){
7335
			returnNode.tree.focusNode(returnNode);
7336
			return returnNode;
7337
		}
7338
	},
7339
 
7340
	_onUpArrow: function(/*Object*/ message){
7341
		// summary: up arrow pressed; move to previous visible node
7342
 
7343
		var nodeWidget = message.node;
7344
		var returnWidget = nodeWidget;
7345
 
7346
		// if younger siblings
7347
		var previousSibling = nodeWidget.getPreviousSibling();
7348
		if(previousSibling){
7349
			nodeWidget = previousSibling;
7350
			// if the previous nodeWidget is expanded, dive in deep
7351
			while(nodeWidget.isExpandable && nodeWidget.isExpanded && nodeWidget.hasChildren()){
7352
				returnWidget = nodeWidget;
7353
				// move to the last child
7354
				var children = nodeWidget.getChildren();
7355
				nodeWidget = children[children.length-1];
7356
			}
7357
		}else{
7358
			// if this is the first child, return the parent
7359
			// unless the parent is the root of a tree with a hidden root
7360
			var parent = nodeWidget.getParent();
7361
			if(!(this._hideRoot && parent === this)){
7362
				nodeWidget = parent;
7363
			}
7364
		}
7365
 
7366
		if(nodeWidget && nodeWidget.isTreeNode){
7367
			returnWidget = nodeWidget;
7368
		}
7369
 
7370
		if(returnWidget && returnWidget.isTreeNode){
7371
			returnWidget.tree.focusNode(returnWidget);
7372
			return returnWidget;
7373
		}
7374
	},
7375
 
7376
	_onRightArrow: function(/*Object*/ message){
7377
		// summary: right arrow pressed; go to child node
7378
		var nodeWidget = message.node;
7379
		var returnWidget = nodeWidget;
7380
 
7381
		// if not expanded, expand, else move to 1st child
7382
		if(nodeWidget.isExpandable && !nodeWidget.isExpanded){
7383
			this._expandNode(nodeWidget);
7384
		}else if(nodeWidget.hasChildren()){
7385
			nodeWidget = nodeWidget.getChildren()[0];
7386
		}
7387
 
7388
		if(nodeWidget && nodeWidget.isTreeNode){
7389
			returnWidget = nodeWidget;
7390
		}
7391
 
7392
		if(returnWidget && returnWidget.isTreeNode){
7393
			returnWidget.tree.focusNode(returnWidget);
7394
			return returnWidget;
7395
		}
7396
	},
7397
 
7398
	_onLeftArrow: function(/*Object*/ message){
7399
		// summary: left arrow pressed; go to parent
7400
 
7401
		var node = message.node;
7402
		var returnWidget = node;
7403
 
7404
		// if not collapsed, collapse, else move to parent
7405
		if(node.isExpandable && node.isExpanded){
7406
			this._collapseNode(node);
7407
		}else{
7408
			node = node.getParent();
7409
		}
7410
		if(node && node.isTreeNode){
7411
			returnWidget = node;
7412
		}
7413
 
7414
		if(returnWidget && returnWidget.isTreeNode){
7415
			returnWidget.tree.focusNode(returnWidget);
7416
			return returnWidget;
7417
		}
7418
	},
7419
 
7420
	_onHomeKey: function(){
7421
		// summary: home pressed; get first visible node, set focus there
7422
		var returnNode = this._navToRootOrFirstNode();
7423
		if(returnNode){
7424
			returnNode.tree.focusNode(returnNode);
7425
			return returnNode;
7426
		}
7427
	},
7428
 
7429
	_onEndKey: function(/*Object*/ message){
7430
		// summary: end pressed; go to last visible node
7431
 
7432
		var returnWidget = message.node.tree;
7433
 
7434
		var lastChild = returnWidget;
7435
		while(lastChild.isExpanded){
7436
			var c = lastChild.getChildren();
7437
			lastChild = c[c.length - 1];
7438
			if(lastChild.isTreeNode){
7439
				returnWidget = lastChild;
7440
			}
7441
		}
7442
 
7443
		if(returnWidget && returnWidget.isTreeNode){
7444
			returnWidget.tree.focusNode(returnWidget);
7445
			return returnWidget;
7446
		}
7447
	},
7448
 
7449
	_onLetterKeyNav: function(message){
7450
		// summary: letter key pressed; search for node starting with first char = key
7451
		var node = startNode = message.node;
7452
		var key = message.key;
7453
		do{
7454
			node = this._navToNextNode(node);
7455
			//check for last node, jump to first node if necessary
7456
			if(!node){
7457
				node = this._navToRootOrFirstNode();
7458
			}
7459
		}while(node !== startNode && (node.label.charAt(0).toLowerCase() != key));
7460
		if(node && node.isTreeNode){
7461
			// no need to set focus if back where we started
7462
			if(node !== startNode){
7463
				node.tree.focusNode(node);
7464
			}
7465
			return node;
7466
		}
7467
	},
7468
 
7469
	_onClick: function(/*Event*/ e){
7470
		// summary: translates click events into commands for the controller to process
7471
		var domElement = e.target;
7472
 
7473
		// find node
7474
		var nodeWidget = dijit.getEnclosingWidget(domElement);
7475
		if(!nodeWidget || !nodeWidget.isTreeNode){
7476
			return;
7477
		}
7478
 
7479
		if(domElement == nodeWidget.expandoNode ||
7480
			 domElement == nodeWidget.expandoNodeText){
7481
			// expando node was clicked
7482
			if(nodeWidget.isExpandable){
7483
				this._onExpandoClick({node:nodeWidget});
7484
			}
7485
		}else{
7486
			this._publish("execute", { item: nodeWidget.item, node: nodeWidget} );
7487
			this.onClick(nodeWidget.item, nodeWidget);
7488
			this.focusNode(nodeWidget);
7489
		}
7490
		dojo.stopEvent(e);
7491
	},
7492
 
7493
	_onExpandoClick: function(/*Object*/ message){
7494
		// summary: user clicked the +/- icon; expand or collapse my children.
7495
		var node = message.node;
7496
		if(node.isExpanded){
7497
			this._collapseNode(node);
7498
		}else{
7499
			this._expandNode(node);
7500
		}
7501
	},
7502
 
7503
	onClick: function(/* dojo.data */ item, /*TreeNode*/ node){
7504
		// summary: user overridable function for executing a tree item
7505
	},
7506
 
7507
	_navToNextNode: function(node){
7508
		// summary: get next visible node
7509
		var returnNode;
7510
		// if this is an expanded node, get the first child
7511
		if(node.isExpandable && node.isExpanded && node.hasChildren()){
7512
			returnNode = node.getChildren()[0];
7513
		}else{
7514
			// find a parent node with a sibling
7515
			while(node && node.isTreeNode){
7516
				returnNode = node.getNextSibling();
7517
				if(returnNode){
7518
					break;
7519
				}
7520
				node = node.getParent();
7521
			}
7522
		}
7523
		return returnNode;
7524
	},
7525
 
7526
	_navToRootOrFirstNode: function(){
7527
		// summary: get first visible node
7528
		if(!this._hideRoot){
7529
			return this;
7530
		}else{
7531
			var returnNode = this.getChildren()[0];
7532
			if(returnNode && returnNode.isTreeNode){
7533
				return returnNode;
7534
			}
7535
		}
7536
	},
7537
 
7538
	_collapseNode: function(/*_TreeNode*/ node){
7539
		// summary: called when the user has requested to collapse the node
7540
 
7541
		if(node.isExpandable){
7542
			if(node.state == "LOADING"){
7543
				// ignore clicks while we are in the process of loading data
7544
				return;
7545
			}
7546
			if(this.lastFocused){
7547
				// are we collapsing a descendant with focus?
7548
				if(dojo.isDescendant(this.lastFocused.domNode, node.domNode)){
7549
					this.focusNode(node);
7550
				}else{
7551
					// clicking the expando node might have erased focus from
7552
					// the current item; restore it
7553
					this.focusNode(this.lastFocused);
7554
				}
7555
			}
7556
			node.collapse();
7557
			if(this.persist && node.item){
7558
				delete this._openedItemIds[this.store.getIdentity(node.item)];
7559
				this._saveState();
7560
			}
7561
		}
7562
	},
7563
 
7564
	_expandNode: function(/*_TreeNode*/ node){
7565
		// summary: called when the user has requested to expand the node
7566
 
7567
		// clicking the expando node might have erased focus from the current item; restore it
7568
		var t = node.tree;
7569
		if(t.lastFocused){ t.focusNode(t.lastFocused); }
7570
 
7571
		if(!node.isExpandable){
7572
			return;
7573
		}
7574
 
7575
		var store = this.store;
7576
		var getValue = this.store.getValue;
7577
 
7578
		switch(node.state){
7579
			case "LOADING":
7580
				// ignore clicks while we are in the process of loading data
7581
				return;
7582
 
7583
			case "UNCHECKED":
7584
				// need to load all the children, and then expand
7585
				node.markProcessing();
7586
				var _this = this;
7587
				var onComplete = function(childItems){
7588
					node.unmarkProcessing();
7589
					_this._onLoadAllItems(node, childItems);
7590
				};
7591
				this.getItemChildren(node.item, onComplete);
7592
				break;
7593
 
7594
			default:
7595
				// data is already loaded; just proceed
7596
				if(node.expand){	// top level Tree doesn't have expand() method
7597
					node.expand();
7598
					if(this.persist && node.item){
7599
						this._openedItemIds[this.store.getIdentity(node.item)] = true;
7600
						this._saveState();
7601
					}
7602
				}
7603
				break;
7604
		}
7605
	},
7606
 
7607
	////////////////// Miscellaneous functions ////////////////
7608
 
7609
	blurNode: function(){
7610
		// summary
7611
		//	Removes focus from the currently focused node (which must be visible).
7612
		//	Usually not called directly (just call focusNode() on another node instead)
7613
		var node = this.lastFocused;
7614
		if(!node){ return; }
7615
		var labelNode = node.labelNode;
7616
		dojo.removeClass(labelNode, "dijitTreeLabelFocused");
7617
		labelNode.setAttribute("tabIndex", "-1");
7618
		this.lastFocused = null;
7619
	},
7620
 
7621
	focusNode: function(/* _tree.Node */ node){
7622
		// summary
7623
		//	Focus on the specified node (which must be visible)
7624
 
7625
		// set focus so that the label will be voiced using screen readers
7626
		node.labelNode.focus();
7627
	},
7628
 
7629
	_onBlur: function(){
7630
		// summary:
7631
		// 		We've moved away from the whole tree.  The currently "focused" node
7632
		//		(see focusNode above) should remain as the lastFocused node so we can
7633
		//		tab back into the tree.  Just change CSS to get rid of the dotted border
7634
		//		until that time
7635
		if(this.lastFocused){
7636
			var labelNode = this.lastFocused.labelNode;
7637
			dojo.removeClass(labelNode, "dijitTreeLabelFocused");
7638
		}
7639
	},
7640
 
7641
	_onTreeFocus: function(evt){
7642
		var node = dijit.getEnclosingWidget(evt.target);
7643
		if(node != this.lastFocused){
7644
			this.blurNode();
7645
		}
7646
		var labelNode = node.labelNode;
7647
		// set tabIndex so that the tab key can find this node
7648
		labelNode.setAttribute("tabIndex", "0");
7649
		dojo.addClass(labelNode, "dijitTreeLabelFocused");
7650
		this.lastFocused = node;
7651
	},
7652
 
7653
	//////////////// Events from data store //////////////////////////
7654
 
7655
 
7656
	_onNewItem: function(/*Object*/ item, parentInfo){
7657
		//summary: callback when new item has been added to the store.
7658
 
7659
		var loadNewItem;	// should new item be displayed in tree?
7660
 
7661
		if(parentInfo){
7662
			var parent = this._itemNodeMap[this.getItemParentIdentity(item, parentInfo)];
7663
 
7664
			// If new item's parent item not in tree view yet, can safely ignore.
7665
			// Also, if a query of specified parent wouldn't return this item, then ignore.
7666
			if(!parent ||
7667
				dojo.indexOf(this.childrenAttr, parentInfo.attribute) == -1){
7668
				return;
7669
			}
7670
		}
7671
 
7672
		var childParams = {
7673
			item: item,
7674
			isExpandable: this.mayHaveChildren(item)
7675
		};
7676
		if(parent){
7677
			if(!parent.isExpandable){
7678
				parent.makeExpandable();
7679
			}
7680
			if(parent.state=="LOADED" || parent.isExpanded){
7681
				var childrenMap=parent._addChildren([childParams]);
7682
			}
7683
		}else{
7684
			// top level node
7685
			var childrenMap=this._addChildren([childParams]);
7686
		}
7687
 
7688
		if(childrenMap){
7689
			dojo.mixin(this._itemNodeMap, childrenMap);
7690
			//this._itemNodeMap[this.store.getIdentity(item)]=child;
7691
		}
7692
	},
7693
 
7694
	_onDeleteItem: function(/*Object*/ item){
7695
		//summary: delete event from the store
7696
		//since the object has just been deleted, we need to
7697
		//use the name directly
7698
		var identity = this.store.getIdentity(item);
7699
		var node = this._itemNodeMap[identity];
7700
 
7701
		if(node){
7702
			var parent = node.getParent();
7703
			parent.deleteNode(node);
7704
			this._itemNodeMap[identity]=null;
7705
		}
7706
	},
7707
 
7708
	_onSetItem: function(/*Object*/ item){
7709
		//summary: set data event  on an item in the store
7710
		var identity = this.store.getIdentity(item);
7711
		node = this._itemNodeMap[identity];
7712
 
7713
		if(node){
7714
			node.setLabelNode(this.getLabel(item));
7715
			node._updateItemClasses(item);
7716
		}
7717
	},
7718
 
7719
	_saveState: function(){
7720
		//summary: create and save a cookie with the currently expanded nodes identifiers
7721
		if(!this.persist){
7722
			return;
7723
		}
7724
		var ary = [];
7725
		for(var id in this._openedItemIds){
7726
			ary.push(id);
7727
		}
7728
		dojo.cookie(this.cookieName, ary.join(","));
7729
	}
7730
});
7731
 
7732
}
7733
 
7734
if(!dojo._hasResource["dijit.form.TextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
7735
dojo._hasResource["dijit.form.TextBox"] = true;
7736
dojo.provide("dijit.form.TextBox");
7737
 
7738
 
7739
 
7740
dojo.declare(
7741
	"dijit.form.TextBox",
7742
	dijit.form._FormWidget,
7743
	{
7744
		// summary:
7745
		//		A generic textbox field.
7746
		//		Serves as a base class to derive more specialized functionality in subclasses.
7747
 
7748
		//	trim: Boolean
7749
		//		Removes leading and trailing whitespace if true.  Default is false.
7750
		trim: false,
7751
 
7752
		//	uppercase: Boolean
7753
		//		Converts all characters to uppercase if true.  Default is false.
7754
		uppercase: false,
7755
 
7756
		//	lowercase: Boolean
7757
		//		Converts all characters to lowercase if true.  Default is false.
7758
		lowercase: false,
7759
 
7760
		//	propercase: Boolean
7761
		//		Converts the first character of each word to uppercase if true.
7762
		propercase: false,
7763
 
7764
		// maxLength: String
7765
		//		HTML INPUT tag maxLength declaration.
7766
		maxLength: "",
7767
 
7768
		templateString:"<input class=\"dojoTextBox\" dojoAttachPoint='textbox,focusNode' name=\"${name}\"\n\tdojoAttachEvent='onmouseenter:_onMouse,onmouseleave:_onMouse,onfocus:_onMouse,onblur:_onMouse,onkeyup,onkeypress:_onKeyPress'\n\tautocomplete=\"off\" type=\"${type}\"\n\t/>\n",
7769
		baseClass: "dijitTextBox",
7770
 
7771
		attributeMap: dojo.mixin(dojo.clone(dijit.form._FormWidget.prototype.attributeMap),
7772
			{maxLength:"focusNode"}),
7773
 
7774
		getDisplayedValue: function(){
7775
			return this.filter(this.textbox.value);
7776
		},
7777
 
7778
		getValue: function(){
7779
			return this.parse(this.getDisplayedValue(), this.constraints);
7780
		},
7781
 
7782
		setValue: function(value, /*Boolean, optional*/ priorityChange, /*String, optional*/ formattedValue){
7783
			var filteredValue = this.filter(value);
7784
			if((typeof filteredValue == typeof value) && (formattedValue == null || formattedValue == undefined)){
7785
				formattedValue = this.format(filteredValue, this.constraints);
7786
			}
7787
			if(formattedValue != null && formattedValue != undefined){
7788
				this.textbox.value = formattedValue;
7789
			}
7790
			dijit.form.TextBox.superclass.setValue.call(this, filteredValue, priorityChange);
7791
		},
7792
 
7793
		setDisplayedValue: function(/*String*/value){
7794
			this.textbox.value = value;
7795
			this.setValue(this.getValue(), true);
7796
		},
7797
 
7798
		forWaiValuenow: function(){
7799
			return this.getDisplayedValue();
7800
		},
7801
 
7802
		format: function(/* String */ value, /* Object */ constraints){
7803
			// summary: Replacable function to convert a value to a properly formatted string
7804
			return ((value == null || value == undefined) ? "" : (value.toString ? value.toString() : value));
7805
		},
7806
 
7807
		parse: function(/* String */ value, /* Object */ constraints){
7808
			// summary: Replacable function to convert a formatted string to a value
7809
			return value;
7810
		},
7811
 
7812
		postCreate: function(){
7813
			// setting the value here is needed since value="" in the template causes "undefined"
7814
			// and setting in the DOM (instead of the JS object) helps with form reset actions
7815
			this.textbox.setAttribute("value", this.getDisplayedValue());
7816
			this.inherited('postCreate', arguments);
7817
 
7818
			if(this.srcNodeRef){
7819
				dojo.style(this.textbox, "cssText", this.style);
7820
				this.textbox.className += " " + this["class"];
7821
			}
7822
			this._layoutHack();
7823
		},
7824
 
7825
		_layoutHack: function(){
7826
			// summary: work around table sizing bugs on FF2 by forcing redraw
7827
			if(dojo.isFF == 2 && this.domNode.tagName=="TABLE"){
7828
				var node=this.domNode;
7829
				var old = node.style.opacity;
7830
				node.style.opacity = "0.999";
7831
				setTimeout(function(){
7832
					node.style.opacity = old;
7833
				}, 0);
7834
			}
7835
		},
7836
 
7837
		filter: function(val){
7838
			// summary: Apply various filters to textbox value
7839
			if(val == undefined || val == null){ return ""; }
7840
			else if(typeof val != "string"){ return val; }
7841
			if(this.trim){
7842
				val = dojo.trim(val);
7843
			}
7844
			if(this.uppercase){
7845
				val = val.toUpperCase();
7846
			}
7847
			if(this.lowercase){
7848
				val = val.toLowerCase();
7849
			}
7850
			if(this.propercase){
7851
				val = val.replace(/[^\s]+/g, function(word){
7852
					return word.substring(0,1).toUpperCase() + word.substring(1);
7853
				});
7854
			}
7855
			return val;
7856
		},
7857
 
7858
		// event handlers, you can over-ride these in your own subclasses
7859
		_onBlur: function(){
7860
			this.setValue(this.getValue(), (this.isValid ? this.isValid() : true));
7861
		},
7862
 
7863
		onkeyup: function(){
7864
			// TODO: it would be nice to massage the value (ie: automatic uppercase, etc) as the user types
7865
			// but this messes up the cursor position if you are typing into the middle of a word, and
7866
			// also trimming doesn't work correctly (it prevents spaces between words too!)
7867
			// this.setValue(this.getValue());
7868
		}
7869
	}
7870
);
7871
 
7872
}
7873
 
7874
if(!dojo._hasResource["dijit.InlineEditBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
7875
dojo._hasResource["dijit.InlineEditBox"] = true;
7876
dojo.provide("dijit.InlineEditBox");
7877
 
7878
 
7879
 
7880
 
7881
 
7882
 
7883
 
7884
 
7885
 
7886
 
7887
dojo.declare("dijit.InlineEditBox",
7888
	dijit._Widget,
7889
	{
7890
	// summary: An element with in-line edit capabilitites
7891
	//
7892
	// description:
7893
	//		Behavior for an existing node (<p>, <div>, <span>, etc.) so that
7894
	// 		when you click it, an editor shows up in place of the original
7895
	//		text.  Optionally, Save and Cancel button are displayed below the edit widget.
7896
	//		When Save is clicked, the text is pulled from the edit
7897
	//		widget and redisplayed and the edit widget is again hidden.
7898
	//		By default a plain Textarea widget is used as the editor (or for
7899
	//		inline values a TextBox), but you can specify an editor such as
7900
	//		dijit.Editor (for editing HTML) or a Slider (for adjusting a number).
7901
	//		An edit widget must support the following API to be used:
7902
	//		String getDisplayedValue() OR String getValue()
7903
	//		void setDisplayedValue(String) OR void setValue(String)
7904
	//		void focus()
7905
	//
7906
	// editing: Boolean
7907
	//		Is the node currently in edit mode?
7908
	editing: false,
7909
 
7910
	// autoSave: Boolean
7911
	//		Changing the value automatically saves it; don't have to push save button
7912
	//		(and save button isn't even displayed)
7913
	autoSave: true,
7914
 
7915
	// buttonSave: String
7916
	//		Save button label
7917
	buttonSave: "",
7918
 
7919
	// buttonCancel: String
7920
	//		Cancel button label
7921
	buttonCancel: "",
7922
 
7923
	// renderAsHtml: Boolean
7924
	//		Set this to true if the specified Editor's value should be interpreted as HTML
7925
	//		rather than plain text (ie, dijit.Editor)
7926
	renderAsHtml: false,
7927
 
7928
	// editor: String
7929
	//		Class name for Editor widget
7930
	editor: "dijit.form.TextBox",
7931
 
7932
	// editorParams: Object
7933
	//		Set of parameters for editor, like {required: true}
7934
	editorParams: {},
7935
 
7936
	onChange: function(value){
7937
		// summary: User should set this handler to be notified of changes to value
7938
	},
7939
 
7940
	// width: String
7941
	//		Width of editor.  By default it's width=100% (ie, block mode)
7942
	width: "100%",
7943
 
7944
	// value: String
7945
	//		The display value of the widget in read-only mode
7946
	value: "",
7947
 
7948
	// noValueIndicator: String
7949
	//		The text that gets displayed when there is no value (so that the user has a place to click to edit)
7950
	noValueIndicator: "<span style='font-family: wingdings; text-decoration: underline;'>&nbsp;&nbsp;&nbsp;&nbsp;&#x270d;&nbsp;&nbsp;&nbsp;&nbsp;</span>",
7951
 
7952
	postMixInProperties: function(){
7953
		this.inherited('postMixInProperties', arguments);
7954
 
7955
		// save pointer to original source node, since Widget nulls-out srcNodeRef
7956
		this.displayNode = this.srcNodeRef;
7957
 
7958
		// connect handlers to the display node
7959
		var events = {
7960
			ondijitclick: "_onClick",
7961
			onmouseover: "_onMouseOver",
7962
			onmouseout: "_onMouseOut",
7963
			onfocus: "_onMouseOver",
7964
			onblur: "_onMouseOut"
7965
		};
7966
		for(var name in events){
7967
			this.connect(this.displayNode, name, events[name]);
7968
		}
7969
		dijit.setWaiRole(this.displayNode, "button");
7970
		if(!this.displayNode.getAttribute("tabIndex")){
7971
			this.displayNode.setAttribute("tabIndex", 0);
7972
		}
7973
 
7974
		if(!this.value){
7975
			this.value = this.displayNode.innerHTML;
7976
		}
7977
		this._setDisplayValue(this.value);	// if blank, change to icon for "input needed"
7978
	},
7979
 
7980
	_onMouseOver: function(){
7981
		dojo.addClass(this.displayNode, this.disabled ? "dijitDisabledClickableRegion" : "dijitClickableRegion");
7982
	},
7983
 
7984
	_onMouseOut: function(){
7985
		dojo.removeClass(this.displayNode, this.disabled ? "dijitDisabledClickableRegion" : "dijitClickableRegion");
7986
	},
7987
 
7988
	_onClick: function(/*Event*/ e){
7989
		if(this.disabled){ return; }
7990
		if(e){ dojo.stopEvent(e); }
7991
		this._onMouseOut();
7992
 
7993
		// Since FF gets upset if you move a node while in an event handler for that node...
7994
		setTimeout(dojo.hitch(this, "_edit"), 0);
7995
	},
7996
 
7997
	_edit: function(){
7998
		// summary: display the editor widget in place of the original (read only) markup
7999
 
8000
		this.editing = true;
8001
 
8002
		var editValue =
8003
				(this.renderAsHtml ?
8004
				this.value :
8005
				this.value.replace(/\s*\r?\n\s*/g,"").replace(/<br\/?>/gi, "\n").replace(/&gt;/g,">").replace(/&lt;/g,"<").replace(/&amp;/g,"&"));
8006
 
8007
		// Placeholder for edit widget
8008
		// Put place holder (and eventually editWidget) before the display node so that it's positioned correctly
8009
		// when Calendar dropdown appears, which happens automatically on focus.
8010
		var placeholder = document.createElement("span");
8011
		dojo.place(placeholder, this.domNode, "before");
8012
 
8013
		var ew = this.editWidget = new dijit._InlineEditor({
8014
			value: dojo.trim(editValue),
8015
			autoSave: this.autoSave,
8016
			buttonSave: this.buttonSave,
8017
			buttonCancel: this.buttonCancel,
8018
			renderAsHtml: this.renderAsHtml,
8019
			editor: this.editor,
8020
			editorParams: this.editorParams,
8021
			style: dojo.getComputedStyle(this.displayNode),
8022
			save: dojo.hitch(this, "save"),
8023
			cancel: dojo.hitch(this, "cancel"),
8024
			width: this.width
8025
		}, placeholder);
8026
 
8027
		// to avoid screen jitter, we first create the editor with position:absolute, visibility:hidden,
8028
		// and then when it's finished rendering, we switch from display mode to editor
8029
		var ews = ew.domNode.style;
8030
		this.displayNode.style.display="none";
8031
		ews.position = "static";
8032
		ews.visibility = "visible";
8033
 
8034
		// Replace the display widget with edit widget, leaving them both displayed for a brief time so that
8035
		// focus can be shifted without incident.  (browser may needs some time to render the editor.)
8036
		this.domNode = ew.domNode;
8037
		setTimeout(function(){
8038
			ew.focus();
8039
		}, 100);
8040
	},
8041
 
8042
	_showText: function(/*Boolean*/ focus){
8043
		// summary: revert to display mode, and optionally focus on display node
8044
 
8045
		// display the read-only text and then quickly hide the editor (to avoid screen jitter)
8046
		this.displayNode.style.display="";
8047
		var ews = this.editWidget.domNode.style;
8048
		ews.position="absolute";
8049
		ews.visibility="hidden";
8050
 
8051
		this.domNode = this.displayNode;
8052
 
8053
		// give the browser some time to render the display node and then shift focus to it
8054
		// and hide the edit widget
8055
		var _this = this;
8056
		setTimeout(function(){
8057
			if(focus){
8058
				dijit.focus(_this.displayNode);
8059
			}
8060
			_this.editWidget.destroy();
8061
			delete _this.editWidget;
8062
		}, 100);
8063
	},
8064
 
8065
	save: function(/*Boolean*/ focus){
8066
		// summary:
8067
		//		Save the contents of the editor and revert to display mode.
8068
		// focus: Boolean
8069
		//		Focus on the display mode text
8070
		this.editing = false;
8071
 
8072
		this.value = this.editWidget.getValue() + "";
8073
		if(this.renderAsHtml){
8074
			this.value = this.value.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;")
8075
				.replace("\n", "<br>");
8076
		}
8077
		this._setDisplayValue(this.value);
8078
 
8079
		// tell the world that we have changed
8080
		this.onChange(this.value);
8081
 
8082
		this._showText(focus);
8083
	},
8084
 
8085
	_setDisplayValue: function(/*String*/ val){
8086
		// summary: inserts specified HTML value into this node, or an "input needed" character if node is blank
8087
		this.displayNode.innerHTML = val || this.noValueIndicator;
8088
	},
8089
 
8090
	cancel: function(/*Boolean*/ focus){
8091
		// summary:
8092
		//		Revert to display mode, discarding any changes made in the editor
8093
		this.editing = false;
8094
		this._showText(focus);
8095
	}
8096
});
8097
 
8098
dojo.declare(
8099
	"dijit._InlineEditor",
8100
	 [dijit._Widget, dijit._Templated],
8101
{
8102
	// summary:
8103
	// 		internal widget used by InlineEditBox, displayed when in editing mode
8104
	//		to display the editor and maybe save/cancel buttons.  Calling code should
8105
	//		connect to save/cancel methods to detect when editing is finished
8106
	//
8107
	//		Has mainly the same parameters as InlineEditBox, plus these values:
8108
	//
8109
	// style: Object
8110
	//		Set of CSS attributes of display node, to replicate in editor
8111
	//
8112
	// value: String
8113
	//		Value as an HTML string or plain text string, depending on renderAsHTML flag
8114
 
8115
	templateString:"<fieldset dojoAttachPoint=\"editNode\" waiRole=\"presentation\" style=\"position: absolute; visibility:hidden\" class=\"dijitReset dijitInline\"\n\tdojoAttachEvent=\"onkeypress: _onKeyPress\" \n\t><input dojoAttachPoint=\"editorPlaceholder\"\n\t/><span dojoAttachPoint=\"buttonContainer\"\n\t\t><button class='saveButton' dojoAttachPoint=\"saveButton\" dojoType=\"dijit.form.Button\" dojoAttachEvent=\"onClick:save\">${buttonSave}</button\n\t\t><button class='cancelButton' dojoAttachPoint=\"cancelButton\" dojoType=\"dijit.form.Button\" dojoAttachEvent=\"onClick:cancel\">${buttonCancel}</button\n\t></span\n></fieldset>\n",
8116
	widgetsInTemplate: true,
8117
 
8118
	postMixInProperties: function(){
8119
		this.inherited('postMixInProperties', arguments);
8120
		this.messages = dojo.i18n.getLocalization("dijit", "common", this.lang);
8121
		dojo.forEach(["buttonSave", "buttonCancel"], function(prop){
8122
			if(!this[prop]){ this[prop] = this.messages[prop]; }
8123
		}, this);
8124
	},
8125
 
8126
	postCreate: function(){
8127
		// Create edit widget in place in the template
8128
		var cls = dojo.getObject(this.editor);
8129
		var ew = this.editWidget = new cls(this.editorParams, this.editorPlaceholder);
8130
 
8131
		// Copy the style from the source
8132
		// Don't copy ALL properties though, just the necessary/applicable ones
8133
		var srcStyle = this.style;
8134
		dojo.forEach(["fontWeight","fontFamily","fontSize","fontStyle"], function(prop){
8135
			ew.focusNode.style[prop]=srcStyle[prop];
8136
		}, this);
8137
		dojo.forEach(["marginTop","marginBottom","marginLeft", "marginRight"], function(prop){
8138
			this.domNode.style[prop]=srcStyle[prop];
8139
		}, this);
8140
		if(this.width=="100%"){
8141
			// block mode
8142
			ew.domNode.style.width = "100%";	// because display: block doesn't work for table widgets
8143
			this.domNode.style.display="block";
8144
		}else{
8145
			// inline-block mode
8146
			ew.domNode.style.width = this.width + (Number(this.width)==this.width ? "px" : "");
8147
		}
8148
 
8149
		this.connect(this.editWidget, "onChange", "_onChange");
8150
 
8151
		// setting the value of the edit widget will cause a possibly asynchronous onChange() call.
8152
		// we need to ignore it, since we are only interested in when the user changes the value.
8153
		this._ignoreNextOnChange = true;
8154
		(this.editWidget.setDisplayedValue||this.editWidget.setValue).call(this.editWidget, this.value);
8155
 
8156
		this._initialText = this.getValue();
8157
 
8158
		if(this.autoSave){
8159
			this.buttonContainer.style.display="none";
8160
		}
8161
	},
8162
 
8163
	destroy: function(){
8164
		this.editWidget.destroy();
8165
		this.inherited(arguments);
8166
	},
8167
 
8168
	getValue: function(){
8169
		var ew = this.editWidget;
8170
		return ew.getDisplayedValue ? ew.getDisplayedValue() : ew.getValue();
8171
	},
8172
 
8173
	_onKeyPress: function(e){
8174
		// summary: Callback when keypress in the edit box (see template).
8175
		// description:
8176
		//		For autoSave widgets, if Esc/Enter, call cancel/save.
8177
		//		For non-autoSave widgets, enable save button if the text value is
8178
		//		different than the original value.
8179
		if(this._exitInProgress){
8180
			return;
8181
		}
8182
		if(this.autoSave){
8183
			// If Enter/Esc pressed, treat as save/cancel.
8184
			if(e.keyCode == dojo.keys.ESCAPE){
8185
				dojo.stopEvent(e);
8186
				this._exitInProgress = true;
8187
				this.cancel(true);
8188
			}else if(e.keyCode == dojo.keys.ENTER){
8189
				dojo.stopEvent(e);
8190
				this._exitInProgress = true;
8191
				this.save(true);
8192
			}
8193
		}else{
8194
			var _this = this;
8195
			// Delay before calling getValue().
8196
			// The delay gives the browser a chance to update the Textarea.
8197
			setTimeout(
8198
				function(){
8199
					_this.saveButton.setDisabled(_this.getValue() == _this._initialText);
8200
				}, 100);
8201
		}
8202
	},
8203
 
8204
	_onBlur: function(){
8205
		// summary:
8206
		//	Called when focus moves outside the editor
8207
		if(this._exitInProgress){
8208
			// when user clicks the "save" button, focus is shifted back to display text, causing this
8209
			// function to be called, but in that case don't do anything
8210
			return;
8211
		}
8212
		if(this.autoSave){
8213
			this._exitInProgress = true;
8214
			if(this.getValue() == this._initialText){
8215
				this.cancel(false);
8216
			}else{
8217
				this.save(false);
8218
			}
8219
		}
8220
	},
8221
 
8222
	enableSave: function(){
8223
		// summary: User replacable function returning a Boolean to indicate
8224
		// 	if the Save button should be enabled or not - usually due to invalid conditions
8225
		return this.editWidget.isValid ? this.editWidget.isValid() : true; // Boolean
8226
	},
8227
 
8228
	_onChange: function(){
8229
		// summary:
8230
		//	Called when the underlying widget fires an onChange event,
8231
		//	which means that the user has finished entering the value
8232
 
8233
		if(this._ignoreNextOnChange){
8234
			delete this._ignoreNextOnChange;
8235
			return;
8236
		}
8237
		if(this._exitInProgress){
8238
			// TODO: the onChange event might happen after the return key for an async widget
8239
			// like FilteringSelect.  Shouldn't be deleting the edit widget on end-of-edit
8240
			return;
8241
		}
8242
		if(this.autoSave){
8243
			this._exitInProgress = true;
8244
			this.save(true);
8245
		}else{
8246
			// in case the keypress event didn't get through (old problem with Textarea that has been fixed
8247
			// in theory) or if the keypress event comes too quickly and the value inside the Textarea hasn't
8248
			// been updated yet)
8249
			this.saveButton.setDisabled((this.getValue() == this._initialText) || !this.enableSave());
8250
		}
8251
	},
8252
 
8253
	enableSave: function(){
8254
		// summary: User replacable function returning a Boolean to indicate
8255
		// 	if the Save button should be enabled or not - usually due to invalid conditions
8256
		return this.editWidget.isValid ? this.editWidget.isValid() : true;
8257
	},
8258
 
8259
	focus: function(){
8260
		this.editWidget.focus();
8261
		dijit.selectInputText(this.editWidget.focusNode);
8262
	}
8263
});
8264
 
8265
dijit.selectInputText = function(/*DomNode*/element){
8266
	// summary: select all the text in an input element
8267
 
8268
	// TODO: use functions in _editor/selection.js?
8269
	var _window = dojo.global;
8270
	var _document = dojo.doc;
8271
	element = dojo.byId(element);
8272
	if(_document["selection"] && dojo.body()["createTextRange"]){ // IE
8273
		if(element.createTextRange){
8274
			var range = element.createTextRange();
8275
			range.moveStart("character", 0);
8276
			range.moveEnd("character", element.value.length);
8277
			range.select();
8278
		}
8279
	}else if(_window["getSelection"]){
8280
		var selection = _window.getSelection();
8281
		// FIXME: does this work on Safari?
8282
		if(element.setSelectionRange){
8283
			element.setSelectionRange(0, element.value.length);
8284
		}
8285
	}
8286
	element.focus();
8287
};
8288
 
8289
 
8290
}
8291
 
8292
if(!dojo._hasResource["dijit.form.CheckBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8293
dojo._hasResource["dijit.form.CheckBox"] = true;
8294
dojo.provide("dijit.form.CheckBox");
8295
 
8296
 
8297
 
8298
dojo.declare(
8299
	"dijit.form.CheckBox",
8300
	dijit.form.ToggleButton,
8301
	{
8302
		// summary:
8303
		// 		Same as an HTML checkbox, but with fancy styling.
8304
		//
8305
		// description:
8306
		// User interacts with real html inputs.
8307
		// On onclick (which occurs by mouse click, space-bar, or
8308
		// using the arrow keys to switch the selected radio button),
8309
		// we update the state of the checkbox/radio.
8310
		//
8311
		// There are two modes:
8312
		//   1. High contrast mode
8313
		//   2. Normal mode
8314
		// In case 1, the regular html inputs are shown and used by the user.
8315
		// In case 2, the regular html inputs are invisible but still used by
8316
		// the user. They are turned quasi-invisible and overlay the background-image.
8317
 
8318
		templateString:"<fieldset class=\"dijitReset dijitInline\" waiRole=\"presentation\"\n\t><input\n\t \ttype=\"${type}\" name=\"${name}\"\n\t\tclass=\"dijitReset dijitCheckBoxInput\"\n\t\tdojoAttachPoint=\"inputNode,focusNode\"\n\t \tdojoAttachEvent=\"onmouseover:_onMouse,onmouseout:_onMouse,onclick:_onClick\"\n/></fieldset>\n",
8319
 
8320
		baseClass: "dijitCheckBox",
8321
 
8322
		//	Value of "type" attribute for <input>
8323
		type: "checkbox",
8324
 
8325
		// value: Value
8326
		//	equivalent to value field on normal checkbox (if checked, the value is passed as
8327
		//	the value when form is submitted)
8328
		value: "on",
8329
 
8330
		postCreate: function(){
8331
			dojo.setSelectable(this.inputNode, false);
8332
			this.setChecked(this.checked);
8333
			this.inherited(arguments);
8334
		},
8335
 
8336
		setChecked: function(/*Boolean*/ checked){
8337
			if(dojo.isIE){
8338
				if(checked){ this.inputNode.setAttribute('checked', 'checked'); }
8339
				else{ this.inputNode.removeAttribute('checked'); }
8340
			}else{ this.inputNode.checked = checked; }
8341
			this.inherited(arguments);
8342
		},
8343
 
8344
		setValue: function(/*String*/ value){
8345
			if(value == null){ value = ""; }
8346
			this.inputNode.value = value;
8347
			dijit.form.CheckBox.superclass.setValue.call(this,value);
8348
		}
8349
	}
8350
);
8351
 
8352
dojo.declare(
8353
	"dijit.form.RadioButton",
8354
	dijit.form.CheckBox,
8355
	{
8356
		// summary:
8357
		// 		Same as an HTML radio, but with fancy styling.
8358
		//
8359
		// description:
8360
		// Implementation details
8361
		//
8362
		// Specialization:
8363
		// We keep track of dijit radio groups so that we can update the state
8364
		// of all the siblings (the "context") in a group based on input
8365
		// events. We don't rely on browser radio grouping.
8366
 
8367
		type: "radio",
8368
		baseClass: "dijitRadio",
8369
 
8370
		// This shared object keeps track of all widgets, grouped by name
8371
		_groups: {},
8372
 
8373
		postCreate: function(){
8374
			// add this widget to _groups
8375
			(this._groups[this.name] = this._groups[this.name] || []).push(this);
8376
 
8377
			this.inherited(arguments);
8378
		},
8379
 
8380
		uninitialize: function(){
8381
			// remove this widget from _groups
8382
			dojo.forEach(this._groups[this.name], function(widget, i, arr){
8383
				if(widget === this){
8384
					arr.splice(i, 1);
8385
					return;
8386
				}
8387
			}, this);
8388
		},
8389
 
8390
		setChecked: function(/*Boolean*/ checked){
8391
			// If I am being checked then have to deselect currently checked radio button
8392
			if(checked){
8393
				dojo.forEach(this._groups[this.name], function(widget){
8394
					if(widget != this && widget.checked){
8395
						widget.setChecked(false);
8396
					}
8397
				}, this);
8398
			}
8399
			this.inherited(arguments);
8400
		},
8401
 
8402
		_clicked: function(/*Event*/ e){
8403
			if(!this.checked){
8404
				this.setChecked(true);
8405
			}
8406
		}
8407
	}
8408
);
8409
 
8410
}
8411
 
8412
if(!dojo._hasResource["dojo.data.util.filter"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8413
dojo._hasResource["dojo.data.util.filter"] = true;
8414
dojo.provide("dojo.data.util.filter");
8415
 
8416
dojo.data.util.filter.patternToRegExp = function(/*String*/pattern, /*boolean?*/ ignoreCase){
8417
	//	summary:
8418
	//		Helper function to convert a simple pattern to a regular expression for matching.
8419
	//	description:
8420
	//		Returns a regular expression object that conforms to the defined conversion rules.
8421
	//		For example:
8422
	//			ca*   -> /^ca.*$/
8423
	//			*ca*  -> /^.*ca.*$/
8424
	//			*c\*a*  -> /^.*c\*a.*$/
8425
	//			*c\*a?*  -> /^.*c\*a..*$/
8426
	//			and so on.
8427
	//
8428
	//	pattern: string
8429
	//		A simple matching pattern to convert that follows basic rules:
8430
	//			* Means match anything, so ca* means match anything starting with ca
8431
	//			? Means match single character.  So, b?b will match to bob and bab, and so on.
8432
	//      	\ is an escape character.  So for example, \* means do not treat * as a match, but literal character *.
8433
	//				To use a \ as a character in the string, it must be escaped.  So in the pattern it should be
8434
	//				represented by \\ to be treated as an ordinary \ character instead of an escape.
8435
	//
8436
	//	ignoreCase:
8437
	//		An optional flag to indicate if the pattern matching should be treated as case-sensitive or not when comparing
8438
	//		By default, it is assumed case sensitive.
8439
 
8440
	var rxp = "^";
8441
	var c = null;
8442
	for(var i = 0; i < pattern.length; i++){
8443
		c = pattern.charAt(i);
8444
		switch (c) {
8445
			case '\\':
8446
				rxp += c;
8447
				i++;
8448
				rxp += pattern.charAt(i);
8449
				break;
8450
			case '*':
8451
				rxp += ".*"; break;
8452
			case '?':
8453
				rxp += "."; break;
8454
			case '$':
8455
			case '^':
8456
			case '/':
8457
			case '+':
8458
			case '.':
8459
			case '|':
8460
			case '(':
8461
			case ')':
8462
			case '{':
8463
			case '}':
8464
			case '[':
8465
			case ']':
8466
				rxp += "\\"; //fallthrough
8467
			default:
8468
				rxp += c;
8469
		}
8470
	}
8471
	rxp += "$";
8472
	if(ignoreCase){
8473
		return new RegExp(rxp,"i"); //RegExp
8474
	}else{
8475
		return new RegExp(rxp); //RegExp
8476
	}
8477
 
8478
};
8479
 
8480
}
8481
 
8482
if(!dojo._hasResource["dojo.data.util.sorter"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8483
dojo._hasResource["dojo.data.util.sorter"] = true;
8484
dojo.provide("dojo.data.util.sorter");
8485
 
8486
dojo.data.util.sorter.basicComparator = function(	/*anything*/ a,
8487
													/*anything*/ b){
8488
	//	summary:
8489
	//		Basic comparision function that compares if an item is greater or less than another item
8490
	//	description:
8491
	//		returns 1 if a > b, -1 if a < b, 0 if equal.
8492
	//		undefined values are treated as larger values so that they're pushed to the end of the list.
8493
 
8494
	var ret = 0;
8495
	if(a > b || typeof a === "undefined" || a === null){
8496
		ret = 1;
8497
	}else if(a < b || typeof b === "undefined" || b === null){
8498
		ret = -1;
8499
	}
8500
	return ret; //int, {-1,0,1}
8501
};
8502
 
8503
dojo.data.util.sorter.createSortFunction = function(	/* attributes array */sortSpec,
8504
														/*dojo.data.core.Read*/ store){
8505
	//	summary:
8506
	//		Helper function to generate the sorting function based off the list of sort attributes.
8507
	//	description:
8508
	//		The sort function creation will look for a property on the store called 'comparatorMap'.  If it exists
8509
	//		it will look in the mapping for comparisons function for the attributes.  If one is found, it will
8510
	//		use it instead of the basic comparator, which is typically used for strings, ints, booleans, and dates.
8511
	//		Returns the sorting function for this particular list of attributes and sorting directions.
8512
	//
8513
	//	sortSpec: array
8514
	//		A JS object that array that defines out what attribute names to sort on and whether it should be descenting or asending.
8515
	//		The objects should be formatted as follows:
8516
	//		{
8517
	//			attribute: "attributeName-string" || attribute,
8518
	//			descending: true|false;   // Default is false.
8519
	//		}
8520
	//	store: object
8521
	//		The datastore object to look up item values from.
8522
	//
8523
	var sortFunctions=[];
8524
 
8525
	function createSortFunction(attr, dir){
8526
		return function(itemA, itemB){
8527
			var a = store.getValue(itemA, attr);
8528
			var b = store.getValue(itemB, attr);
8529
			//See if we have a override for an attribute comparison.
8530
			var comparator = null;
8531
			if(store.comparatorMap){
8532
				if(typeof attr !== "string"){
8533
					 attr = store.getIdentity(attr);
8534
				}
8535
				comparator = store.comparatorMap[attr]||dojo.data.util.sorter.basicComparator;
8536
			}
8537
			comparator = comparator||dojo.data.util.sorter.basicComparator;
8538
			return dir * comparator(a,b); //int
8539
		};
8540
	}
8541
 
8542
	for(var i = 0; i < sortSpec.length; i++){
8543
		sortAttribute = sortSpec[i];
8544
		if(sortAttribute.attribute){
8545
			var direction = (sortAttribute.descending) ? -1 : 1;
8546
			sortFunctions.push(createSortFunction(sortAttribute.attribute, direction));
8547
		}
8548
	}
8549
 
8550
	return function(rowA, rowB){
8551
		var i=0;
8552
		while(i < sortFunctions.length){
8553
			var ret = sortFunctions[i++](rowA, rowB);
8554
			if(ret !== 0){
8555
				return ret;//int
8556
			}
8557
		}
8558
		return 0; //int
8559
	};  //  Function
8560
};
8561
 
8562
}
8563
 
8564
if(!dojo._hasResource["dojo.data.util.simpleFetch"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8565
dojo._hasResource["dojo.data.util.simpleFetch"] = true;
8566
dojo.provide("dojo.data.util.simpleFetch");
8567
 
8568
 
8569
dojo.data.util.simpleFetch.fetch = function(/* Object? */ request){
8570
	//	summary:
8571
	//		The simpleFetch mixin is designed to serve as a set of function(s) that can
8572
	//		be mixed into other datastore implementations to accelerate their development.
8573
	//		The simpleFetch mixin should work well for any datastore that can respond to a _fetchItems()
8574
	//		call by returning an array of all the found items that matched the query.  The simpleFetch mixin
8575
	//		is not designed to work for datastores that respond to a fetch() call by incrementally
8576
	//		loading items, or sequentially loading partial batches of the result
8577
	//		set.  For datastores that mixin simpleFetch, simpleFetch
8578
	//		implements a fetch method that automatically handles eight of the fetch()
8579
	//		arguments -- onBegin, onItem, onComplete, onError, start, count, sort and scope
8580
	//		The class mixing in simpleFetch should not implement fetch(),
8581
	//		but should instead implement a _fetchItems() method.  The _fetchItems()
8582
	//		method takes three arguments, the keywordArgs object that was passed
8583
	//		to fetch(), a callback function to be called when the result array is
8584
	//		available, and an error callback to be called if something goes wrong.
8585
	//		The _fetchItems() method should ignore any keywordArgs parameters for
8586
	//		start, count, onBegin, onItem, onComplete, onError, sort, and scope.
8587
	//		The _fetchItems() method needs to correctly handle any other keywordArgs
8588
	//		parameters, including the query parameter and any optional parameters
8589
	//		(such as includeChildren).  The _fetchItems() method should create an array of
8590
	//		result items and pass it to the fetchHandler along with the original request object
8591
	//		-- or, the _fetchItems() method may, if it wants to, create an new request object
8592
	//		with other specifics about the request that are specific to the datastore and pass
8593
	//		that as the request object to the handler.
8594
	//
8595
	//		For more information on this specific function, see dojo.data.api.Read.fetch()
8596
	request = request || {};
8597
	if(!request.store){
8598
		request.store = this;
8599
	}
8600
	var self = this;
8601
 
8602
	var _errorHandler = function(errorData, requestObject){
8603
		if(requestObject.onError){
8604
			var scope = requestObject.scope || dojo.global;
8605
			requestObject.onError.call(scope, errorData, requestObject);
8606
		}
8607
	};
8608
 
8609
	var _fetchHandler = function(items, requestObject){
8610
		var oldAbortFunction = requestObject.abort || null;
8611
		var aborted = false;
8612
 
8613
		var startIndex = requestObject.start?requestObject.start:0;
8614
		var endIndex   = requestObject.count?(startIndex + requestObject.count):items.length;
8615
 
8616
		requestObject.abort = function(){
8617
			aborted = true;
8618
			if(oldAbortFunction){
8619
				oldAbortFunction.call(requestObject);
8620
			}
8621
		};
8622
 
8623
		var scope = requestObject.scope || dojo.global;
8624
		if(!requestObject.store){
8625
			requestObject.store = self;
8626
		}
8627
		if(requestObject.onBegin){
8628
			requestObject.onBegin.call(scope, items.length, requestObject);
8629
		}
8630
		if(requestObject.sort){
8631
			items.sort(dojo.data.util.sorter.createSortFunction(requestObject.sort, self));
8632
		}
8633
		if(requestObject.onItem){
8634
			for(var i = startIndex; (i < items.length) && (i < endIndex); ++i){
8635
				var item = items[i];
8636
				if(!aborted){
8637
					requestObject.onItem.call(scope, item, requestObject);
8638
				}
8639
			}
8640
		}
8641
		if(requestObject.onComplete && !aborted){
8642
			var subset = null;
8643
			if (!requestObject.onItem) {
8644
				subset = items.slice(startIndex, endIndex);
8645
			}
8646
			requestObject.onComplete.call(scope, subset, requestObject);
8647
		}
8648
	};
8649
	this._fetchItems(request, _fetchHandler, _errorHandler);
8650
	return request;	// Object
8651
};
8652
 
8653
}
8654
 
8655
if(!dojo._hasResource["dojo.data.ItemFileReadStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8656
dojo._hasResource["dojo.data.ItemFileReadStore"] = true;
8657
dojo.provide("dojo.data.ItemFileReadStore");
8658
 
8659
 
8660
 
8661
 
8662
 
8663
dojo.declare("dojo.data.ItemFileReadStore", null,{
8664
	//	summary:
8665
	//		The ItemFileReadStore implements the dojo.data.api.Read API and reads
8666
	//		data from JSON files that have contents in this format --
8667
	//		{ items: [
8668
	//			{ name:'Kermit', color:'green', age:12, friends:['Gonzo', {_reference:{name:'Fozzie Bear'}}]},
8669
	//			{ name:'Fozzie Bear', wears:['hat', 'tie']},
8670
	//			{ name:'Miss Piggy', pets:'Foo-Foo'}
8671
	//		]}
8672
	//		Note that it can also contain an 'identifer' property that specified which attribute on the items
8673
	//		in the array of items that acts as the unique identifier for that item.
8674
	//
8675
	constructor: function(/* Object */ keywordParameters){
8676
		//	summary: constructor
8677
		//	keywordParameters: {url: String}
8678
		//	keywordParameters: {data: jsonObject}
8679
		//	keywordParameters: {typeMap: object)
8680
		//		The structure of the typeMap object is as follows:
8681
		//		{
8682
		//			type0: function || object,
8683
		//			type1: function || object,
8684
		//			...
8685
		//			typeN: function || object
8686
		//		}
8687
		//		Where if it is a function, it is assumed to be an object constructor that takes the
8688
		//		value of _value as the initialization parameters.  If it is an object, then it is assumed
8689
		//		to be an object of general form:
8690
		//		{
8691
		//			type: function, //constructor.
8692
		//			deserialize:	function(value) //The function that parses the value and constructs the object defined by type appropriately.
8693
		//		}
8694
 
8695
		this._arrayOfAllItems = [];
8696
		this._arrayOfTopLevelItems = [];
8697
		this._loadFinished = false;
8698
		this._jsonFileUrl = keywordParameters.url;
8699
		this._jsonData = keywordParameters.data;
8700
		this._datatypeMap = keywordParameters.typeMap || {};
8701
		if(!this._datatypeMap['Date']){
8702
			//If no default mapping for dates, then set this as default.
8703
			//We use the dojo.date.stamp here because the ISO format is the 'dojo way'
8704
			//of generically representing dates.
8705
			this._datatypeMap['Date'] = {
8706
											type: Date,
8707
											deserialize: function(value){
8708
												return dojo.date.stamp.fromISOString(value);
8709
											}
8710
										};
8711
		}
8712
		this._features = {'dojo.data.api.Read':true, 'dojo.data.api.Identity':true};
8713
		this._itemsByIdentity = null;
8714
		this._storeRefPropName = "_S";  // Default name for the store reference to attach to every item.
8715
		this._itemNumPropName = "_0"; // Default Item Id for isItem to attach to every item.
8716
		this._rootItemPropName = "_RI"; // Default Item Id for isItem to attach to every item.
8717
		this._loadInProgress = false;	//Got to track the initial load to prevent duelling loads of the dataset.
8718
		this._queuedFetches = [];
8719
	},
8720
 
8721
	url: "",	// use "" rather than undefined for the benefit of the parser (#3539)
8722
 
8723
	_assertIsItem: function(/* item */ item){
8724
		//	summary:
8725
		//		This function tests whether the item passed in is indeed an item in the store.
8726
		//	item:
8727
		//		The item to test for being contained by the store.
8728
		if(!this.isItem(item)){
8729
			throw new Error("dojo.data.ItemFileReadStore: Invalid item argument.");
8730
		}
8731
	},
8732
 
8733
	_assertIsAttribute: function(/* attribute-name-string */ attribute){
8734
		//	summary:
8735
		//		This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
8736
		//	attribute:
8737
		//		The attribute to test for being contained by the store.
8738
		if(typeof attribute !== "string"){
8739
			throw new Error("dojo.data.ItemFileReadStore: Invalid attribute argument.");
8740
		}
8741
	},
8742
 
8743
	getValue: function(	/* item */ item,
8744
						/* attribute-name-string */ attribute,
8745
						/* value? */ defaultValue){
8746
		//	summary:
8747
		//		See dojo.data.api.Read.getValue()
8748
		var values = this.getValues(item, attribute);
8749
		return (values.length > 0)?values[0]:defaultValue; // mixed
8750
	},
8751
 
8752
	getValues: function(/* item */ item,
8753
						/* attribute-name-string */ attribute){
8754
		//	summary:
8755
		//		See dojo.data.api.Read.getValues()
8756
 
8757
		this._assertIsItem(item);
8758
		this._assertIsAttribute(attribute);
8759
		return item[attribute] || []; // Array
8760
	},
8761
 
8762
	getAttributes: function(/* item */ item){
8763
		//	summary:
8764
		//		See dojo.data.api.Read.getAttributes()
8765
		this._assertIsItem(item);
8766
		var attributes = [];
8767
		for(var key in item){
8768
			// Save off only the real item attributes, not the special id marks for O(1) isItem.
8769
			if((key !== this._storeRefPropName) && (key !== this._itemNumPropName) && (key !== this._rootItemPropName)){
8770
				attributes.push(key);
8771
			}
8772
		}
8773
		return attributes; // Array
8774
	},
8775
 
8776
	hasAttribute: function(	/* item */ item,
8777
							/* attribute-name-string */ attribute) {
8778
		//	summary:
8779
		//		See dojo.data.api.Read.hasAttribute()
8780
		return this.getValues(item, attribute).length > 0;
8781
	},
8782
 
8783
	containsValue: function(/* item */ item,
8784
							/* attribute-name-string */ attribute,
8785
							/* anything */ value){
8786
		//	summary:
8787
		//		See dojo.data.api.Read.containsValue()
8788
		var regexp = undefined;
8789
		if(typeof value === "string"){
8790
			regexp = dojo.data.util.filter.patternToRegExp(value, false);
8791
		}
8792
		return this._containsValue(item, attribute, value, regexp); //boolean.
8793
	},
8794
 
8795
	_containsValue: function(	/* item */ item,
8796
								/* attribute-name-string */ attribute,
8797
								/* anything */ value,
8798
								/* RegExp?*/ regexp){
8799
		//	summary:
8800
		//		Internal function for looking at the values contained by the item.
8801
		//	description:
8802
		//		Internal function for looking at the values contained by the item.  This
8803
		//		function allows for denoting if the comparison should be case sensitive for
8804
		//		strings or not (for handling filtering cases where string case should not matter)
8805
		//
8806
		//	item:
8807
		//		The data item to examine for attribute values.
8808
		//	attribute:
8809
		//		The attribute to inspect.
8810
		//	value:
8811
		//		The value to match.
8812
		//	regexp:
8813
		//		Optional regular expression generated off value if value was of string type to handle wildcarding.
8814
		//		If present and attribute values are string, then it can be used for comparison instead of 'value'
8815
		return dojo.some(this.getValues(item, attribute), function(possibleValue){
8816
			if(possibleValue !== null && !dojo.isObject(possibleValue) && regexp){
8817
				if(possibleValue.toString().match(regexp)){
8818
					return true; // Boolean
8819
				}
8820
			}else if(value === possibleValue){
8821
				return true; // Boolean
8822
			}
8823
		});
8824
	},
8825
 
8826
	isItem: function(/* anything */ something){
8827
		//	summary:
8828
		//		See dojo.data.api.Read.isItem()
8829
		if(something && something[this._storeRefPropName] === this){
8830
			if(this._arrayOfAllItems[something[this._itemNumPropName]] === something){
8831
				return true;
8832
			}
8833
		}
8834
		return false; // Boolean
8835
	},
8836
 
8837
	isItemLoaded: function(/* anything */ something){
8838
		//	summary:
8839
		//		See dojo.data.api.Read.isItemLoaded()
8840
		return this.isItem(something); //boolean
8841
	},
8842
 
8843
	loadItem: function(/* object */ keywordArgs){
8844
		//	summary:
8845
		//		See dojo.data.api.Read.loadItem()
8846
		this._assertIsItem(keywordArgs.item);
8847
	},
8848
 
8849
	getFeatures: function(){
8850
		//	summary:
8851
		//		See dojo.data.api.Read.getFeatures()
8852
		return this._features; //Object
8853
	},
8854
 
8855
	getLabel: function(/* item */ item){
8856
		//	summary:
8857
		//		See dojo.data.api.Read.getLabel()
8858
		if(this._labelAttr && this.isItem(item)){
8859
			return this.getValue(item,this._labelAttr); //String
8860
		}
8861
		return undefined; //undefined
8862
	},
8863
 
8864
	getLabelAttributes: function(/* item */ item){
8865
		//	summary:
8866
		//		See dojo.data.api.Read.getLabelAttributes()
8867
		if(this._labelAttr){
8868
			return [this._labelAttr]; //array
8869
		}
8870
		return null; //null
8871
	},
8872
 
8873
	_fetchItems: function(	/* Object */ keywordArgs,
8874
							/* Function */ findCallback,
8875
							/* Function */ errorCallback){
8876
		//	summary:
8877
		//		See dojo.data.util.simpleFetch.fetch()
8878
		var self = this;
8879
		var filter = function(requestArgs, arrayOfItems){
8880
			var items = [];
8881
			if(requestArgs.query){
8882
				var ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
8883
 
8884
				//See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
8885
				//same value for each item examined.  Much more efficient.
8886
				var regexpList = {};
8887
				for(var key in requestArgs.query){
8888
					var value = requestArgs.query[key];
8889
					if(typeof value === "string"){
8890
						regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
8891
					}
8892
				}
8893
 
8894
				for(var i = 0; i < arrayOfItems.length; ++i){
8895
					var match = true;
8896
					var candidateItem = arrayOfItems[i];
8897
					if(candidateItem === null){
8898
						match = false;
8899
					}else{
8900
						for(var key in requestArgs.query) {
8901
							var value = requestArgs.query[key];
8902
							if (!self._containsValue(candidateItem, key, value, regexpList[key])){
8903
								match = false;
8904
							}
8905
						}
8906
					}
8907
					if(match){
8908
						items.push(candidateItem);
8909
					}
8910
				}
8911
				findCallback(items, requestArgs);
8912
			}else{
8913
				// We want a copy to pass back in case the parent wishes to sort the array.
8914
				// We shouldn't allow resort of the internal list, so that multiple callers
8915
				// can get lists and sort without affecting each other.  We also need to
8916
				// filter out any null values that have been left as a result of deleteItem()
8917
				// calls in ItemFileWriteStore.
8918
				for(var i = 0; i < arrayOfItems.length; ++i){
8919
					var item = arrayOfItems[i];
8920
					if(item !== null){
8921
						items.push(item);
8922
					}
8923
				}
8924
				findCallback(items, requestArgs);
8925
			}
8926
		};
8927
 
8928
		if(this._loadFinished){
8929
			filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
8930
		}else{
8931
 
8932
			if(this._jsonFileUrl){
8933
				//If fetches come in before the loading has finished, but while
8934
				//a load is in progress, we have to defer the fetching to be
8935
				//invoked in the callback.
8936
				if(this._loadInProgress){
8937
					this._queuedFetches.push({args: keywordArgs, filter: filter});
8938
				}else{
8939
					this._loadInProgress = true;
8940
					var getArgs = {
8941
							url: self._jsonFileUrl,
8942
							handleAs: "json-comment-optional"
8943
						};
8944
					var getHandler = dojo.xhrGet(getArgs);
8945
					getHandler.addCallback(function(data){
8946
						try{
8947
							self._getItemsFromLoadedData(data);
8948
							self._loadFinished = true;
8949
							self._loadInProgress = false;
8950
 
8951
							filter(keywordArgs, self._getItemsArray(keywordArgs.queryOptions));
8952
							self._handleQueuedFetches();
8953
						}catch(e){
8954
							self._loadFinished = true;
8955
							self._loadInProgress = false;
8956
							errorCallback(e, keywordArgs);
8957
						}
8958
					});
8959
					getHandler.addErrback(function(error){
8960
						self._loadInProgress = false;
8961
						errorCallback(error, keywordArgs);
8962
					});
8963
				}
8964
			}else if(this._jsonData){
8965
				try{
8966
					this._loadFinished = true;
8967
					this._getItemsFromLoadedData(this._jsonData);
8968
					this._jsonData = null;
8969
					filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
8970
				}catch(e){
8971
					errorCallback(e, keywordArgs);
8972
				}
8973
			}else{
8974
				errorCallback(new Error("dojo.data.ItemFileReadStore: No JSON source data was provided as either URL or a nested Javascript object."), keywordArgs);
8975
			}
8976
		}
8977
	},
8978
 
8979
	_handleQueuedFetches: function(){
8980
		//	summary:
8981
		//		Internal function to execute delayed request in the store.
8982
		//Execute any deferred fetches now.
8983
		if (this._queuedFetches.length > 0) {
8984
			for(var i = 0; i < this._queuedFetches.length; i++){
8985
				var fData = this._queuedFetches[i];
8986
				var delayedQuery = fData.args;
8987
				var delayedFilter = fData.filter;
8988
				if(delayedFilter){
8989
					delayedFilter(delayedQuery, this._getItemsArray(delayedQuery.queryOptions));
8990
				}else{
8991
					this.fetchItemByIdentity(delayedQuery);
8992
				}
8993
			}
8994
			this._queuedFetches = [];
8995
		}
8996
	},
8997
 
8998
	_getItemsArray: function(/*object?*/queryOptions){
8999
		//	summary:
9000
		//		Internal function to determine which list of items to search over.
9001
		//	queryOptions: The query options parameter, if any.
9002
		if(queryOptions && queryOptions.deep) {
9003
			return this._arrayOfAllItems;
9004
		}
9005
		return this._arrayOfTopLevelItems;
9006
	},
9007
 
9008
	close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
9009
		 //	summary:
9010
		 //		See dojo.data.api.Read.close()
9011
	},
9012
 
9013
	_getItemsFromLoadedData: function(/* Object */ dataObject){
9014
		//	summary:
9015
		//		Function to parse the loaded data into item format and build the internal items array.
9016
		//	description:
9017
		//		Function to parse the loaded data into item format and build the internal items array.
9018
		//
9019
		//	dataObject:
9020
		//		The JS data object containing the raw data to convery into item format.
9021
		//
9022
		// 	returns: array
9023
		//		Array of items in store item format.
9024
 
9025
		// First, we define a couple little utility functions...
9026
 
9027
		function valueIsAnItem(/* anything */ aValue){
9028
			// summary:
9029
			//		Given any sort of value that could be in the raw json data,
9030
			//		return true if we should interpret the value as being an
9031
			//		item itself, rather than a literal value or a reference.
9032
			// example:
9033
			// 	|	false == valueIsAnItem("Kermit");
9034
			// 	|	false == valueIsAnItem(42);
9035
			// 	|	false == valueIsAnItem(new Date());
9036
			// 	|	false == valueIsAnItem({_type:'Date', _value:'May 14, 1802'});
9037
			// 	|	false == valueIsAnItem({_reference:'Kermit'});
9038
			// 	|	true == valueIsAnItem({name:'Kermit', color:'green'});
9039
			// 	|	true == valueIsAnItem({iggy:'pop'});
9040
			// 	|	true == valueIsAnItem({foo:42});
9041
			var isItem = (
9042
				(aValue != null) &&
9043
				(typeof aValue == "object") &&
9044
				(!dojo.isArray(aValue)) &&
9045
				(!dojo.isFunction(aValue)) &&
9046
				(aValue.constructor == Object) &&
9047
				(typeof aValue._reference == "undefined") &&
9048
				(typeof aValue._type == "undefined") &&
9049
				(typeof aValue._value == "undefined")
9050
			);
9051
			return isItem;
9052
		}
9053
 
9054
		var self = this;
9055
		function addItemAndSubItemsToArrayOfAllItems(/* Item */ anItem){
9056
			self._arrayOfAllItems.push(anItem);
9057
			for(var attribute in anItem){
9058
				var valueForAttribute = anItem[attribute];
9059
				if(valueForAttribute){
9060
					if(dojo.isArray(valueForAttribute)){
9061
						var valueArray = valueForAttribute;
9062
						for(var k = 0; k < valueArray.length; ++k){
9063
							var singleValue = valueArray[k];
9064
							if(valueIsAnItem(singleValue)){
9065
								addItemAndSubItemsToArrayOfAllItems(singleValue);
9066
							}
9067
						}
9068
					}else{
9069
						if(valueIsAnItem(valueForAttribute)){
9070
							addItemAndSubItemsToArrayOfAllItems(valueForAttribute);
9071
						}
9072
					}
9073
				}
9074
			}
9075
		}
9076
 
9077
		this._labelAttr = dataObject.label;
9078
 
9079
		// We need to do some transformations to convert the data structure
9080
		// that we read from the file into a format that will be convenient
9081
		// to work with in memory.
9082
 
9083
		// Step 1: Walk through the object hierarchy and build a list of all items
9084
		var i;
9085
		var item;
9086
		this._arrayOfAllItems = [];
9087
		this._arrayOfTopLevelItems = dataObject.items;
9088
 
9089
		for(i = 0; i < this._arrayOfTopLevelItems.length; ++i){
9090
			item = this._arrayOfTopLevelItems[i];
9091
			addItemAndSubItemsToArrayOfAllItems(item);
9092
			item[this._rootItemPropName]=true;
9093
		}
9094
 
9095
		// Step 2: Walk through all the attribute values of all the items,
9096
		// and replace single values with arrays.  For example, we change this:
9097
		//		{ name:'Miss Piggy', pets:'Foo-Foo'}
9098
		// into this:
9099
		//		{ name:['Miss Piggy'], pets:['Foo-Foo']}
9100
		//
9101
		// We also store the attribute names so we can validate our store
9102
		// reference and item id special properties for the O(1) isItem
9103
		var allAttributeNames = {};
9104
		var key;
9105
 
9106
		for(i = 0; i < this._arrayOfAllItems.length; ++i){
9107
			item = this._arrayOfAllItems[i];
9108
			for(key in item){
9109
				if (key !== this._rootItemPropName)
9110
				{
9111
					var value = item[key];
9112
					if(value !== null){
9113
						if(!dojo.isArray(value)){
9114
							item[key] = [value];
9115
						}
9116
					}else{
9117
						item[key] = [null];
9118
					}
9119
				}
9120
				allAttributeNames[key]=key;
9121
			}
9122
		}
9123
 
9124
		// Step 3: Build unique property names to use for the _storeRefPropName and _itemNumPropName
9125
		// This should go really fast, it will generally never even run the loop.
9126
		while(allAttributeNames[this._storeRefPropName]){
9127
			this._storeRefPropName += "_";
9128
		}
9129
		while(allAttributeNames[this._itemNumPropName]){
9130
			this._itemNumPropName += "_";
9131
		}
9132
 
9133
		// Step 4: Some data files specify an optional 'identifier', which is
9134
		// the name of an attribute that holds the identity of each item.
9135
		// If this data file specified an identifier attribute, then build a
9136
		// hash table of items keyed by the identity of the items.
9137
		var arrayOfValues;
9138
 
9139
		var identifier = dataObject.identifier;
9140
		if(identifier){
9141
			this._itemsByIdentity = {};
9142
			this._features['dojo.data.api.Identity'] = identifier;
9143
			for(i = 0; i < this._arrayOfAllItems.length; ++i){
9144
				item = this._arrayOfAllItems[i];
9145
				arrayOfValues = item[identifier];
9146
				var identity = arrayOfValues[0];
9147
				if(!this._itemsByIdentity[identity]){
9148
					this._itemsByIdentity[identity] = item;
9149
				}else{
9150
					if(this._jsonFileUrl){
9151
						throw new Error("dojo.data.ItemFileReadStore:  The json data as specified by: [" + this._jsonFileUrl + "] is malformed.  Items within the list have identifier: [" + identifier + "].  Value collided: [" + identity + "]");
9152
					}else if(this._jsonData){
9153
						throw new Error("dojo.data.ItemFileReadStore:  The json data provided by the creation arguments is malformed.  Items within the list have identifier: [" + identifier + "].  Value collided: [" + identity + "]");
9154
					}
9155
				}
9156
			}
9157
		}else{
9158
			this._features['dojo.data.api.Identity'] = Number;
9159
		}
9160
 
9161
		// Step 5: Walk through all the items, and set each item's properties
9162
		// for _storeRefPropName and _itemNumPropName, so that store.isItem() will return true.
9163
		for(i = 0; i < this._arrayOfAllItems.length; ++i){
9164
			item = this._arrayOfAllItems[i];
9165
			item[this._storeRefPropName] = this;
9166
			item[this._itemNumPropName] = i;
9167
		}
9168
 
9169
		// Step 6: We walk through all the attribute values of all the items,
9170
		// looking for type/value literals and item-references.
9171
		//
9172
		// We replace item-references with pointers to items.  For example, we change:
9173
		//		{ name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
9174
		// into this:
9175
		//		{ name:['Kermit'], friends:[miss_piggy] }
9176
		// (where miss_piggy is the object representing the 'Miss Piggy' item).
9177
		//
9178
		// We replace type/value pairs with typed-literals.  For example, we change:
9179
		//		{ name:['Nelson Mandela'], born:[{_type:'Date', _value:'July 18, 1918'}] }
9180
		// into this:
9181
		//		{ name:['Kermit'], born:(new Date('July 18, 1918')) }
9182
		//
9183
		// We also generate the associate map for all items for the O(1) isItem function.
9184
		for(i = 0; i < this._arrayOfAllItems.length; ++i){
9185
			item = this._arrayOfAllItems[i]; // example: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
9186
			for(key in item){
9187
				arrayOfValues = item[key]; // example: [{_reference:{name:'Miss Piggy'}}]
9188
				for(var j = 0; j < arrayOfValues.length; ++j) {
9189
					value = arrayOfValues[j]; // example: {_reference:{name:'Miss Piggy'}}
9190
					if(value !== null && typeof value == "object"){
9191
						if(value._type && value._value){
9192
							var type = value._type; // examples: 'Date', 'Color', or 'ComplexNumber'
9193
							var mappingObj = this._datatypeMap[type]; // examples: Date, dojo.Color, foo.math.ComplexNumber, {type: dojo.Color, deserialize(value){ return new dojo.Color(value)}}
9194
							if(!mappingObj){
9195
								throw new Error("dojo.data.ItemFileReadStore: in the typeMap constructor arg, no object class was specified for the datatype '" + type + "'");
9196
							}else if(dojo.isFunction(mappingObj)){
9197
								arrayOfValues[j] = new mappingObj(value._value);
9198
							}else if(dojo.isFunction(mappingObj.deserialize)){
9199
								arrayOfValues[j] = mappingObj.deserialize(value._value);
9200
							}else{
9201
								throw new Error("dojo.data.ItemFileReadStore: Value provided in typeMap was neither a constructor, nor a an object with a deserialize function");
9202
							}
9203
						}
9204
						if(value._reference){
9205
							var referenceDescription = value._reference; // example: {name:'Miss Piggy'}
9206
							if(dojo.isString(referenceDescription)){
9207
								// example: 'Miss Piggy'
9208
								// from an item like: { name:['Kermit'], friends:[{_reference:'Miss Piggy'}]}
9209
								arrayOfValues[j] = this._itemsByIdentity[referenceDescription];
9210
							}else{
9211
								// example: {name:'Miss Piggy'}
9212
								// from an item like: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
9213
								for(var k = 0; k < this._arrayOfAllItems.length; ++k){
9214
									var candidateItem = this._arrayOfAllItems[k];
9215
									var found = true;
9216
									for(var refKey in referenceDescription){
9217
										if(candidateItem[refKey] != referenceDescription[refKey]){
9218
											found = false;
9219
										}
9220
									}
9221
									if(found){
9222
										arrayOfValues[j] = candidateItem;
9223
									}
9224
								}
9225
							}
9226
						}
9227
					}
9228
				}
9229
			}
9230
		}
9231
	},
9232
 
9233
	getIdentity: function(/* item */ item){
9234
		//	summary:
9235
		//		See dojo.data.api.Identity.getIdentity()
9236
		var identifier = this._features['dojo.data.api.Identity'];
9237
		if(identifier === Number){
9238
			return item[this._itemNumPropName]; // Number
9239
		}else{
9240
			var arrayOfValues = item[identifier];
9241
			if(arrayOfValues){
9242
				return arrayOfValues[0]; // Object || String
9243
			}
9244
		}
9245
		return null; // null
9246
	},
9247
 
9248
	fetchItemByIdentity: function(/* Object */ keywordArgs){
9249
		//	summary:
9250
		//		See dojo.data.api.Identity.fetchItemByIdentity()
9251
 
9252
		// Hasn't loaded yet, we have to trigger the load.
9253
		if(!this._loadFinished){
9254
			var self = this;
9255
			if(this._jsonFileUrl){
9256
 
9257
				if(this._loadInProgress){
9258
					this._queuedFetches.push({args: keywordArgs});
9259
				}else{
9260
					this._loadInProgress = true;
9261
					var getArgs = {
9262
							url: self._jsonFileUrl,
9263
							handleAs: "json-comment-optional"
9264
					};
9265
					var getHandler = dojo.xhrGet(getArgs);
9266
					getHandler.addCallback(function(data){
9267
						var scope =  keywordArgs.scope?keywordArgs.scope:dojo.global;
9268
						try{
9269
							self._getItemsFromLoadedData(data);
9270
							self._loadFinished = true;
9271
							self._loadInProgress = false;
9272
							var item = self._getItemByIdentity(keywordArgs.identity);
9273
							if(keywordArgs.onItem){
9274
								keywordArgs.onItem.call(scope, item);
9275
							}
9276
							self._handleQueuedFetches();
9277
						}catch(error){
9278
							self._loadInProgress = false;
9279
							if(keywordArgs.onError){
9280
								keywordArgs.onError.call(scope, error);
9281
							}
9282
						}
9283
					});
9284
					getHandler.addErrback(function(error){
9285
						self._loadInProgress = false;
9286
						if(keywordArgs.onError){
9287
							var scope =  keywordArgs.scope?keywordArgs.scope:dojo.global;
9288
							keywordArgs.onError.call(scope, error);
9289
						}
9290
					});
9291
				}
9292
 
9293
			}else if(this._jsonData){
9294
				// Passed in data, no need to xhr.
9295
				self._getItemsFromLoadedData(self._jsonData);
9296
				self._jsonData = null;
9297
				self._loadFinished = true;
9298
				var item = self._getItemByIdentity(keywordArgs.identity);
9299
				if(keywordArgs.onItem){
9300
					var scope =  keywordArgs.scope?keywordArgs.scope:dojo.global;
9301
					keywordArgs.onItem.call(scope, item);
9302
				}
9303
			}
9304
		}else{
9305
			// Already loaded.  We can just look it up and call back.
9306
			var item = this._getItemByIdentity(keywordArgs.identity);
9307
			if(keywordArgs.onItem){
9308
				var scope =  keywordArgs.scope?keywordArgs.scope:dojo.global;
9309
				keywordArgs.onItem.call(scope, item);
9310
			}
9311
		}
9312
	},
9313
 
9314
	_getItemByIdentity: function(/* Object */ identity){
9315
		//	summary:
9316
		//		Internal function to look an item up by its identity map.
9317
		var item = null;
9318
		if(this._itemsByIdentity){
9319
			item = this._itemsByIdentity[identity];
9320
		}else{
9321
			item = this._arrayOfAllItems[identity];
9322
		}
9323
		if(item === undefined){
9324
			item = null;
9325
		}
9326
		return item; // Object
9327
	},
9328
 
9329
	getIdentityAttributes: function(/* item */ item){
9330
		//	summary:
9331
		//		See dojo.data.api.Identity.getIdentifierAttributes()
9332
 
9333
		var identifier = this._features['dojo.data.api.Identity'];
9334
		if(identifier === Number){
9335
			// If (identifier === Number) it means getIdentity() just returns
9336
			// an integer item-number for each item.  The dojo.data.api.Identity
9337
			// spec says we need to return null if the identity is not composed
9338
			// of attributes
9339
			return null; // null
9340
		}else{
9341
			return [identifier]; // Array
9342
		}
9343
	},
9344
 
9345
	_forceLoad: function(){
9346
		//	summary:
9347
		//		Internal function to force a load of the store if it hasn't occurred yet.  This is required
9348
		//		for specific functions to work properly.
9349
		var self = this;
9350
		if(this._jsonFileUrl){
9351
				var getArgs = {
9352
					url: self._jsonFileUrl,
9353
					handleAs: "json-comment-optional",
9354
					sync: true
9355
				};
9356
			var getHandler = dojo.xhrGet(getArgs);
9357
			getHandler.addCallback(function(data){
9358
				try{
9359
					//Check to be sure there wasn't another load going on concurrently
9360
					//So we don't clobber data that comes in on it.  If there is a load going on
9361
					//then do not save this data.  It will potentially clobber current data.
9362
					//We mainly wanted to sync/wait here.
9363
					//TODO:  Revisit the loading scheme of this store to improve multi-initial
9364
					//request handling.
9365
					if (self._loadInProgress !== true && !self._loadFinished) {
9366
						self._getItemsFromLoadedData(data);
9367
						self._loadFinished = true;
9368
					}
9369
				}catch(e){
9370
					console.log(e);
9371
					throw e;
9372
				}
9373
			});
9374
			getHandler.addErrback(function(error){
9375
				throw error;
9376
			});
9377
		}else if(this._jsonData){
9378
			self._getItemsFromLoadedData(self._jsonData);
9379
			self._jsonData = null;
9380
			self._loadFinished = true;
9381
		}
9382
	}
9383
});
9384
//Mix in the simple fetch implementation to this class.
9385
dojo.extend(dojo.data.ItemFileReadStore,dojo.data.util.simpleFetch);
9386
 
9387
}
9388
 
9389
if(!dojo._hasResource["dijit.form.ValidationTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9390
dojo._hasResource["dijit.form.ValidationTextBox"] = true;
9391
dojo.provide("dijit.form.ValidationTextBox");
9392
 
9393
 
9394
 
9395
 
9396
 
9397
 
9398
 
9399
 
9400
dojo.declare(
9401
	"dijit.form.ValidationTextBox",
9402
	dijit.form.TextBox,
9403
	{
9404
		// summary:
9405
		//		A subclass of TextBox.
9406
		//		Over-ride isValid in subclasses to perform specific kinds of validation.
9407
 
9408
		templateString:"<table style=\"display: -moz-inline-stack;\" class=\"dijit dijitReset dijitInlineTable\" cellspacing=\"0\" cellpadding=\"0\"\n\tid=\"widget_${id}\" name=\"${name}\"\n\tdojoAttachEvent=\"onmouseenter:_onMouse,onmouseleave:_onMouse\" waiRole=\"presentation\"\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset dijitInputField\" width=\"100%\"\n\t\t\t><input dojoAttachPoint='textbox,focusNode' dojoAttachEvent='onfocus,onblur:_onMouse,onkeyup,onkeypress:_onKeyPress' autocomplete=\"off\"\n\t\t\ttype='${type}' name='${name}'\n\t\t/></td\n\t\t><td class=\"dijitReset dijitValidationIconField\" width=\"0%\"\n\t\t\t><div dojoAttachPoint='iconNode' class='dijitValidationIcon'></div><div class='dijitValidationIconText'>&Chi;</div\n\t\t></td\n\t></tr\n></table>\n",
9409
		baseClass: "dijitTextBox",
9410
 
9411
		// default values for new subclass properties
9412
		// required: Boolean
9413
		//		Can be true or false, default is false.
9414
		required: false,
9415
		// promptMessage: String
9416
		//		Hint string
9417
		promptMessage: "",
9418
		// invalidMessage: String
9419
		// 		The message to display if value is invalid.
9420
		invalidMessage: "$_unset_$", // read from the message file if not overridden
9421
		// constraints: Object
9422
		//		user-defined object needed to pass parameters to the validator functions
9423
		constraints: {},
9424
		// regExp: String
9425
		//		regular expression string used to validate the input
9426
		//		Do not specify both regExp and regExpGen
9427
		regExp: ".*",
9428
		// regExpGen: Function
9429
		//		user replaceable function used to generate regExp when dependent on constraints
9430
		//		Do not specify both regExp and regExpGen
9431
		regExpGen: function(constraints){ return this.regExp; },
9432
 
9433
		// state: String
9434
		//		Shows current state (ie, validation result) of input (Normal, Warning, or Error)
9435
		state: "",
9436
 
9437
		setValue: function(){
9438
			this.inherited('setValue', arguments);
9439
			this.validate(false);
9440
		},
9441
 
9442
		validator: function(value,constraints){
9443
			// summary: user replaceable function used to validate the text input against the regular expression.
9444
			return (new RegExp("^(" + this.regExpGen(constraints) + ")"+(this.required?"":"?")+"$")).test(value) &&
9445
				(!this.required || !this._isEmpty(value)) &&
9446
				(this._isEmpty(value) || this.parse(value, constraints) !== null);
9447
		},
9448
 
9449
		isValid: function(/* Boolean*/ isFocused){
9450
			// summary: Need to over-ride with your own validation code in subclasses
9451
			return this.validator(this.textbox.value, this.constraints);
9452
		},
9453
 
9454
		_isEmpty: function(value){
9455
			// summary: Checks for whitespace
9456
			return /^\s*$/.test(value); // Boolean
9457
		},
9458
 
9459
		getErrorMessage: function(/* Boolean*/ isFocused){
9460
			// summary: return an error message to show if appropriate
9461
			return this.invalidMessage;
9462
		},
9463
 
9464
		getPromptMessage: function(/* Boolean*/ isFocused){
9465
			// summary: return a hint to show if appropriate
9466
			return this.promptMessage;
9467
		},
9468
 
9469
		validate: function(/* Boolean*/ isFocused){
9470
			// summary:
9471
			//		Called by oninit, onblur, and onkeypress.
9472
			// description:
9473
			//		Show missing or invalid messages if appropriate, and highlight textbox field.
9474
			var message = "";
9475
			var isValid = this.isValid(isFocused);
9476
			var isEmpty = this._isEmpty(this.textbox.value);
9477
			this.state = (isValid || (!this._hasBeenBlurred && isEmpty)) ? "" : "Error";
9478
			this._setStateClass();
9479
			dijit.setWaiState(this.focusNode, "invalid", (isValid? "false" : "true"));
9480
			if(isFocused){
9481
				if(isEmpty){
9482
					message = this.getPromptMessage(true);
9483
				}
9484
				if(!message && !isValid){
9485
					message = this.getErrorMessage(true);
9486
				}
9487
			}
9488
			this._displayMessage(message);
9489
		},
9490
 
9491
		// currently displayed message
9492
		_message: "",
9493
 
9494
		_displayMessage: function(/*String*/ message){
9495
			if(this._message == message){ return; }
9496
			this._message = message;
9497
			this.displayMessage(message);
9498
		},
9499
 
9500
		displayMessage: function(/*String*/ message){
9501
			// summary:
9502
			//		User overridable method to display validation errors/hints.
9503
			//		By default uses a tooltip.
9504
			if(message){
9505
				dijit.showTooltip(message, this.domNode);
9506
			}else{
9507
				dijit.hideTooltip(this.domNode);
9508
			}
9509
		},
9510
 
9511
		_hasBeenBlurred: false,
9512
 
9513
		_onBlur: function(evt){
9514
			this._hasBeenBlurred = true;
9515
			this.validate(false);
9516
			this.inherited('_onBlur', arguments);
9517
		},
9518
 
9519
		onfocus: function(evt){
9520
			// TODO: change to _onFocus?
9521
			this.validate(true);
9522
			this._onMouse(evt);	// update CSS classes
9523
		},
9524
 
9525
		onkeyup: function(evt){
9526
			this.onfocus(evt);
9527
		},
9528
 
9529
		//////////// INITIALIZATION METHODS ///////////////////////////////////////
9530
		constructor: function(){
9531
			this.constraints = {};
9532
		},
9533
 
9534
		postMixInProperties: function(){
9535
			this.inherited('postMixInProperties', arguments);
9536
			this.constraints.locale=this.lang;
9537
			this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
9538
			if(this.invalidMessage == "$_unset_$"){ this.invalidMessage = this.messages.invalidMessage; }
9539
			var p = this.regExpGen(this.constraints);
9540
			this.regExp = p;
9541
			// make value a string for all types so that form reset works well
9542
		}
9543
	}
9544
);
9545
 
9546
dojo.declare(
9547
	"dijit.form.MappedTextBox",
9548
	dijit.form.ValidationTextBox,
9549
	{
9550
		// summary:
9551
		//		A subclass of ValidationTextBox.
9552
		//		Provides a hidden input field and a serialize method to override
9553
 
9554
		serialize: function(val, /*Object?*/options){
9555
			// summary: user replaceable function used to convert the getValue() result to a String
9556
			return (val.toString ? val.toString() : "");
9557
		},
9558
 
9559
		toString: function(){
9560
			// summary: display the widget as a printable string using the widget's value
9561
			var val = this.filter(this.getValue());
9562
			return (val!=null) ? ((typeof val == "string") ? val : this.serialize(val, this.constraints)) : "";
9563
		},
9564
 
9565
		validate: function(){
9566
			this.valueNode.value = this.toString();
9567
			this.inherited('validate', arguments);
9568
		},
9569
 
9570
		postCreate: function(){
9571
			var textbox = this.textbox;
9572
			var valueNode = (this.valueNode = document.createElement("input"));
9573
			valueNode.setAttribute("type", textbox.type);
9574
			valueNode.setAttribute("value", this.toString());
9575
			dojo.style(valueNode, "display", "none");
9576
			valueNode.name = this.textbox.name;
9577
			this.textbox.name = "_" + this.textbox.name + "_displayed_";
9578
			this.textbox.removeAttribute("name");
9579
			dojo.place(valueNode, textbox, "after");
9580
 
9581
			this.inherited('postCreate', arguments);
9582
		}
9583
	}
9584
);
9585
 
9586
dojo.declare(
9587
	"dijit.form.RangeBoundTextBox",
9588
	dijit.form.MappedTextBox,
9589
	{
9590
		// summary:
9591
		//		A subclass of MappedTextBox.
9592
		//		Tests for a value out-of-range
9593
		/*===== contraints object:
9594
		// min: Number
9595
		//		Minimum signed value.  Default is -Infinity
9596
		min: undefined,
9597
		// max: Number
9598
		//		Maximum signed value.  Default is +Infinity
9599
		max: undefined,
9600
		=====*/
9601
 
9602
		// rangeMessage: String
9603
		//		The message to display if value is out-of-range
9604
		rangeMessage: "",
9605
 
9606
		compare: function(val1, val2){
9607
			// summary: compare 2 values
9608
			return val1 - val2;
9609
		},
9610
 
9611
		rangeCheck: function(/* Number */ primitive, /* Object */ constraints){
9612
			// summary: user replaceable function used to validate the range of the numeric input value
9613
			var isMin = (typeof constraints.min != "undefined");
9614
			var isMax = (typeof constraints.max != "undefined");
9615
			if(isMin || isMax){
9616
				return (!isMin || this.compare(primitive,constraints.min) >= 0) &&
9617
					(!isMax || this.compare(primitive,constraints.max) <= 0);
9618
			}else{ return true; }
9619
		},
9620
 
9621
		isInRange: function(/* Boolean*/ isFocused){
9622
			// summary: Need to over-ride with your own validation code in subclasses
9623
			return this.rangeCheck(this.getValue(), this.constraints);
9624
		},
9625
 
9626
		isValid: function(/* Boolean*/ isFocused){
9627
			return this.inherited('isValid', arguments) &&
9628
				((this._isEmpty(this.textbox.value) && !this.required) || this.isInRange(isFocused));
9629
		},
9630
 
9631
		getErrorMessage: function(/* Boolean*/ isFocused){
9632
			if(dijit.form.RangeBoundTextBox.superclass.isValid.call(this, false) && !this.isInRange(isFocused)){ return this.rangeMessage; }
9633
			else{ return this.inherited('getErrorMessage', arguments); }
9634
		},
9635
 
9636
		postMixInProperties: function(){
9637
			this.inherited('postMixInProperties', arguments);
9638
			if(!this.rangeMessage){
9639
				this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
9640
				this.rangeMessage = this.messages.rangeMessage;
9641
			}
9642
		},
9643
 
9644
		postCreate: function(){
9645
			this.inherited('postCreate', arguments);
9646
			if(typeof this.constraints.min != "undefined"){
9647
				dijit.setWaiState(this.focusNode, "valuemin", this.constraints.min);
9648
			}
9649
			if(typeof this.constraints.max != "undefined"){
9650
				dijit.setWaiState(this.focusNode, "valuemax", this.constraints.max);
9651
			}
9652
		}
9653
	}
9654
);
9655
 
9656
}
9657
 
9658
if(!dojo._hasResource["dijit.form.ComboBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9659
dojo._hasResource["dijit.form.ComboBox"] = true;
9660
dojo.provide("dijit.form.ComboBox");
9661
 
9662
 
9663
 
9664
 
9665
 
9666
dojo.declare(
9667
	"dijit.form.ComboBoxMixin",
9668
	null,
9669
	{
9670
		// summary:
9671
		//		Auto-completing text box, and base class for FilteringSelect widget.
9672
		//
9673
		//		The drop down box's values are populated from an class called
9674
		//		a data provider, which returns a list of values based on the characters
9675
		//		that the user has typed into the input box.
9676
		//
9677
		//		Some of the options to the ComboBox are actually arguments to the data
9678
		//		provider.
9679
		//
9680
		//		You can assume that all the form widgets (and thus anything that mixes
9681
		//		in ComboBoxMixin) will inherit from _FormWidget and thus the "this"
9682
		//		reference will also "be a" _FormWidget.
9683
 
9684
		// item: Object
9685
		//		This is the item returned by the dojo.data.store implementation that
9686
		//		provides the data for this cobobox, it's the currently selected item.
9687
		item: null,
9688
 
9689
		// pageSize: Integer
9690
		//		Argument to data provider.
9691
		//		Specifies number of search results per page (before hitting "next" button)
9692
		pageSize: Infinity,
9693
 
9694
		// store: Object
9695
		//		Reference to data provider object used by this ComboBox
9696
		store: null,
9697
 
9698
		// query: Object
9699
		//		A query that can be passed to 'store' to initially filter the items,
9700
		//		before doing further filtering based on searchAttr and the key.
9701
		query: {},
9702
 
9703
		// autoComplete: Boolean
9704
		//		If you type in a partial string, and then tab out of the <input> box,
9705
		//		automatically copy the first entry displayed in the drop down list to
9706
		//		the <input> field
9707
		autoComplete: true,
9708
 
9709
		// searchDelay: Integer
9710
		//		Delay in milliseconds between when user types something and we start
9711
		//		searching based on that value
9712
		searchDelay: 100,
9713
 
9714
		// searchAttr: String
9715
		//		Searches pattern match against this field
9716
		searchAttr: "name",
9717
 
9718
		// ignoreCase: Boolean
9719
		//		Set true if the ComboBox should ignore case when matching possible items
9720
		ignoreCase: true,
9721
 
9722
		// hasDownArrow: Boolean
9723
		//		Set this textbox to have a down arrow button.
9724
		//		Defaults to true.
9725
		hasDownArrow:true,
9726
 
9727
		// _hasFocus: Boolean
9728
		//		Represents focus state of the textbox
9729
		// TODO: get rid of this; it's unnecessary (but currently referenced in FilteringSelect)
9730
		_hasFocus:false,
9731
 
9732
		templateString:"<table class=\"dijit dijitReset dijitInlineTable dijitLeft\" cellspacing=\"0\" cellpadding=\"0\"\n\tid=\"widget_${id}\" name=\"${name}\" dojoAttachEvent=\"onmouseenter:_onMouse,onmouseleave:_onMouse\" waiRole=\"presentation\"\n\t><tr class=\"dijitReset\"\n\t\t><td class='dijitReset dijitStretch dijitInputField' width=\"100%\"\n\t\t\t><input type=\"text\" autocomplete=\"off\" name=\"${name}\"\n\t\t\tdojoAttachEvent=\"onkeypress, onkeyup, onfocus, compositionend\"\n\t\t\tdojoAttachPoint=\"textbox,focusNode\" waiRole=\"combobox\"\n\t\t/></td\n\t\t><td class=\"dijitReset dijitValidationIconField\" width=\"0%\"\n\t\t\t><div dojoAttachPoint='iconNode' class='dijitValidationIcon'></div\n\t\t\t><div class='dijitValidationIconText'>&Chi;</div\n\t\t></td\n\t\t><td class='dijitReset dijitRight dijitButtonNode dijitDownArrowButton' width=\"0%\"\n\t\t\tdojoAttachPoint=\"downArrowNode\"\n\t\t\tdojoAttachEvent=\"onmousedown:_onArrowMouseDown,onmouseup:_onMouse,onmouseenter:_onMouse,onmouseleave:_onMouse\"\n\t\t\t><div class=\"dijitDownArrowButtonInner\" waiRole=\"presentation\"\n\t\t\t\t><div class=\"dijitDownArrowButtonChar\">&#9660;</div\n\t\t\t></div\n\t\t></td\t\n\t></tr\n></table>\n",
9733
 
9734
		baseClass:"dijitComboBox",
9735
 
9736
		_lastDisplayedValue: "",
9737
 
9738
		getValue:function(){
9739
			// don't get the textbox value but rather the previously set hidden value
9740
			return dijit.form.TextBox.superclass.getValue.apply(this, arguments);
9741
		},
9742
 
9743
		setDisplayedValue:function(/*String*/ value){
9744
			this._lastDisplayedValue = value;
9745
			this.setValue(value, true);
9746
		},
9747
 
9748
		_getCaretPos: function(/*DomNode*/ element){
9749
			// khtml 3.5.2 has selection* methods as does webkit nightlies from 2005-06-22
9750
			if(typeof(element.selectionStart)=="number"){
9751
				// FIXME: this is totally borked on Moz < 1.3. Any recourse?
9752
				return element.selectionStart;
9753
			}else if(dojo.isIE){
9754
				// in the case of a mouse click in a popup being handled,
9755
				// then the document.selection is not the textarea, but the popup
9756
				// var r = document.selection.createRange();
9757
				// hack to get IE 6 to play nice. What a POS browser.
9758
				var tr = document.selection.createRange().duplicate();
9759
				var ntr = element.createTextRange();
9760
				tr.move("character",0);
9761
				ntr.move("character",0);
9762
				try{
9763
					// If control doesnt have focus, you get an exception.
9764
					// Seems to happen on reverse-tab, but can also happen on tab (seems to be a race condition - only happens sometimes).
9765
					// There appears to be no workaround for this - googled for quite a while.
9766
					ntr.setEndPoint("EndToEnd", tr);
9767
					return String(ntr.text).replace(/\r/g,"").length;
9768
				}catch(e){
9769
					return 0; // If focus has shifted, 0 is fine for caret pos.
9770
				}
9771
			}
9772
		},
9773
 
9774
		_setCaretPos: function(/*DomNode*/ element, /*Number*/ location){
9775
			location = parseInt(location);
9776
			this._setSelectedRange(element, location, location);
9777
		},
9778
 
9779
		_setSelectedRange: function(/*DomNode*/ element, /*Number*/ start, /*Number*/ end){
9780
			if(!end){
9781
				end = element.value.length;
9782
			}  // NOTE: Strange - should be able to put caret at start of text?
9783
			// Mozilla
9784
			// parts borrowed from http://www.faqts.com/knowledge_base/view.phtml/aid/13562/fid/130
9785
			if(element.setSelectionRange){
9786
				dijit.focus(element);
9787
				element.setSelectionRange(start, end);
9788
			}else if(element.createTextRange){ // IE
9789
				var range = element.createTextRange();
9790
				with(range){
9791
					collapse(true);
9792
					moveEnd('character', end);
9793
					moveStart('character', start);
9794
					select();
9795
				}
9796
			}else{ //otherwise try the event-creation hack (our own invention)
9797
				// do we need these?
9798
				element.value = element.value;
9799
				element.blur();
9800
				dijit.focus(element);
9801
				// figure out how far back to go
9802
				var dist = parseInt(element.value.length)-end;
9803
				var tchar = String.fromCharCode(37);
9804
				var tcc = tchar.charCodeAt(0);
9805
				for(var x = 0; x < dist; x++){
9806
					var te = document.createEvent("KeyEvents");
9807
					te.initKeyEvent("keypress", true, true, null, false, false, false, false, tcc, tcc);
9808
					element.dispatchEvent(te);
9809
				}
9810
			}
9811
		},
9812
 
9813
		onkeypress: function(/*Event*/ evt){
9814
			// summary: handles keyboard events
9815
 
9816
			//except for pasting case - ctrl + v(118)
9817
			if(evt.altKey || (evt.ctrlKey && evt.charCode != 118)){
9818
				return;
9819
			}
9820
			var doSearch = false;
9821
			this.item = null; // #4872
9822
			if(this._isShowingNow){this._popupWidget.handleKey(evt);}
9823
			switch(evt.keyCode){
9824
				case dojo.keys.PAGE_DOWN:
9825
				case dojo.keys.DOWN_ARROW:
9826
					if(!this._isShowingNow||this._prev_key_esc){
9827
						this._arrowPressed();
9828
						doSearch=true;
9829
					}else{
9830
						this._announceOption(this._popupWidget.getHighlightedOption());
9831
					}
9832
					dojo.stopEvent(evt);
9833
					this._prev_key_backspace = false;
9834
					this._prev_key_esc = false;
9835
					break;
9836
 
9837
				case dojo.keys.PAGE_UP:
9838
				case dojo.keys.UP_ARROW:
9839
					if(this._isShowingNow){
9840
						this._announceOption(this._popupWidget.getHighlightedOption());
9841
					}
9842
					dojo.stopEvent(evt);
9843
					this._prev_key_backspace = false;
9844
					this._prev_key_esc = false;
9845
					break;
9846
 
9847
				case dojo.keys.ENTER:
9848
					// prevent submitting form if user presses enter
9849
					// also prevent accepting the value if either Next or Previous are selected
9850
					var highlighted;
9851
					if(this._isShowingNow&&(highlighted=this._popupWidget.getHighlightedOption())){
9852
						// only stop event on prev/next
9853
						if(highlighted==this._popupWidget.nextButton){
9854
							this._nextSearch(1);
9855
							dojo.stopEvent(evt);
9856
							break;
9857
						}
9858
						else if(highlighted==this._popupWidget.previousButton){
9859
							this._nextSearch(-1);
9860
							dojo.stopEvent(evt);
9861
							break;
9862
						}
9863
					}else{
9864
						this.setDisplayedValue(this.getDisplayedValue());
9865
					}
9866
					// default case:
9867
					// prevent submit, but allow event to bubble
9868
					evt.preventDefault();
9869
					// fall through
9870
 
9871
				case dojo.keys.TAB:
9872
					var newvalue=this.getDisplayedValue();
9873
					// #4617: if the user had More Choices selected fall into the _onBlur handler
9874
					if(this._popupWidget &&
9875
						(newvalue == this._popupWidget._messages["previousMessage"] ||
9876
							newvalue == this._popupWidget._messages["nextMessage"])){
9877
						break;
9878
					}
9879
					if(this._isShowingNow){
9880
						this._prev_key_backspace = false;
9881
						this._prev_key_esc = false;
9882
						if(this._popupWidget.getHighlightedOption()){
9883
							this._popupWidget.setValue({target:this._popupWidget.getHighlightedOption()}, true);
9884
						}
9885
						this._hideResultList();
9886
					}
9887
					break;
9888
 
9889
				case dojo.keys.SPACE:
9890
					this._prev_key_backspace = false;
9891
					this._prev_key_esc = false;
9892
					if(this._isShowingNow && this._popupWidget.getHighlightedOption()){
9893
						dojo.stopEvent(evt);
9894
						this._selectOption();
9895
						this._hideResultList();
9896
					}else{
9897
						doSearch = true;
9898
					}
9899
					break;
9900
 
9901
				case dojo.keys.ESCAPE:
9902
					this._prev_key_backspace = false;
9903
					this._prev_key_esc = true;
9904
					this._hideResultList();
9905
					if(this._lastDisplayedValue != this.getDisplayedValue()){
9906
						this.setDisplayedValue(this._lastDisplayedValue);
9907
						dojo.stopEvent(evt);
9908
					}else{
9909
						this.setValue(this.getValue(), false);
9910
					}
9911
					break;
9912
 
9913
				case dojo.keys.DELETE:
9914
				case dojo.keys.BACKSPACE:
9915
					this._prev_key_esc = false;
9916
					this._prev_key_backspace = true;
9917
					doSearch = true;
9918
					break;
9919
 
9920
				case dojo.keys.RIGHT_ARROW: // fall through
9921
 
9922
				case dojo.keys.LEFT_ARROW: // fall through
9923
					this._prev_key_backspace = false;
9924
					this._prev_key_esc = false;
9925
					break;
9926
 
9927
				default:// non char keys (F1-F12 etc..)  shouldn't open list
9928
					this._prev_key_backspace = false;
9929
					this._prev_key_esc = false;
9930
					if(dojo.isIE || evt.charCode != 0){
9931
						doSearch=true;
9932
					}
9933
			}
9934
			if(this.searchTimer){
9935
				clearTimeout(this.searchTimer);
9936
			}
9937
			if(doSearch){
9938
				// need to wait a tad before start search so that the event bubbles through DOM and we have value visible
9939
				this.searchTimer = setTimeout(dojo.hitch(this, this._startSearchFromInput), this.searchDelay);
9940
			}
9941
		},
9942
 
9943
		_autoCompleteText: function(/*String*/ text){
9944
			// summary:
9945
			// Fill in the textbox with the first item from the drop down list, and
9946
			// highlight the characters that were auto-completed.   For example, if user
9947
			// typed "CA" and the drop down list appeared, the textbox would be changed to
9948
			// "California" and "ifornia" would be highlighted.
9949
 
9950
			// IE7: clear selection so next highlight works all the time
9951
			this._setSelectedRange(this.focusNode, this.focusNode.value.length, this.focusNode.value.length);
9952
			// does text autoComplete the value in the textbox?
9953
			// #3744: escape regexp so the user's input isn't treated as a regular expression.
9954
			// Example: If the user typed "(" then the regexp would throw "unterminated parenthetical."
9955
			// Also see #2558 for the autocompletion bug this regular expression fixes.
9956
			if(new RegExp("^"+escape(this.focusNode.value), this.ignoreCase ? "i" : "").test(escape(text))){
9957
				var cpos = this._getCaretPos(this.focusNode);
9958
				// only try to extend if we added the last character at the end of the input
9959
				if((cpos+1) > this.focusNode.value.length){
9960
					// only add to input node as we would overwrite Capitalisation of chars
9961
					// actually, that is ok
9962
					this.focusNode.value = text;//.substr(cpos);
9963
					// visually highlight the autocompleted characters
9964
					this._setSelectedRange(this.focusNode, cpos, this.focusNode.value.length);
9965
					dijit.setWaiState(this.focusNode, "valuenow", text);
9966
				}
9967
			}else{
9968
				// text does not autoComplete; replace the whole value and highlight
9969
				this.focusNode.value = text;
9970
				this._setSelectedRange(this.focusNode, 0, this.focusNode.value.length);
9971
				dijit.setWaiState(this.focusNode, "valuenow", text);
9972
			}
9973
		},
9974
 
9975
		_openResultList: function(/*Object*/ results, /*Object*/ dataObject){
9976
			if(this.disabled || dataObject.query[this.searchAttr] != this._lastQuery){
9977
				return;
9978
			}
9979
			this._popupWidget.clearResultList();
9980
			if(!results.length){
9981
				this._hideResultList();
9982
				return;
9983
			}
9984
 
9985
			// Fill in the textbox with the first item from the drop down list, and
9986
			// highlight the characters that were auto-completed.   For example, if user
9987
			// typed "CA" and the drop down list appeared, the textbox would be changed to
9988
			// "California" and "ifornia" would be highlighted.
9989
 
9990
			var zerothvalue=new String(this.store.getValue(results[0], this.searchAttr));
9991
			if(zerothvalue && this.autoComplete && !this._prev_key_backspace &&
9992
			// when the user clicks the arrow button to show the full list,
9993
			// startSearch looks for "*".
9994
			// it does not make sense to autocomplete
9995
			// if they are just previewing the options available.
9996
				(dataObject.query[this.searchAttr] != "*")){
9997
				this._autoCompleteText(zerothvalue);
9998
				// announce the autocompleted value
9999
				dijit.setWaiState(this.focusNode || this.domNode, "valuenow", zerothvalue);
10000
			}
10001
			this._popupWidget.createOptions(results, dataObject, dojo.hitch(this, this._getMenuLabelFromItem));
10002
 
10003
			// show our list (only if we have content, else nothing)
10004
			this._showResultList();
10005
 
10006
			// #4091: tell the screen reader that the paging callback finished by shouting the next choice
10007
			if(dataObject.direction){
10008
				if(dataObject.direction==1){
10009
					this._popupWidget.highlightFirstOption();
10010
				}else if(dataObject.direction==-1){
10011
					this._popupWidget.highlightLastOption();
10012
				}
10013
				this._announceOption(this._popupWidget.getHighlightedOption());
10014
			}
10015
		},
10016
 
10017
		_showResultList: function(){
10018
			this._hideResultList();
10019
			var items = this._popupWidget.getItems(),
10020
				visibleCount = Math.min(items.length,this.maxListLength);
10021
			this._arrowPressed();
10022
			// hide the tooltip
10023
			this._displayMessage("");
10024
 
10025
			// Position the list and if it's too big to fit on the screen then
10026
			// size it to the maximum possible height
10027
			// Our dear friend IE doesnt take max-height so we need to calculate that on our own every time
10028
			// TODO: want to redo this, see http://trac.dojotoolkit.org/ticket/3272, http://trac.dojotoolkit.org/ticket/4108
10029
			with(this._popupWidget.domNode.style){
10030
				// natural size of the list has changed, so erase old width/height settings,
10031
				// which were hardcoded in a previous call to this function (via dojo.marginBox() call)
10032
				width="";
10033
				height="";
10034
			}
10035
			var best=this.open();
10036
			// #3212: only set auto scroll bars if necessary
10037
			// prevents issues with scroll bars appearing when they shouldn't when node is made wider (fractional pixels cause this)
10038
			var popupbox=dojo.marginBox(this._popupWidget.domNode);
10039
			this._popupWidget.domNode.style.overflow=((best.h==popupbox.h)&&(best.w==popupbox.w))?"hidden":"auto";
10040
			// #4134: borrow TextArea scrollbar test so content isn't covered by scrollbar and horizontal scrollbar doesn't appear
10041
			var newwidth=best.w;
10042
			if(best.h<this._popupWidget.domNode.scrollHeight){newwidth+=16;}
10043
			dojo.marginBox(this._popupWidget.domNode, {h:best.h,w:Math.max(newwidth,this.domNode.offsetWidth)});
10044
		},
10045
 
10046
		_hideResultList: function(){
10047
			if(this._isShowingNow){
10048
				dijit.popup.close(this._popupWidget);
10049
				this._arrowIdle();
10050
				this._isShowingNow=false;
10051
			}
10052
		},
10053
 
10054
		_onBlur: function(){
10055
			// summary: called magically when focus has shifted away from this widget and it's dropdown
10056
			this._hasFocus=false;
10057
			this._hasBeenBlurred = true;
10058
			this._hideResultList();
10059
			this._arrowIdle();
10060
			// if the user clicks away from the textbox OR tabs away, set the value to the textbox value
10061
			// #4617: if value is now more choices or previous choices, revert the value
10062
			var newvalue=this.getDisplayedValue();
10063
			if(this._popupWidget&&(newvalue==this._popupWidget._messages["previousMessage"]||newvalue==this._popupWidget._messages["nextMessage"])){
10064
				this.setValue(this._lastValueReported, true);
10065
			}else{
10066
				this.setDisplayedValue(newvalue);
10067
			}
10068
		},
10069
 
10070
		onfocus:function(/*Event*/ evt){
10071
			this._hasFocus=true;
10072
 
10073
			// update styling to reflect that we are focused
10074
			this._onMouse(evt);
10075
		},
10076
 
10077
		_announceOption: function(/*Node*/ node){
10078
			// summary:
10079
			//	a11y code that puts the highlighted option in the textbox
10080
			//	This way screen readers will know what is happening in the menu
10081
 
10082
			if(node==null){return;}
10083
			// pull the text value from the item attached to the DOM node
10084
			var newValue;
10085
			if(node==this._popupWidget.nextButton||node==this._popupWidget.previousButton){
10086
				newValue=node.innerHTML;
10087
			}else{
10088
				newValue=this.store.getValue(node.item, this.searchAttr);
10089
			}
10090
			// get the text that the user manually entered (cut off autocompleted text)
10091
			this.focusNode.value=this.focusNode.value.substring(0, this._getCaretPos(this.focusNode));
10092
			// autocomplete the rest of the option to announce change
10093
			this._autoCompleteText(newValue);
10094
		},
10095
 
10096
		_selectOption: function(/*Event*/ evt){
10097
			var tgt = null;
10098
			if(!evt){
10099
				evt ={ target: this._popupWidget.getHighlightedOption()};
10100
			}
10101
				// what if nothing is highlighted yet?
10102
			if(!evt.target){
10103
				// handle autocompletion where the the user has hit ENTER or TAB
10104
				this.setDisplayedValue(this.getDisplayedValue());
10105
				return;
10106
			// otherwise the user has accepted the autocompleted value
10107
			}else{
10108
				tgt = evt.target;
10109
			}
10110
			if(!evt.noHide){
10111
				this._hideResultList();
10112
				this._setCaretPos(this.focusNode, this.store.getValue(tgt.item, this.searchAttr).length);
10113
			}
10114
			this._doSelect(tgt);
10115
		},
10116
 
10117
		_doSelect: function(tgt){
10118
			this.item = tgt.item;
10119
			this.setValue(this.store.getValue(tgt.item, this.searchAttr), true);
10120
		},
10121
 
10122
		_onArrowMouseDown: function(evt){
10123
			// summary: callback when arrow is clicked
10124
			if(this.disabled){
10125
				return;
10126
			}
10127
			dojo.stopEvent(evt);
10128
			this.focus();
10129
			if(this._isShowingNow){
10130
				this._hideResultList();
10131
			}else{
10132
				// forces full population of results, if they click
10133
				// on the arrow it means they want to see more options
10134
				this._startSearch("");
10135
			}
10136
		},
10137
 
10138
		_startSearchFromInput: function(){
10139
			this._startSearch(this.focusNode.value);
10140
		},
10141
 
10142
		_startSearch: function(/*String*/ key){
10143
			if(!this._popupWidget){
10144
				this._popupWidget = new dijit.form._ComboBoxMenu({
10145
					onChange: dojo.hitch(this, this._selectOption)
10146
				});
10147
			}
10148
			// create a new query to prevent accidentally querying for a hidden value from FilteringSelect's keyField
10149
			var query=this.query;
10150
			this._lastQuery=query[this.searchAttr]=key+"*";
10151
			var dataObject=this.store.fetch({queryOptions:{ignoreCase:this.ignoreCase, deep:true}, query: query, onComplete:dojo.hitch(this, "_openResultList"), start:0, count:this.pageSize});
10152
			function nextSearch(dataObject, direction){
10153
				dataObject.start+=dataObject.count*direction;
10154
				// #4091: tell callback the direction of the paging so the screen reader knows which menu option to shout
10155
				dataObject.direction=direction;
10156
				dataObject.store.fetch(dataObject);
10157
			}
10158
			this._nextSearch=this._popupWidget.onPage=dojo.hitch(this, nextSearch, dataObject);
10159
		},
10160
 
10161
		_getValueField:function(){
10162
			return this.searchAttr;
10163
		},
10164
 
10165
		/////////////// Event handlers /////////////////////
10166
 
10167
		_arrowPressed: function(){
10168
			if(!this.disabled&&this.hasDownArrow){
10169
				dojo.addClass(this.downArrowNode, "dijitArrowButtonActive");
10170
			}
10171
		},
10172
 
10173
		_arrowIdle: function(){
10174
			if(!this.disabled&&this.hasDownArrow){
10175
				dojo.removeClass(this.downArrowNode, "dojoArrowButtonPushed");
10176
			}
10177
		},
10178
 
10179
		compositionend: function(/*Event*/ evt){
10180
			// summary: When inputting characters using an input method, such as Asian
10181
			// languages, it will generate this event instead of onKeyDown event
10182
			// Note: this event is only triggered in FF (not in IE)
10183
			this.onkeypress({charCode:-1});
10184
		},
10185
 
10186
		//////////// INITIALIZATION METHODS ///////////////////////////////////////
10187
		constructor: function(){
10188
			this.query={};
10189
		},
10190
 
10191
		postMixInProperties: function(){
10192
			if(!this.hasDownArrow){
10193
				this.baseClass = "dijitTextBox";
10194
			}
10195
			if(!this.store){
10196
				// if user didn't specify store, then assume there are option tags
10197
				var items = this.srcNodeRef ? dojo.query("> option", this.srcNodeRef).map(function(node){
10198
					node.style.display="none";
10199
					return { value: node.getAttribute("value"), name: String(node.innerHTML) };
10200
				}) : {};
10201
				this.store = new dojo.data.ItemFileReadStore({data: {identifier:this._getValueField(), items:items}});
10202
 
10203
				// if there is no value set and there is an option list,
10204
				// set the value to the first value to be consistent with native Select
10205
				if(items && items.length && !this.value){
10206
					// For <select>, IE does not let you set the value attribute of the srcNodeRef (and thus dojo.mixin does not copy it).
10207
					// IE does understand selectedIndex though, which is automatically set by the selected attribute of an option tag
10208
					this.value = items[this.srcNodeRef.selectedIndex != -1 ? this.srcNodeRef.selectedIndex : 0]
10209
						[this._getValueField()];
10210
				}
10211
			}
10212
		},
10213
 
10214
		uninitialize:function(){
10215
			if(this._popupWidget){
10216
				this._hideResultList();
10217
				this._popupWidget.destroy()
10218
			};
10219
		},
10220
 
10221
		_getMenuLabelFromItem:function(/*Item*/ item){
10222
			return {html:false, label:this.store.getValue(item, this.searchAttr)};
10223
		},
10224
 
10225
		open:function(){
10226
			this._isShowingNow=true;
10227
			return dijit.popup.open({
10228
				popup: this._popupWidget,
10229
				around: this.domNode,
10230
				parent: this
10231
			});
10232
		}
10233
	}
10234
);
10235
 
10236
dojo.declare(
10237
	"dijit.form._ComboBoxMenu",
10238
	[dijit._Widget, dijit._Templated],
10239
 
10240
	{
10241
		// summary:
10242
		//	Focus-less div based menu for internal use in ComboBox
10243
 
10244
		templateString:"<div class='dijitMenu' dojoAttachEvent='onmousedown,onmouseup,onmouseover,onmouseout' tabIndex='-1' style='overflow:\"auto\";'>"
10245
				+"<div class='dijitMenuItem dijitMenuPreviousButton' dojoAttachPoint='previousButton'></div>"
10246
				+"<div class='dijitMenuItem dijitMenuNextButton' dojoAttachPoint='nextButton'></div>"
10247
			+"</div>",
10248
		_messages:null,
10249
 
10250
		postMixInProperties:function(){
10251
			this._messages = dojo.i18n.getLocalization("dijit.form", "ComboBox", this.lang);
10252
			this.inherited("postMixInProperties", arguments);
10253
		},
10254
 
10255
		setValue:function(/*Object*/ value){
10256
			this.value=value;
10257
			this.onChange(value);
10258
		},
10259
 
10260
		onChange:function(/*Object*/ value){},
10261
		onPage:function(/*Number*/ direction){},
10262
 
10263
		postCreate:function(){
10264
			// fill in template with i18n messages
10265
			this.previousButton.innerHTML=this._messages["previousMessage"];
10266
			this.nextButton.innerHTML=this._messages["nextMessage"];
10267
			this.inherited("postCreate", arguments);
10268
		},
10269
 
10270
		onClose:function(){
10271
			this._blurOptionNode();
10272
		},
10273
 
10274
		_createOption:function(/*Object*/ item, labelFunc){
10275
			// summary: creates an option to appear on the popup menu
10276
			// subclassed by FilteringSelect
10277
 
10278
			var labelObject=labelFunc(item);
10279
			var menuitem = document.createElement("div");
10280
			if(labelObject.html){menuitem.innerHTML=labelObject.label;}
10281
			else{menuitem.appendChild(document.createTextNode(labelObject.label));}
10282
			// #3250: in blank options, assign a normal height
10283
			if(menuitem.innerHTML==""){
10284
				menuitem.innerHTML="&nbsp;"
10285
			}
10286
			menuitem.item=item;
10287
			return menuitem;
10288
		},
10289
 
10290
		createOptions:function(results, dataObject, labelFunc){
10291
			//this._dataObject=dataObject;
10292
			//this._dataObject.onComplete=dojo.hitch(comboBox, comboBox._openResultList);
10293
			// display "Previous . . ." button
10294
			this.previousButton.style.display=dataObject.start==0?"none":"";
10295
			// create options using _createOption function defined by parent ComboBox (or FilteringSelect) class
10296
			// #2309: iterate over cache nondestructively
10297
			var _this=this;
10298
			dojo.forEach(results, function(item){
10299
				var menuitem=_this._createOption(item, labelFunc);
10300
				menuitem.className = "dijitMenuItem";
10301
				_this.domNode.insertBefore(menuitem, _this.nextButton);
10302
			});
10303
			// display "Next . . ." button
10304
			this.nextButton.style.display=dataObject.count==results.length?"":"none";
10305
		},
10306
 
10307
		clearResultList:function(){
10308
			// keep the previous and next buttons of course
10309
			while(this.domNode.childNodes.length>2){
10310
				this.domNode.removeChild(this.domNode.childNodes[this.domNode.childNodes.length-2]);
10311
			}
10312
		},
10313
 
10314
		// these functions are called in showResultList
10315
		getItems:function(){
10316
			return this.domNode.childNodes;
10317
		},
10318
 
10319
		getListLength:function(){
10320
			return this.domNode.childNodes.length-2;
10321
		},
10322
 
10323
		onmousedown:function(/*Event*/ evt){
10324
			dojo.stopEvent(evt);
10325
		},
10326
 
10327
		onmouseup:function(/*Event*/ evt){
10328
			if(evt.target === this.domNode){
10329
				return;
10330
			}else if(evt.target==this.previousButton){
10331
				this.onPage(-1);
10332
			}else if(evt.target==this.nextButton){
10333
				this.onPage(1);
10334
			}else{
10335
				var tgt=evt.target;
10336
				// while the clicked node is inside the div
10337
				while(!tgt.item){
10338
					// recurse to the top
10339
					tgt=tgt.parentNode;
10340
				}
10341
				this.setValue({target:tgt}, true);
10342
			}
10343
		},
10344
 
10345
		onmouseover:function(/*Event*/ evt){
10346
			if(evt.target === this.domNode){ return; }
10347
			var tgt=evt.target;
10348
			if(!(tgt==this.previousButton||tgt==this.nextButton)){
10349
				// while the clicked node is inside the div
10350
				while(!tgt.item){
10351
					// recurse to the top
10352
					tgt=tgt.parentNode;
10353
				}
10354
			}
10355
			this._focusOptionNode(tgt);
10356
		},
10357
 
10358
		onmouseout:function(/*Event*/ evt){
10359
			if(evt.target === this.domNode){ return; }
10360
			this._blurOptionNode();
10361
		},
10362
 
10363
		_focusOptionNode:function(/*DomNode*/ node){
10364
			// summary:
10365
			//	does the actual highlight
10366
			if(this._highlighted_option != node){
10367
				this._blurOptionNode();
10368
				this._highlighted_option = node;
10369
				dojo.addClass(this._highlighted_option, "dijitMenuItemHover");
10370
			}
10371
		},
10372
 
10373
		_blurOptionNode:function(){
10374
			// summary:
10375
			//	removes highlight on highlighted option
10376
			if(this._highlighted_option){
10377
				dojo.removeClass(this._highlighted_option, "dijitMenuItemHover");
10378
				this._highlighted_option = null;
10379
			}
10380
		},
10381
 
10382
		_highlightNextOption:function(){
10383
			// because each press of a button clears the menu,
10384
			// the highlighted option sometimes becomes detached from the menu!
10385
			// test to see if the option has a parent to see if this is the case.
10386
			if(!this.getHighlightedOption()){
10387
				this._focusOptionNode(this.domNode.firstChild.style.display=="none"?this.domNode.firstChild.nextSibling:this.domNode.firstChild);
10388
			}else if(this._highlighted_option.nextSibling&&this._highlighted_option.nextSibling.style.display!="none"){
10389
				this._focusOptionNode(this._highlighted_option.nextSibling);
10390
			}
10391
			// scrollIntoView is called outside of _focusOptionNode because in IE putting it inside causes the menu to scroll up on mouseover
10392
			dijit.scrollIntoView(this._highlighted_option);
10393
		},
10394
 
10395
		highlightFirstOption:function(){
10396
			// highlight the non-Previous choices option
10397
			this._focusOptionNode(this.domNode.firstChild.nextSibling);
10398
			dijit.scrollIntoView(this._highlighted_option);
10399
		},
10400
 
10401
		highlightLastOption:function(){
10402
			// highlight the noon-More choices option
10403
			this._focusOptionNode(this.domNode.lastChild.previousSibling);
10404
			dijit.scrollIntoView(this._highlighted_option);
10405
		},
10406
 
10407
		_highlightPrevOption:function(){
10408
			// if nothing selected, highlight last option
10409
			// makes sense if you select Previous and try to keep scrolling up the list
10410
			if(!this.getHighlightedOption()){
10411
				this._focusOptionNode(this.domNode.lastChild.style.display=="none"?this.domNode.lastChild.previousSibling:this.domNode.lastChild);
10412
			}else if(this._highlighted_option.previousSibling&&this._highlighted_option.previousSibling.style.display!="none"){
10413
				this._focusOptionNode(this._highlighted_option.previousSibling);
10414
			}
10415
			dijit.scrollIntoView(this._highlighted_option);
10416
		},
10417
 
10418
		_page:function(/*Boolean*/ up){
10419
			var scrollamount=0;
10420
			var oldscroll=this.domNode.scrollTop;
10421
			var height=parseInt(dojo.getComputedStyle(this.domNode).height);
10422
			// if no item is highlighted, highlight the first option
10423
			if(!this.getHighlightedOption()){this._highlightNextOption();}
10424
			while(scrollamount<height){
10425
				if(up){
10426
					// stop at option 1
10427
					if(!this.getHighlightedOption().previousSibling||this._highlighted_option.previousSibling.style.display=="none"){break;}
10428
					this._highlightPrevOption();
10429
				}else{
10430
					// stop at last option
10431
					if(!this.getHighlightedOption().nextSibling||this._highlighted_option.nextSibling.style.display=="none"){break;}
10432
					this._highlightNextOption();
10433
				}
10434
				// going backwards
10435
				var newscroll=this.domNode.scrollTop;
10436
				scrollamount+=(newscroll-oldscroll)*(up ? -1:1);
10437
				oldscroll=newscroll;
10438
			}
10439
		},
10440
 
10441
		pageUp:function(){
10442
			this._page(true);
10443
		},
10444
 
10445
		pageDown:function(){
10446
			this._page(false);
10447
		},
10448
 
10449
		getHighlightedOption:function(){
10450
			// summary:
10451
			//	Returns the highlighted option.
10452
			return this._highlighted_option&&this._highlighted_option.parentNode ? this._highlighted_option : null;
10453
		},
10454
 
10455
		handleKey:function(evt){
10456
			switch(evt.keyCode){
10457
				case dojo.keys.DOWN_ARROW:
10458
					this._highlightNextOption();
10459
					break;
10460
				case dojo.keys.PAGE_DOWN:
10461
					this.pageDown();
10462
					break;
10463
				case dojo.keys.UP_ARROW:
10464
					this._highlightPrevOption();
10465
					break;
10466
				case dojo.keys.PAGE_UP:
10467
					this.pageUp();
10468
					break;
10469
			}
10470
		}
10471
	}
10472
);
10473
 
10474
dojo.declare(
10475
	"dijit.form.ComboBox",
10476
	[dijit.form.ValidationTextBox, dijit.form.ComboBoxMixin],
10477
	{
10478
		postMixInProperties: function(){
10479
			dijit.form.ComboBoxMixin.prototype.postMixInProperties.apply(this, arguments);
10480
			dijit.form.ValidationTextBox.prototype.postMixInProperties.apply(this, arguments);
10481
		}
10482
	}
10483
);
10484
 
10485
}
10486
 
10487
if(!dojo._hasResource["dojo.cldr.monetary"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
10488
dojo._hasResource["dojo.cldr.monetary"] = true;
10489
dojo.provide("dojo.cldr.monetary");
10490
 
10491
dojo.cldr.monetary.getData = function(code){
10492
// summary: A mapping of currency code to currency-specific formatting information. Returns a unique object with properties: places, round.
10493
// code: an iso4217 currency code
10494
 
10495
// from http://www.unicode.org/cldr/data/common/supplemental/supplementalData.xml:supplementalData/currencyData/fractions
10496
 
10497
	var placesData = {
10498
		ADP:0,BHD:3,BIF:0,BYR:0,CLF:0,CLP:0,DJF:0,ESP:0,GNF:0,
10499
		IQD:3,ITL:0,JOD:3,JPY:0,KMF:0,KRW:0,KWD:3,LUF:0,LYD:3,
10500
		MGA:0,MGF:0,OMR:3,PYG:0,RWF:0,TND:3,TRL:0,VUV:0,XAF:0,
10501
		XOF:0,XPF:0
10502
	};
10503
 
10504
	var roundingData = {CHF:5};
10505
 
10506
	var places = placesData[code], round = roundingData[code];
10507
	if(typeof places == "undefined"){ places = 2; }
10508
	if(typeof round == "undefined"){ round = 0; }
10509
 
10510
	return {places: places, round: round}; // Object
10511
};
10512
 
10513
}
10514
 
10515
if(!dojo._hasResource["dojo.currency"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
10516
dojo._hasResource["dojo.currency"] = true;
10517
dojo.provide("dojo.currency");
10518
 
10519
 
10520
 
10521
 
10522
 
10523
 
10524
dojo.currency._mixInDefaults = function(options){
10525
	options = options || {};
10526
	options.type = "currency";
10527
 
10528
	// Get locale-depenent currency data, like the symbol
10529
	var bundle = dojo.i18n.getLocalization("dojo.cldr", "currency", options.locale) || {};
10530
 
10531
	// Mixin locale-independent currency data, like # of places
10532
	var iso = options.currency;
10533
	var data = dojo.cldr.monetary.getData(iso);
10534
 
10535
	dojo.forEach(["displayName","symbol","group","decimal"], function(prop){
10536
		data[prop] = bundle[iso+"_"+prop];
10537
	});
10538
 
10539
	data.fractional = [true, false];
10540
 
10541
	// Mixin with provided options
10542
	return dojo.mixin(data, options);
10543
}
10544
 
10545
dojo.currency.format = function(/*Number*/value, /*Object?*/options){
10546
// summary:
10547
//		Format a Number as a String, using locale-specific settings
10548
//
10549
// description:
10550
//		Create a string from a Number using a known localized pattern.
10551
//		Formatting patterns appropriate to the locale are chosen from the CLDR http://unicode.org/cldr
10552
//		as well as the appropriate symbols and delimiters.  See http://www.unicode.org/reports/tr35/#Number_Elements
10553
//
10554
// value:
10555
//		the number to be formatted.
10556
//
10557
// options: object {currency: String, pattern: String?, places: Number?, round: Number?, symbol: String?, locale: String?}
10558
//		currency- the ISO4217 currency code, a three letter sequence like "USD"
10559
//			See http://en.wikipedia.org/wiki/ISO_4217
10560
//		symbol- override currency symbol. Normally, will be looked up in table of supported currencies, and ISO currency code will
10561
//			be used if not found.  See dojo.i18n.cldr.nls->currency.js
10562
//		pattern- override formatting pattern with this string (see dojo.number.applyPattern)
10563
//		places- fixed number of decimal places to show.  Default is defined by the currency.
10564
//	    round- 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1 means don't round.
10565
//		locale- override the locale used to determine formatting rules
10566
 
10567
	return dojo.number.format(value, dojo.currency._mixInDefaults(options));
10568
}
10569
 
10570
dojo.currency.regexp = function(/*Object?*/options){
10571
//
10572
// summary:
10573
//		Builds the regular needed to parse a number
10574
//
10575
// description:
10576
//		Returns regular expression with positive and negative match, group and decimal separators
10577
//
10578
// options: object {pattern: String, locale: String, strict: Boolean, places: mixed}
10579
//		currency- the ISO4217 currency code, a three letter sequence like "USD"
10580
//			See http://en.wikipedia.org/wiki/ISO_4217
10581
//		symbol- override currency symbol. Normally, will be looked up in table of supported currencies, and ISO currency code will
10582
//			be used if not found.  See dojo.i18n.cldr.nls->currency.js
10583
//		pattern- override pattern with this string
10584
//		locale- override the locale used to determine formatting rules
10585
//		strict- strict parsing, false by default
10586
//		places- number of decimal places to accept.  Default is defined by currency.
10587
	return dojo.number.regexp(dojo.currency._mixInDefaults(options)); // String
10588
}
10589
 
10590
dojo.currency.parse = function(/*String*/expression, /*Object?*/options){
10591
//
10592
// summary:
10593
//		Convert a properly formatted string to a primitive Number,
10594
//		using locale-specific settings.
10595
//
10596
// description:
10597
//		Create a Number from a string using a known localized pattern.
10598
//		Formatting patterns are chosen appropriate to the locale.
10599
//		Formatting patterns are implemented using the syntax described at *URL*
10600
//
10601
// expression: A string representation of a Number
10602
//
10603
// options: object {pattern: string, locale: string, strict: boolean}
10604
//		currency- the ISO4217 currency code, a three letter sequence like "USD"
10605
//			See http://en.wikipedia.org/wiki/ISO_4217
10606
//		symbol- override currency symbol. Normally, will be looked up in table of supported currencies, and ISO currency code will
10607
//			be used if not found.  See dojo.i18n.cldr.nls->currency.js
10608
//		pattern- override pattern with this string
10609
//		locale- override the locale used to determine formatting rules
10610
//		strict- strict parsing, false by default
10611
//		places- number of decimal places to accept.  Default is defined by currency.
10612
//		fractional- where places are implied by pattern or explicit 'places' parameter, whether to include the fractional portion.
10613
//			By default for currencies, it the fractional portion is optional.
10614
	return dojo.number.parse(expression, dojo.currency._mixInDefaults(options));
10615
}
10616
 
10617
}
10618
 
10619
if(!dojo._hasResource["dijit.form.NumberTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
10620
dojo._hasResource["dijit.form.NumberTextBox"] = true;
10621
dojo.provide("dijit.form.NumberTextBox");
10622
 
10623
 
10624
 
10625
 
10626
dojo.declare(
10627
	"dijit.form.NumberTextBoxMixin",
10628
	null,
10629
	{
10630
		// summary:
10631
		//		A mixin for all number textboxes
10632
		regExpGen: dojo.number.regexp,
10633
 
10634
		format: function(/*Number*/ value, /*Object*/ constraints){
10635
			if(isNaN(value)){ return ""; }
10636
			return dojo.number.format(value, constraints);
10637
		},
10638
 
10639
		parse: dojo.number.parse,
10640
 
10641
		filter: function(/*Number*/ value){
10642
			if(typeof value == "string"){ return this.inherited('filter', arguments); }
10643
			return (isNaN(value) ? '' : value);
10644
		},
10645
 
10646
		value: NaN
10647
	}
10648
);
10649
 
10650
dojo.declare(
10651
	"dijit.form.NumberTextBox",
10652
	[dijit.form.RangeBoundTextBox,dijit.form.NumberTextBoxMixin],
10653
	{
10654
		// summary:
10655
		//		A validating, serializable, range-bound text box.
10656
		// constraints object: min, max, places
10657
	}
10658
);
10659
 
10660
}
10661
 
10662
if(!dojo._hasResource["dijit.form.CurrencyTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
10663
dojo._hasResource["dijit.form.CurrencyTextBox"] = true;
10664
dojo.provide("dijit.form.CurrencyTextBox");
10665
 
10666
//FIXME: dojo.experimental throws an unreadable exception?
10667
//dojo.experimental("dijit.form.CurrencyTextBox");
10668
 
10669
 
10670
 
10671
 
10672
dojo.declare(
10673
	"dijit.form.CurrencyTextBox",
10674
	dijit.form.NumberTextBox,
10675
	{
10676
		// code: String
10677
		//		the ISO4217 currency code, a three letter sequence like "USD"
10678
		//		See http://en.wikipedia.org/wiki/ISO_4217
10679
		currency: "",
10680
 
10681
		regExpGen: dojo.currency.regexp,
10682
		format: dojo.currency.format,
10683
		parse: dojo.currency.parse,
10684
 
10685
		postMixInProperties: function(){
10686
			if(this.constraints === dijit.form.ValidationTextBox.prototype.constraints){
10687
				// declare a constraints property on 'this' so we don't overwrite the shared default object in 'prototype'
10688
				this.constraints = {};
10689
			}
10690
			this.constraints.currency = this.currency;
10691
			dijit.form.CurrencyTextBox.superclass.postMixInProperties.apply(this, arguments);
10692
		}
10693
	}
10694
);
10695
 
10696
}
10697
 
10698
if(!dojo._hasResource["dojo.cldr.supplemental"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
10699
dojo._hasResource["dojo.cldr.supplemental"] = true;
10700
dojo.provide("dojo.cldr.supplemental");
10701
 
10702
 
10703
 
10704
dojo.cldr.supplemental.getFirstDayOfWeek = function(/*String?*/locale){
10705
// summary: Returns a zero-based index for first day of the week
10706
// description:
10707
//		Returns a zero-based index for first day of the week, as used by the local (Gregorian) calendar.
10708
//		e.g. Sunday (returns 0), or Monday (returns 1)
10709
 
10710
	// from http://www.unicode.org/cldr/data/common/supplemental/supplementalData.xml:supplementalData/weekData/firstDay
10711
	var firstDay = {/*default is 1=Monday*/
10712
		mv:5,
10713
		ae:6,af:6,bh:6,dj:6,dz:6,eg:6,er:6,et:6,iq:6,ir:6,jo:6,ke:6,kw:6,lb:6,ly:6,ma:6,om:6,qa:6,sa:6,
10714
		sd:6,so:6,tn:6,ye:6,
10715
		as:0,au:0,az:0,bw:0,ca:0,cn:0,fo:0,ge:0,gl:0,gu:0,hk:0,ie:0,il:0,is:0,jm:0,jp:0,kg:0,kr:0,la:0,
10716
		mh:0,mo:0,mp:0,mt:0,nz:0,ph:0,pk:0,sg:0,th:0,tt:0,tw:0,um:0,us:0,uz:0,vi:0,za:0,zw:0,
10717
		et:0,mw:0,ng:0,tj:0,
10718
		gb:0,
10719
		sy:4
10720
	};
10721
 
10722
	var country = dojo.cldr.supplemental._region(locale);
10723
	var dow = firstDay[country];
10724
	return (typeof dow == 'undefined') ? 1 : dow; /*Number*/
10725
};
10726
 
10727
dojo.cldr.supplemental._region = function(/*String?*/locale){
10728
	locale = dojo.i18n.normalizeLocale(locale);
10729
	var tags = locale.split('-');
10730
	var region = tags[1];
10731
	if(!region){
10732
		// IE often gives language only (#2269)
10733
		// Arbitrary mappings of language-only locales to a country:
10734
        region = {de:"de", en:"us", es:"es", fi:"fi", fr:"fr", hu:"hu", it:"it",
10735
        ja:"jp", ko:"kr", nl:"nl", pt:"br", sv:"se", zh:"cn"}[tags[0]];
10736
	}else if(region.length == 4){
10737
		// The ISO 3166 country code is usually in the second position, unless a
10738
		// 4-letter script is given. See http://www.ietf.org/rfc/rfc4646.txt
10739
		region = tags[2];
10740
	}
10741
	return region;
10742
}
10743
 
10744
dojo.cldr.supplemental.getWeekend = function(/*String?*/locale){
10745
// summary: Returns a hash containing the start and end days of the weekend
10746
// description:
10747
//		Returns a hash containing the start and end days of the weekend according to local custom using locale,
10748
//		or by default in the user's locale.
10749
//		e.g. {start:6, end:0}
10750
 
10751
	// from http://www.unicode.org/cldr/data/common/supplemental/supplementalData.xml:supplementalData/weekData/weekend{Start,End}
10752
	var weekendStart = {/*default is 6=Saturday*/
10753
		eg:5,il:5,sy:5,
10754
		'in':0,
10755
		ae:4,bh:4,dz:4,iq:4,jo:4,kw:4,lb:4,ly:4,ma:4,om:4,qa:4,sa:4,sd:4,tn:4,ye:4
10756
	};
10757
 
10758
	var weekendEnd = {/*default is 0=Sunday*/
10759
		ae:5,bh:5,dz:5,iq:5,jo:5,kw:5,lb:5,ly:5,ma:5,om:5,qa:5,sa:5,sd:5,tn:5,ye:5,af:5,ir:5,
10760
		eg:6,il:6,sy:6
10761
	};
10762
 
10763
	var country = dojo.cldr.supplemental._region(locale);
10764
	var start = weekendStart[country];
10765
	var end = weekendEnd[country];
10766
	if(typeof start == 'undefined'){start=6;}
10767
	if(typeof end == 'undefined'){end=0;}
10768
	return {start:start, end:end}; /*Object {start,end}*/
10769
};
10770
 
10771
}
10772
 
10773
if(!dojo._hasResource["dojo.date"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
10774
dojo._hasResource["dojo.date"] = true;
10775
dojo.provide("dojo.date");
10776
 
10777
dojo.date.getDaysInMonth = function(/*Date*/dateObject){
10778
	//	summary:
10779
	//		Returns the number of days in the month used by dateObject
10780
	var month = dateObject.getMonth();
10781
	var days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
10782
	if(month == 1 && dojo.date.isLeapYear(dateObject)){ return 29; } // Number
10783
	return days[month]; // Number
10784
}
10785
 
10786
dojo.date.isLeapYear = function(/*Date*/dateObject){
10787
	//	summary:
10788
	//		Determines if the year of the dateObject is a leap year
10789
	//	description:
10790
	//		Leap years are years with an additional day YYYY-02-29, where the
10791
	//		year number is a multiple of four with the following exception: If
10792
	//		a year is a multiple of 100, then it is only a leap year if it is
10793
	//		also a multiple of 400. For example, 1900 was not a leap year, but
10794
	//		2000 is one.
10795
 
10796
	var year = dateObject.getFullYear();
10797
	return !(year%400) || (!(year%4) && !!(year%100)); // Boolean
10798
}
10799
 
10800
// FIXME: This is not localized
10801
dojo.date.getTimezoneName = function(/*Date*/dateObject){
10802
	//	summary:
10803
	//		Get the user's time zone as provided by the browser
10804
	// dateObject:
10805
	//		Needed because the timezone may vary with time (daylight savings)
10806
	//	description:
10807
	//		Try to get time zone info from toString or toLocaleString method of
10808
	//		the Date object -- UTC offset is not a time zone.  See
10809
	//		http://www.twinsun.com/tz/tz-link.htm Note: results may be
10810
	//		inconsistent across browsers.
10811
 
10812
	var str = dateObject.toString(); // Start looking in toString
10813
	var tz = ''; // The result -- return empty string if nothing found
10814
	var match;
10815
 
10816
	// First look for something in parentheses -- fast lookup, no regex
10817
	var pos = str.indexOf('(');
10818
	if(pos > -1){
10819
		tz = str.substring(++pos, str.indexOf(')'));
10820
	}else{
10821
		// If at first you don't succeed ...
10822
		// If IE knows about the TZ, it appears before the year
10823
		// Capital letters or slash before a 4-digit year
10824
		// at the end of string
10825
		var pat = /([A-Z\/]+) \d{4}$/;
10826
		if((match = str.match(pat))){
10827
			tz = match[1];
10828
		}else{
10829
		// Some browsers (e.g. Safari) glue the TZ on the end
10830
		// of toLocaleString instead of putting it in toString
10831
			str = dateObject.toLocaleString();
10832
			// Capital letters or slash -- end of string,
10833
			// after space
10834
			pat = / ([A-Z\/]+)$/;
10835
			if((match = str.match(pat))){
10836
				tz = match[1];
10837
			}
10838
		}
10839
	}
10840
 
10841
	// Make sure it doesn't somehow end up return AM or PM
10842
	return (tz == 'AM' || tz == 'PM') ? '' : tz; // String
10843
}
10844
 
10845
// Utility methods to do arithmetic calculations with Dates
10846
 
10847
dojo.date.compare = function(/*Date*/date1, /*Date?*/date2, /*String?*/portion){
10848
	//	summary:
10849
	//		Compare two date objects by date, time, or both.
10850
	//	description:
10851
	//  	Returns 0 if equal, positive if a > b, else negative.
10852
	//	date1:
10853
	//		Date object
10854
	//	date2:
10855
	//		Date object.  If not specified, the current Date is used.
10856
	//	portion:
10857
	//		A string indicating the "date" or "time" portion of a Date object.
10858
	//		Compares both "date" and "time" by default.  One of the following:
10859
	//		"date", "time", "datetime"
10860
 
10861
	// Extra step required in copy for IE - see #3112
10862
	date1 = new Date(Number(date1));
10863
	date2 = new Date(Number(date2 || new Date()));
10864
 
10865
	if(typeof portion !== "undefined"){
10866
		if(portion == "date"){
10867
			// Ignore times and compare dates.
10868
			date1.setHours(0, 0, 0, 0);
10869
			date2.setHours(0, 0, 0, 0);
10870
		}else if(portion == "time"){
10871
			// Ignore dates and compare times.
10872
			date1.setFullYear(0, 0, 0);
10873
			date2.setFullYear(0, 0, 0);
10874
		}
10875
	}
10876
 
10877
	if(date1 > date2){ return 1; } // int
10878
	if(date1 < date2){ return -1; } // int
10879
	return 0; // int
10880
};
10881
 
10882
dojo.date.add = function(/*Date*/date, /*String*/interval, /*int*/amount){
10883
	//	summary:
10884
	//		Add to a Date in intervals of different size, from milliseconds to years
10885
	//	date: Date
10886
	//		Date object to start with
10887
	//	interval:
10888
	//		A string representing the interval.  One of the following:
10889
	//			"year", "month", "day", "hour", "minute", "second",
10890
	//			"millisecond", "quarter", "week", "weekday"
10891
	//	amount:
10892
	//		How much to add to the date.
10893
 
10894
	var sum = new Date(Number(date)); // convert to Number before copying to accomodate IE (#3112)
10895
	var fixOvershoot = false;
10896
	var property = "Date";
10897
 
10898
	switch(interval){
10899
		case "day":
10900
			break;
10901
		case "weekday":
10902
			//i18n FIXME: assumes Saturday/Sunday weekend, but even this is not standard.  There are CLDR entries to localize this.
10903
			var days, weeks;
10904
			var adj = 0;
10905
			// Divide the increment time span into weekspans plus leftover days
10906
			// e.g., 8 days is one 5-day weekspan / and two leftover days
10907
			// Can't have zero leftover days, so numbers divisible by 5 get
10908
			// a days value of 5, and the remaining days make up the number of weeks
10909
			var mod = amount % 5;
10910
			if(!mod){
10911
				days = (amount > 0) ? 5 : -5;
10912
				weeks = (amount > 0) ? ((amount-5)/5) : ((amount+5)/5);
10913
			}else{
10914
				days = mod;
10915
				weeks = parseInt(amount/5);
10916
			}
10917
			// Get weekday value for orig date param
10918
			var strt = date.getDay();
10919
			// Orig date is Sat / positive incrementer
10920
			// Jump over Sun
10921
			if(strt == 6 && amount > 0){
10922
				adj = 1;
10923
			}else if(strt == 0 && amount < 0){
10924
			// Orig date is Sun / negative incrementer
10925
			// Jump back over Sat
10926
				adj = -1;
10927
			}
10928
			// Get weekday val for the new date
10929
			var trgt = strt + days;
10930
			// New date is on Sat or Sun
10931
			if(trgt == 0 || trgt == 6){
10932
				adj = (amount > 0) ? 2 : -2;
10933
			}
10934
			// Increment by number of weeks plus leftover days plus
10935
			// weekend adjustments
10936
			amount = 7 * weeks + days + adj;
10937
			break;
10938
		case "year":
10939
			property = "FullYear";
10940
			// Keep increment/decrement from 2/29 out of March
10941
			fixOvershoot = true;
10942
			break;
10943
		case "week":
10944
			amount *= 7;
10945
			break;
10946
		case "quarter":
10947
			// Naive quarter is just three months
10948
			amount *= 3;
10949
			// fallthrough...
10950
		case "month":
10951
			// Reset to last day of month if you overshoot
10952
			fixOvershoot = true;
10953
			property = "Month";
10954
			break;
10955
		case "hour":
10956
		case "minute":
10957
		case "second":
10958
		case "millisecond":
10959
			property = "UTC" + interval.charAt(0).toUpperCase() + interval.substring(1) + "s";
10960
	}
10961
 
10962
	if(property){
10963
		sum["set"+property](sum["get"+property]()+amount);
10964
	}
10965
 
10966
	if(fixOvershoot && (sum.getDate() < date.getDate())){
10967
		sum.setDate(0);
10968
	}
10969
 
10970
	return sum; // Date
10971
};
10972
 
10973
dojo.date.difference = function(/*Date*/date1, /*Date?*/date2, /*String?*/interval){
10974
	//	summary:
10975
	//		Get the difference in a specific unit of time (e.g., number of
10976
	//		months, weeks, days, etc.) between two dates, rounded to the
10977
	//		nearest integer.
10978
	//	date1:
10979
	//		Date object
10980
	//	date2:
10981
	//		Date object.  If not specified, the current Date is used.
10982
	//	interval:
10983
	//		A string representing the interval.  One of the following:
10984
	//			"year", "month", "day", "hour", "minute", "second",
10985
	//			"millisecond", "quarter", "week", "weekday"
10986
	//		Defaults to "day".
10987
 
10988
	date2 = date2 || new Date();
10989
	interval = interval || "day";
10990
	var yearDiff = date2.getFullYear() - date1.getFullYear();
10991
	var delta = 1; // Integer return value
10992
 
10993
	switch(interval){
10994
		case "quarter":
10995
			var m1 = date1.getMonth();
10996
			var m2 = date2.getMonth();
10997
			// Figure out which quarter the months are in
10998
			var q1 = Math.floor(m1/3) + 1;
10999
			var q2 = Math.floor(m2/3) + 1;
11000
			// Add quarters for any year difference between the dates
11001
			q2 += (yearDiff * 4);
11002
			delta = q2 - q1;
11003
			break;
11004
		case "weekday":
11005
			var days = Math.round(dojo.date.difference(date1, date2, "day"));
11006
			var weeks = parseInt(dojo.date.difference(date1, date2, "week"));
11007
			var mod = days % 7;
11008
 
11009
			// Even number of weeks
11010
			if(mod == 0){
11011
				days = weeks*5;
11012
			}else{
11013
				// Weeks plus spare change (< 7 days)
11014
				var adj = 0;
11015
				var aDay = date1.getDay();
11016
				var bDay = date2.getDay();
11017
 
11018
				weeks = parseInt(days/7);
11019
				mod = days % 7;
11020
				// Mark the date advanced by the number of
11021
				// round weeks (may be zero)
11022
				var dtMark = new Date(date1);
11023
				dtMark.setDate(dtMark.getDate()+(weeks*7));
11024
				var dayMark = dtMark.getDay();
11025
 
11026
				// Spare change days -- 6 or less
11027
				if(days > 0){
11028
					switch(true){
11029
						// Range starts on Sat
11030
						case aDay == 6:
11031
							adj = -1;
11032
							break;
11033
						// Range starts on Sun
11034
						case aDay == 0:
11035
							adj = 0;
11036
							break;
11037
						// Range ends on Sat
11038
						case bDay == 6:
11039
							adj = -1;
11040
							break;
11041
						// Range ends on Sun
11042
						case bDay == 0:
11043
							adj = -2;
11044
							break;
11045
						// Range contains weekend
11046
						case (dayMark + mod) > 5:
11047
							adj = -2;
11048
					}
11049
				}else if(days < 0){
11050
					switch(true){
11051
						// Range starts on Sat
11052
						case aDay == 6:
11053
							adj = 0;
11054
							break;
11055
						// Range starts on Sun
11056
						case aDay == 0:
11057
							adj = 1;
11058
							break;
11059
						// Range ends on Sat
11060
						case bDay == 6:
11061
							adj = 2;
11062
							break;
11063
						// Range ends on Sun
11064
						case bDay == 0:
11065
							adj = 1;
11066
							break;
11067
						// Range contains weekend
11068
						case (dayMark + mod) < 0:
11069
							adj = 2;
11070
					}
11071
				}
11072
				days += adj;
11073
				days -= (weeks*2);
11074
			}
11075
			delta = days;
11076
			break;
11077
		case "year":
11078
			delta = yearDiff;
11079
			break;
11080
		case "month":
11081
			delta = (date2.getMonth() - date1.getMonth()) + (yearDiff * 12);
11082
			break;
11083
		case "week":
11084
			// Truncate instead of rounding
11085
			// Don't use Math.floor -- value may be negative
11086
			delta = parseInt(dojo.date.difference(date1, date2, "day")/7);
11087
			break;
11088
		case "day":
11089
			delta /= 24;
11090
			// fallthrough
11091
		case "hour":
11092
			delta /= 60;
11093
			// fallthrough
11094
		case "minute":
11095
			delta /= 60;
11096
			// fallthrough
11097
		case "second":
11098
			delta /= 1000;
11099
			// fallthrough
11100
		case "millisecond":
11101
			delta *= date2.getTime() - date1.getTime();
11102
	}
11103
 
11104
	// Round for fractional values and DST leaps
11105
	return Math.round(delta); // Number (integer)
11106
};
11107
 
11108
}
11109
 
11110
if(!dojo._hasResource["dojo.date.locale"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
11111
dojo._hasResource["dojo.date.locale"] = true;
11112
dojo.provide("dojo.date.locale");
11113
 
11114
// Localization methods for Date.   Honor local customs using locale-dependent dojo.cldr data.
11115
 
11116
 
11117
 
11118
 
11119
 
11120
 
11121
 
11122
// Load the bundles containing localization information for
11123
// names and formats
11124
 
11125
 
11126
//NOTE: Everything in this module assumes Gregorian calendars.
11127
// Other calendars will be implemented in separate modules.
11128
 
11129
(function(){
11130
	// Format a pattern without literals
11131
	function formatPattern(dateObject, bundle, pattern){
11132
		return pattern.replace(/([a-z])\1*/ig, function(match){
11133
			var s;
11134
			var c = match.charAt(0);
11135
			var l = match.length;
11136
			var pad;
11137
			var widthList = ["abbr", "wide", "narrow"];
11138
			switch(c){
11139
				case 'G':
11140
					s = bundle[(l < 4) ? "eraAbbr" : "eraNames"][dateObject.getFullYear() < 0 ? 0 : 1];
11141
					break;
11142
				case 'y':
11143
					s = dateObject.getFullYear();
11144
					switch(l){
11145
						case 1:
11146
							break;
11147
						case 2:
11148
							s = String(s); s = s.substr(s.length - 2);
11149
							break;
11150
						default:
11151
							pad = true;
11152
					}
11153
					break;
11154
				case 'Q':
11155
				case 'q':
11156
					s = Math.ceil((dateObject.getMonth()+1)/3);
11157
//					switch(l){
11158
//						case 1: case 2:
11159
							pad = true;
11160
//							break;
11161
//						case 3: case 4: // unimplemented
11162
//					}
11163
					break;
11164
				case 'M':
11165
				case 'L':
11166
					var m = dateObject.getMonth();
11167
					var width;
11168
					switch(l){
11169
						case 1: case 2:
11170
							s = m+1; pad = true;
11171
							break;
11172
						case 3: case 4: case 5:
11173
							width = widthList[l-3];
11174
							break;
11175
					}
11176
					if(width){
11177
						var type = (c == "L") ? "standalone" : "format";
11178
						var prop = ["months",type,width].join("-");
11179
						s = bundle[prop][m];
11180
					}
11181
					break;
11182
				case 'w':
11183
					var firstDay = 0;
11184
					s = dojo.date.locale._getWeekOfYear(dateObject, firstDay); pad = true;
11185
					break;
11186
				case 'd':
11187
					s = dateObject.getDate(); pad = true;
11188
					break;
11189
				case 'D':
11190
					s = dojo.date.locale._getDayOfYear(dateObject); pad = true;
11191
					break;
11192
				case 'E':
11193
				case 'e':
11194
				case 'c': // REVIEW: don't see this in the spec?
11195
					var d = dateObject.getDay();
11196
					var width;
11197
					switch(l){
11198
						case 1: case 2:
11199
							if(c == 'e'){
11200
								var first = dojo.cldr.supplemental.getFirstDayOfWeek(options.locale);
11201
								d = (d-first+7)%7;
11202
							}
11203
							if(c != 'c'){
11204
								s = d+1; pad = true;
11205
								break;
11206
							}
11207
							// else fallthrough...
11208
						case 3: case 4: case 5:
11209
							width = widthList[l-3];
11210
							break;
11211
					}
11212
					if(width){
11213
						var type = (c == "c") ? "standalone" : "format";
11214
						var prop = ["days",type,width].join("-");
11215
						s = bundle[prop][d];
11216
					}
11217
					break;
11218
				case 'a':
11219
					var timePeriod = (dateObject.getHours() < 12) ? 'am' : 'pm';
11220
					s = bundle[timePeriod];
11221
					break;
11222
				case 'h':
11223
				case 'H':
11224
				case 'K':
11225
				case 'k':
11226
					var h = dateObject.getHours();
11227
					// strange choices in the date format make it impossible to write this succinctly
11228
					switch (c) {
11229
						case 'h': // 1-12
11230
							s = (h % 12) || 12;
11231
							break;
11232
						case 'H': // 0-23
11233
							s = h;
11234
							break;
11235
						case 'K': // 0-11
11236
							s = (h % 12);
11237
							break;
11238
						case 'k': // 1-24
11239
							s = h || 24;
11240
							break;
11241
					}
11242
					pad = true;
11243
					break;
11244
				case 'm':
11245
					s = dateObject.getMinutes(); pad = true;
11246
					break;
11247
				case 's':
11248
					s = dateObject.getSeconds(); pad = true;
11249
					break;
11250
				case 'S':
11251
					s = Math.round(dateObject.getMilliseconds() * Math.pow(10, l-3));
11252
					break;
11253
				case 'v': // FIXME: don't know what this is. seems to be same as z?
11254
				case 'z':
11255
					// We only have one timezone to offer; the one from the browser
11256
					s = dojo.date.getTimezoneName(dateObject);
11257
					if(s){break;}
11258
					l=4;
11259
					// fallthrough... use GMT if tz not available
11260
				case 'Z':
11261
					var offset = dateObject.getTimezoneOffset();
11262
					var tz = [
11263
						(offset<=0 ? "+" : "-"),
11264
						dojo.string.pad(Math.floor(Math.abs(offset)/60), 2),
11265
						dojo.string.pad(Math.abs(offset)% 60, 2)
11266
					];
11267
					if(l==4){
11268
						tz.splice(0, 0, "GMT");
11269
						tz.splice(3, 0, ":");
11270
					}
11271
					s = tz.join("");
11272
					break;
11273
//				case 'Y': case 'u': case 'W': case 'F': case 'g': case 'A':
11274
//					console.debug(match+" modifier unimplemented");
11275
				default:
11276
					throw new Error("dojo.date.locale.format: invalid pattern char: "+pattern);
11277
			}
11278
			if(pad){ s = dojo.string.pad(s, l); }
11279
			return s;
11280
		});
11281
	}
11282
 
11283
dojo.date.locale.format = function(/*Date*/dateObject, /*Object?*/options){
11284
	// summary:
11285
	//		Format a Date object as a String, using locale-specific settings.
11286
	//
11287
	// description:
11288
	//		Create a string from a Date object using a known localized pattern.
11289
	//		By default, this method formats both date and time from dateObject.
11290
	//		Formatting patterns are chosen appropriate to the locale.  Different
11291
	//		formatting lengths may be chosen, with "full" used by default.
11292
	//		Custom patterns may be used or registered with translations using
11293
	//		the addCustomFormats method.
11294
	//		Formatting patterns are implemented using the syntax described at
11295
	//		http://www.unicode.org/reports/tr35/tr35-4.html#Date_Format_Patterns
11296
	//
11297
	// dateObject:
11298
	//		the date and/or time to be formatted.  If a time only is formatted,
11299
	//		the values in the year, month, and day fields are irrelevant.  The
11300
	//		opposite is true when formatting only dates.
11301
	//
11302
	// options: object {selector: string, formatLength: string, datePattern: string, timePattern: string, locale: string}
11303
	//		selector- choice of 'time','date' (default: date and time)
11304
	//		formatLength- choice of long, short, medium or full (plus any custom additions).  Defaults to 'short'
11305
	//		datePattern,timePattern- override pattern with this string
11306
	//		am,pm- override strings for am/pm in times
11307
	//		locale- override the locale used to determine formatting rules
11308
 
11309
	options = options || {};
11310
 
11311
	var locale = dojo.i18n.normalizeLocale(options.locale);
11312
	var formatLength = options.formatLength || 'short';
11313
	var bundle = dojo.date.locale._getGregorianBundle(locale);
11314
	var str = [];
11315
	var sauce = dojo.hitch(this, formatPattern, dateObject, bundle);
11316
	if(options.selector == "year"){
11317
		// Special case as this is not yet driven by CLDR data
11318
		var year = dateObject.getFullYear();
11319
		if(locale.match(/^zh|^ja/)){
11320
			year += "\u5E74";
11321
		}
11322
		return year;
11323
	}
11324
	if(options.selector != "time"){
11325
		var datePattern = options.datePattern || bundle["dateFormat-"+formatLength];
11326
		if(datePattern){str.push(_processPattern(datePattern, sauce));}
11327
	}
11328
	if(options.selector != "date"){
11329
		var timePattern = options.timePattern || bundle["timeFormat-"+formatLength];
11330
		if(timePattern){str.push(_processPattern(timePattern, sauce));}
11331
	}
11332
	var result = str.join(" "); //TODO: use locale-specific pattern to assemble date + time
11333
	return result; // String
11334
};
11335
 
11336
dojo.date.locale.regexp = function(/*Object?*/options){
11337
	// summary:
11338
	//		Builds the regular needed to parse a localized date
11339
	//
11340
	// options: object {selector: string, formatLength: string, datePattern: string, timePattern: string, locale: string, strict: boolean}
11341
	//		selector- choice of 'time', 'date' (default: date and time)
11342
	//		formatLength- choice of long, short, medium or full (plus any custom additions).  Defaults to 'short'
11343
	//		datePattern,timePattern- override pattern with this string
11344
	//		locale- override the locale used to determine formatting rules
11345
 
11346
	return dojo.date.locale._parseInfo(options).regexp; // String
11347
};
11348
 
11349
dojo.date.locale._parseInfo = function(/*Object?*/options){
11350
	options = options || {};
11351
	var locale = dojo.i18n.normalizeLocale(options.locale);
11352
	var bundle = dojo.date.locale._getGregorianBundle(locale);
11353
	var formatLength = options.formatLength || 'short';
11354
	var datePattern = options.datePattern || bundle["dateFormat-" + formatLength];
11355
	var timePattern = options.timePattern || bundle["timeFormat-" + formatLength];
11356
	var pattern;
11357
	if(options.selector == 'date'){
11358
		pattern = datePattern;
11359
	}else if(options.selector == 'time'){
11360
		pattern = timePattern;
11361
	}else{
11362
		pattern = datePattern + ' ' + timePattern; //TODO: use locale-specific pattern to assemble date + time
11363
	}
11364
 
11365
	var tokens = [];
11366
	var re = _processPattern(pattern, dojo.hitch(this, _buildDateTimeRE, tokens, bundle, options));
11367
	return {regexp: re, tokens: tokens, bundle: bundle};
11368
};
11369
 
11370
dojo.date.locale.parse = function(/*String*/value, /*Object?*/options){
11371
	// summary:
11372
	//		Convert a properly formatted string to a primitive Date object,
11373
	//		using locale-specific settings.
11374
	//
11375
	// description:
11376
	//		Create a Date object from a string using a known localized pattern.
11377
	//		By default, this method parses looking for both date and time in the string.
11378
	//		Formatting patterns are chosen appropriate to the locale.  Different
11379
	//		formatting lengths may be chosen, with "full" used by default.
11380
	//		Custom patterns may be used or registered with translations using
11381
	//		the addCustomFormats method.
11382
	//		Formatting patterns are implemented using the syntax described at
11383
	//		http://www.unicode.org/reports/tr35/#Date_Format_Patterns
11384
	//
11385
	// value:
11386
	//		A string representation of a date
11387
	//
11388
	// options: object {selector: string, formatLength: string, datePattern: string, timePattern: string, locale: string, strict: boolean}
11389
	//		selector- choice of 'time', 'date' (default: date and time)
11390
	//		formatLength- choice of long, short, medium or full (plus any custom additions).  Defaults to 'short'
11391
	//		datePattern,timePattern- override pattern with this string
11392
	//		am,pm- override strings for am/pm in times
11393
	//		locale- override the locale used to determine formatting rules
11394
	//		strict- strict parsing, off by default
11395
 
11396
	var info = dojo.date.locale._parseInfo(options);
11397
	var tokens = info.tokens, bundle = info.bundle;
11398
	var re = new RegExp("^" + info.regexp + "$");
11399
	var match = re.exec(value);
11400
	if(!match){ return null; } // null
11401
 
11402
	var widthList = ['abbr', 'wide', 'narrow'];
11403
	//1972 is a leap year.  We want to avoid Feb 29 rolling over into Mar 1,
11404
	//in the cases where the year is parsed after the month and day.
11405
	var result = new Date(1972, 0);
11406
	var expected = {};
11407
	var amPm = "";
11408
	dojo.forEach(match, function(v, i){
11409
		if(!i){return;}
11410
		var token=tokens[i-1];
11411
		var l=token.length;
11412
		switch(token.charAt(0)){
11413
			case 'y':
11414
				if(l != 2){
11415
					//interpret year literally, so '5' would be 5 A.D.
11416
					result.setFullYear(v);
11417
					expected.year = v;
11418
				}else{
11419
					if(v<100){
11420
						v = Number(v);
11421
						//choose century to apply, according to a sliding window
11422
						//of 80 years before and 20 years after present year
11423
						var year = '' + new Date().getFullYear();
11424
						var century = year.substring(0, 2) * 100;
11425
						var yearPart = Number(year.substring(2, 4));
11426
						var cutoff = Math.min(yearPart + 20, 99);
11427
						var num = (v < cutoff) ? century + v : century - 100 + v;
11428
						result.setFullYear(num);
11429
						expected.year = num;
11430
					}else{
11431
						//we expected 2 digits and got more...
11432
						if(options.strict){
11433
							return null;
11434
						}
11435
						//interpret literally, so '150' would be 150 A.D.
11436
						//also tolerate '1950', if 'yyyy' input passed to 'yy' format
11437
						result.setFullYear(v);
11438
						expected.year = v;
11439
					}
11440
				}
11441
				break;
11442
			case 'M':
11443
				if(l>2){
11444
					var months = bundle['months-format-' + widthList[l-3]].concat();
11445
					if(!options.strict){
11446
						//Tolerate abbreviating period in month part
11447
						//Case-insensitive comparison
11448
						v = v.replace(".","").toLowerCase();
11449
						months = dojo.map(months, function(s){ return s.replace(".","").toLowerCase(); } );
11450
					}
11451
					v = dojo.indexOf(months, v);
11452
					if(v == -1){
11453
//						console.debug("dojo.date.locale.parse: Could not parse month name: '" + v + "'.");
11454
						return null;
11455
					}
11456
				}else{
11457
					v--;
11458
				}
11459
				result.setMonth(v);
11460
				expected.month = v;
11461
				break;
11462
			case 'E':
11463
			case 'e':
11464
				var days = bundle['days-format-' + widthList[l-3]].concat();
11465
				if(!options.strict){
11466
					//Case-insensitive comparison
11467
					v = v.toLowerCase();
11468
					days = dojo.map(days, "".toLowerCase);
11469
				}
11470
				v = dojo.indexOf(days, v);
11471
				if(v == -1){
11472
//					console.debug("dojo.date.locale.parse: Could not parse weekday name: '" + v + "'.");
11473
					return null;
11474
				}
11475
 
11476
				//TODO: not sure what to actually do with this input,
11477
				//in terms of setting something on the Date obj...?
11478
				//without more context, can't affect the actual date
11479
				//TODO: just validate?
11480
				break;
11481
			case 'd':
11482
				result.setDate(v);
11483
				expected.date = v;
11484
				break;
11485
			case 'D':
11486
				//FIXME: need to defer this until after the year is set for leap-year?
11487
				result.setMonth(0);
11488
				result.setDate(v);
11489
				break;
11490
			case 'a': //am/pm
11491
				var am = options.am || bundle.am;
11492
				var pm = options.pm || bundle.pm;
11493
				if(!options.strict){
11494
					var period = /\./g;
11495
					v = v.replace(period,'').toLowerCase();
11496
					am = am.replace(period,'').toLowerCase();
11497
					pm = pm.replace(period,'').toLowerCase();
11498
				}
11499
				if(options.strict && v != am && v != pm){
11500
//					console.debug("dojo.date.locale.parse: Could not parse am/pm part.");
11501
					return null;
11502
				}
11503
 
11504
				// we might not have seen the hours field yet, so store the state and apply hour change later
11505
				amPm = (v == pm) ? 'p' : (v == am) ? 'a' : '';
11506
				break;
11507
			case 'K': //hour (1-24)
11508
				if(v==24){v=0;}
11509
				// fallthrough...
11510
			case 'h': //hour (1-12)
11511
			case 'H': //hour (0-23)
11512
			case 'k': //hour (0-11)
11513
				//TODO: strict bounds checking, padding
11514
				if(v > 23){
11515
//					console.debug("dojo.date.locale.parse: Illegal hours value");
11516
					return null;
11517
				}
11518
 
11519
				//in the 12-hour case, adjusting for am/pm requires the 'a' part
11520
				//which could come before or after the hour, so we will adjust later
11521
				result.setHours(v);
11522
				break;
11523
			case 'm': //minutes
11524
				result.setMinutes(v);
11525
				break;
11526
			case 's': //seconds
11527
				result.setSeconds(v);
11528
				break;
11529
			case 'S': //milliseconds
11530
				result.setMilliseconds(v);
11531
//				break;
11532
//			case 'w':
11533
//TODO				var firstDay = 0;
11534
//			default:
11535
//TODO: throw?
11536
//				console.debug("dojo.date.locale.parse: unsupported pattern char=" + token.charAt(0));
11537
		}
11538
	});
11539
 
11540
	var hours = result.getHours();
11541
	if(amPm === 'p' && hours < 12){
11542
		result.setHours(hours + 12); //e.g., 3pm -> 15
11543
	}else if(amPm === 'a' && hours == 12){
11544
		result.setHours(0); //12am -> 0
11545
	}
11546
 
11547
	//validate parse date fields versus input date fields
11548
	if(expected.year && result.getFullYear() != expected.year){
11549
//		console.debug("dojo.date.locale.parse: Parsed year: '" + result.getFullYear() + "' did not match input year: '" + expected.year + "'.");
11550
		return null;
11551
	}
11552
	if(expected.month && result.getMonth() != expected.month){
11553
//		console.debug("dojo.date.locale.parse: Parsed month: '" + result.getMonth() + "' did not match input month: '" + expected.month + "'.");
11554
		return null;
11555
	}
11556
	if(expected.date && result.getDate() != expected.date){
11557
//		console.debug("dojo.date.locale.parse: Parsed day of month: '" + result.getDate() + "' did not match input day of month: '" + expected.date + "'.");
11558
		return null;
11559
	}
11560
 
11561
	//TODO: implement a getWeekday() method in order to test
11562
	//validity of input strings containing 'EEE' or 'EEEE'...
11563
	return result; // Date
11564
};
11565
 
11566
function _processPattern(pattern, applyPattern, applyLiteral, applyAll){
11567
	//summary: Process a pattern with literals in it
11568
 
11569
	// Break up on single quotes, treat every other one as a literal, except '' which becomes '
11570
	var identity = function(x){return x;};
11571
	applyPattern = applyPattern || identity;
11572
	applyLiteral = applyLiteral || identity;
11573
	applyAll = applyAll || identity;
11574
 
11575
	//split on single quotes (which escape literals in date format strings)
11576
	//but preserve escaped single quotes (e.g., o''clock)
11577
	var chunks = pattern.match(/(''|[^'])+/g);
11578
	var literal = false;
11579
 
11580
	dojo.forEach(chunks, function(chunk, i){
11581
		if(!chunk){
11582
			chunks[i]='';
11583
		}else{
11584
			chunks[i]=(literal ? applyLiteral : applyPattern)(chunk);
11585
			literal = !literal;
11586
		}
11587
	});
11588
	return applyAll(chunks.join(''));
11589
}
11590
 
11591
function _buildDateTimeRE(tokens, bundle, options, pattern){
11592
	pattern = dojo.regexp.escapeString(pattern);
11593
	if(!options.strict){ pattern = pattern.replace(" a", " ?a"); } // kludge to tolerate no space before am/pm
11594
	return pattern.replace(/([a-z])\1*/ig, function(match){
11595
		// Build a simple regexp.  Avoid captures, which would ruin the tokens list
11596
		var s;
11597
		var c = match.charAt(0);
11598
		var l = match.length;
11599
		var p2 = '', p3 = '';
11600
		if(options.strict){
11601
			if(l > 1){ p2 = '0' + '{'+(l-1)+'}'; }
11602
			if(l > 2){ p3 = '0' + '{'+(l-2)+'}'; }
11603
		}else{
11604
			p2 = '0?'; p3 = '0{0,2}';
11605
		}
11606
		switch(c){
11607
			case 'y':
11608
				s = '\\d{2,4}';
11609
				break;
11610
			case 'M':
11611
				s = (l>2) ? '\\S+' : p2+'[1-9]|1[0-2]';
11612
				break;
11613
			case 'D':
11614
				s = p2+'[1-9]|'+p3+'[1-9][0-9]|[12][0-9][0-9]|3[0-5][0-9]|36[0-6]';
11615
				break;
11616
			case 'd':
11617
				s = p2+'[1-9]|[12]\\d|3[01]';
11618
				break;
11619
			case 'w':
11620
				s = p2+'[1-9]|[1-4][0-9]|5[0-3]';
11621
				break;
11622
		    case 'E':
11623
				s = '\\S+';
11624
				break;
11625
			case 'h': //hour (1-12)
11626
				s = p2+'[1-9]|1[0-2]';
11627
				break;
11628
			case 'k': //hour (0-11)
11629
				s = p2+'\\d|1[01]';
11630
				break;
11631
			case 'H': //hour (0-23)
11632
				s = p2+'\\d|1\\d|2[0-3]';
11633
				break;
11634
			case 'K': //hour (1-24)
11635
				s = p2+'[1-9]|1\\d|2[0-4]';
11636
				break;
11637
			case 'm':
11638
			case 's':
11639
				s = '[0-5]\\d';
11640
				break;
11641
			case 'S':
11642
				s = '\\d{'+l+'}';
11643
				break;
11644
			case 'a':
11645
				var am = options.am || bundle.am || 'AM';
11646
				var pm = options.pm || bundle.pm || 'PM';
11647
				if(options.strict){
11648
					s = am + '|' + pm;
11649
				}else{
11650
					s = am + '|' + pm;
11651
					if(am != am.toLowerCase()){ s += '|' + am.toLowerCase(); }
11652
					if(pm != pm.toLowerCase()){ s += '|' + pm.toLowerCase(); }
11653
				}
11654
				break;
11655
			default:
11656
			// case 'v':
11657
			// case 'z':
11658
			// case 'Z':
11659
				s = ".*";
11660
//				console.debug("parse of date format, pattern=" + pattern);
11661
		}
11662
 
11663
		if(tokens){ tokens.push(match); }
11664
 
11665
		return "(" + s + ")"; // add capture
11666
	}).replace(/[\xa0 ]/g, "[\\s\\xa0]"); // normalize whitespace.  Need explicit handling of \xa0 for IE.
11667
}
11668
})();
11669
 
11670
(function(){
11671
var _customFormats = [];
11672
dojo.date.locale.addCustomFormats = function(/*String*/packageName, /*String*/bundleName){
11673
	// summary:
11674
	//		Add a reference to a bundle containing localized custom formats to be
11675
	//		used by date/time formatting and parsing routines.
11676
	//
11677
	// description:
11678
	//		The user may add custom localized formats where the bundle has properties following the
11679
	//		same naming convention used by dojo for the CLDR data: dateFormat-xxxx / timeFormat-xxxx
11680
	//		The pattern string should match the format used by the CLDR.
11681
	//		See dojo.date.format for details.
11682
	//		The resources must be loaded by dojo.requireLocalization() prior to use
11683
 
11684
	_customFormats.push({pkg:packageName,name:bundleName});
11685
};
11686
 
11687
dojo.date.locale._getGregorianBundle = function(/*String*/locale){
11688
	var gregorian = {};
11689
	dojo.forEach(_customFormats, function(desc){
11690
		var bundle = dojo.i18n.getLocalization(desc.pkg, desc.name, locale);
11691
		gregorian = dojo.mixin(gregorian, bundle);
11692
	}, this);
11693
	return gregorian; /*Object*/
11694
};
11695
})();
11696
 
11697
dojo.date.locale.addCustomFormats("dojo.cldr","gregorian");
11698
 
11699
dojo.date.locale.getNames = function(/*String*/item, /*String*/type, /*String?*/use, /*String?*/locale){
11700
	// summary:
11701
	//		Used to get localized strings from dojo.cldr for day or month names.
11702
	//
11703
	// item: 'months' || 'days'
11704
	// type: 'wide' || 'narrow' || 'abbr' (e.g. "Monday", "Mon", or "M" respectively, in English)
11705
	// use: 'standAlone' || 'format' (default)
11706
	// locale: override locale used to find the names
11707
 
11708
	var label;
11709
	var lookup = dojo.date.locale._getGregorianBundle(locale);
11710
	var props = [item, use, type];
11711
	if(use == 'standAlone'){
11712
		label = lookup[props.join('-')];
11713
	}
11714
	props[1] = 'format';
11715
 
11716
	// return by copy so changes won't be made accidentally to the in-memory model
11717
	return (label || lookup[props.join('-')]).concat(); /*Array*/
11718
};
11719
 
11720
dojo.date.locale.isWeekend = function(/*Date?*/dateObject, /*String?*/locale){
11721
	// summary:
11722
	//	Determines if the date falls on a weekend, according to local custom.
11723
 
11724
	var weekend = dojo.cldr.supplemental.getWeekend(locale);
11725
	var day = (dateObject || new Date()).getDay();
11726
	if(weekend.end < weekend.start){
11727
		weekend.end += 7;
11728
		if(day < weekend.start){ day += 7; }
11729
	}
11730
	return day >= weekend.start && day <= weekend.end; // Boolean
11731
};
11732
 
11733
// These are used only by format and strftime.  Do they need to be public?  Which module should they go in?
11734
 
11735
dojo.date.locale._getDayOfYear = function(/*Date*/dateObject){
11736
	// summary: gets the day of the year as represented by dateObject
11737
	return dojo.date.difference(new Date(dateObject.getFullYear(), 0, 1), dateObject) + 1; // Number
11738
};
11739
 
11740
dojo.date.locale._getWeekOfYear = function(/*Date*/dateObject, /*Number*/firstDayOfWeek){
11741
	if(arguments.length == 1){ firstDayOfWeek = 0; } // Sunday
11742
 
11743
	var firstDayOfYear = new Date(dateObject.getFullYear(), 0, 1).getDay();
11744
	var adj = (firstDayOfYear - firstDayOfWeek + 7) % 7;
11745
	var week = Math.floor((dojo.date.locale._getDayOfYear(dateObject) + adj - 1) / 7);
11746
 
11747
	// if year starts on the specified day, start counting weeks at 1
11748
	if(firstDayOfYear == firstDayOfWeek){ week++; }
11749
 
11750
	return week; // Number
11751
};
11752
 
11753
}
11754
 
11755
if(!dojo._hasResource["dijit._Calendar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
11756
dojo._hasResource["dijit._Calendar"] = true;
11757
dojo.provide("dijit._Calendar");
11758
 
11759
 
11760
 
11761
 
11762
 
11763
 
11764
 
11765
 
11766
dojo.declare(
11767
	"dijit._Calendar",
11768
	[dijit._Widget, dijit._Templated],
11769
	{
11770
		/*
11771
		summary:
11772
			A simple GUI for choosing a date in the context of a monthly calendar.
11773
 
11774
		description:
11775
			This widget is used internally by other widgets and is not accessible
11776
			as a standalone widget.
11777
			This widget can't be used in a form because it doesn't serialize the date to an
11778
			<input> field.  For a form element, use DateTextBox instead.
11779
 
11780
			Note that the parser takes all dates attributes passed in the `RFC 3339` format:
11781
			http://www.faqs.org/rfcs/rfc3339.html (2005-06-30T08:05:00-07:00)
11782
			so that they are serializable and locale-independent.
11783
 
11784
		usage:
11785
			var calendar = new dijit._Calendar({}, dojo.byId("calendarNode"));
11786
		 	-or-
11787
			<div dojoType="dijit._Calendar"></div>
11788
		*/
11789
		templateString:"<table cellspacing=\"0\" cellpadding=\"0\" class=\"dijitCalendarContainer\">\n\t<thead>\n\t\t<tr class=\"dijitReset dijitCalendarMonthContainer\" valign=\"top\">\n\t\t\t<th class='dijitReset' dojoAttachPoint=\"decrementMonth\">\n\t\t\t\t<span class=\"dijitInline dijitCalendarIncrementControl dijitCalendarDecrease\"><span dojoAttachPoint=\"decreaseArrowNode\" class=\"dijitA11ySideArrow dijitCalendarIncrementControl dijitCalendarDecreaseInner\">-</span></span>\n\t\t\t</th>\n\t\t\t<th class='dijitReset' colspan=\"5\">\n\t\t\t\t<div dojoAttachPoint=\"monthLabelSpacer\" class=\"dijitCalendarMonthLabelSpacer\"></div>\n\t\t\t\t<div dojoAttachPoint=\"monthLabelNode\" class=\"dijitCalendarMonth\"></div>\n\t\t\t</th>\n\t\t\t<th class='dijitReset' dojoAttachPoint=\"incrementMonth\">\n\t\t\t\t<div class=\"dijitInline dijitCalendarIncrementControl dijitCalendarIncrease\"><span dojoAttachPoint=\"increaseArrowNode\" class=\"dijitA11ySideArrow dijitCalendarIncrementControl dijitCalendarIncreaseInner\">+</span></div>\n\t\t\t</th>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<th class=\"dijitReset dijitCalendarDayLabelTemplate\"><span class=\"dijitCalendarDayLabel\"></span></th>\n\t\t</tr>\n\t</thead>\n\t<tbody dojoAttachEvent=\"onclick: _onDayClick\" class=\"dijitReset dijitCalendarBodyContainer\">\n\t\t<tr class=\"dijitReset dijitCalendarWeekTemplate\">\n\t\t\t<td class=\"dijitReset dijitCalendarDateTemplate\"><span class=\"dijitCalendarDateLabel\"></span></td>\n\t\t</tr>\n\t</tbody>\n\t<tfoot class=\"dijitReset dijitCalendarYearContainer\">\n\t\t<tr>\n\t\t\t<td class='dijitReset' valign=\"top\" colspan=\"7\">\n\t\t\t\t<h3 class=\"dijitCalendarYearLabel\">\n\t\t\t\t\t<span dojoAttachPoint=\"previousYearLabelNode\" class=\"dijitInline dijitCalendarPreviousYear\"></span>\n\t\t\t\t\t<span dojoAttachPoint=\"currentYearLabelNode\" class=\"dijitInline dijitCalendarSelectedYear\"></span>\n\t\t\t\t\t<span dojoAttachPoint=\"nextYearLabelNode\" class=\"dijitInline dijitCalendarNextYear\"></span>\n\t\t\t\t</h3>\n\t\t\t</td>\n\t\t</tr>\n\t</tfoot>\n</table>\t\n",
11790
 
11791
		// value: Date
11792
		// the currently selected Date
11793
		value: new Date(),
11794
 
11795
		// dayWidth: String
11796
		// How to represent the days of the week in the calendar header. See dojo.date.locale
11797
		dayWidth: "narrow",
11798
 
11799
		setValue: function(/*Date*/ value){
11800
			// summary: set the current date and update the UI.  If the date is disabled, the selection will
11801
			//	not change, but the display will change to the corresponding month.
11802
			if(!this.value || dojo.date.compare(value, this.value)){
11803
				value = new Date(value);
11804
				this.displayMonth = new Date(value);
11805
				if(!this.isDisabledDate(value, this.lang)){
11806
					this.value = value;
11807
					this.value.setHours(0,0,0,0);
11808
					this.onChange(this.value);
11809
				}
11810
				this._populateGrid();
11811
			}
11812
		},
11813
 
11814
		_setText: function(node, text){
11815
			while(node.firstChild){
11816
				node.removeChild(node.firstChild);
11817
			}
11818
			node.appendChild(document.createTextNode(text));
11819
		},
11820
 
11821
		_populateGrid: function(){
11822
			var month = this.displayMonth;
11823
			month.setDate(1);
11824
			var firstDay = month.getDay();
11825
			var daysInMonth = dojo.date.getDaysInMonth(month);
11826
			var daysInPreviousMonth = dojo.date.getDaysInMonth(dojo.date.add(month, "month", -1));
11827
			var today = new Date();
11828
			var selected = this.value;
11829
 
11830
			var dayOffset = dojo.cldr.supplemental.getFirstDayOfWeek(this.lang);
11831
			if(dayOffset > firstDay){ dayOffset -= 7; }
11832
 
11833
			// Iterate through dates in the calendar and fill in date numbers and style info
11834
			dojo.query(".dijitCalendarDateTemplate", this.domNode).forEach(function(template, i){
11835
				i += dayOffset;
11836
				var date = new Date(month);
11837
				var number, clazz = "dijitCalendar", adj = 0;
11838
 
11839
				if(i < firstDay){
11840
					number = daysInPreviousMonth - firstDay + i + 1;
11841
					adj = -1;
11842
					clazz += "Previous";
11843
				}else if(i >= (firstDay + daysInMonth)){
11844
					number = i - firstDay - daysInMonth + 1;
11845
					adj = 1;
11846
					clazz += "Next";
11847
				}else{
11848
					number = i - firstDay + 1;
11849
					clazz += "Current";
11850
				}
11851
 
11852
				if(adj){
11853
					date = dojo.date.add(date, "month", adj);
11854
				}
11855
				date.setDate(number);
11856
 
11857
				if(!dojo.date.compare(date, today, "date")){
11858
					clazz = "dijitCalendarCurrentDate " + clazz;
11859
				}
11860
 
11861
				if(!dojo.date.compare(date, selected, "date")){
11862
					clazz = "dijitCalendarSelectedDate " + clazz;
11863
				}
11864
 
11865
				if(this.isDisabledDate(date, this.lang)){
11866
					clazz = "dijitCalendarDisabledDate " + clazz;
11867
				}
11868
 
11869
				template.className =  clazz + "Month dijitCalendarDateTemplate";
11870
				template.dijitDateValue = date.valueOf();
11871
				var label = dojo.query(".dijitCalendarDateLabel", template)[0];
11872
				this._setText(label, date.getDate());
11873
			}, this);
11874
 
11875
			// Fill in localized month name
11876
			var monthNames = dojo.date.locale.getNames('months', 'wide', 'standAlone', this.lang);
11877
			this._setText(this.monthLabelNode, monthNames[month.getMonth()]);
11878
 
11879
			// Fill in localized prev/current/next years
11880
			var y = month.getFullYear() - 1;
11881
			dojo.forEach(["previous", "current", "next"], function(name){
11882
				this._setText(this[name+"YearLabelNode"],
11883
					dojo.date.locale.format(new Date(y++, 0), {selector:'year', locale:this.lang}));
11884
			}, this);
11885
 
11886
			// Set up repeating mouse behavior
11887
			var _this = this;
11888
			var typematic = function(nodeProp, dateProp, adj){
11889
				dijit.typematic.addMouseListener(_this[nodeProp], _this, function(count){
11890
					if(count >= 0){ _this._adjustDisplay(dateProp, adj); }
11891
				}, 0.8, 500);
11892
			};
11893
			typematic("incrementMonth", "month", 1);
11894
			typematic("decrementMonth", "month", -1);
11895
			typematic("nextYearLabelNode", "year", 1);
11896
			typematic("previousYearLabelNode", "year", -1);
11897
		},
11898
 
11899
		postCreate: function(){
11900
			dijit._Calendar.superclass.postCreate.apply(this);
11901
 
11902
			var cloneClass = dojo.hitch(this, function(clazz, n){
11903
				var template = dojo.query(clazz, this.domNode)[0];
11904
	 			for(var i=0; i<n; i++){
11905
					template.parentNode.appendChild(template.cloneNode(true));
11906
				}
11907
			});
11908
 
11909
			// clone the day label and calendar day templates 6 times to make 7 columns
11910
			cloneClass(".dijitCalendarDayLabelTemplate", 6);
11911
			cloneClass(".dijitCalendarDateTemplate", 6);
11912
 
11913
			// now make 6 week rows
11914
			cloneClass(".dijitCalendarWeekTemplate", 5);
11915
 
11916
			// insert localized day names in the header
11917
			var dayNames = dojo.date.locale.getNames('days', this.dayWidth, 'standAlone', this.lang);
11918
			var dayOffset = dojo.cldr.supplemental.getFirstDayOfWeek(this.lang);
11919
			dojo.query(".dijitCalendarDayLabel", this.domNode).forEach(function(label, i){
11920
				this._setText(label, dayNames[(i + dayOffset) % 7]);
11921
			}, this);
11922
 
11923
			// Fill in spacer element with all the month names (invisible) so that the maximum width will affect layout
11924
			var monthNames = dojo.date.locale.getNames('months', 'wide', 'standAlone', this.lang);
11925
			dojo.forEach(monthNames, function(name){
11926
				var monthSpacer = dojo.doc.createElement("div");
11927
				this._setText(monthSpacer, name);
11928
				this.monthLabelSpacer.appendChild(monthSpacer);
11929
			}, this);
11930
 
11931
			this.value = null;
11932
			this.setValue(new Date());
11933
		},
11934
 
11935
		_adjustDisplay: function(/*String*/part, /*int*/amount){
11936
			this.displayMonth = dojo.date.add(this.displayMonth, part, amount);
11937
			this._populateGrid();
11938
		},
11939
 
11940
		_onDayClick: function(/*Event*/evt){
11941
			var node = evt.target;
11942
			dojo.stopEvent(evt);
11943
			while(!node.dijitDateValue){
11944
				node = node.parentNode;
11945
			}
11946
			if(!dojo.hasClass(node, "dijitCalendarDisabledDate")){
11947
				this.setValue(node.dijitDateValue);
11948
				this.onValueSelected(this.value);
11949
			}
11950
		},
11951
 
11952
		onValueSelected: function(/*Date*/date){
11953
			//summary: a date cell was selected.  It may be the same as the previous value.
11954
		},
11955
 
11956
		onChange: function(/*Date*/date){
11957
			//summary: called only when the selected date has changed
11958
		},
11959
 
11960
		isDisabledDate: function(/*Date*/dateObject, /*String?*/locale){
11961
			// summary:
11962
			//	May be overridden to disable certain dates in the calendar e.g. isDisabledDate=dojo.date.locale.isWeekend
11963
			return false; // Boolean
11964
		}
11965
	}
11966
);
11967
 
11968
}
11969
 
11970
if(!dojo._hasResource["dijit._TimePicker"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
11971
dojo._hasResource["dijit._TimePicker"] = true;
11972
dojo.provide("dijit._TimePicker");
11973
 
11974
 
11975
 
11976
 
11977
dojo.declare("dijit._TimePicker",
11978
	[dijit._Widget, dijit._Templated],
11979
	{
11980
		// summary:
11981
		// A graphical time picker that TimeTextBox pops up
11982
		// It is functionally modeled after the Java applet at http://java.arcadevillage.com/applets/timepica.htm
11983
		// See ticket #599
11984
 
11985
		templateString:"<div id=\"widget_${id}\" class=\"dijitMenu\"\n    ><div dojoAttachPoint=\"upArrow\" class=\"dijitButtonNode\"><span class=\"dijitTimePickerA11yText\">&#9650;</span></div\n    ><div dojoAttachPoint=\"timeMenu,focusNode\" dojoAttachEvent=\"onclick:_onOptionSelected,onmouseover,onmouseout\"></div\n    ><div dojoAttachPoint=\"downArrow\" class=\"dijitButtonNode\"><span class=\"dijitTimePickerA11yText\">&#9660;</span></div\n></div>\n",
11986
		baseClass: "dijitTimePicker",
11987
 
11988
		// clickableIncrement: String
11989
		//		ISO-8601 string representing the amount by which
11990
		//		every clickable element in the time picker increases
11991
		//		Set in non-Zulu time, without a time zone
11992
		//		Example: "T00:15:00" creates 15 minute increments
11993
		//		Must divide visibleIncrement evenly
11994
		clickableIncrement: "T00:15:00",
11995
 
11996
		// visibleIncrement: String
11997
		//		ISO-8601 string representing the amount by which
11998
		//		every element with a visible time in the time picker increases
11999
		//		Set in non Zulu time, without a time zone
12000
		//		Example: "T01:00:00" creates text in every 1 hour increment
12001
		visibleIncrement: "T01:00:00",
12002
 
12003
		// visibleRange: String
12004
		//		ISO-8601 string representing the range of this TimePicker
12005
		//		The TimePicker will only display times in this range
12006
		//		Example: "T05:00:00" displays 5 hours of options
12007
		visibleRange: "T05:00:00",
12008
 
12009
		// value: String
12010
		//		Date to display.
12011
		//		Defaults to current time and date.
12012
		//		Can be a Date object or an ISO-8601 string
12013
		//		If you specify the GMT time zone ("-01:00"),
12014
		//		the time will be converted to the local time in the local time zone.
12015
		//		Otherwise, the time is considered to be in the local time zone.
12016
		//		If you specify the date and isDate is true, the date is used.
12017
		//		Example: if your local time zone is GMT -05:00,
12018
		//		"T10:00:00" becomes "T10:00:00-05:00" (considered to be local time),
12019
		//		"T10:00:00-01:00" becomes "T06:00:00-05:00" (4 hour difference),
12020
		//		"T10:00:00Z" becomes "T05:00:00-05:00" (5 hour difference between Zulu and local time)
12021
		//		"yyyy-mm-ddThh:mm:ss" is the format to set the date and time
12022
		//		Example: "2007-06-01T09:00:00"
12023
		value: new Date(),
12024
 
12025
		_visibleIncrement:2,
12026
		_clickableIncrement:1,
12027
		_totalIncrements:10,
12028
		constraints:{},
12029
 
12030
		serialize: dojo.date.stamp.toISOString,
12031
 
12032
		setValue:function(/*Date*/ date, /*Boolean*/ priority){
12033
			// summary:
12034
			//	Set the value of the TimePicker
12035
			//	Redraws the TimePicker around the new date
12036
 
12037
			//dijit._TimePicker.superclass.setValue.apply(this, arguments);
12038
			this.value=date;
12039
			this._showText();
12040
		},
12041
 
12042
		isDisabledDate: function(/*Date*/dateObject, /*String?*/locale){
12043
			// summary:
12044
			//	May be overridden to disable certain dates in the TimePicker e.g. isDisabledDate=dojo.date.locale.isWeekend
12045
			return false; // Boolean
12046
		},
12047
 
12048
		_showText:function(){
12049
			this.timeMenu.innerHTML="";
12050
			var fromIso = dojo.date.stamp.fromISOString;
12051
			this._clickableIncrementDate=fromIso(this.clickableIncrement);
12052
			this._visibleIncrementDate=fromIso(this.visibleIncrement);
12053
			this._visibleRangeDate=fromIso(this.visibleRange);
12054
			// get the value of the increments and the range in seconds (since 00:00:00) to find out how many divs to create
12055
			var sinceMidnight = function(/*Date*/ date){
12056
				return date.getHours()*60*60+date.getMinutes()*60+date.getSeconds();
12057
			};
12058
 
12059
			var clickableIncrementSeconds = sinceMidnight(this._clickableIncrementDate);
12060
			var visibleIncrementSeconds = sinceMidnight(this._visibleIncrementDate);
12061
			var visibleRangeSeconds = sinceMidnight(this._visibleRangeDate);
12062
 
12063
			// round reference date to previous visible increment
12064
			var time = this.value.getTime();
12065
			this._refDate = new Date(time - time % (visibleIncrementSeconds*1000));
12066
 
12067
			// assume clickable increment is the smallest unit
12068
			this._clickableIncrement=1;
12069
			// divide the visible range by the clickable increment to get the number of divs to create
12070
			// example: 10:00:00/00:15:00 -> display 40 divs
12071
			this._totalIncrements=visibleRangeSeconds/clickableIncrementSeconds;
12072
			// divide the visible increments by the clickable increments to get how often to display the time inline
12073
			// example: 01:00:00/00:15:00 -> display the time every 4 divs
12074
			this._visibleIncrement=visibleIncrementSeconds/clickableIncrementSeconds;
12075
			for(var i=-this._totalIncrements/2; i<=this._totalIncrements/2; i+=this._clickableIncrement){
12076
				var div=this._createOption(i);
12077
				this.timeMenu.appendChild(div);
12078
			}
12079
 
12080
			// TODO:
12081
			// I commented this out because it
12082
			// causes problems for a TimeTextBox in a Dialog, or as the editor of an InlineEditBox,
12083
			// because the timeMenu node isn't visible yet. -- Bill (Bug #????)
12084
			// dijit.focus(this.timeMenu);
12085
		},
12086
 
12087
		postCreate:function(){
12088
			// instantiate constraints
12089
			if(this.constraints===dijit._TimePicker.prototype.constraints){
12090
				this.constraints={};
12091
			}
12092
			// dojo.date.locale needs the lang in the constraints as locale
12093
			if(!this.constraints.locale){
12094
				this.constraints.locale=this.lang;
12095
			}
12096
 
12097
			// assign typematic mouse listeners to the arrow buttons
12098
			this.connect(this.timeMenu, dojo.isIE ? "onmousewheel" : 'DOMMouseScroll', "_mouseWheeled");
12099
			dijit.typematic.addMouseListener(this.upArrow,this,this._onArrowUp, 0.8, 500);
12100
			dijit.typematic.addMouseListener(this.downArrow,this,this._onArrowDown, 0.8, 500);
12101
			//dijit.typematic.addListener(this.upArrow,this.timeMenu, {keyCode:dojo.keys.UP_ARROW,ctrlKey:false,altKey:false,shiftKey:false}, this, "_onArrowUp", 0.8, 500);
12102
			//dijit.typematic.addListener(this.downArrow, this.timeMenu, {keyCode:dojo.keys.DOWN_ARROW,ctrlKey:false,altKey:false,shiftKey:false}, this, "_onArrowDown", 0.8,500);
12103
 
12104
			this.inherited("postCreate", arguments);
12105
			this.setValue(this.value);
12106
		},
12107
 
12108
		_createOption:function(/*Number*/ index){
12109
			// summary: creates a clickable time option
12110
			var div=document.createElement("div");
12111
			var date = (div.date = new Date(this._refDate));
12112
			div.index=index;
12113
			var incrementDate = this._clickableIncrementDate;
12114
			date.setHours(date.getHours()+incrementDate.getHours()*index,
12115
				date.getMinutes()+incrementDate.getMinutes()*index,
12116
				date.getSeconds()+incrementDate.getSeconds()*index);
12117
 
12118
			var innerDiv = document.createElement('div');
12119
			dojo.addClass(div,this.baseClass+"Item");
12120
			dojo.addClass(innerDiv,this.baseClass+"ItemInner");
12121
			innerDiv.innerHTML=dojo.date.locale.format(date, this.constraints);
12122
			div.appendChild(innerDiv);
12123
 
12124
			if(index%this._visibleIncrement<1 && index%this._visibleIncrement>-1){
12125
				dojo.addClass(div, this.baseClass+"Marker");
12126
			}else if(index%this._clickableIncrement==0){
12127
				dojo.addClass(div, this.baseClass+"Tick");
12128
			}
12129
 
12130
			if(this.isDisabledDate(date)){
12131
				// set disabled
12132
				dojo.addClass(div, this.baseClass+"ItemDisabled");
12133
			}
12134
			if(dojo.date.compare(this.value, date, this.constraints.selector)==0){
12135
				div.selected=true;
12136
				dojo.addClass(div, this.baseClass+"ItemSelected");
12137
			}
12138
			return div;
12139
		},
12140
 
12141
		_onOptionSelected:function(/*Object*/ tgt){
12142
			var tdate = tgt.target.date || tgt.target.parentNode.date;
12143
			if(!tdate||this.isDisabledDate(tdate)){return;}
12144
			this.setValue(tdate);
12145
			this.onValueSelected(tdate);
12146
		},
12147
 
12148
		onValueSelected:function(value){
12149
		},
12150
 
12151
		onmouseover:function(/*Event*/ e){
12152
			var tgr = (e.target.parentNode === this.timeMenu) ? e.target : e.target.parentNode;
12153
			this._highlighted_option=tgr;
12154
			dojo.addClass(tgr, this.baseClass+"ItemHover");
12155
		},
12156
 
12157
		onmouseout:function(/*Event*/ e){
12158
			var tgr = (e.target.parentNode === this.timeMenu) ? e.target : e.target.parentNode;
12159
			if(this._highlighted_option===tgr){
12160
				dojo.removeClass(tgr, this.baseClass+"ItemHover");
12161
			}
12162
		},
12163
 
12164
		_mouseWheeled:function(/*Event*/e){
12165
			// summary: handle the mouse wheel listener
12166
			dojo.stopEvent(e);
12167
			// we're not _measuring_ the scroll amount, just direction
12168
			var scrollAmount = (dojo.isIE ? e.wheelDelta : -e.detail);
12169
			this[(scrollAmount>0 ? "_onArrowUp" : "_onArrowDown")](); // yes, we're making a new dom node every time you mousewheel, or click
12170
		},
12171
 
12172
		_onArrowUp:function(){
12173
			// summary: remove the bottom time and add one to the top
12174
			var index=this.timeMenu.childNodes[0].index-1;
12175
			var div=this._createOption(index);
12176
			this.timeMenu.removeChild(this.timeMenu.childNodes[this.timeMenu.childNodes.length-1]);
12177
			this.timeMenu.insertBefore(div, this.timeMenu.childNodes[0]);
12178
		},
12179
 
12180
		_onArrowDown:function(){
12181
			// summary: remove the top time and add one to the bottom
12182
			var index=this.timeMenu.childNodes[this.timeMenu.childNodes.length-1].index+1;
12183
			var div=this._createOption(index);
12184
			this.timeMenu.removeChild(this.timeMenu.childNodes[0]);
12185
			this.timeMenu.appendChild(div);
12186
		}
12187
	}
12188
);
12189
 
12190
}
12191
 
12192
if(!dojo._hasResource["dijit.form.TimeTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
12193
dojo._hasResource["dijit.form.TimeTextBox"] = true;
12194
dojo.provide("dijit.form.TimeTextBox");
12195
 
12196
 
12197
 
12198
 
12199
 
12200
 
12201
 
12202
dojo.declare(
12203
	"dijit.form.TimeTextBox",
12204
	dijit.form.RangeBoundTextBox,
12205
	{
12206
		// summary:
12207
		//		A validating, serializable, range-bound date text box.
12208
 
12209
		// constraints object: min, max
12210
		regExpGen: dojo.date.locale.regexp,
12211
		compare: dojo.date.compare,
12212
		format: function(/*Date*/ value, /*Object*/ constraints){
12213
			if(!value || value.toString() == this._invalid){ return null; }
12214
			return dojo.date.locale.format(value, constraints);
12215
		},
12216
		parse: dojo.date.locale.parse,
12217
		serialize: dojo.date.stamp.toISOString,
12218
 
12219
		value: new Date(""),	// NaN
12220
		_invalid: (new Date("")).toString(),	// NaN
12221
 
12222
		_popupClass: "dijit._TimePicker",
12223
 
12224
		postMixInProperties: function(){
12225
			//dijit.form.RangeBoundTextBox.prototype.postMixInProperties.apply(this, arguments);
12226
			this.inherited("postMixInProperties",arguments);
12227
			var constraints = this.constraints;
12228
			constraints.selector = 'time';
12229
			if(typeof constraints.min == "string"){ constraints.min = dojo.date.stamp.fromISOString(constraints.min); }
12230
 			if(typeof constraints.max == "string"){ constraints.max = dojo.date.stamp.fromISOString(constraints.max); }
12231
		},
12232
 
12233
		_onFocus: function(/*Event*/ evt){
12234
			// summary: open the TimePicker popup
12235
			this._open();
12236
		},
12237
 
12238
		setValue: function(/*Date*/ value, /*Boolean, optional*/ priorityChange){
12239
			// summary:
12240
			//	Sets the date on this textbox
12241
			this.inherited('setValue', arguments);
12242
			if(this._picker){
12243
				// #3948: fix blank date on popup only
12244
				if(!value || value.toString() == this._invalid){value=new Date();}
12245
				this._picker.setValue(value);
12246
			}
12247
		},
12248
 
12249
		_open: function(){
12250
			// summary:
12251
			//	opens the TimePicker, and sets the onValueSelected value
12252
 
12253
			if(this.disabled){return;}
12254
 
12255
			var self = this;
12256
 
12257
			if(!this._picker){
12258
				var popupProto=dojo.getObject(this._popupClass, false);
12259
				this._picker = new popupProto({
12260
					onValueSelected: function(value){
12261
 
12262
						self.focus(); // focus the textbox before the popup closes to avoid reopening the popup
12263
						setTimeout(dojo.hitch(self, "_close"), 1); // allow focus time to take
12264
 
12265
						// this will cause InlineEditBox and other handlers to do stuff so make sure it's last
12266
						dijit.form.TimeTextBox.superclass.setValue.call(self, value, true);
12267
					},
12268
					lang: this.lang,
12269
					constraints:this.constraints,
12270
					isDisabledDate: function(/*Date*/ date){
12271
						// summary:
12272
						// 	disables dates outside of the min/max of the TimeTextBox
12273
						return self.constraints && (dojo.date.compare(self.constraints.min,date) > 0 || dojo.date.compare(self.constraints.max,date) < 0);
12274
					}
12275
				});
12276
				this._picker.setValue(this.getValue() || new Date());
12277
			}
12278
			if(!this._opened){
12279
				dijit.popup.open({
12280
					parent: this,
12281
					popup: this._picker,
12282
					around: this.domNode,
12283
					onCancel: dojo.hitch(this, this._close),
12284
					onClose: function(){ self._opened=false; }
12285
				});
12286
				this._opened=true;
12287
			}
12288
 
12289
			dojo.marginBox(this._picker.domNode,{ w:this.domNode.offsetWidth });
12290
		},
12291
 
12292
		_close: function(){
12293
			if(this._opened){
12294
				dijit.popup.close(this._picker);
12295
				this._opened=false;
12296
			}
12297
		},
12298
 
12299
		_onBlur: function(){
12300
			// summary: called magically when focus has shifted away from this widget and it's dropdown
12301
			this._close();
12302
			this.inherited('_onBlur', arguments);
12303
			// don't focus on <input>.  the user has explicitly focused on something else.
12304
		},
12305
 
12306
		getDisplayedValue:function(){
12307
			return this.textbox.value;
12308
		},
12309
 
12310
		setDisplayedValue:function(/*String*/ value){
12311
			this.textbox.value=value;
12312
		}
12313
	}
12314
);
12315
 
12316
}
12317
 
12318
if(!dojo._hasResource["dijit.form.DateTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
12319
dojo._hasResource["dijit.form.DateTextBox"] = true;
12320
dojo.provide("dijit.form.DateTextBox");
12321
 
12322
 
12323
 
12324
 
12325
dojo.declare(
12326
	"dijit.form.DateTextBox",
12327
	dijit.form.TimeTextBox,
12328
	{
12329
		// summary:
12330
		//		A validating, serializable, range-bound date text box.
12331
 
12332
		_popupClass: "dijit._Calendar",
12333
 
12334
		postMixInProperties: function(){
12335
			this.inherited('postMixInProperties', arguments);
12336
			this.constraints.selector = 'date';
12337
		}
12338
	}
12339
);
12340
 
12341
}
12342
 
12343
if(!dojo._hasResource["dijit.form.FilteringSelect"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
12344
dojo._hasResource["dijit.form.FilteringSelect"] = true;
12345
dojo.provide("dijit.form.FilteringSelect");
12346
 
12347
 
12348
 
12349
dojo.declare(
12350
	"dijit.form.FilteringSelect",
12351
	[dijit.form.MappedTextBox, dijit.form.ComboBoxMixin],
12352
	{
12353
		/*
12354
		 * summary
12355
		 *	Enhanced version of HTML's <select> tag.
12356
		 *
12357
		 *	Similar features:
12358
		 *	  - There is a drop down list of possible values.
12359
		 *	- You can only enter a value from the drop down list.  (You can't enter an arbitrary value.)
12360
		 *	- The value submitted with the form is the hidden value (ex: CA),
12361
		 *	  not the displayed value a.k.a. label (ex: California)
12362
		 *
12363
		 *	Enhancements over plain HTML version:
12364
		 *	- If you type in some text then it will filter down the list of possible values in the drop down list.
12365
		 *	- List can be specified either as a static list or via a javascript function (that can get the list from a server)
12366
		 */
12367
 
12368
		// searchAttr: String
12369
		//		Searches pattern match against this field
12370
 
12371
		// labelAttr: String
12372
		//		Optional.  The text that actually appears in the drop down.
12373
		//		If not specified, the searchAttr text is used instead.
12374
		labelAttr: "",
12375
 
12376
		// labelType: String
12377
		//		"html" or "text"
12378
		labelType: "text",
12379
 
12380
		_isvalid:true,
12381
 
12382
		isValid:function(){
12383
			return this._isvalid;
12384
		},
12385
 
12386
		_callbackSetLabel: function(/*Array*/ result, /*Object*/ dataObject, /*Boolean, optional*/ priorityChange){
12387
			// summary
12388
			//	Callback function that dynamically sets the label of the ComboBox
12389
 
12390
			// setValue does a synchronous lookup,
12391
			// so it calls _callbackSetLabel directly,
12392
			// and so does not pass dataObject
12393
			// dataObject==null means do not test the lastQuery, just continue
12394
			if(dataObject&&dataObject.query[this.searchAttr]!=this._lastQuery){return;}
12395
			if(!result.length){
12396
				//#3268: do nothing on bad input
12397
				//this._setValue("", "");
12398
				//#3285: change CSS to indicate error
12399
				if(!this._hasFocus){ this.valueNode.value=""; }
12400
				dijit.form.TextBox.superclass.setValue.call(this, undefined, !this._hasFocus);
12401
				this._isvalid=false;
12402
				this.validate(this._hasFocus);
12403
			}else{
12404
				this._setValueFromItem(result[0], priorityChange);
12405
			}
12406
		},
12407
 
12408
		_openResultList: function(/*Object*/ results, /*Object*/ dataObject){
12409
			// #3285: tap into search callback to see if user's query resembles a match
12410
			if(dataObject.query[this.searchAttr]!=this._lastQuery){return;}
12411
			this._isvalid=results.length!=0;
12412
			this.validate(true);
12413
			dijit.form.ComboBoxMixin.prototype._openResultList.apply(this, arguments);
12414
		},
12415
 
12416
		getValue:function(){
12417
			// don't get the textbox value but rather the previously set hidden value
12418
			return this.valueNode.value;
12419
		},
12420
 
12421
		_getValueField:function(){
12422
			// used for option tag selects
12423
			return "value";
12424
		},
12425
 
12426
		_setValue:function(/*String*/ value, /*String*/ displayedValue, /*Boolean, optional*/ priorityChange){
12427
			this.valueNode.value = value;
12428
			dijit.form.FilteringSelect.superclass.setValue.call(this, value, priorityChange, displayedValue);
12429
			this._lastDisplayedValue = displayedValue;
12430
		},
12431
 
12432
		setValue: function(/*String*/ value, /*Boolean, optional*/ priorityChange){
12433
			// summary
12434
			//	Sets the value of the select.
12435
			//	Also sets the label to the corresponding value by reverse lookup.
12436
 
12437
			//#3347: fetchItemByIdentity if no keyAttr specified
12438
			var self=this;
12439
			var handleFetchByIdentity = function(item, priorityChange){
12440
				if(item){
12441
					if(self.store.isItemLoaded(item)){
12442
						self._callbackSetLabel([item], undefined, priorityChange);
12443
					}else{
12444
						self.store.loadItem({item:item, onItem: function(result, dataObject){self._callbackSetLabel(result, dataObject, priorityChange)}});
12445
					}
12446
				}else{
12447
					self._isvalid=false;
12448
					// prevent errors from Tooltip not being created yet
12449
					self.validate(false);
12450
				}
12451
			}
12452
			this.store.fetchItemByIdentity({identity: value, onItem: function(item){handleFetchByIdentity(item, priorityChange)}});
12453
		},
12454
 
12455
		_setValueFromItem: function(/*item*/ item, /*Boolean, optional*/ priorityChange){
12456
			// summary
12457
			//	Set the displayed valued in the input box, based on a selected item.
12458
			//	Users shouldn't call this function; they should be calling setDisplayedValue() instead
12459
			this._isvalid=true;
12460
			this._setValue(this.store.getIdentity(item), this.labelFunc(item, this.store), priorityChange);
12461
		},
12462
 
12463
		labelFunc: function(/*item*/ item, /*dojo.data.store*/ store){
12464
			// summary: Event handler called when the label changes
12465
			// returns the label that the ComboBox should display
12466
			return store.getValue(item, this.searchAttr);
12467
		},
12468
 
12469
		onkeyup: function(/*Event*/ evt){
12470
			// summary: internal function
12471
			// FilteringSelect needs to wait for the complete label before committing to a reverse lookup
12472
			//this.setDisplayedValue(this.textbox.value);
12473
		},
12474
 
12475
		_doSelect: function(/*Event*/ tgt){
12476
			// summary:
12477
			//	ComboBox's menu callback function
12478
			//	FilteringSelect overrides this to set both the visible and hidden value from the information stored in the menu
12479
			this.item = tgt.item;
12480
			this._setValueFromItem(tgt.item, true);
12481
		},
12482
 
12483
		setDisplayedValue:function(/*String*/ label){
12484
			// summary:
12485
			//	Set textbox to display label
12486
			//	Also performs reverse lookup to set the hidden value
12487
			//	Used in InlineEditBox
12488
 
12489
			if(this.store){
12490
				var query={};
12491
				this._lastQuery=query[this.searchAttr]=label;
12492
				// if the label is not valid, the callback will never set it,
12493
				// so the last valid value will get the warning textbox
12494
				// set the textbox value now so that the impending warning will make sense to the user
12495
				this.textbox.value=label;
12496
				this._lastDisplayedValue=label;
12497
				this.store.fetch({query:query, queryOptions:{ignoreCase:this.ignoreCase, deep:true}, onComplete: dojo.hitch(this, this._callbackSetLabel)});
12498
			}
12499
		},
12500
 
12501
		_getMenuLabelFromItem:function(/*Item*/ item){
12502
			// internal function to help ComboBoxMenu figure out what to display
12503
			if(this.labelAttr){return {html:this.labelType=="html", label:this.store.getValue(item, this.labelAttr)};}
12504
			else{
12505
				// because this function is called by ComboBoxMenu, this.inherited tries to find the superclass of ComboBoxMenu
12506
				return dijit.form.ComboBoxMixin.prototype._getMenuLabelFromItem.apply(this, arguments);
12507
			}
12508
		},
12509
 
12510
		postMixInProperties: function(){
12511
			dijit.form.ComboBoxMixin.prototype.postMixInProperties.apply(this, arguments);
12512
			dijit.form.MappedTextBox.prototype.postMixInProperties.apply(this, arguments);
12513
		}
12514
	}
12515
);
12516
 
12517
}
12518
 
12519
if(!dojo._hasResource["dijit.form._Spinner"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
12520
dojo._hasResource["dijit.form._Spinner"] = true;
12521
dojo.provide("dijit.form._Spinner");
12522
 
12523
 
12524
 
12525
dojo.declare(
12526
	"dijit.form._Spinner",
12527
	dijit.form.RangeBoundTextBox,
12528
	{
12529
 
12530
		// summary: Mixin for validation widgets with a spinner
12531
		// description: This class basically (conceptually) extends dijit.form.ValidationTextBox.
12532
		//	It modifies the template to have up/down arrows, and provides related handling code.
12533
 
12534
		// defaultTimeout: Number
12535
		//	  number of milliseconds before a held key or button becomes typematic
12536
		defaultTimeout: 500,
12537
 
12538
		// timeoutChangeRate: Number
12539
		//	  fraction of time used to change the typematic timer between events
12540
		//	  1.0 means that each typematic event fires at defaultTimeout intervals
12541
		//	  < 1.0 means that each typematic event fires at an increasing faster rate
12542
		timeoutChangeRate: 0.90,
12543
 
12544
		// smallDelta: Number
12545
		//	  adjust the value by this much when spinning using the arrow keys/buttons
12546
		smallDelta: 1,
12547
		// largeDelta: Number
12548
		//	  adjust the value by this much when spinning using the PgUp/Dn keys
12549
		largeDelta: 10,
12550
 
12551
		templateString:"<table class=\"dijit dijitReset dijitInlineTable dijitLeft\" cellspacing=\"0\" cellpadding=\"0\"\n\tid=\"widget_${id}\" name=\"${name}\"\n\tdojoAttachEvent=\"onmouseenter:_onMouse,onmouseleave:_onMouse,onkeypress:_onKeyPress\"\n\twaiRole=\"presentation\"\n\t><tr class=\"dijitReset\"\n\t\t><td rowspan=\"2\" class=\"dijitReset dijitStretch dijitInputField\" width=\"100%\"\n\t\t\t><input dojoAttachPoint=\"textbox,focusNode\" type=\"${type}\" dojoAttachEvent=\"onfocus,onkeyup\"\n\t\t\t\twaiRole=\"spinbutton\" autocomplete=\"off\" name=\"${name}\"\n\t\t></td\n\t\t><td rowspan=\"2\" class=\"dijitReset dijitValidationIconField\" width=\"0%\" \n\t\t\t><div dojoAttachPoint='iconNode' class='dijitValidationIcon'></div\n\t\t></td\n\t\t><td class=\"dijitReset dijitRight dijitButtonNode dijitUpArrowButton\" width=\"0%\"\n\t\t\t\tdojoAttachPoint=\"upArrowNode\"\n\t\t\t\tdojoAttachEvent=\"onmousedown:_handleUpArrowEvent,onmouseup:_handleUpArrowEvent,onmouseover:_handleUpArrowEvent,onmouseout:_handleUpArrowEvent\"\n\t\t\t\tstateModifier=\"UpArrow\"\n\t\t\t><div class=\"dijitA11yUpArrow\">&#9650;</div\n\t\t></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset dijitRight dijitButtonNode dijitDownArrowButton\" width=\"0%\"\n\t\t\t\tdojoAttachPoint=\"downArrowNode\"\n\t\t\t\tdojoAttachEvent=\"onmousedown:_handleDownArrowEvent,onmouseup:_handleDownArrowEvent,onmouseover:_handleDownArrowEvent,onmouseout:_handleDownArrowEvent\"\n\t\t\t\tstateModifier=\"DownArrow\"\n\t\t\t><div class=\"dijitA11yDownArrow\">&#9660;</div\n\t\t></td\n\t></tr\n></table>\n\n",
12552
		baseClass: "dijitSpinner",
12553
 
12554
		adjust: function(/* Object */ val, /*Number*/ delta){
12555
			// summary: user replaceable function used to adjust a primitive value(Number/Date/...) by the delta amount specified
12556
			// the val is adjusted in a way that makes sense to the object type
12557
			return val;
12558
		},
12559
 
12560
		_handleUpArrowEvent : function(/*Event*/ e){
12561
			this._onMouse(e, this.upArrowNode);
12562
		},
12563
 
12564
		_handleDownArrowEvent : function(/*Event*/ e){
12565
			this._onMouse(e, this.downArrowNode);
12566
		},
12567
 
12568
 
12569
		_arrowPressed: function(/*Node*/ nodePressed, /*Number*/ direction){
12570
			if(this.disabled){ return; }
12571
			dojo.addClass(nodePressed, "dijitSpinnerButtonActive");
12572
			this.setValue(this.adjust(this.getValue(), direction*this.smallDelta), false);
12573
		},
12574
 
12575
		_arrowReleased: function(/*Node*/ node){
12576
			if(this.disabled){ return; }
12577
			this._wheelTimer = null;
12578
			dijit.focus(this.textbox);
12579
			dojo.removeClass(node, "dijitSpinnerButtonActive");
12580
		},
12581
 
12582
		_typematicCallback: function(/*Number*/ count, /*DOMNode*/ node, /*Event*/ evt){
12583
			if(node == this.textbox){ node = (evt.keyCode == dojo.keys.UP_ARROW) ? this.upArrowNode : this.downArrowNode; }
12584
			if(count == -1){ this._arrowReleased(node); }
12585
			else{ this._arrowPressed(node, (node == this.upArrowNode) ? 1 : -1); }
12586
		},
12587
 
12588
		_wheelTimer: null,
12589
		_mouseWheeled: function(/*Event*/ evt){
12590
			dojo.stopEvent(evt);
12591
			var scrollAmount = 0;
12592
			if(typeof evt.wheelDelta == 'number'){ // IE
12593
				scrollAmount = evt.wheelDelta;
12594
			}else if(typeof evt.detail == 'number'){ // Mozilla+Firefox
12595
				scrollAmount = -evt.detail;
12596
			}
12597
			if(scrollAmount > 0){
12598
				var node = this.upArrowNode;
12599
				var dir = +1;
12600
			}else if(scrollAmount < 0){
12601
				var node = this.downArrowNode;
12602
				var dir = -1;
12603
			}else{ return; }
12604
			this._arrowPressed(node, dir);
12605
			if(this._wheelTimer != null){
12606
				clearTimeout(this._wheelTimer);
12607
			}
12608
			var _this = this;
12609
			this._wheelTimer = setTimeout(function(){_this._arrowReleased(node);}, 50);
12610
		},
12611
 
12612
		postCreate: function(){
12613
			this.inherited('postCreate', arguments);
12614
 
12615
			// extra listeners
12616
			this.connect(this.textbox, dojo.isIE ? "onmousewheel" : 'DOMMouseScroll', "_mouseWheeled");
12617
			dijit.typematic.addListener(this.upArrowNode, this.textbox, {keyCode:dojo.keys.UP_ARROW,ctrlKey:false,altKey:false,shiftKey:false}, this, "_typematicCallback", this.timeoutChangeRate, this.defaultTimeout);
12618
			dijit.typematic.addListener(this.downArrowNode, this.textbox, {keyCode:dojo.keys.DOWN_ARROW,ctrlKey:false,altKey:false,shiftKey:false}, this, "_typematicCallback", this.timeoutChangeRate, this.defaultTimeout);
12619
		}
12620
});
12621
 
12622
}
12623
 
12624
if(!dojo._hasResource["dijit.form.NumberSpinner"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
12625
dojo._hasResource["dijit.form.NumberSpinner"] = true;
12626
dojo.provide("dijit.form.NumberSpinner");
12627
 
12628
 
12629
 
12630
 
12631
dojo.declare(
12632
"dijit.form.NumberSpinner",
12633
[dijit.form._Spinner, dijit.form.NumberTextBoxMixin],
12634
{
12635
	// summary: Number Spinner
12636
	// description: This widget is the same as NumberTextBox but with up/down arrows added
12637
 
12638
	required: true,
12639
 
12640
	adjust: function(/* Object */ val, /*Number*/ delta){
12641
		// summary: change Number val by the given amount
12642
		var newval = val+delta;
12643
		if(isNaN(val) || isNaN(newval)){ return val; }
12644
		if((typeof this.constraints.max == "number") && (newval > this.constraints.max)){
12645
			newval = this.constraints.max;
12646
		}
12647
		if((typeof this.constraints.min == "number") && (newval < this.constraints.min)){
12648
			newval = this.constraints.min;
12649
		}
12650
		return newval;
12651
	}
12652
});
12653
 
12654
}
12655
 
12656
if(!dojo._hasResource["dijit.form.Slider"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
12657
dojo._hasResource["dijit.form.Slider"] = true;
12658
dojo.provide("dijit.form.Slider");
12659
 
12660
 
12661
 
12662
 
12663
 
12664
 
12665
 
12666
dojo.declare(
12667
	"dijit.form.HorizontalSlider",
12668
	[dijit.form._FormWidget, dijit._Container],
12669
{
12670
	// summary
12671
	//	A form widget that allows one to select a value with a horizontally draggable image
12672
 
12673
	templateString:"<table class=\"dijit dijitReset dijitSlider\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" rules=\"none\"\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\" colspan=\"2\"></td\n\t\t><td dojoAttachPoint=\"containerNode,topDecoration\" class=\"dijitReset\" style=\"text-align:center;width:100%;\"></td\n\t\t><td class=\"dijitReset\" colspan=\"2\"></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset dijitSliderButtonContainer dijitHorizontalSliderButtonContainer\"\n\t\t\t><div class=\"dijitHorizontalSliderDecrementIcon\" tabIndex=\"-1\" style=\"display:none\" dojoAttachPoint=\"decrementButton\" dojoAttachEvent=\"onclick: decrement\"><span class=\"dijitSliderButtonInner\">-</span></div\n\t\t></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><div class=\"dijitSliderBar dijitSliderBumper dijitHorizontalSliderBumper dijitSliderLeftBumper dijitHorizontalSliderLeftBumper\"></div\n\t\t></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><input dojoAttachPoint=\"valueNode\" type=\"hidden\" name=\"${name}\"\n\t\t\t/><div style=\"position:relative;\" dojoAttachPoint=\"sliderBarContainer\"\n\t\t\t\t><div dojoAttachPoint=\"progressBar\" class=\"dijitSliderBar dijitHorizontalSliderBar dijitSliderProgressBar dijitHorizontalSliderProgressBar\" dojoAttachEvent=\"onclick:_onBarClick\"\n\t\t\t\t\t><div dojoAttachPoint=\"sliderHandle,focusNode\" class=\"dijitSliderMoveable dijitHorizontalSliderMoveable\" dojoAttachEvent=\"onkeypress:_onKeyPress,onclick:_onHandleClick\" waiRole=\"slider\" valuemin=\"${minimum}\" valuemax=\"${maximum}\"\n\t\t\t\t\t\t><div class=\"dijitSliderImageHandle dijitHorizontalSliderImageHandle\"></div\n\t\t\t\t\t></div\n\t\t\t\t></div\n\t\t\t\t><div dojoAttachPoint=\"remainingBar\" class=\"dijitSliderBar dijitHorizontalSliderBar dijitSliderRemainingBar dijitHorizontalSliderRemainingBar\" dojoAttachEvent=\"onclick:_onBarClick\"></div\n\t\t\t></div\n\t\t></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><div class=\"dijitSliderBar dijitSliderBumper dijitHorizontalSliderBumper dijitSliderRightBumper dijitHorizontalSliderRightBumper\"></div\n\t\t></td\n\t\t><td class=\"dijitReset dijitSliderButtonContainer dijitHorizontalSliderButtonContainer\" style=\"right:0px;\"\n\t\t\t><div class=\"dijitHorizontalSliderIncrementIcon\" tabIndex=\"-1\" style=\"display:none\" dojoAttachPoint=\"incrementButton\" dojoAttachEvent=\"onclick: increment\"><span class=\"dijitSliderButtonInner\">+</span></div\n\t\t></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\" colspan=\"2\"></td\n\t\t><td dojoAttachPoint=\"containerNode,bottomDecoration\" class=\"dijitReset\" style=\"text-align:center;\"></td\n\t\t><td class=\"dijitReset\" colspan=\"2\"></td\n\t></tr\n></table>\n",
12674
	value: 0,
12675
 
12676
	// showButtons: boolean
12677
	//	Show increment/decrement buttons at the ends of the slider?
12678
	showButtons: true,
12679
 
12680
	// minimum:: integer
12681
	//	The minimum value allowed.
12682
	minimum: 0,
12683
 
12684
	// maximum: integer
12685
	//	The maximum allowed value.
12686
	maximum: 100,
12687
 
12688
	// discreteValues: integer
12689
	//	The maximum allowed values dispersed evenly between minimum and maximum (inclusive).
12690
	discreteValues: Infinity,
12691
 
12692
	// pageIncrement: integer
12693
	//	The amount of change with shift+arrow
12694
	pageIncrement: 2,
12695
 
12696
	// clickSelect: boolean
12697
	//	If clicking the progress bar changes the value or not
12698
	clickSelect: true,
12699
 
12700
	widgetsInTemplate: true,
12701
 
12702
	attributeMap: dojo.mixin(dojo.clone(dijit.form._FormWidget.prototype.attributeMap),
12703
		{id:"", name:"valueNode"}),
12704
 
12705
	baseClass: "dijitSlider",
12706
 
12707
	_mousePixelCoord: "pageX",
12708
	_pixelCount: "w",
12709
	_startingPixelCoord: "x",
12710
	_startingPixelCount: "l",
12711
	_handleOffsetCoord: "left",
12712
	_progressPixelSize: "width",
12713
	_upsideDown: false,
12714
 
12715
	_onKeyPress: function(/*Event*/ e){
12716
		if(this.disabled || e.altKey || e.ctrlKey){ return; }
12717
		switch(e.keyCode){
12718
			case dojo.keys.HOME:
12719
				this.setValue(this.minimum, false);
12720
				break;
12721
			case dojo.keys.END:
12722
				this.setValue(this.maximum, false);
12723
				break;
12724
			case dojo.keys.UP_ARROW:
12725
			case (this._isReversed() ? dojo.keys.LEFT_ARROW : dojo.keys.RIGHT_ARROW):
12726
			case dojo.keys.PAGE_UP:
12727
				this.increment(e);
12728
				break;
12729
			case dojo.keys.DOWN_ARROW:
12730
			case (this._isReversed() ? dojo.keys.RIGHT_ARROW : dojo.keys.LEFT_ARROW):
12731
			case dojo.keys.PAGE_DOWN:
12732
				this.decrement(e);
12733
				break;
12734
			default:
12735
				this.inherited("_onKeyPress", arguments);
12736
				return;
12737
		}
12738
		dojo.stopEvent(e);
12739
	},
12740
 
12741
	_onHandleClick: function(e){
12742
		if(this.disabled){ return; }
12743
		if(!dojo.isIE){
12744
			// make sure you get focus when dragging the handle
12745
			// (but don't do on IE because it causes a flicker on mouse up (due to blur then focus)
12746
			dijit.focus(this.sliderHandle);
12747
		}
12748
		dojo.stopEvent(e);
12749
	},
12750
 
12751
	_isReversed: function() {
12752
		return !(this._upsideDown || this.isLeftToRight());
12753
	},
12754
 
12755
	_onBarClick: function(e){
12756
		if(this.disabled || !this.clickSelect){ return; }
12757
		dijit.focus(this.sliderHandle);
12758
		dojo.stopEvent(e);
12759
		var abspos = dojo.coords(this.sliderBarContainer, true);
12760
		var pixelValue = e[this._mousePixelCoord] - abspos[this._startingPixelCoord];
12761
		this._setPixelValue(this._isReversed() || this._upsideDown ? (abspos[this._pixelCount] - pixelValue) : pixelValue, abspos[this._pixelCount], true);
12762
	},
12763
 
12764
	_setPixelValue: function(/*Number*/ pixelValue, /*Number*/ maxPixels, /*Boolean, optional*/ priorityChange){
12765
		if(this.disabled){ return; }
12766
		pixelValue = pixelValue < 0 ? 0 : maxPixels < pixelValue ? maxPixels : pixelValue;
12767
		var count = this.discreteValues;
12768
		if(count <= 1 || count == Infinity){ count = maxPixels; }
12769
		count--;
12770
		var pixelsPerValue = maxPixels / count;
12771
		var wholeIncrements = Math.round(pixelValue / pixelsPerValue);
12772
		this.setValue((this.maximum-this.minimum)*wholeIncrements/count + this.minimum, priorityChange);
12773
	},
12774
 
12775
	setValue: function(/*Number*/ value, /*Boolean, optional*/ priorityChange){
12776
		this.valueNode.value = this.value = value;
12777
		this.inherited('setValue', arguments);
12778
		var percent = (value - this.minimum) / (this.maximum - this.minimum);
12779
		this.progressBar.style[this._progressPixelSize] = (percent*100) + "%";
12780
		this.remainingBar.style[this._progressPixelSize] = ((1-percent)*100) + "%";
12781
	},
12782
 
12783
	_bumpValue: function(signedChange){
12784
		if(this.disabled){ return; }
12785
		var s = dojo.getComputedStyle(this.sliderBarContainer);
12786
		var c = dojo._getContentBox(this.sliderBarContainer, s);
12787
		var count = this.discreteValues;
12788
		if(count <= 1 || count == Infinity){ count = c[this._pixelCount]; }
12789
		count--;
12790
		var value = (this.value - this.minimum) * count / (this.maximum - this.minimum) + signedChange;
12791
		if(value < 0){ value = 0; }
12792
		if(value > count){ value = count; }
12793
		value = value * (this.maximum - this.minimum) / count + this.minimum;
12794
		this.setValue(value, true);
12795
	},
12796
 
12797
	decrement: function(e){
12798
		// summary
12799
		//	decrement slider by 1 unit
12800
		this._bumpValue(e.keyCode == dojo.keys.PAGE_DOWN?-this.pageIncrement:-1);
12801
	},
12802
 
12803
	increment: function(e){
12804
		// summary
12805
		//	increment slider by 1 unit
12806
		this._bumpValue(e.keyCode == dojo.keys.PAGE_UP?this.pageIncrement:1);
12807
	},
12808
 
12809
	_mouseWheeled: function(/*Event*/ evt){
12810
		dojo.stopEvent(evt);
12811
		var scrollAmount = 0;
12812
		if(typeof evt.wheelDelta == 'number'){ // IE
12813
			scrollAmount = evt.wheelDelta;
12814
		}else if(typeof evt.detail == 'number'){ // Mozilla+Firefox
12815
			scrollAmount = -evt.detail;
12816
		}
12817
		if(scrollAmount > 0){
12818
			this.increment(evt);
12819
		}else if(scrollAmount < 0){
12820
			this.decrement(evt);
12821
		}
12822
	},
12823
 
12824
	startup: function(){
12825
		dojo.forEach(this.getChildren(), function(child){
12826
			if(this[child.container] != this.containerNode){
12827
				this[child.container].appendChild(child.domNode);
12828
			}
12829
		}, this);
12830
	},
12831
 
12832
	_onBlur: function(){
12833
		dijit.form.HorizontalSlider.superclass.setValue.call(this, this.value, true);
12834
	},
12835
 
12836
	postCreate: function(){
12837
		if(this.showButtons){
12838
			this.incrementButton.style.display="";
12839
			this.decrementButton.style.display="";
12840
		}
12841
		this.connect(this.domNode, dojo.isIE ? "onmousewheel" : 'DOMMouseScroll', "_mouseWheeled");
12842
 
12843
		// define a custom constructor for a SliderMover that points back to me
12844
		var _self = this;
12845
		var mover = function(){
12846
			dijit.form._SliderMover.apply(this, arguments);
12847
			this.widget = _self;
12848
		};
12849
		dojo.extend(mover, dijit.form._SliderMover.prototype);
12850
 
12851
		this._movable = new dojo.dnd.Moveable(this.sliderHandle, {mover: mover});
12852
		this.inherited('postCreate', arguments);
12853
	},
12854
 
12855
	destroy: function(){
12856
		this._movable.destroy();
12857
		this.inherited('destroy', arguments);
12858
	}
12859
});
12860
 
12861
dojo.declare(
12862
	"dijit.form.VerticalSlider",
12863
	dijit.form.HorizontalSlider,
12864
{
12865
	// summary
12866
	//	A form widget that allows one to select a value with a vertically draggable image
12867
 
12868
	templateString:"<table class=\"dijitReset dijitSlider\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" rules=\"none\"\n><tbody class=\"dijitReset\"\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\"></td\n\t\t><td class=\"dijitReset dijitSliderButtonContainer dijitVerticalSliderButtonContainer\"\n\t\t\t><div class=\"dijitVerticalSliderIncrementIcon\" tabIndex=\"-1\" style=\"display:none\" dojoAttachPoint=\"incrementButton\" dojoAttachEvent=\"onclick: increment\"><span class=\"dijitSliderButtonInner\">+</span></div\n\t\t></td\n\t\t><td class=\"dijitReset\"></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\"></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><center><div class=\"dijitSliderBar dijitSliderBumper dijitVerticalSliderBumper dijitSliderTopBumper dijitVerticalSliderTopBumper\"></div></center\n\t\t></td\n\t\t><td class=\"dijitReset\"></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td dojoAttachPoint=\"leftDecoration\" class=\"dijitReset\" style=\"text-align:center;height:100%;\"></td\n\t\t><td class=\"dijitReset\" style=\"height:100%;\"\n\t\t\t><input dojoAttachPoint=\"valueNode\" type=\"hidden\" name=\"${name}\"\n\t\t\t/><center style=\"position:relative;height:100%;\" dojoAttachPoint=\"sliderBarContainer\"\n\t\t\t\t><div dojoAttachPoint=\"remainingBar\" class=\"dijitSliderBar dijitVerticalSliderBar dijitSliderRemainingBar dijitVerticalSliderRemainingBar\" dojoAttachEvent=\"onclick:_onBarClick\"></div\n\t\t\t\t><div dojoAttachPoint=\"progressBar\" class=\"dijitSliderBar dijitVerticalSliderBar dijitSliderProgressBar dijitVerticalSliderProgressBar\" dojoAttachEvent=\"onclick:_onBarClick\"\n\t\t\t\t\t><div dojoAttachPoint=\"sliderHandle,focusNode\" class=\"dijitSliderMoveable\" dojoAttachEvent=\"onkeypress:_onKeyPress,onclick:_onHandleClick\" style=\"vertical-align:top;\" waiRole=\"slider\" valuemin=\"${minimum}\" valuemax=\"${maximum}\"\n\t\t\t\t\t\t><div class=\"dijitSliderImageHandle dijitVerticalSliderImageHandle\"></div\n\t\t\t\t\t></div\n\t\t\t\t></div\n\t\t\t></center\n\t\t></td\n\t\t><td dojoAttachPoint=\"containerNode,rightDecoration\" class=\"dijitReset\" style=\"text-align:center;height:100%;\"></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\"></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><center><div class=\"dijitSliderBar dijitSliderBumper dijitVerticalSliderBumper dijitSliderBottomBumper dijitVerticalSliderBottomBumper\"></div></center\n\t\t></td\n\t\t><td class=\"dijitReset\"></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\"></td\n\t\t><td class=\"dijitReset dijitSliderButtonContainer dijitVerticalSliderButtonContainer\"\n\t\t\t><div class=\"dijitVerticalSliderDecrementIcon\" tabIndex=\"-1\" style=\"display:none\" dojoAttachPoint=\"decrementButton\" dojoAttachEvent=\"onclick: decrement\"><span class=\"dijitSliderButtonInner\">-</span></div\n\t\t></td\n\t\t><td class=\"dijitReset\"></td\n\t></tr\n></tbody></table>\n",
12869
	_mousePixelCoord: "pageY",
12870
	_pixelCount: "h",
12871
	_startingPixelCoord: "y",
12872
	_startingPixelCount: "t",
12873
	_handleOffsetCoord: "top",
12874
	_progressPixelSize: "height",
12875
	_upsideDown: true
12876
});
12877
 
12878
dojo.declare("dijit.form._SliderMover",
12879
	dojo.dnd.Mover,
12880
{
12881
	onMouseMove: function(e){
12882
		var widget = this.widget;
12883
		var c = this.constraintBox;
12884
		if(!c){
12885
			var container = widget.sliderBarContainer;
12886
			var s = dojo.getComputedStyle(container);
12887
			var c = dojo._getContentBox(container, s);
12888
			c[widget._startingPixelCount] = 0;
12889
			this.constraintBox = c;
12890
		}
12891
		var m = this.marginBox;
12892
		var pixelValue = widget._isReversed() ?
12893
			e[widget._mousePixelCoord] - dojo._abs(widget.sliderBarContainer).x :
12894
			m[widget._startingPixelCount] + e[widget._mousePixelCoord];
12895
		dojo.hitch(widget, "_setPixelValue")(widget._isReversed() || widget._upsideDown? (c[widget._pixelCount]-pixelValue) : pixelValue, c[widget._pixelCount]);
12896
	},
12897
 
12898
	destroy: function(e){
12899
		var widget = this.widget;
12900
		widget.setValue(widget.value, true);
12901
		dojo.dnd.Mover.prototype.destroy.call(this);
12902
	}
12903
});
12904
 
12905
 
12906
dojo.declare("dijit.form.HorizontalRule", [dijit._Widget, dijit._Templated],
12907
{
12908
	//	Summary:
12909
	//		Create hash marks for the Horizontal slider
12910
	templateString: '<div class="RuleContainer HorizontalRuleContainer"></div>',
12911
 
12912
	// count: Integer
12913
	//		Number of hash marks to generate
12914
	count: 3,
12915
 
12916
	// container: Node
12917
	//		If this is a child widget, connect it to this parent node
12918
	container: "containerNode",
12919
 
12920
	// ruleStyle: String
12921
	//		CSS style to apply to individual hash marks
12922
	ruleStyle: "",
12923
 
12924
	_positionPrefix: '<div class="RuleMark HorizontalRuleMark" style="left:',
12925
	_positionSuffix: '%;',
12926
	_suffix: '"></div>',
12927
 
12928
	_genHTML: function(pos, ndx){
12929
		return this._positionPrefix + pos + this._positionSuffix + this.ruleStyle + this._suffix;
12930
	},
12931
 
12932
	_isHorizontal: true,
12933
 
12934
	postCreate: function(){
12935
		if(this.count==1){
12936
			var innerHTML = this._genHTML(50, 0);
12937
		}else{
12938
			var interval = 100 / (this.count-1);
12939
			if(!this._isHorizontal || this.isLeftToRight()){
12940
				var innerHTML = this._genHTML(0, 0);
12941
				for(var i=1; i < this.count-1; i++){
12942
					innerHTML += this._genHTML(interval*i, i);
12943
				}
12944
				innerHTML += this._genHTML(100, this.count-1);
12945
			}else{
12946
				var innerHTML = this._genHTML(100, 0);
12947
				for(var i=1; i < this.count-1; i++){
12948
					innerHTML += this._genHTML(100-interval*i, i);
12949
				}
12950
				innerHTML += this._genHTML(0, this.count-1);
12951
			}
12952
		}
12953
		this.domNode.innerHTML = innerHTML;
12954
	}
12955
});
12956
 
12957
dojo.declare("dijit.form.VerticalRule", dijit.form.HorizontalRule,
12958
{
12959
	//	Summary:
12960
	//		Create hash marks for the Vertical slider
12961
	templateString: '<div class="RuleContainer VerticalRuleContainer"></div>',
12962
	_positionPrefix: '<div class="RuleMark VerticalRuleMark" style="top:',
12963
 
12964
	_isHorizontal: false
12965
});
12966
 
12967
dojo.declare("dijit.form.HorizontalRuleLabels", dijit.form.HorizontalRule,
12968
{
12969
	//	Summary:
12970
	//		Create labels for the Horizontal slider
12971
	templateString: '<div class="RuleContainer HorizontalRuleContainer"></div>',
12972
 
12973
	// labelStyle: String
12974
	//		CSS style to apply to individual text labels
12975
	labelStyle: "",
12976
 
12977
	// labels: Array
12978
	//	Array of text labels to render - evenly spaced from left-to-right or bottom-to-top
12979
	labels: [],
12980
 
12981
	// numericMargin: Integer
12982
	//	Number of generated numeric labels that should be rendered as '' on the ends when labels[] are not specified
12983
	numericMargin: 0,
12984
 
12985
	// numericMinimum: Integer
12986
	//	Leftmost label value for generated numeric labels when labels[] are not specified
12987
	minimum: 0,
12988
 
12989
	// numericMaximum: Integer
12990
	//	Rightmost label value for generated numeric labels when labels[] are not specified
12991
	maximum: 1,
12992
 
12993
	// constraints: object
12994
	//	pattern, places, lang, et al (see dojo.number) for generated numeric labels when labels[] are not specified
12995
	constraints: {pattern:"#%"},
12996
 
12997
	_positionPrefix: '<div class="RuleLabelContainer HorizontalRuleLabelContainer" style="left:',
12998
	_labelPrefix: '"><span class="RuleLabel HorizontalRuleLabel">',
12999
	_suffix: '</span></div>',
13000
 
13001
	_calcPosition: function(pos){
13002
		return pos;
13003
	},
13004
 
13005
	_genHTML: function(pos, ndx){
13006
		return this._positionPrefix + this._calcPosition(pos) + this._positionSuffix + this.labelStyle + this._labelPrefix + this.labels[ndx] + this._suffix;
13007
	},
13008
 
13009
	getLabels: function(){
13010
		// summary: user replaceable function to return the labels array
13011
 
13012
		// if the labels array was not specified directly, then see if <li> children were
13013
		var labels = this.labels;
13014
		if(!labels.length){
13015
			// for markup creation, labels are specified as child elements
13016
			labels = dojo.query("> li", this.srcNodeRef).map(function(node){
13017
				return String(node.innerHTML);
13018
			});
13019
		}
13020
		this.srcNodeRef.innerHTML = '';
13021
		// if the labels were not specified directly and not as <li> children, then calculate numeric labels
13022
		if(!labels.length && this.count > 1){
13023
			var start = this.minimum;
13024
			var inc = (this.maximum - start) / (this.count-1);
13025
			for (var i=0; i < this.count; i++){
13026
				labels.push((i<this.numericMargin||i>=(this.count-this.numericMargin))? '' : dojo.number.format(start, this.constraints));
13027
				start += inc;
13028
			}
13029
		}
13030
		return labels;
13031
	},
13032
 
13033
	postMixInProperties: function(){
13034
		this.inherited('postMixInProperties', arguments);
13035
		this.labels = this.getLabels();
13036
		this.count = this.labels.length;
13037
	}
13038
});
13039
 
13040
dojo.declare("dijit.form.VerticalRuleLabels", dijit.form.HorizontalRuleLabels,
13041
{
13042
	//	Summary:
13043
	//		Create labels for the Vertical slider
13044
	templateString: '<div class="RuleContainer VerticalRuleContainer"></div>',
13045
 
13046
	_positionPrefix: '<div class="RuleLabelContainer VerticalRuleLabelContainer" style="top:',
13047
	_labelPrefix: '"><span class="RuleLabel VerticalRuleLabel">',
13048
 
13049
	_calcPosition: function(pos){
13050
		return 100-pos;
13051
	},
13052
 
13053
	_isHorizontal: false
13054
});
13055
 
13056
}
13057
 
13058
if(!dojo._hasResource["dijit.form.Textarea"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
13059
dojo._hasResource["dijit.form.Textarea"] = true;
13060
dojo.provide("dijit.form.Textarea");
13061
 
13062
 
13063
 
13064
 
13065
 
13066
dojo.declare(
13067
	"dijit.form.Textarea",
13068
	dijit.form._FormWidget,
13069
{
13070
	// summary
13071
	//	A textarea that resizes vertically to contain the data.
13072
	//	Takes nearly all the parameters (name, value, etc.) that a vanilla textarea takes.
13073
	//	Cols is not supported and the width should be specified with style width.
13074
	//	Rows is not supported since this widget adjusts the height.
13075
	// usage:
13076
	//	<textarea dojoType="dijit.form.TextArea">...</textarea>
13077
 
13078
	attributeMap: dojo.mixin(dojo.clone(dijit.form._FormWidget.prototype.attributeMap),
13079
		{style:"styleNode", 'class':"styleNode"}),
13080
 
13081
	templateString: (dojo.isIE || dojo.isSafari || dojo.isMozilla) ?
13082
				((dojo.isIE || dojo.isSafari) ? '<fieldset id="${id}" class="dijitInline dijitInputField dijitTextArea" dojoAttachPoint="styleNode" waiRole="presentation"><div dojoAttachPoint="editNode,focusNode,eventNode" dojoAttachEvent="onpaste:_changing,oncut:_changing" waiRole="textarea" style="text-decoration:none;_padding-bottom:16px;display:block;overflow:auto;" contentEditable="true"></div>'
13083
					: '<span id="${id}" class="dijitReset">'+
13084
					'<iframe src="javascript:<html><head><title>${_iframeEditTitle}</title></head><body><script>var _postCreate=window.frameElement?window.frameElement.postCreate:null;if(_postCreate)_postCreate();</script></body></html>"'+
13085
							' dojoAttachPoint="iframe,styleNode" dojoAttachEvent="onblur:_onIframeBlur" class="dijitInline dijitInputField dijitTextArea"></iframe>')
13086
				+ '<textarea name="${name}" value="${value}" dojoAttachPoint="formValueNode" style="display:none;"></textarea>'
13087
				+ ((dojo.isIE || dojo.isSafari) ? '</fieldset>':'</span>')
13088
			: '<textarea id="${id}" name="${name}" value="${value}" dojoAttachPoint="formValueNode,editNode,focusNode,styleNode" class="dijitInputField dijitTextArea"></textarea>',
13089
 
13090
	focus: function(){
13091
		// summary: Received focus, needed for the InlineEditBox widget
13092
		if(!this.disabled){
13093
			this._changing(); // set initial height
13094
		}
13095
		if(dojo.isMozilla){
13096
			dijit.focus(this.iframe);
13097
		}else{
13098
			dijit.focus(this.focusNode);
13099
		}
13100
	},
13101
 
13102
	setValue: function(/*String*/ value, /*Boolean, optional*/ priorityChange){
13103
		var editNode = this.editNode;
13104
		if(typeof value == "string"){
13105
			editNode.innerHTML = ""; // wipe out old nodes
13106
			if(value.split){
13107
				var _this=this;
13108
				var isFirst = true;
13109
				dojo.forEach(value.split("\n"), function(line){
13110
					if(isFirst){ isFirst = false; }
13111
					else {
13112
						editNode.appendChild(document.createElement("BR")); // preserve line breaks
13113
					}
13114
					editNode.appendChild(document.createTextNode(line)); // use text nodes so that imbedded tags can be edited
13115
				});
13116
			}else{
13117
				editNode.appendChild(document.createTextNode(value));
13118
			}
13119
		}else{
13120
			// blah<BR>blah --> blah\nblah
13121
			// <P>blah</P><P>blah</P> --> blah\nblah
13122
			// <DIV>blah</DIV><DIV>blah</DIV> --> blah\nblah
13123
			// &amp;&lt;&gt; -->&< >
13124
			value = editNode.innerHTML;
13125
			if(this.iframe){ // strip sizeNode
13126
				value = value.replace(/<div><\/div>\r?\n?$/i,"");
13127
			}
13128
			value = value.replace(/\s*\r?\n|^\s+|\s+$|&nbsp;/g,"").replace(/>\s+</g,"><").replace(/<\/(p|div)>$|^<(p|div)[^>]*>/gi,"").replace(/([^>])<div>/g,"$1\n").replace(/<\/p>\s*<p[^>]*>|<br[^>]*>/gi,"\n").replace(/<[^>]*>/g,"").replace(/&amp;/gi,"\&").replace(/&lt;/gi,"<").replace(/&gt;/gi,">");
13129
		}
13130
		this.value = this.formValueNode.value = value;
13131
		if(this.iframe){
13132
			var sizeNode = document.createElement('div');
13133
			editNode.appendChild(sizeNode);
13134
			var newHeight = sizeNode.offsetTop;
13135
			if(editNode.scrollWidth > editNode.clientWidth){ newHeight+=16; } // scrollbar space needed?
13136
			if(this.lastHeight != newHeight){ // cache size so that we don't get a resize event because of a resize event
13137
				if(newHeight == 0){ newHeight = 16; } // height = 0 causes the browser to not set scrollHeight
13138
				dojo.contentBox(this.iframe, {h: newHeight});
13139
				this.lastHeight = newHeight;
13140
			}
13141
			editNode.removeChild(sizeNode);
13142
		}
13143
		dijit.form.Textarea.superclass.setValue.call(this, this.getValue(), priorityChange);
13144
	},
13145
 
13146
	getValue: function(){
13147
		return this.formValueNode.value.replace(/\r/g,"");
13148
	},
13149
 
13150
	postMixInProperties: function(){
13151
		dijit.form.Textarea.superclass.postMixInProperties.apply(this,arguments);
13152
		// don't let the source text be converted to a DOM structure since we just want raw text
13153
		if(this.srcNodeRef && this.srcNodeRef.innerHTML != ""){
13154
			this.value = this.srcNodeRef.innerHTML;
13155
			this.srcNodeRef.innerHTML = "";
13156
		}
13157
		if((!this.value || this.value == "") && this.srcNodeRef && this.srcNodeRef.value){
13158
			this.value = this.srcNodeRef.value;
13159
		}
13160
		if(!this.value){ this.value = ""; }
13161
		this.value = this.value.replace(/\r\n/g,"\n").replace(/&gt;/g,">").replace(/&lt;/g,"<").replace(/&amp;/g,"&");
13162
		if(dojo.isMozilla){
13163
			// In the case of Firefox an iframe is used and when the text gets focus,
13164
			// focus is fired from the document object.  There isn't a way to put a
13165
			// waiRole on the document object and as a result screen readers don't
13166
			// announce the role.  As a result screen reader users are lost.
13167
			//
13168
			// An additional problem is that the browser gives the document object a
13169
			// very cryptic accessible name, e.g.
13170
			// wysiwyg://13/http://archive.dojotoolkit.org/nightly/dojotoolkit/dijit/tests/form/test_InlineEditBox.html
13171
			// When focus is fired from the document object, the screen reader speaks
13172
			// the accessible name.  The cyptic accessile name is confusing.
13173
			//
13174
			// A workaround for both of these problems is to give the iframe's
13175
			// document a title, the name of which is similar to a role name, i.e.
13176
			// "edit area".  This will be used as the accessible name which will replace
13177
			// the cryptic name and will also convey the role information to the user.
13178
			// Because it is read directly to the user, the string must be localized.
13179
			// In addition, since a <label> element can not be associated with an iframe, if
13180
			// this control has a label, insert the text into the title as well.
13181
			var _nlsResources = dojo.i18n.getLocalization("dijit", "Textarea");
13182
			this._iframeEditTitle = _nlsResources.iframeEditTitle;
13183
			this._iframeFocusTitle = _nlsResources.iframeFocusTitle;
13184
			var label=dojo.query('label[for="'+this.id+'"]');
13185
			if(label.length){
13186
				this._iframeEditTitle = label[0].innerHTML + " " + this._iframeEditTitle;
13187
			}
13188
			var body = this.focusNode = this.editNode = document.createElement('BODY');
13189
			body.style.margin="0px";
13190
			body.style.padding="0px";
13191
			body.style.border="0px";
13192
		}
13193
	},
13194
 
13195
	postCreate: function(){
13196
		if(dojo.isIE || dojo.isSafari){
13197
			this.domNode.style.overflowY = 'hidden';
13198
		}else if(dojo.isMozilla){
13199
			var w = this.iframe.contentWindow;
13200
			try { // #4715: peeking at the title can throw a security exception during iframe setup
13201
				var title = this.iframe.contentDocument.title;
13202
			} catch(e) { var title = ''; }
13203
			if(!w || !title){
13204
				this.iframe.postCreate = dojo.hitch(this, this.postCreate);
13205
				return;
13206
			}
13207
			var d = w.document;
13208
			d.getElementsByTagName('HTML')[0].replaceChild(this.editNode, d.getElementsByTagName('BODY')[0]);
13209
			if(!this.isLeftToRight()){
13210
				d.getElementsByTagName('HTML')[0].dir = "rtl";
13211
			}
13212
			this.iframe.style.overflowY = 'hidden';
13213
			this.eventNode = d;
13214
			// this.connect won't destroy this handler cleanly since its on the iframe's window object
13215
			// resize is a method of window, not document
13216
			w.addEventListener("resize", dojo.hitch(this, this._changed), false); // resize is only on the window object
13217
		}else{
13218
			this.focusNode = this.domNode;
13219
		}
13220
		if(this.eventNode){
13221
			this.connect(this.eventNode, "keypress", this._onKeyPress);
13222
			this.connect(this.eventNode, "mousemove", this._changed);
13223
			this.connect(this.eventNode, "focus", this._focused);
13224
			this.connect(this.eventNode, "blur", this._blurred);
13225
		}
13226
		if(this.editNode){
13227
			this.connect(this.editNode, "change", this._changed); // needed for mouse paste events per #3479
13228
		}
13229
		this.inherited('postCreate', arguments);
13230
	},
13231
 
13232
	// event handlers, you can over-ride these in your own subclasses
13233
	_focused: function(e){
13234
		dojo.addClass(this.iframe||this.domNode, "dijitInputFieldFocused");
13235
		this._changed(e);
13236
	},
13237
 
13238
	_blurred: function(e){
13239
		dojo.removeClass(this.iframe||this.domNode, "dijitInputFieldFocused");
13240
		this._changed(e, true);
13241
	},
13242
 
13243
	_onIframeBlur: function(){
13244
		// Reset the title back to "edit area".
13245
		this.iframe.contentDocument.title = this._iframeEditTitle;
13246
	},
13247
 
13248
	_onKeyPress: function(e){
13249
		if(e.keyCode == dojo.keys.TAB && !e.shiftKey && !e.ctrlKey && !e.altKey && this.iframe){
13250
			// Pressing the tab key in the iframe (with designMode on) will cause the
13251
			// entry of a tab character so we have to trap that here.  Since we don't
13252
			// know the next focusable object we put focus on the iframe and then the
13253
			// user has to press tab again (which then does the expected thing).
13254
			// A problem with that is that the screen reader user hears "edit area"
13255
			// announced twice which causes confusion.  By setting the
13256
			// contentDocument's title to "edit area frame" the confusion should be
13257
			// eliminated.
13258
			this.iframe.contentDocument.title = this._iframeFocusTitle;
13259
			// Place focus on the iframe. A subsequent tab or shift tab will put focus
13260
			// on the correct control.
13261
			// Note: Can't use this.focus() because that results in a call to
13262
			// dijit.focus and if that receives an iframe target it will set focus
13263
			// on the iframe's contentWindow.
13264
			this.iframe.focus();  // this.focus(); won't work
13265
			dojo.stopEvent(e);
13266
		}else if(e.keyCode == dojo.keys.ENTER){
13267
			e.stopPropagation();
13268
		}else if(this.inherited("_onKeyPress", arguments) && this.iframe){
13269
			// #3752:
13270
			// The key press will not make it past the iframe.
13271
			// If a widget is listening outside of the iframe, (like InlineEditBox)
13272
			// it will not hear anything.
13273
			// Create an equivalent event so everyone else knows what is going on.
13274
			var te = document.createEvent("KeyEvents");
13275
			te.initKeyEvent("keypress", true, true, null, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.keyCode, e.charCode);
13276
			this.iframe.dispatchEvent(te);
13277
		}
13278
		this._changing();
13279
	},
13280
 
13281
	_changing: function(e){
13282
		// summary: event handler for when a change is imminent
13283
		setTimeout(dojo.hitch(this, "_changed", e, false), 1);
13284
	},
13285
 
13286
	_changed: function(e, priorityChange){
13287
		// summary: event handler for when a change has already happened
13288
		if(this.iframe && this.iframe.contentDocument.designMode != "on"){
13289
			this.iframe.contentDocument.designMode="on"; // in case this failed on init due to being hidden
13290
		}
13291
		this.setValue(null, priorityChange);
13292
	}
13293
});
13294
 
13295
}
13296
 
13297
if(!dojo._hasResource["dijit.layout.StackContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
13298
dojo._hasResource["dijit.layout.StackContainer"] = true;
13299
dojo.provide("dijit.layout.StackContainer");
13300
 
13301
 
13302
 
13303
 
13304
 
13305
dojo.declare(
13306
	"dijit.layout.StackContainer",
13307
	dijit.layout._LayoutWidget,
13308
 
13309
	// summary
13310
	//	A container that has multiple children, but shows only
13311
	//	one child at a time (like looking at the pages in a book one by one).
13312
	//
13313
	//	Publishes topics <widgetId>-addChild, <widgetId>-removeChild, and <widgetId>-selectChild
13314
	//
13315
	//	Can be base class for container, Wizard, Show, etc.
13316
{
13317
	// doLayout: Boolean
13318
	//  if true, change the size of my currently displayed child to match my size
13319
	doLayout: true,
13320
 
13321
	_started: false,
13322
 
13323
	// selectedChildWidget: Widget
13324
	//	References the currently selected child widget, if any
13325
 
13326
	postCreate: function(){
13327
		dijit.setWaiRole((this.containerNode || this.domNode), "tabpanel");
13328
		this.connect(this.domNode, "onkeypress", this._onKeyPress);
13329
	},
13330
 
13331
	startup: function(){
13332
		if(this._started){ return; }
13333
 
13334
		var children = this.getChildren();
13335
 
13336
		// Setup each page panel
13337
		dojo.forEach(children, this._setupChild, this);
13338
 
13339
		// Figure out which child to initially display
13340
		dojo.some(children, function(child){
13341
			if(child.selected){
13342
				this.selectedChildWidget = child;
13343
			}
13344
			return child.selected;
13345
		}, this);
13346
 
13347
		var selected = this.selectedChildWidget;
13348
 
13349
		// Default to the first child
13350
		if(!selected && children[0]){
13351
			selected = this.selectedChildWidget = children[0];
13352
			selected.selected = true;
13353
		}
13354
		if(selected){
13355
			this._showChild(selected);
13356
		}
13357
 
13358
		// Now publish information about myself so any StackControllers can initialize..
13359
		dojo.publish(this.id+"-startup", [{children: children, selected: selected}]);
13360
		this.inherited("startup",arguments);
13361
		this._started = true;
13362
	},
13363
 
13364
	_setupChild: function(/*Widget*/ page){
13365
		// Summary: prepare the given child
13366
 
13367
		page.domNode.style.display = "none";
13368
 
13369
		// since we are setting the width/height of the child elements, they need
13370
		// to be position:relative, or IE has problems (See bug #2033)
13371
		page.domNode.style.position = "relative";
13372
 
13373
		return page; // dijit._Widget
13374
	},
13375
 
13376
	addChild: function(/*Widget*/ child, /*Integer?*/ insertIndex){
13377
		// summary: Adds a widget to the stack
13378
 
13379
		dijit._Container.prototype.addChild.apply(this, arguments);
13380
		child = this._setupChild(child);
13381
 
13382
		if(this._started){
13383
			// in case the tab titles have overflowed from one line to two lines
13384
			this.layout();
13385
 
13386
			dojo.publish(this.id+"-addChild", [child, insertIndex]);
13387
 
13388
			// if this is the first child, then select it
13389
			if(!this.selectedChildWidget){
13390
				this.selectChild(child);
13391
			}
13392
		}
13393
	},
13394
 
13395
	removeChild: function(/*Widget*/ page){
13396
		// summary: Removes the pane from the stack
13397
 
13398
		dijit._Container.prototype.removeChild.apply(this, arguments);
13399
 
13400
		// If we are being destroyed than don't run the code below (to select another page), because we are deleting
13401
		// every page one by one
13402
		if(this._beingDestroyed){ return; }
13403
 
13404
		if(this._started){
13405
			// this will notify any tablists to remove a button; do this first because it may affect sizing
13406
			dojo.publish(this.id+"-removeChild", [page]);
13407
 
13408
			// in case the tab titles now take up one line instead of two lines
13409
			this.layout();
13410
		}
13411
 
13412
		if(this.selectedChildWidget === page){
13413
			this.selectedChildWidget = undefined;
13414
			if(this._started){
13415
				var children = this.getChildren();
13416
				if(children.length){
13417
					this.selectChild(children[0]);
13418
				}
13419
			}
13420
		}
13421
	},
13422
 
13423
	selectChild: function(/*Widget*/ page){
13424
		// summary:
13425
		//	Show the given widget (which must be one of my children)
13426
 
13427
		page = dijit.byId(page);
13428
 
13429
		if(this.selectedChildWidget != page){
13430
			// Deselect old page and select new one
13431
			this._transition(page, this.selectedChildWidget);
13432
			this.selectedChildWidget = page;
13433
			dojo.publish(this.id+"-selectChild", [page]);
13434
		}
13435
	},
13436
 
13437
	_transition: function(/*Widget*/newWidget, /*Widget*/oldWidget){
13438
		if(oldWidget){
13439
			this._hideChild(oldWidget);
13440
		}
13441
		this._showChild(newWidget);
13442
 
13443
		// Size the new widget, in case this is the first time it's being shown,
13444
		// or I have been resized since the last time it was shown.
13445
		// page must be visible for resizing to work
13446
		if(this.doLayout && newWidget.resize){
13447
			newWidget.resize(this._containerContentBox || this._contentBox);
13448
		}
13449
	},
13450
 
13451
	_adjacent: function(/*Boolean*/ forward){
13452
		// summary: Gets the next/previous child widget in this container from the current selection
13453
		var children = this.getChildren();
13454
		var index = dojo.indexOf(children, this.selectedChildWidget);
13455
		index += forward ? 1 : children.length - 1;
13456
		return children[ index % children.length ]; // dijit._Widget
13457
	},
13458
 
13459
	forward: function(){
13460
		// Summary: advance to next page
13461
		this.selectChild(this._adjacent(true));
13462
	},
13463
 
13464
	back: function(){
13465
		// Summary: go back to previous page
13466
		this.selectChild(this._adjacent(false));
13467
	},
13468
 
13469
	_onKeyPress: function(e){
13470
		dojo.publish(this.id+"-containerKeyPress", [{ e: e, page: this}]);
13471
	},
13472
 
13473
	layout: function(){
13474
		if(this.doLayout && this.selectedChildWidget && this.selectedChildWidget.resize){
13475
			this.selectedChildWidget.resize(this._contentBox);
13476
		}
13477
	},
13478
 
13479
	_showChild: function(/*Widget*/ page){
13480
		var children = this.getChildren();
13481
		page.isFirstChild = (page == children[0]);
13482
		page.isLastChild = (page == children[children.length-1]);
13483
		page.selected = true;
13484
 
13485
		page.domNode.style.display="";
13486
		if(page._loadCheck){
13487
			page._loadCheck(); // trigger load in ContentPane
13488
		}
13489
		if(page.onShow){
13490
			page.onShow();
13491
		}
13492
	},
13493
 
13494
	_hideChild: function(/*Widget*/ page){
13495
		page.selected=false;
13496
		page.domNode.style.display="none";
13497
		if(page.onHide){
13498
			page.onHide();
13499
		}
13500
	},
13501
 
13502
	closeChild: function(/*Widget*/ page){
13503
		// summary
13504
		//	callback when user clicks the [X] to remove a page
13505
		//	if onClose() returns true then remove and destroy the childd
13506
		var remove = page.onClose(this, page);
13507
		if(remove){
13508
			this.removeChild(page);
13509
			// makes sure we can clean up executeScripts in ContentPane onUnLoad
13510
			page.destroy();
13511
		}
13512
	},
13513
 
13514
	destroy: function(){
13515
		this._beingDestroyed = true;
13516
		this.inherited("destroy",arguments);
13517
	}
13518
});
13519
 
13520
dojo.declare(
13521
	"dijit.layout.StackController",
13522
	[dijit._Widget, dijit._Templated, dijit._Container],
13523
	{
13524
		// summary:
13525
		//	Set of buttons to select a page in a page list.
13526
		//	Monitors the specified StackContainer, and whenever a page is
13527
		//	added, deleted, or selected, updates itself accordingly.
13528
 
13529
		templateString: "<span wairole='tablist' dojoAttachEvent='onkeypress' class='dijitStackController'></span>",
13530
 
13531
		// containerId: String
13532
		//	the id of the page container that I point to
13533
		containerId: "",
13534
 
13535
		// buttonWidget: String
13536
		//	the name of the button widget to create to correspond to each page
13537
		buttonWidget: "dijit.layout._StackButton",
13538
 
13539
		postCreate: function(){
13540
			dijit.setWaiRole(this.domNode, "tablist");
13541
 
13542
			this.pane2button = {};		// mapping from panes to buttons
13543
			this._subscriptions=[
13544
				dojo.subscribe(this.containerId+"-startup", this, "onStartup"),
13545
				dojo.subscribe(this.containerId+"-addChild", this, "onAddChild"),
13546
				dojo.subscribe(this.containerId+"-removeChild", this, "onRemoveChild"),
13547
				dojo.subscribe(this.containerId+"-selectChild", this, "onSelectChild"),
13548
				dojo.subscribe(this.containerId+"-containerKeyPress", this, "onContainerKeyPress")
13549
			];
13550
		},
13551
 
13552
		onStartup: function(/*Object*/ info){
13553
			// summary: called after StackContainer has finished initializing
13554
			dojo.forEach(info.children, this.onAddChild, this);
13555
			this.onSelectChild(info.selected);
13556
		},
13557
 
13558
		destroy: function(){
13559
			dojo.forEach(this._subscriptions, dojo.unsubscribe);
13560
			this.inherited("destroy",arguments);
13561
		},
13562
 
13563
		onAddChild: function(/*Widget*/ page, /*Integer?*/ insertIndex){
13564
			// summary:
13565
			//   Called whenever a page is added to the container.
13566
			//   Create button corresponding to the page.
13567
 
13568
			// add a node that will be promoted to the button widget
13569
			var refNode = document.createElement("span");
13570
			this.domNode.appendChild(refNode);
13571
			// create an instance of the button widget
13572
			var cls = dojo.getObject(this.buttonWidget);
13573
			var button = new cls({label: page.title, closeButton: page.closable}, refNode);
13574
			this.addChild(button, insertIndex);
13575
			this.pane2button[page] = button;
13576
			page.controlButton = button;	// this value might be overwritten if two tabs point to same container
13577
 
13578
			dojo.connect(button, "onClick", dojo.hitch(this,"onButtonClick",page));
13579
			dojo.connect(button, "onClickCloseButton", dojo.hitch(this,"onCloseButtonClick",page));
13580
 
13581
			if(!this._currentChild){ // put the first child into the tab order
13582
				button.focusNode.setAttribute("tabIndex","0");
13583
				this._currentChild = page;
13584
			}
13585
		},
13586
 
13587
		onRemoveChild: function(/*Widget*/ page){
13588
			// summary:
13589
			//   Called whenever a page is removed from the container.
13590
			//   Remove the button corresponding to the page.
13591
			if(this._currentChild === page){ this._currentChild = null; }
13592
			var button = this.pane2button[page];
13593
			if(button){
13594
				// TODO? if current child { reassign }
13595
				button.destroy();
13596
			}
13597
			this.pane2button[page] = null;
13598
		},
13599
 
13600
		onSelectChild: function(/*Widget*/ page){
13601
			// summary:
13602
			//	Called when a page has been selected in the StackContainer, either by me or by another StackController
13603
 
13604
			if(!page){ return; }
13605
 
13606
			if(this._currentChild){
13607
				var oldButton=this.pane2button[this._currentChild];
13608
				oldButton.setChecked(false);
13609
				oldButton.focusNode.setAttribute("tabIndex", "-1");
13610
			}
13611
 
13612
			var newButton=this.pane2button[page];
13613
			newButton.setChecked(true);
13614
			this._currentChild = page;
13615
			newButton.focusNode.setAttribute("tabIndex", "0");
13616
		},
13617
 
13618
		onButtonClick: function(/*Widget*/ page){
13619
			// summary:
13620
			//   Called whenever one of my child buttons is pressed in an attempt to select a page
13621
			var container = dijit.byId(this.containerId);	// TODO: do this via topics?
13622
			container.selectChild(page);
13623
		},
13624
 
13625
		onCloseButtonClick: function(/*Widget*/ page){
13626
			// summary:
13627
			//   Called whenever one of my child buttons [X] is pressed in an attempt to close a page
13628
			var container = dijit.byId(this.containerId);
13629
			container.closeChild(page);
13630
			var b = this.pane2button[this._currentChild];
13631
			if(b){
13632
				dijit.focus(b.focusNode || b.domNode);
13633
			}
13634
		},
13635
 
13636
		// TODO: this is a bit redundant with forward, back api in StackContainer
13637
		adjacent: function(/*Boolean*/ forward){
13638
			// find currently focused button in children array
13639
			var children = this.getChildren();
13640
			var current = dojo.indexOf(children, this.pane2button[this._currentChild]);
13641
			// pick next button to focus on
13642
			var offset = forward ? 1 : children.length - 1;
13643
			return children[ (current + offset) % children.length ]; // dijit._Widget
13644
		},
13645
 
13646
		onkeypress: function(/*Event*/ e){
13647
			// summary:
13648
			//   Handle keystrokes on the page list, for advancing to next/previous button
13649
			//   and closing the current page if the page is closable.
13650
 
13651
			if(this.disabled || e.altKey ){ return; }
13652
			var forward = true;
13653
			if(e.ctrlKey || !e._djpage){
13654
				var k = dojo.keys;
13655
				switch(e.keyCode){
13656
					case k.LEFT_ARROW:
13657
					case k.UP_ARROW:
13658
					case k.PAGE_UP:
13659
						forward = false;
13660
						// fall through
13661
					case k.RIGHT_ARROW:
13662
					case k.DOWN_ARROW:
13663
					case k.PAGE_DOWN:
13664
						this.adjacent(forward).onClick();
13665
						dojo.stopEvent(e);
13666
						break;
13667
					case k.DELETE:
13668
						if(this._currentChild.closable){
13669
							this.onCloseButtonClick(this._currentChild);
13670
						}
13671
						dojo.stopEvent(e);
13672
						break;
13673
					default:
13674
						if(e.ctrlKey){
13675
							if(e.keyCode == k.TAB){
13676
								this.adjacent(!e.shiftKey).onClick();
13677
								dojo.stopEvent(e);
13678
							}else if(e.keyChar == "w"){
13679
								if(this._currentChild.closable){
13680
									this.onCloseButtonClick(this._currentChild);
13681
								}
13682
								dojo.stopEvent(e); // avoid browser tab closing.
13683
							}
13684
						}
13685
				}
13686
			}
13687
		},
13688
 
13689
		onContainerKeyPress: function(/*Object*/ info){
13690
			info.e._djpage = info.page;
13691
			this.onkeypress(info.e);
13692
		}
13693
});
13694
 
13695
dojo.declare("dijit.layout._StackButton",
13696
	dijit.form.ToggleButton,
13697
	{
13698
	// summary
13699
	//	Internal widget used by StackContainer.
13700
	//	The button-like or tab-like object you click to select or delete a page
13701
 
13702
	tabIndex: "-1", // StackContainer buttons are not in the tab order by default
13703
 
13704
	postCreate: function(/*Event*/ evt){
13705
		dijit.setWaiRole((this.focusNode || this.domNode), "tab");
13706
		this.inherited("postCreate", arguments);
13707
	},
13708
 
13709
	onClick: function(/*Event*/ evt){
13710
		// summary: This is for TabContainer where the tabs are <span> rather than button,
13711
		// 	so need to set focus explicitly (on some browsers)
13712
		dijit.focus(this.focusNode);
13713
 
13714
		// ... now let StackController catch the event and tell me what to do
13715
	},
13716
 
13717
	onClickCloseButton: function(/*Event*/ evt){
13718
		// summary
13719
		//	StackContainer connects to this function; if your widget contains a close button
13720
		//	then clicking it should call this function.
13721
		evt.stopPropagation();
13722
	}
13723
});
13724
 
13725
// These arguments can be specified for the children of a StackContainer.
13726
// Since any widget can be specified as a StackContainer child, mix them
13727
// into the base widget class.  (This is a hack, but it's effective.)
13728
dojo.extend(dijit._Widget, {
13729
	// title: String
13730
	//		Title of this widget.  Used by TabContainer to the name the tab, etc.
13731
	title: "",
13732
 
13733
	// selected: Boolean
13734
	//		Is this child currently selected?
13735
	selected: false,
13736
 
13737
	// closable: Boolean
13738
	//		True if user can close (destroy) this child, such as (for example) clicking the X on the tab.
13739
	closable: false,	// true if user can close this tab pane
13740
 
13741
	onClose: function(){
13742
		// summary: Callback if someone tries to close the child, child will be closed if func returns true
13743
		return true;
13744
	}
13745
});
13746
 
13747
}
13748
 
13749
if(!dojo._hasResource["dijit.layout.AccordionContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
13750
dojo._hasResource["dijit.layout.AccordionContainer"] = true;
13751
dojo.provide("dijit.layout.AccordionContainer");
13752
 
13753
 
13754
 
13755
 
13756
 
13757
 
13758
 
13759
 
13760
dojo.declare(
13761
	"dijit.layout.AccordionContainer",
13762
	dijit.layout.StackContainer,
13763
	{
13764
		// summary:
13765
		//		Holds a set of panes where every pane's title is visible, but only one pane's content is visible at a time,
13766
		//		and switching between panes is visualized by sliding the other panes up/down.
13767
		// usage:
13768
		// 	<div dojoType="dijit.layout.AccordionContainer">
13769
		// 		<div dojoType="dijit.layout.AccordionPane" title="pane 1">
13770
		// 			<div dojoType="dijit.layout.ContentPane">...</div>
13771
		//  	</div>
13772
		// 		<div dojoType="dijit.layout.AccordionPane" title="pane 2">
13773
		//			<p>This is some text</p>
13774
		// 		...
13775
		// 	</div>
13776
 
13777
		// duration: Integer
13778
		//		Amount of time (in ms) it takes to slide panes
13779
		duration: 250,
13780
 
13781
		_verticalSpace: 0,
13782
 
13783
		postCreate: function(){
13784
			this.domNode.style.overflow="hidden";
13785
			this.inherited("postCreate",arguments);
13786
			dijit.setWaiRole(this.domNode, "tablist");
13787
			dojo.addClass(this.domNode,"dijitAccordionContainer");
13788
		},
13789
 
13790
		startup: function(){
13791
			if(this._started){ return; }
13792
			this.inherited("startup",arguments);
13793
			if(this.selectedChildWidget){
13794
				var style = this.selectedChildWidget.containerNode.style;
13795
				style.display = "";
13796
				style.overflow = "auto";
13797
				this.selectedChildWidget._setSelectedState(true);
13798
			}
13799
		},
13800
 
13801
		layout: function(){
13802
			// summary
13803
			//		Set the height of the open pane based on what room remains
13804
			// get cumulative height of all the title bars, and figure out which pane is open
13805
			var totalCollapsedHeight = 0;
13806
			var openPane = this.selectedChildWidget;
13807
			dojo.forEach(this.getChildren(), function(child){
13808
				totalCollapsedHeight += child.getTitleHeight();
13809
			});
13810
			var mySize = this._contentBox;
13811
			this._verticalSpace = (mySize.h - totalCollapsedHeight);
13812
			if(openPane){
13813
				openPane.containerNode.style.height = this._verticalSpace + "px";
13814
/***
13815
TODO: this is wrong.  probably you wanted to call resize on the SplitContainer
13816
inside the AccordionPane??
13817
				if(openPane.resize){
13818
					openPane.resize({h: this._verticalSpace});
13819
				}
13820
***/
13821
			}
13822
		},
13823
 
13824
		_setupChild: function(/*Widget*/ page){
13825
			// Summary: prepare the given child
13826
			return page;
13827
		},
13828
 
13829
		_transition: function(/*Widget?*/newWidget, /*Widget?*/oldWidget){
13830
//TODO: should be able to replace this with calls to slideIn/slideOut
13831
			if(this._inTransition){ return; }
13832
			this._inTransition = true;
13833
			var animations = [];
13834
			var paneHeight = this._verticalSpace;
13835
			if(newWidget){
13836
				newWidget.setSelected(true);
13837
				var newContents = newWidget.containerNode;
13838
				newContents.style.display = "";
13839
 
13840
				animations.push(dojo.animateProperty({
13841
					node: newContents,
13842
					duration: this.duration,
13843
					properties: {
13844
						height: { start: "1", end: paneHeight }
13845
					},
13846
					onEnd: function(){
13847
						newContents.style.overflow = "auto";
13848
					}
13849
				}));
13850
			}
13851
			if(oldWidget){
13852
				oldWidget.setSelected(false);
13853
				var oldContents = oldWidget.containerNode;
13854
				oldContents.style.overflow = "hidden";
13855
				animations.push(dojo.animateProperty({
13856
					node: oldContents,
13857
					duration: this.duration,
13858
					properties: {
13859
						height: { start: paneHeight, end: "1" }
13860
					},
13861
					onEnd: function(){
13862
						oldContents.style.display = "none";
13863
					}
13864
				}));
13865
			}
13866
 
13867
			this._inTransition = false;
13868
 
13869
			dojo.fx.combine(animations).play();
13870
		},
13871
 
13872
		// note: we are treating the container as controller here
13873
		_onKeyPress: function(/*Event*/ e){
13874
			if(this.disabled || e.altKey ){ return; }
13875
			var k = dojo.keys;
13876
			switch(e.keyCode){
13877
				case k.LEFT_ARROW:
13878
				case k.UP_ARROW:
13879
				case k.PAGE_UP:
13880
					this._adjacent(false)._onTitleClick();
13881
					dojo.stopEvent(e);
13882
					break;
13883
				case k.RIGHT_ARROW:
13884
				case k.DOWN_ARROW:
13885
				case k.PAGE_DOWN:
13886
					this._adjacent(true)._onTitleClick();
13887
					dojo.stopEvent(e);
13888
					break;
13889
				default:
13890
					if(e.ctrlKey && e.keyCode == k.TAB){
13891
						this._adjacent(e._dijitWidget, !e.shiftKey)._onTitleClick();
13892
						dojo.stopEvent(e);
13893
					}
13894
 
13895
			}
13896
		}
13897
	}
13898
);
13899
 
13900
dojo.declare(
13901
	"dijit.layout.AccordionPane",
13902
	[dijit.layout.ContentPane, dijit._Templated, dijit._Contained],
13903
{
13904
	// summary
13905
	//		AccordionPane is a ContentPane with a title that may contain another widget.
13906
	//		Nested layout widgets, such as SplitContainer, are not supported at this time.
13907
 
13908
	templateString:"<div class='dijitAccordionPane'\n\t><div dojoAttachPoint='titleNode,focusNode' dojoAttachEvent='ondijitclick:_onTitleClick,onkeypress:_onTitleKeyPress,onfocus:_handleFocus,onblur:_handleFocus'\n\t\tclass='dijitAccordionTitle' wairole=\"tab\"\n\t\t><div class='dijitAccordionArrow'></div\n\t\t><div class='arrowTextUp' waiRole=\"presentation\">&#9650;</div\n\t\t><div class='arrowTextDown' waiRole=\"presentation\">&#9660;</div\n\t\t><div dojoAttachPoint='titleTextNode' class='dijitAccordionText'>${title}</div></div\n\t><div><div dojoAttachPoint='containerNode' style='overflow: hidden; height: 1px; display: none'\n\t\tclass='dijitAccordionBody' wairole=\"tabpanel\"\n\t></div></div>\n</div>\n",
13909
 
13910
	postCreate: function(){
13911
		this.inherited("postCreate",arguments)
13912
		dojo.setSelectable(this.titleNode, false);
13913
		this.setSelected(this.selected);
13914
	},
13915
 
13916
	getTitleHeight: function(){
13917
		// summary: returns the height of the title dom node
13918
		return dojo.marginBox(this.titleNode).h;	// Integer
13919
	},
13920
 
13921
	_onTitleClick: function(){
13922
		// summary: callback when someone clicks my title
13923
		var parent = this.getParent();
13924
		if(!parent._inTransition){
13925
			parent.selectChild(this);
13926
			dijit.focus(this.focusNode);
13927
		}
13928
	},
13929
 
13930
	_onTitleKeyPress: function(/*Event*/ evt){
13931
		evt._dijitWidget = this;
13932
		return this.getParent()._onKeyPress(evt);
13933
	},
13934
 
13935
	_setSelectedState: function(/*Boolean*/ isSelected){
13936
		this.selected = isSelected;
13937
		dojo[(isSelected ? "addClass" : "removeClass")](this.domNode,"dijitAccordionPane-selected");
13938
		this.focusNode.setAttribute("tabIndex", isSelected ? "0" : "-1");
13939
	},
13940
 
13941
	_handleFocus: function(/*Event*/e){
13942
		// summary: handle the blur and focus state of this widget
13943
		dojo[(e.type=="focus" ? "addClass" : "removeClass")](this.focusNode,"dijitAccordionPaneFocused");
13944
	},
13945
 
13946
	setSelected: function(/*Boolean*/ isSelected){
13947
		// summary: change the selected state on this pane
13948
		this._setSelectedState(isSelected);
13949
		if(isSelected){ this.onSelected(); }
13950
	},
13951
 
13952
	onSelected: function(){
13953
		// summary: called when this pane is selected
13954
	}
13955
});
13956
 
13957
}
13958
 
13959
if(!dojo._hasResource["dijit.layout.LayoutContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
13960
dojo._hasResource["dijit.layout.LayoutContainer"] = true;
13961
dojo.provide("dijit.layout.LayoutContainer");
13962
 
13963
 
13964
 
13965
dojo.declare(
13966
	"dijit.layout.LayoutContainer",
13967
	dijit.layout._LayoutWidget,
13968
{
13969
	// summary
13970
	//	Provides Delphi-style panel layout semantics.
13971
	//
13972
	// details
13973
	//	A LayoutContainer is a box with a specified size (like style="width: 500px; height: 500px;"),
13974
	//	that contains children widgets marked with "layoutAlign" of "left", "right", "bottom", "top", and "client".
13975
	//	It takes it's children marked as left/top/bottom/right, and lays them out along the edges of the box,
13976
	//	and then it takes the child marked "client" and puts it into the remaining space in the middle.
13977
	//
13978
	//  Left/right positioning is similar to CSS's "float: left" and "float: right",
13979
	//	and top/bottom positioning would be similar to "float: top" and "float: bottom", if there were such
13980
	//	CSS.
13981
	//
13982
	//	Note that there can only be one client element, but there can be multiple left, right, top,
13983
	//	or bottom elements.
13984
	//
13985
	// usage
13986
	//	<style>
13987
	//		html, body{ height: 100%; width: 100%; }
13988
	//	</style>
13989
	//	<div dojoType="dijit.layout.LayoutContainer" style="width: 100%; height: 100%">
13990
	//		<div dojoType="dijit.layout.ContentPane" layoutAlign="top">header text</div>
13991
	//		<div dojoType="dijit.layout.ContentPane" layoutAlign="left" style="width: 200px;">table of contents</div>
13992
	//		<div dojoType="dijit.layout.ContentPane" layoutAlign="client">client area</div>
13993
	//	</div>
13994
	//
13995
	//	Lays out each child in the natural order the children occur in.
13996
	//	Basically each child is laid out into the "remaining space", where "remaining space" is initially
13997
	//	the content area of this widget, but is reduced to a smaller rectangle each time a child is added.
13998
	//
13999
 
14000
	layout: function(){
14001
		dijit.layout.layoutChildren(this.domNode, this._contentBox, this.getChildren());
14002
	},
14003
 
14004
	addChild: function(/*Widget*/ child, /*Integer?*/ insertIndex){
14005
		dijit._Container.prototype.addChild.apply(this, arguments);
14006
		if(this._started){
14007
			dijit.layout.layoutChildren(this.domNode, this._contentBox, this.getChildren());
14008
		}
14009
	},
14010
 
14011
	removeChild: function(/*Widget*/ widget){
14012
		dijit._Container.prototype.removeChild.apply(this, arguments);
14013
		if(this._started){
14014
			dijit.layout.layoutChildren(this.domNode, this._contentBox, this.getChildren());
14015
		}
14016
	}
14017
});
14018
 
14019
// This argument can be specified for the children of a LayoutContainer.
14020
// Since any widget can be specified as a LayoutContainer child, mix it
14021
// into the base widget class.  (This is a hack, but it's effective.)
14022
dojo.extend(dijit._Widget, {
14023
	// layoutAlign: String
14024
	//		"none", "left", "right", "bottom", "top", and "client".
14025
	//		See the LayoutContainer description for details on this parameter.
14026
	layoutAlign: 'none'
14027
});
14028
 
14029
}
14030
 
14031
if(!dojo._hasResource["dijit.layout.LinkPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
14032
dojo._hasResource["dijit.layout.LinkPane"] = true;
14033
dojo.provide("dijit.layout.LinkPane");
14034
 
14035
 
14036
 
14037
 
14038
dojo.declare("dijit.layout.LinkPane",
14039
	[dijit.layout.ContentPane, dijit._Templated],
14040
	{
14041
	// summary:
14042
	//	A ContentPane that loads data remotely
14043
	// description:
14044
	//	LinkPane is just a ContentPane that loads data remotely (via the href attribute),
14045
	//	and has markup similar to an anchor.  The anchor's body (the words between <a> and </a>)
14046
	//	become the title of the widget (used for TabContainer, AccordionContainer, etc.)
14047
	// example:
14048
	//	<a href="foo.html">my title</a>
14049
 
14050
	// I'm using a template because the user may specify the input as
14051
	// <a href="foo.html">title</a>, in which case we need to get rid of the
14052
	// <a> because we don't want a link.
14053
	templateString: '<div class="dijitLinkPane"></div>',
14054
 
14055
	postCreate: function(){
14056
 
14057
		// If user has specified node contents, they become the title
14058
		// (the link must be plain text)
14059
		if(this.srcNodeRef){
14060
			this.title += this.srcNodeRef.innerHTML;
14061
		}
14062
		this.inherited("postCreate",arguments);
14063
	}
14064
});
14065
 
14066
}
14067
 
14068
if(!dojo._hasResource["dijit.layout.SplitContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
14069
dojo._hasResource["dijit.layout.SplitContainer"] = true;
14070
dojo.provide("dijit.layout.SplitContainer");
14071
 
14072
//
14073
// FIXME: make it prettier
14074
// FIXME: active dragging upwards doesn't always shift other bars (direction calculation is wrong in this case)
14075
//
14076
 
14077
 
14078
 
14079
 
14080
dojo.declare("dijit.layout.SplitContainer",
14081
	dijit.layout._LayoutWidget,
14082
	{
14083
	// summary:
14084
	//	A Container widget with sizing handles in-between each child
14085
	// description:
14086
	//		Contains multiple children widgets, all of which are displayed side by side
14087
	//		(either horizontally or vertically); there's a bar between each of the children,
14088
	//		and you can adjust the relative size of each child by dragging the bars.
14089
	//
14090
	//		You must specify a size (width and height) for the SplitContainer.
14091
	//
14092
	// activeSizing: Boolean
14093
	//		If true, the children's size changes as you drag the bar;
14094
	//		otherwise, the sizes don't change until you drop the bar (by mouse-up)
14095
	activeSizing: false,
14096
 
14097
	// sizerWidth: Integer
14098
	//		Size in pixels of the bar between each child
14099
	sizerWidth: 7, // FIXME: this should be a CSS attribute (at 7 because css wants it to be 7 until we fix to css)
14100
 
14101
	// orientation: String
14102
	//		either 'horizontal' or vertical; indicates whether the children are
14103
	//		arranged side-by-side or up/down.
14104
	orientation: 'horizontal',
14105
 
14106
	// persist: Boolean
14107
	//		Save splitter positions in a cookie
14108
	persist: true,
14109
 
14110
	postMixInProperties: function(){
14111
		this.inherited("postMixInProperties",arguments);
14112
		this.isHorizontal = (this.orientation == 'horizontal');
14113
	},
14114
 
14115
	postCreate: function(){
14116
		this.inherited("postCreate",arguments);
14117
		this.sizers = [];
14118
		dojo.addClass(this.domNode, "dijitSplitContainer");
14119
		// overflow has to be explicitly hidden for splitContainers using gekko (trac #1435)
14120
		// to keep other combined css classes from inadvertantly making the overflow visible
14121
		if(dojo.isMozilla){
14122
			this.domNode.style.overflow = '-moz-scrollbars-none'; // hidden doesn't work
14123
		}
14124
 
14125
		// create the fake dragger
14126
		if(typeof this.sizerWidth == "object"){
14127
			try{ //FIXME: do this without a try/catch
14128
				this.sizerWidth = parseInt(this.sizerWidth.toString());
14129
			}catch(e){ this.sizerWidth = 7; }
14130
		}
14131
		var sizer = this.virtualSizer = document.createElement('div');
14132
		sizer.style.position = 'relative';
14133
 
14134
		// #1681: work around the dreaded 'quirky percentages in IE' layout bug
14135
		// If the splitcontainer's dimensions are specified in percentages, it
14136
		// will be resized when the virtualsizer is displayed in _showSizingLine
14137
		// (typically expanding its bounds unnecessarily). This happens because
14138
		// we use position: relative for .dijitSplitContainer.
14139
		// The workaround: instead of changing the display style attribute,
14140
		// switch to changing the zIndex (bring to front/move to back)
14141
 
14142
		sizer.style.zIndex = 10;
14143
		sizer.className = this.isHorizontal ? 'dijitSplitContainerVirtualSizerH' : 'dijitSplitContainerVirtualSizerV';
14144
		this.domNode.appendChild(sizer);
14145
		dojo.setSelectable(sizer, false);
14146
	},
14147
 
14148
	startup: function(){
14149
		if(this._started){ return; }
14150
		dojo.forEach(this.getChildren(), function(child, i, children){
14151
			// attach the children and create the draggers
14152
			this._injectChild(child);
14153
 
14154
			if(i < children.length-1){
14155
				this._addSizer();
14156
			}
14157
		}, this);
14158
 
14159
		if(this.persist){
14160
			this._restoreState();
14161
		}
14162
		this.inherited("startup",arguments);
14163
		this._started = true;
14164
	},
14165
 
14166
	_injectChild: function(child){
14167
		child.domNode.style.position = "absolute";
14168
		dojo.addClass(child.domNode, "dijitSplitPane");
14169
	},
14170
 
14171
	_addSizer: function(){
14172
		var i = this.sizers.length;
14173
 
14174
		// TODO: use a template for this!!!
14175
		var sizer = this.sizers[i] = document.createElement('div');
14176
		sizer.className = this.isHorizontal ? 'dijitSplitContainerSizerH' : 'dijitSplitContainerSizerV';
14177
 
14178
		// add the thumb div
14179
		var thumb = document.createElement('div');
14180
		thumb.className = 'thumb';
14181
		sizer.appendChild(thumb);
14182
 
14183
		// FIXME: are you serious? why aren't we using mover start/stop combo?
14184
		var self = this;
14185
		var handler = (function(){ var sizer_i = i; return function(e){ self.beginSizing(e, sizer_i); } })();
14186
		dojo.connect(sizer, "onmousedown", handler);
14187
 
14188
		this.domNode.appendChild(sizer);
14189
		dojo.setSelectable(sizer, false);
14190
	},
14191
 
14192
	removeChild: function(widget){
14193
		// summary: Remove sizer, but only if widget is really our child and
14194
		// we have at least one sizer to throw away
14195
		if(this.sizers.length && dojo.indexOf(this.getChildren(), widget) != -1){
14196
			var i = this.sizers.length - 1;
14197
			dojo._destroyElement(this.sizers[i]);
14198
			this.sizers.length--;
14199
		}
14200
 
14201
		// Remove widget and repaint
14202
		this.inherited("removeChild",arguments);
14203
		if(this._started){
14204
			this.layout();
14205
		}
14206
	},
14207
 
14208
	addChild: function(/*Widget*/ child, /*Integer?*/ insertIndex){
14209
		// summary: Add a child widget to the container
14210
		// child: a widget to add
14211
		// insertIndex: postion in the "stack" to add the child widget
14212
 
14213
		this.inherited("addChild",arguments);
14214
 
14215
		if(this._started){
14216
			// Do the stuff that startup() does for each widget
14217
			this._injectChild(child);
14218
			var children = this.getChildren();
14219
			if(children.length > 1){
14220
				this._addSizer();
14221
			}
14222
 
14223
			// and then reposition (ie, shrink) every pane to make room for the new guy
14224
			this.layout();
14225
		}
14226
	},
14227
 
14228
	layout: function(){
14229
		// summary:
14230
		//		Do layout of panels
14231
 
14232
		// base class defines this._contentBox on initial creation and also
14233
		// on resize
14234
		this.paneWidth = this._contentBox.w;
14235
		this.paneHeight = this._contentBox.h;
14236
 
14237
		var children = this.getChildren();
14238
		if(!children.length){ return; }
14239
 
14240
		//
14241
		// calculate space
14242
		//
14243
 
14244
		var space = this.isHorizontal ? this.paneWidth : this.paneHeight;
14245
		if(children.length > 1){
14246
			space -= this.sizerWidth * (children.length - 1);
14247
		}
14248
 
14249
		//
14250
		// calculate total of SizeShare values
14251
		//
14252
		var outOf = 0;
14253
		dojo.forEach(children, function(child){
14254
			outOf += child.sizeShare;
14255
		});
14256
 
14257
		//
14258
		// work out actual pixels per sizeshare unit
14259
		//
14260
		var pixPerUnit = space / outOf;
14261
 
14262
		//
14263
		// set the SizeActual member of each pane
14264
		//
14265
		var totalSize = 0;
14266
		dojo.forEach(children.slice(0, children.length - 1), function(child){
14267
			var size = Math.round(pixPerUnit * child.sizeShare);
14268
			child.sizeActual = size;
14269
			totalSize += size;
14270
		});
14271
 
14272
		children[children.length-1].sizeActual = space - totalSize;
14273
 
14274
		//
14275
		// make sure the sizes are ok
14276
		//
14277
		this._checkSizes();
14278
 
14279
		//
14280
		// now loop, positioning each pane and letting children resize themselves
14281
		//
14282
 
14283
		var pos = 0;
14284
		var size = children[0].sizeActual;
14285
		this._movePanel(children[0], pos, size);
14286
		children[0].position = pos;
14287
		pos += size;
14288
 
14289
		// if we don't have any sizers, our layout method hasn't been called yet
14290
		// so bail until we are called..TODO: REVISIT: need to change the startup
14291
		// algorithm to guaranteed the ordering of calls to layout method
14292
		if(!this.sizers){
14293
			return;
14294
		}
14295
 
14296
		dojo.some(children.slice(1), function(child, i){
14297
			// error-checking
14298
			if(!this.sizers[i]){
14299
				return true;
14300
			}
14301
			// first we position the sizing handle before this pane
14302
			this._moveSlider(this.sizers[i], pos, this.sizerWidth);
14303
			this.sizers[i].position = pos;
14304
			pos += this.sizerWidth;
14305
 
14306
			size = child.sizeActual;
14307
			this._movePanel(child, pos, size);
14308
			child.position = pos;
14309
			pos += size;
14310
		}, this);
14311
	},
14312
 
14313
	_movePanel: function(panel, pos, size){
14314
		if(this.isHorizontal){
14315
			panel.domNode.style.left = pos + 'px';	// TODO: resize() takes l and t parameters too, don't need to set manually
14316
			panel.domNode.style.top = 0;
14317
			var box = {w: size, h: this.paneHeight};
14318
			if(panel.resize){
14319
				panel.resize(box);
14320
			}else{
14321
				dojo.marginBox(panel.domNode, box);
14322
			}
14323
		}else{
14324
			panel.domNode.style.left = 0;	// TODO: resize() takes l and t parameters too, don't need to set manually
14325
			panel.domNode.style.top = pos + 'px';
14326
			var box = {w: this.paneWidth, h: size};
14327
			if(panel.resize){
14328
				panel.resize(box);
14329
			}else{
14330
				dojo.marginBox(panel.domNode, box);
14331
			}
14332
		}
14333
	},
14334
 
14335
	_moveSlider: function(slider, pos, size){
14336
		if(this.isHorizontal){
14337
			slider.style.left = pos + 'px';
14338
			slider.style.top = 0;
14339
			dojo.marginBox(slider, { w: size, h: this.paneHeight });
14340
		}else{
14341
			slider.style.left = 0;
14342
			slider.style.top = pos + 'px';
14343
			dojo.marginBox(slider, { w: this.paneWidth, h: size });
14344
		}
14345
	},
14346
 
14347
	_growPane: function(growth, pane){
14348
		if(growth > 0){
14349
			if(pane.sizeActual > pane.sizeMin){
14350
				if((pane.sizeActual - pane.sizeMin) > growth){
14351
 
14352
					// stick all the growth in this pane
14353
					pane.sizeActual = pane.sizeActual - growth;
14354
					growth = 0;
14355
				}else{
14356
					// put as much growth in here as we can
14357
					growth -= pane.sizeActual - pane.sizeMin;
14358
					pane.sizeActual = pane.sizeMin;
14359
				}
14360
			}
14361
		}
14362
		return growth;
14363
	},
14364
 
14365
	_checkSizes: function(){
14366
 
14367
		var totalMinSize = 0;
14368
		var totalSize = 0;
14369
		var children = this.getChildren();
14370
 
14371
		dojo.forEach(children, function(child){
14372
			totalSize += child.sizeActual;
14373
			totalMinSize += child.sizeMin;
14374
		});
14375
 
14376
		// only make adjustments if we have enough space for all the minimums
14377
 
14378
		if(totalMinSize <= totalSize){
14379
 
14380
			var growth = 0;
14381
 
14382
			dojo.forEach(children, function(child){
14383
				if(child.sizeActual < child.sizeMin){
14384
					growth += child.sizeMin - child.sizeActual;
14385
					child.sizeActual = child.sizeMin;
14386
				}
14387
			});
14388
 
14389
			if(growth > 0){
14390
				var list = this.isDraggingLeft ? children.reverse() : children;
14391
				dojo.forEach(list, function(child){
14392
					growth = this._growPane(growth, child);
14393
				}, this);
14394
			}
14395
		}else{
14396
			dojo.forEach(children, function(child){
14397
				child.sizeActual = Math.round(totalSize * (child.sizeMin / totalMinSize));
14398
			});
14399
		}
14400
	},
14401
 
14402
	beginSizing: function(e, i){
14403
		var children = this.getChildren();
14404
		this.paneBefore = children[i];
14405
		this.paneAfter = children[i+1];
14406
 
14407
		this.isSizing = true;
14408
		this.sizingSplitter = this.sizers[i];
14409
 
14410
		if(!this.cover){
14411
			this.cover = dojo.doc.createElement('div');
14412
			this.domNode.appendChild(this.cover);
14413
			var s = this.cover.style;
14414
			s.position = 'absolute';
14415
			s.zIndex = 1;
14416
			s.top = 0;
14417
			s.left = 0;
14418
			s.width = "100%";
14419
			s.height = "100%";
14420
		}else{
14421
			this.cover.style.zIndex = 1;
14422
		}
14423
		this.sizingSplitter.style.zIndex = 2;
14424
 
14425
		// TODO: REVISIT - we want MARGIN_BOX and core hasn't exposed that yet (but can't we use it anyway if we pay attention? we do elsewhere.)
14426
		this.originPos = dojo.coords(children[0].domNode, true);
14427
		if(this.isHorizontal){
14428
			var client = (e.layerX ? e.layerX : e.offsetX);
14429
			var screen = e.pageX;
14430
			this.originPos = this.originPos.x;
14431
		}else{
14432
			var client = (e.layerY ? e.layerY : e.offsetY);
14433
			var screen = e.pageY;
14434
			this.originPos = this.originPos.y;
14435
		}
14436
		this.startPoint = this.lastPoint = screen;
14437
		this.screenToClientOffset = screen - client;
14438
		this.dragOffset = this.lastPoint - this.paneBefore.sizeActual - this.originPos - this.paneBefore.position;
14439
 
14440
		if(!this.activeSizing){
14441
			this._showSizingLine();
14442
		}
14443
 
14444
		//
14445
		// attach mouse events
14446
		//
14447
		this._connects = [];
14448
		this._connects.push(dojo.connect(document.documentElement, "onmousemove", this, "changeSizing"));
14449
		this._connects.push(dojo.connect(document.documentElement, "onmouseup", this, "endSizing"));
14450
 
14451
		dojo.stopEvent(e);
14452
	},
14453
 
14454
	changeSizing: function(e){
14455
		if(!this.isSizing){ return; }
14456
		this.lastPoint = this.isHorizontal ? e.pageX : e.pageY;
14457
		this.movePoint();
14458
		if(this.activeSizing){
14459
			this._updateSize();
14460
		}else{
14461
			this._moveSizingLine();
14462
		}
14463
		dojo.stopEvent(e);
14464
	},
14465
 
14466
	endSizing: function(e){
14467
		if(!this.isSizing){ return; }
14468
		if(this.cover){
14469
			this.cover.style.zIndex = -1;
14470
		}
14471
		if(!this.activeSizing){
14472
			this._hideSizingLine();
14473
		}
14474
 
14475
		this._updateSize();
14476
 
14477
		this.isSizing = false;
14478
 
14479
		if(this.persist){
14480
			this._saveState(this);
14481
		}
14482
 
14483
		dojo.forEach(this._connects,dojo.disconnect);
14484
	},
14485
 
14486
	movePoint: function(){
14487
 
14488
		// make sure lastPoint is a legal point to drag to
14489
		var p = this.lastPoint - this.screenToClientOffset;
14490
 
14491
		var a = p - this.dragOffset;
14492
		a = this.legaliseSplitPoint(a);
14493
		p = a + this.dragOffset;
14494
 
14495
		this.lastPoint = p + this.screenToClientOffset;
14496
	},
14497
 
14498
	legaliseSplitPoint: function(a){
14499
 
14500
		a += this.sizingSplitter.position;
14501
 
14502
		this.isDraggingLeft = !!(a > 0);
14503
 
14504
		if(!this.activeSizing){
14505
			var min = this.paneBefore.position + this.paneBefore.sizeMin;
14506
			if(a < min){
14507
				a = min;
14508
			}
14509
 
14510
			var max = this.paneAfter.position + (this.paneAfter.sizeActual - (this.sizerWidth + this.paneAfter.sizeMin));
14511
			if(a > max){
14512
				a = max;
14513
			}
14514
		}
14515
 
14516
		a -= this.sizingSplitter.position;
14517
 
14518
		this._checkSizes();
14519
 
14520
		return a;
14521
	},
14522
 
14523
	_updateSize: function(){
14524
	//FIXME: sometimes this.lastPoint is NaN
14525
		var pos = this.lastPoint - this.dragOffset - this.originPos;
14526
 
14527
		var start_region = this.paneBefore.position;
14528
		var end_region   = this.paneAfter.position + this.paneAfter.sizeActual;
14529
 
14530
		this.paneBefore.sizeActual = pos - start_region;
14531
		this.paneAfter.position	= pos + this.sizerWidth;
14532
		this.paneAfter.sizeActual  = end_region - this.paneAfter.position;
14533
 
14534
		dojo.forEach(this.getChildren(), function(child){
14535
			child.sizeShare = child.sizeActual;
14536
		});
14537
 
14538
		if(this._started){
14539
			this.layout();
14540
		}
14541
	},
14542
 
14543
	_showSizingLine: function(){
14544
 
14545
		this._moveSizingLine();
14546
 
14547
		dojo.marginBox(this.virtualSizer,
14548
			this.isHorizontal ? { w: this.sizerWidth, h: this.paneHeight } : { w: this.paneWidth, h: this.sizerWidth });
14549
 
14550
		this.virtualSizer.style.display = 'block';
14551
	},
14552
 
14553
	_hideSizingLine: function(){
14554
		this.virtualSizer.style.display = 'none';
14555
	},
14556
 
14557
	_moveSizingLine: function(){
14558
		var pos = (this.lastPoint - this.startPoint) + this.sizingSplitter.position;
14559
		dojo.style(this.virtualSizer,(this.isHorizontal ? "left" : "top"),pos+"px");
14560
		// this.virtualSizer.style[ this.isHorizontal ? "left" : "top" ] = pos + 'px'; // FIXME: remove this line if the previous is better
14561
	},
14562
 
14563
	_getCookieName: function(i){
14564
		return this.id + "_" + i;
14565
	},
14566
 
14567
	_restoreState: function(){
14568
		dojo.forEach(this.getChildren(), function(child, i){
14569
			var cookieName = this._getCookieName(i);
14570
			var cookieValue = dojo.cookie(cookieName);
14571
			if(cookieValue){
14572
				var pos = parseInt(cookieValue);
14573
				if(typeof pos == "number"){
14574
					child.sizeShare = pos;
14575
				}
14576
			}
14577
		}, this);
14578
	},
14579
 
14580
	_saveState: function(){
14581
		dojo.forEach(this.getChildren(), function(child, i){
14582
			dojo.cookie(this._getCookieName(i), child.sizeShare);
14583
		}, this);
14584
	}
14585
});
14586
 
14587
// These arguments can be specified for the children of a SplitContainer.
14588
// Since any widget can be specified as a SplitContainer child, mix them
14589
// into the base widget class.  (This is a hack, but it's effective.)
14590
dojo.extend(dijit._Widget, {
14591
	// sizeMin: Integer
14592
	//	Minimum size (width or height) of a child of a SplitContainer.
14593
	//	The value is relative to other children's sizeShare properties.
14594
	sizeMin: 10,
14595
 
14596
	// sizeShare: Integer
14597
	//	Size (width or height) of a child of a SplitContainer.
14598
	//	The value is relative to other children's sizeShare properties.
14599
	//	For example, if there are two children and each has sizeShare=10, then
14600
	//	each takes up 50% of the available space.
14601
	sizeShare: 10
14602
});
14603
 
14604
}
14605
 
14606
if(!dojo._hasResource["dijit.layout.TabContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
14607
dojo._hasResource["dijit.layout.TabContainer"] = true;
14608
dojo.provide("dijit.layout.TabContainer");
14609
 
14610
 
14611
 
14612
 
14613
dojo.declare("dijit.layout.TabContainer",
14614
	[dijit.layout.StackContainer, dijit._Templated],
14615
	{
14616
	// summary:
14617
	//	A Container with Title Tabs, each one pointing at a pane in the container.
14618
	// description:
14619
	//	A TabContainer is a container that has multiple panes, but shows only
14620
	//	one pane at a time.  There are a set of tabs corresponding to each pane,
14621
	//	where each tab has the title (aka title) of the pane, and optionally a close button.
14622
	//
14623
	//	Publishes topics <widgetId>-addChild, <widgetId>-removeChild, and <widgetId>-selectChild
14624
	//	(where <widgetId> is the id of the TabContainer itself.
14625
	//
14626
	// tabPosition: String
14627
	//   Defines where tabs go relative to tab content.
14628
	//   "top", "bottom", "left-h", "right-h"
14629
	tabPosition: "top",
14630
 
14631
	templateString: null,	// override setting in StackContainer
14632
	templateString:"<div class=\"dijitTabContainer\">\n\t<div dojoAttachPoint=\"tablistNode\"></div>\n\t<div class=\"dijitTabPaneWrapper\" dojoAttachPoint=\"containerNode\"></div>\n</div>\n",
14633
 
14634
	postCreate: function(){
14635
		dijit.layout.TabContainer.superclass.postCreate.apply(this, arguments);
14636
		// create the tab list that will have a tab (a.k.a. tab button) for each tab panel
14637
		this.tablist = new dijit.layout.TabController(
14638
			{
14639
				id: this.id + "_tablist",
14640
				tabPosition: this.tabPosition,
14641
				doLayout: this.doLayout,
14642
				containerId: this.id
14643
			}, this.tablistNode);
14644
	},
14645
 
14646
	_setupChild: function(/* Widget */tab){
14647
		dojo.addClass(tab.domNode, "dijitTabPane");
14648
		this.inherited("_setupChild",arguments);
14649
		return tab; // Widget
14650
	},
14651
 
14652
	startup: function(){
14653
		if(this._started){ return; }
14654
 
14655
		// wire up the tablist and its tabs
14656
		this.tablist.startup();
14657
		this.inherited("startup",arguments);
14658
 
14659
		if(dojo.isSafari){
14660
			// sometimes safari 3.0.3 miscalculates the height of the tab labels, see #4058
14661
			setTimeout(dojo.hitch(this, "layout"), 0);
14662
		}
14663
	},
14664
 
14665
	layout: function(){
14666
		// Summary: Configure the content pane to take up all the space except for where the tabs are
14667
		if(!this.doLayout){ return; }
14668
 
14669
		// position and size the titles and the container node
14670
		var titleAlign=this.tabPosition.replace(/-h/,"");
14671
		var children = [
14672
			{domNode: this.tablist.domNode, layoutAlign: titleAlign},
14673
			{domNode: this.containerNode, layoutAlign: "client"}
14674
		];
14675
		dijit.layout.layoutChildren(this.domNode, this._contentBox, children);
14676
 
14677
		// Compute size to make each of my children.
14678
		// children[1] is the margin-box size of this.containerNode, set by layoutChildren() call above
14679
		this._containerContentBox = dijit.layout.marginBox2contentBox(this.containerNode, children[1]);
14680
 
14681
		if(this.selectedChildWidget){
14682
			this._showChild(this.selectedChildWidget);
14683
			if(this.doLayout && this.selectedChildWidget.resize){
14684
				this.selectedChildWidget.resize(this._containerContentBox);
14685
			}
14686
		}
14687
	},
14688
 
14689
	destroy: function(){
14690
		this.tablist.destroy();
14691
		this.inherited("destroy",arguments);
14692
	}
14693
});
14694
 
14695
//TODO: make private?
14696
dojo.declare("dijit.layout.TabController",
14697
	dijit.layout.StackController,
14698
	{
14699
	// summary:
14700
	// 	Set of tabs (the things with titles and a close button, that you click to show a tab panel).
14701
	// description:
14702
	//	Lets the user select the currently shown pane in a TabContainer or StackContainer.
14703
	//	TabController also monitors the TabContainer, and whenever a pane is
14704
	//	added or deleted updates itself accordingly.
14705
 
14706
	templateString: "<div wairole='tablist' dojoAttachEvent='onkeypress:onkeypress'></div>",
14707
 
14708
	// tabPosition: String
14709
	//   Defines where tabs go relative to the content.
14710
	//   "top", "bottom", "left-h", "right-h"
14711
	tabPosition: "top",
14712
 
14713
	// doLayout: Boolean
14714
	// 	TODOC: deprecate doLayout? not sure.
14715
	doLayout: true,
14716
 
14717
	// buttonWidget: String
14718
	//	the name of the tab widget to create to correspond to each page
14719
	buttonWidget: "dijit.layout._TabButton",
14720
 
14721
	postMixInProperties: function(){
14722
		this["class"] = "dijitTabLabels-" + this.tabPosition + (this.doLayout ? "" : " dijitTabNoLayout");
14723
		this.inherited("postMixInProperties",arguments);
14724
	}
14725
});
14726
 
14727
dojo.declare("dijit.layout._TabButton",
14728
	dijit.layout._StackButton,
14729
	{
14730
	// summary:
14731
	//	A tab (the thing you click to select a pane).
14732
	// description:
14733
	//	Contains the title of the pane, and optionally a close-button to destroy the pane.
14734
	//	This is an internal widget and should not be instantiated directly.
14735
 
14736
	baseClass: "dijitTab",
14737
 
14738
	templateString:"<div dojoAttachEvent='onclick:onClick,onmouseenter:_onMouse,onmouseleave:_onMouse'>\n    <div class='dijitTabInnerDiv' dojoAttachPoint='innerDiv'>\n        <span dojoAttachPoint='containerNode,focusNode'>${!label}</span>\n        <span dojoAttachPoint='closeButtonNode' class='closeImage' dojoAttachEvent='onmouseenter:_onMouse, onmouseleave:_onMouse, onclick:onClickCloseButton' stateModifier='CloseButton'>\n            <span dojoAttachPoint='closeText' class='closeText'>x</span>\n        </span>\n    </div>\n</div>\n",
14739
 
14740
	postCreate: function(){
14741
		if(this.closeButton){
14742
			dojo.addClass(this.innerDiv, "dijitClosable");
14743
		} else {
14744
			this.closeButtonNode.style.display="none";
14745
		}
14746
		this.inherited("postCreate",arguments);
14747
		dojo.setSelectable(this.containerNode, false);
14748
	}
14749
});
14750
 
14751
}
14752
 
14753
if(!dojo._hasResource["dijit.dijit-all"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
14754
dojo._hasResource["dijit.dijit-all"] = true;
14755
console.warn("dijit-all may include much more code than your application actually requires. We strongly recommend that you investigate a custom build or the web build tool");
14756
dojo.provide("dijit.dijit-all");
14757
 
14758
 
14759
 
14760
 
14761
 
14762
 
14763
 
14764
 
14765
 
14766
 
14767
 
14768
 
14769
 
14770
 
14771
 
14772
 
14773
 
14774
 
14775
 
14776
 
14777
 
14778
 
14779
 
14780
 
14781
 
14782
 
14783
 
14784
 
14785
 
14786
 
14787
 
14788
 
14789
 
14790
 
14791
 
14792
 
14793
 
14794
 
14795
}
14796
 
14797
 
14798
dojo.i18n._preloadLocalizations("dijit.nls.dijit-all", ["es-es", "es", "hu", "it-it", "de", "pt-br", "pl", "fr-fr", "zh-cn", "pt", "en-us", "zh", "ru", "xx", "fr", "zh-tw", "it", "cs", "en-gb", "de-de", "ja-jp", "ko-kr", "ko", "en", "ROOT", "ja"]);