]> git.pond.sub.org Git - eow/blob - static/dojo-release-1.1.1/dijit/dijit-all.js.uncompressed.js
Comment class stub
[eow] / static / dojo-release-1.1.1 / dijit / dijit-all.js.uncompressed.js
1 /*
2         Copyright (c) 2004-2008, 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 //TODO: this module appears to break naming conventions
25
26 /*=====
27 dojo.colors = {
28         // summary: Color utilities
29 }
30 =====*/
31
32 (function(){
33         // this is a standard conversion prescribed by the CSS3 Color Module
34         var hue2rgb = function(m1, m2, h){
35                 if(h < 0){ ++h; }
36                 if(h > 1){ --h; }
37                 var h6 = 6 * h;
38                 if(h6 < 1){ return m1 + (m2 - m1) * h6; }
39                 if(2 * h < 1){ return m2; }
40                 if(3 * h < 2){ return m1 + (m2 - m1) * (2 / 3 - h) * 6; }
41                 return m1;
42         };
43         
44         dojo.colorFromRgb = function(/*String*/ color, /*dojo.Color?*/ obj){
45                 // summary:
46                 //              get rgb(a) array from css-style color declarations
47                 // description:
48                 //              this function can handle all 4 CSS3 Color Module formats: rgb,
49                 //              rgba, hsl, hsla, including rgb(a) with percentage values.
50                 var m = color.toLowerCase().match(/^(rgba?|hsla?)\(([\s\.\-,%0-9]+)\)/);
51                 if(m){
52                         var c = m[2].split(/\s*,\s*/), l = c.length, t = m[1];
53                         if((t == "rgb" && l == 3) || (t == "rgba" && l == 4)){
54                                 var r = c[0];
55                                 if(r.charAt(r.length - 1) == "%"){
56                                         // 3 rgb percentage values
57                                         var a = dojo.map(c, function(x){
58                                                 return parseFloat(x) * 2.56;
59                                         });
60                                         if(l == 4){ a[3] = c[3]; }
61                                         return dojo.colorFromArray(a, obj);     // dojo.Color
62                                 }
63                                 return dojo.colorFromArray(c, obj);     // dojo.Color
64                         }
65                         if((t == "hsl" && l == 3) || (t == "hsla" && l == 4)){
66                                 // normalize hsl values
67                                 var H = ((parseFloat(c[0]) % 360) + 360) % 360 / 360,
68                                         S = parseFloat(c[1]) / 100,
69                                         L = parseFloat(c[2]) / 100,
70                                         // calculate rgb according to the algorithm 
71                                         // recommended by the CSS3 Color Module 
72                                         m2 = L <= 0.5 ? L * (S + 1) : L + S - L * S, 
73                                         m1 = 2 * L - m2,
74                                         a = [hue2rgb(m1, m2, H + 1 / 3) * 256,
75                                                 hue2rgb(m1, m2, H) * 256, hue2rgb(m1, m2, H - 1 / 3) * 256, 1];
76                                 if(l == 4){ a[3] = c[3]; }
77                                 return dojo.colorFromArray(a, obj);     // dojo.Color
78                         }
79                 }
80                 return null;    // dojo.Color
81         };
82         
83         var confine = function(c, low, high){
84                 // summary:
85                 //              sanitize a color component by making sure it is a number,
86                 //              and clamping it to valid values
87                 c = Number(c);
88                 return isNaN(c) ? high : c < low ? low : c > high ? high : c;   // Number
89         };
90         
91         dojo.Color.prototype.sanitize = function(){
92                 // summary: makes sure that the object has correct attributes
93                 var t = this;
94                 t.r = Math.round(confine(t.r, 0, 255));
95                 t.g = Math.round(confine(t.g, 0, 255));
96                 t.b = Math.round(confine(t.b, 0, 255));
97                 t.a = confine(t.a, 0, 1);
98                 return this;    // dojo.Color
99         };
100 })();
101
102
103 dojo.colors.makeGrey = function(/*Number*/ g, /*Number?*/ a){
104         // summary: creates a greyscale color with an optional alpha
105         return dojo.colorFromArray([g, g, g, a]);
106 };
107
108 // mixin all CSS3 named colors not already in _base, along with SVG 1.0 variant spellings
109 dojo.Color.named = dojo.mixin({
110         aliceblue:      [240,248,255],
111         antiquewhite:   [250,235,215],
112         aquamarine:     [127,255,212],
113         azure:  [240,255,255],
114         beige:  [245,245,220],
115         bisque: [255,228,196],
116         blanchedalmond: [255,235,205],
117         blueviolet:     [138,43,226],
118         brown:  [165,42,42],
119         burlywood:      [222,184,135],
120         cadetblue:      [95,158,160],
121         chartreuse:     [127,255,0],
122         chocolate:      [210,105,30],
123         coral:  [255,127,80],
124         cornflowerblue: [100,149,237],
125         cornsilk:       [255,248,220],
126         crimson:        [220,20,60],
127         cyan:   [0,255,255],
128         darkblue:       [0,0,139],
129         darkcyan:       [0,139,139],
130         darkgoldenrod:  [184,134,11],
131         darkgray:       [169,169,169],
132         darkgreen:      [0,100,0],
133         darkgrey:       [169,169,169],
134         darkkhaki:      [189,183,107],
135         darkmagenta:    [139,0,139],
136         darkolivegreen: [85,107,47],
137         darkorange:     [255,140,0],
138         darkorchid:     [153,50,204],
139         darkred:        [139,0,0],
140         darksalmon:     [233,150,122],
141         darkseagreen:   [143,188,143],
142         darkslateblue:  [72,61,139],
143         darkslategray:  [47,79,79],
144         darkslategrey:  [47,79,79],
145         darkturquoise:  [0,206,209],
146         darkviolet:     [148,0,211],
147         deeppink:       [255,20,147],
148         deepskyblue:    [0,191,255],
149         dimgray:        [105,105,105],
150         dimgrey:        [105,105,105],
151         dodgerblue:     [30,144,255],
152         firebrick:      [178,34,34],
153         floralwhite:    [255,250,240],
154         forestgreen:    [34,139,34],
155         gainsboro:      [220,220,220],
156         ghostwhite:     [248,248,255],
157         gold:   [255,215,0],
158         goldenrod:      [218,165,32],
159         greenyellow:    [173,255,47],
160         grey:   [128,128,128],
161         honeydew:       [240,255,240],
162         hotpink:        [255,105,180],
163         indianred:      [205,92,92],
164         indigo: [75,0,130],
165         ivory:  [255,255,240],
166         khaki:  [240,230,140],
167         lavender:       [230,230,250],
168         lavenderblush:  [255,240,245],
169         lawngreen:      [124,252,0],
170         lemonchiffon:   [255,250,205],
171         lightblue:      [173,216,230],
172         lightcoral:     [240,128,128],
173         lightcyan:      [224,255,255],
174         lightgoldenrodyellow:   [250,250,210],
175         lightgray:      [211,211,211],
176         lightgreen:     [144,238,144],
177         lightgrey:      [211,211,211],
178         lightpink:      [255,182,193],
179         lightsalmon:    [255,160,122],
180         lightseagreen:  [32,178,170],
181         lightskyblue:   [135,206,250],
182         lightslategray: [119,136,153],
183         lightslategrey: [119,136,153],
184         lightsteelblue: [176,196,222],
185         lightyellow:    [255,255,224],
186         limegreen:      [50,205,50],
187         linen:  [250,240,230],
188         magenta:        [255,0,255],
189         mediumaquamarine:       [102,205,170],
190         mediumblue:     [0,0,205],
191         mediumorchid:   [186,85,211],
192         mediumpurple:   [147,112,219],
193         mediumseagreen: [60,179,113],
194         mediumslateblue:        [123,104,238],
195         mediumspringgreen:      [0,250,154],
196         mediumturquoise:        [72,209,204],
197         mediumvioletred:        [199,21,133],
198         midnightblue:   [25,25,112],
199         mintcream:      [245,255,250],
200         mistyrose:      [255,228,225],
201         moccasin:       [255,228,181],
202         navajowhite:    [255,222,173],
203         oldlace:        [253,245,230],
204         olivedrab:      [107,142,35],
205         orange: [255,165,0],
206         orangered:      [255,69,0],
207         orchid: [218,112,214],
208         palegoldenrod:  [238,232,170],
209         palegreen:      [152,251,152],
210         paleturquoise:  [175,238,238],
211         palevioletred:  [219,112,147],
212         papayawhip:     [255,239,213],
213         peachpuff:      [255,218,185],
214         peru:   [205,133,63],
215         pink:   [255,192,203],
216         plum:   [221,160,221],
217         powderblue:     [176,224,230],
218         rosybrown:      [188,143,143],
219         royalblue:      [65,105,225],
220         saddlebrown:    [139,69,19],
221         salmon: [250,128,114],
222         sandybrown:     [244,164,96],
223         seagreen:       [46,139,87],
224         seashell:       [255,245,238],
225         sienna: [160,82,45],
226         skyblue:        [135,206,235],
227         slateblue:      [106,90,205],
228         slategray:      [112,128,144],
229         slategrey:      [112,128,144],
230         snow:   [255,250,250],
231         springgreen:    [0,255,127],
232         steelblue:      [70,130,180],
233         tan:    [210,180,140],
234         thistle:        [216,191,216],
235         tomato: [255,99,71],
236         transparent: [0, 0, 0, 0],
237         turquoise:      [64,224,208],
238         violet: [238,130,238],
239         wheat:  [245,222,179],
240         whitesmoke:     [245,245,245],
241         yellowgreen:    [154,205,50]
242 }, dojo.Color.named);
243
244 }
245
246 if(!dojo._hasResource["dojo.i18n"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
247 dojo._hasResource["dojo.i18n"] = true;
248 dojo.provide("dojo.i18n");
249
250 /*=====
251 dojo.i18n = {
252         // summary: Utility classes to enable loading of resources for internationalization (i18n)
253 };
254 =====*/
255
256 dojo.i18n.getLocalization = function(/*String*/packageName, /*String*/bundleName, /*String?*/locale){
257         //      summary:
258         //              Returns an Object containing the localization for a given resource
259         //              bundle in a package, matching the specified locale.
260         //      description:
261         //              Returns a hash containing name/value pairs in its prototypesuch
262         //              that values can be easily overridden.  Throws an exception if the
263         //              bundle is not found.  Bundle must have already been loaded by
264         //              `dojo.requireLocalization()` or by a build optimization step.  NOTE:
265         //              try not to call this method as part of an object property
266         //              definition (`var foo = { bar: dojo.i18n.getLocalization() }`).  In
267         //              some loading situations, the bundle may not be available in time
268         //              for the object definition.  Instead, call this method inside a
269         //              function that is run after all modules load or the page loads (like
270         //              in `dojo.addOnLoad()`), or in a widget lifecycle method.
271         //      packageName:
272         //              package which is associated with this resource
273         //      bundleName:
274         //              the base filename of the resource bundle (without the ".js" suffix)
275         //      locale:
276         //              the variant to load (optional).  By default, the locale defined by
277         //              the host environment: dojo.locale
278
279         locale = dojo.i18n.normalizeLocale(locale);
280
281         // look for nearest locale match
282         var elements = locale.split('-');
283         var module = [packageName,"nls",bundleName].join('.');
284         var bundle = dojo._loadedModules[module];
285         if(bundle){
286                 var localization;
287                 for(var i = elements.length; i > 0; i--){
288                         var loc = elements.slice(0, i).join('_');
289                         if(bundle[loc]){
290                                 localization = bundle[loc];
291                                 break;
292                         }
293                 }
294                 if(!localization){
295                         localization = bundle.ROOT;
296                 }
297
298                 // make a singleton prototype so that the caller won't accidentally change the values globally
299                 if(localization){
300                         var clazz = function(){};
301                         clazz.prototype = localization;
302                         return new clazz(); // Object
303                 }
304         }
305
306         throw new Error("Bundle not found: " + bundleName + " in " + packageName+" , locale=" + locale);
307 };
308
309 dojo.i18n.normalizeLocale = function(/*String?*/locale){
310         //      summary:
311         //              Returns canonical form of locale, as used by Dojo.
312         //
313         //  description:
314         //              All variants are case-insensitive and are separated by '-' as specified in [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt).
315         //              If no locale is specified, the dojo.locale is returned.  dojo.locale is defined by
316         //              the user agent's locale unless overridden by djConfig.
317
318         var result = locale ? locale.toLowerCase() : dojo.locale;
319         if(result == "root"){
320                 result = "ROOT";
321         }
322         return result; // String
323 };
324
325 dojo.i18n._requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String?*/availableFlatLocales){
326         //      summary:
327         //              See dojo.requireLocalization()
328         //      description:
329         //              Called by the bootstrap, but factored out so that it is only
330         //              included in the build when needed.
331
332         var targetLocale = dojo.i18n.normalizeLocale(locale);
333         var bundlePackage = [moduleName, "nls", bundleName].join(".");
334         // NOTE: 
335         //              When loading these resources, the packaging does not match what is
336         //              on disk.  This is an implementation detail, as this is just a
337         //              private data structure to hold the loaded resources.  e.g.
338         //              `tests/hello/nls/en-us/salutations.js` is loaded as the object
339         //              `tests.hello.nls.salutations.en_us={...}` The structure on disk is
340         //              intended to be most convenient for developers and translators, but
341         //              in memory it is more logical and efficient to store in a different
342         //              order.  Locales cannot use dashes, since the resulting path will
343         //              not evaluate as valid JS, so we translate them to underscores.
344         
345         //Find the best-match locale to load if we have available flat locales.
346         var bestLocale = "";
347         if(availableFlatLocales){
348                 var flatLocales = availableFlatLocales.split(",");
349                 for(var i = 0; i < flatLocales.length; i++){
350                         //Locale must match from start of string.
351                         if(targetLocale.indexOf(flatLocales[i]) == 0){
352                                 if(flatLocales[i].length > bestLocale.length){
353                                         bestLocale = flatLocales[i];
354                                 }
355                         }
356                 }
357                 if(!bestLocale){
358                         bestLocale = "ROOT";
359                 }               
360         }
361
362         //See if the desired locale is already loaded.
363         var tempLocale = availableFlatLocales ? bestLocale : targetLocale;
364         var bundle = dojo._loadedModules[bundlePackage];
365         var localizedBundle = null;
366         if(bundle){
367                 if(dojo.config.localizationComplete && bundle._built){return;}
368                 var jsLoc = tempLocale.replace(/-/g, '_');
369                 var translationPackage = bundlePackage+"."+jsLoc;
370                 localizedBundle = dojo._loadedModules[translationPackage];
371         }
372
373         if(!localizedBundle){
374                 bundle = dojo["provide"](bundlePackage);
375                 var syms = dojo._getModuleSymbols(moduleName);
376                 var modpath = syms.concat("nls").join("/");
377                 var parent;
378
379                 dojo.i18n._searchLocalePath(tempLocale, availableFlatLocales, function(loc){
380                         var jsLoc = loc.replace(/-/g, '_');
381                         var translationPackage = bundlePackage + "." + jsLoc;
382                         var loaded = false;
383                         if(!dojo._loadedModules[translationPackage]){
384                                 // Mark loaded whether it's found or not, so that further load attempts will not be made
385                                 dojo["provide"](translationPackage);
386                                 var module = [modpath];
387                                 if(loc != "ROOT"){module.push(loc);}
388                                 module.push(bundleName);
389                                 var filespec = module.join("/") + '.js';
390                                 loaded = dojo._loadPath(filespec, null, function(hash){
391                                         // Use singleton with prototype to point to parent bundle, then mix-in result from loadPath
392                                         var clazz = function(){};
393                                         clazz.prototype = parent;
394                                         bundle[jsLoc] = new clazz();
395                                         for(var j in hash){ bundle[jsLoc][j] = hash[j]; }
396                                 });
397                         }else{
398                                 loaded = true;
399                         }
400                         if(loaded && bundle[jsLoc]){
401                                 parent = bundle[jsLoc];
402                         }else{
403                                 bundle[jsLoc] = parent;
404                         }
405                         
406                         if(availableFlatLocales){
407                                 //Stop the locale path searching if we know the availableFlatLocales, since
408                                 //the first call to this function will load the only bundle that is needed.
409                                 return true;
410                         }
411                 });
412         }
413
414         //Save the best locale bundle as the target locale bundle when we know the
415         //the available bundles.
416         if(availableFlatLocales && targetLocale != bestLocale){
417                 bundle[targetLocale.replace(/-/g, '_')] = bundle[bestLocale.replace(/-/g, '_')];
418         }
419 };
420
421 (function(){
422         // If other locales are used, dojo.requireLocalization should load them as
423         // well, by default. 
424         // 
425         // Override dojo.requireLocalization to do load the default bundle, then
426         // iterate through the extraLocale list and load those translations as
427         // well, unless a particular locale was requested.
428
429         var extra = dojo.config.extraLocale;
430         if(extra){
431                 if(!extra instanceof Array){
432                         extra = [extra];
433                 }
434
435                 var req = dojo.i18n._requireLocalization;
436                 dojo.i18n._requireLocalization = function(m, b, locale, availableFlatLocales){
437                         req(m,b,locale, availableFlatLocales);
438                         if(locale){return;}
439                         for(var i=0; i<extra.length; i++){
440                                 req(m,b,extra[i], availableFlatLocales);
441                         }
442                 };
443         }
444 })();
445
446 dojo.i18n._searchLocalePath = function(/*String*/locale, /*Boolean*/down, /*Function*/searchFunc){
447         //      summary:
448         //              A helper method to assist in searching for locale-based resources.
449         //              Will iterate through the variants of a particular locale, either up
450         //              or down, executing a callback function.  For example, "en-us" and
451         //              true will try "en-us" followed by "en" and finally "ROOT".
452
453         locale = dojo.i18n.normalizeLocale(locale);
454
455         var elements = locale.split('-');
456         var searchlist = [];
457         for(var i = elements.length; i > 0; i--){
458                 searchlist.push(elements.slice(0, i).join('-'));
459         }
460         searchlist.push(false);
461         if(down){searchlist.reverse();}
462
463         for(var j = searchlist.length - 1; j >= 0; j--){
464                 var loc = searchlist[j] || "ROOT";
465                 var stop = searchFunc(loc);
466                 if(stop){ break; }
467         }
468 };
469
470 dojo.i18n._preloadLocalizations = function(/*String*/bundlePrefix, /*Array*/localesGenerated){
471         //      summary:
472         //              Load built, flattened resource bundles, if available for all
473         //              locales used in the page. Only called by built layer files.
474
475         function preload(locale){
476                 locale = dojo.i18n.normalizeLocale(locale);
477                 dojo.i18n._searchLocalePath(locale, true, function(loc){
478                         for(var i=0; i<localesGenerated.length;i++){
479                                 if(localesGenerated[i] == loc){
480                                         dojo["require"](bundlePrefix+"_"+loc);
481                                         return true; // Boolean
482                                 }
483                         }
484                         return false; // Boolean
485                 });
486         }
487         preload();
488         var extra = dojo.config.extraLocale||[];
489         for(var i=0; i<extra.length; i++){
490                 preload(extra[i]);
491         }
492 };
493
494 }
495
496 if(!dojo._hasResource["dijit.ColorPalette"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
497 dojo._hasResource["dijit.ColorPalette"] = true;
498 dojo.provide("dijit.ColorPalette");
499
500
501
502
503
504
505
506 dojo.declare("dijit.ColorPalette",
507         [dijit._Widget, dijit._Templated],
508         {
509         // summary: A keyboard accessible color-picking widget
510         // description:
511         //      Grid showing various colors, so the user can pick a certain color
512         //      Can be used standalone, or as a popup.
513         //
514         // example:
515         // |    <div dojoType="dijit.ColorPalette"></div>
516         //
517         // example:
518         // |    var picker = new dijit.ColorPalette({ },srcNode);
519         // |    picker.startup();
520         //
521         // defaultTimeout: Number
522         //              number of milliseconds before a held key or button becomes typematic
523         defaultTimeout: 500,
524
525         // timeoutChangeRate: Number
526         //              fraction of time used to change the typematic timer between events
527         //              1.0 means that each typematic event fires at defaultTimeout intervals
528         //              < 1.0 means that each typematic event fires at an increasing faster rate
529         timeoutChangeRate: 0.90,
530
531         // palette: String
532         //              Size of grid, either "7x10" or "3x4".
533         palette: "7x10",
534
535         //_value: String
536         //              The value of the selected color.
537         value: null,
538
539         //_currentFocus: Integer
540         //              Index of the currently focused color.
541         _currentFocus: 0,
542
543         // _xDim: Integer
544         //              This is the number of colors horizontally across.
545         _xDim: null,
546
547         // _yDim: Integer
548         ///             This is the number of colors vertically down.
549         _yDim: null,
550
551         // _palettes: Map
552         //              This represents the value of the colors.
553         //              The first level is a hashmap of the different arrays available
554         //              The next two dimensions represent the columns and rows of colors.
555         _palettes: {
556
557                 "7x10": [["white", "seashell", "cornsilk", "lemonchiffon","lightyellow", "palegreen", "paleturquoise", "lightcyan",     "lavender", "plum"],
558                                 ["lightgray", "pink", "bisque", "moccasin", "khaki", "lightgreen", "lightseagreen", "lightskyblue", "cornflowerblue", "violet"],
559                                 ["silver", "lightcoral", "sandybrown", "orange", "palegoldenrod", "chartreuse", "mediumturquoise",      "skyblue", "mediumslateblue","orchid"],
560                                 ["gray", "red", "orangered", "darkorange", "yellow", "limegreen",       "darkseagreen", "royalblue", "slateblue", "mediumorchid"],
561                                 ["dimgray", "crimson",  "chocolate", "coral", "gold", "forestgreen", "seagreen", "blue", "blueviolet", "darkorchid"],
562                                 ["darkslategray","firebrick","saddlebrown", "sienna", "olive", "green", "darkcyan", "mediumblue","darkslateblue", "darkmagenta" ],
563                                 ["black", "darkred", "maroon", "brown", "darkolivegreen", "darkgreen", "midnightblue", "navy", "indigo",        "purple"]],
564
565                 "3x4": [["white", "lime", "green", "blue"],
566                         ["silver", "yellow", "fuchsia", "navy"],
567                         ["gray", "red", "purple", "black"]]     
568
569         },
570
571         // _imagePaths: Map
572         //              This is stores the path to the palette images
573         _imagePaths: {
574                 "7x10": dojo.moduleUrl("dijit", "templates/colors7x10.png"),
575                 "3x4": dojo.moduleUrl("dijit", "templates/colors3x4.png")
576         },
577
578         // _paletteCoords: Map
579         //              This is a map that is used to calculate the coordinates of the
580         //              images that make up the palette.
581         _paletteCoords: {
582                 "leftOffset": 3, "topOffset": 3,
583                 "cWidth": 20, "cHeight": 20
584                 
585         },
586
587         // templatePath: String
588         //              Path to the template of this widget.
589         templateString:"<div class=\"dijitInline dijitColorPalette\">\n\t<div class=\"dijitColorPaletteInner\" dojoAttachPoint=\"divNode\" waiRole=\"grid\" tabIndex=\"${tabIndex}\">\n\t\t<img class=\"dijitColorPaletteUnder\" dojoAttachPoint=\"imageNode\" waiRole=\"presentation\">\n\t</div>\t\n</div>\n",
590
591         // _paletteDims: Object
592         //              Size of the supported palettes for alignment purposes.
593         _paletteDims: {
594                 "7x10": {"width": "206px", "height": "145px"},
595                 "3x4": {"width": "86px", "height": "64px"}
596         },
597
598         // tabIndex: String
599         //              Widget tabindex.
600         tabIndex: "0",
601
602         postCreate: function(){
603                 // A name has to be given to the colorMap, this needs to be unique per Palette.
604                 dojo.mixin(this.divNode.style, this._paletteDims[this.palette]);
605                 this.imageNode.setAttribute("src", this._imagePaths[this.palette]);
606                 var choices = this._palettes[this.palette];     
607                 this.domNode.style.position = "relative";
608                 this._cellNodes = [];   
609                 this.colorNames = dojo.i18n.getLocalization("dojo", "colors", this.lang);
610                 var url = dojo.moduleUrl("dojo", "resources/blank.gif"),
611             colorObject = new dojo.Color(),
612                     coords = this._paletteCoords;
613                 for(var row=0; row < choices.length; row++){
614                         for(var col=0; col < choices[row].length; col++) {
615                 var imgNode = dojo.doc.createElement("img");
616                 imgNode.src = url;
617                 dojo.addClass(imgNode, "dijitPaletteImg");
618                 var color = choices[row][col],
619                         colorValue = colorObject.setColor(dojo.Color.named[color]);
620                 imgNode.alt = this.colorNames[color];
621                 imgNode.color = colorValue.toHex();
622                 var imgStyle = imgNode.style;
623                 imgStyle.color = imgStyle.backgroundColor = imgNode.color;
624                 var cellNode = dojo.doc.createElement("span");
625                 cellNode.appendChild(imgNode);
626                 dojo.forEach(["Dijitclick", "MouseEnter", "Focus", "Blur"], function(handler) {
627                     this.connect(cellNode, "on" + handler.toLowerCase(), "_onCell" + handler);
628                 }, this);
629                 this.divNode.appendChild(cellNode);
630                 var cellStyle = cellNode.style;
631                 cellStyle.top = coords.topOffset + (row * coords.cHeight) + "px";
632                 cellStyle.left = coords.leftOffset + (col * coords.cWidth) + "px";
633                 dojo.attr(cellNode, "tabindex", "-1");
634                 cellNode.title = this.colorNames[color];
635                 dojo.addClass(cellNode, "dijitPaletteCell");
636                 dijit.setWaiRole(cellNode, "gridcell");
637                 cellNode.index = this._cellNodes.length;
638                 this._cellNodes.push(cellNode);
639             }
640                 }
641                 this._xDim = choices[0].length;
642                 this._yDim = choices.length;
643                 this.connect(this.divNode, "onfocus", "_onDivNodeFocus");
644
645                 // Now set all events
646                 // The palette itself is navigated to with the tab key on the keyboard
647                 // Keyboard navigation within the Palette is with the arrow keys
648                 // Spacebar selects the color.
649                 // For the up key the index is changed by negative the x dimension.             
650
651                 var keyIncrementMap = {
652                         UP_ARROW: -this._xDim,
653                         // The down key the index is increase by the x dimension.       
654                         DOWN_ARROW: this._xDim,
655                         // Right and left move the index by 1.
656                         RIGHT_ARROW: 1,
657                         LEFT_ARROW: -1
658                 };
659                 for(var key in keyIncrementMap){
660                         this._connects.push(dijit.typematic.addKeyListener(this.domNode,
661                                 {keyCode:dojo.keys[key], ctrlKey:false, altKey:false, shiftKey:false},
662                                 this,
663                                 function(){
664                                         var increment = keyIncrementMap[key];
665                                         return function(count){ this._navigateByKey(increment, count); };
666                                 }(),
667                                 this.timeoutChangeRate, this.defaultTimeout));
668                 }
669         },
670
671         focus: function(){
672                 // summary:
673                 //              Focus this ColorPalette.  Puts focus on the first swatch.
674                 this._focusFirst();
675         },
676
677         onChange: function(color){
678                 // summary:
679                 //              Callback when a color is selected.
680                 // color: String
681                 //              Hex value corresponding to color.
682 //              console.debug("Color selected is: "+color);
683         },
684
685         _focusFirst: function(){
686                 this._currentFocus = 0;
687                 var cellNode = this._cellNodes[this._currentFocus];
688                 window.setTimeout(function(){dijit.focus(cellNode)}, 0);
689         },
690
691         _onDivNodeFocus: function(evt){
692                 // focus bubbles on Firefox 2, so just make sure that focus has really
693                 // gone to the container
694                 if(evt.target === this.divNode){
695                         this._focusFirst();
696                 }
697         },
698
699         _onFocus: function(){
700                 // while focus is on the palette, set its tabindex to -1 so that on a
701                 // shift-tab from a cell, the container is not in the tab order
702                 dojo.attr(this.divNode, "tabindex", "-1");
703         },
704
705         _onBlur: function(){
706                 this._removeCellHighlight(this._currentFocus);
707                 // when focus leaves the palette, restore its tabindex, since it was
708                 // modified by _onFocus().
709                 dojo.attr(this.divNode, "tabindex", this.tabIndex);
710         },
711
712         _onCellDijitclick: function(/*Event*/ evt){
713                 // summary:
714                 //              Handler for click, enter key & space key. Selects the color.
715                 // evt:
716                 //              The event.
717                 var target = evt.currentTarget;
718                 if (this._currentFocus != target.index){
719                         this._currentFocus = target.index;
720                         window.setTimeout(function(){dijit.focus(target)}, 0);
721                 }
722                 this._selectColor(target);
723                 dojo.stopEvent(evt);
724         },
725
726         _onCellMouseEnter: function(/*Event*/ evt){
727                 // summary:
728                 //              Handler for onMouseOver. Put focus on the color under the mouse.
729                 // evt:
730                 //              The mouse event.
731                 var target = evt.currentTarget;
732                 window.setTimeout(function(){dijit.focus(target)}, 0);
733         },
734
735         _onCellFocus: function(/*Event*/ evt){
736                 // summary:
737                 //              Handler for onFocus. Removes highlight of
738                 //              the color that just lost focus, and highlights
739                 //              the new color.
740                 // evt:
741                 //              The focus event.
742                 this._removeCellHighlight(this._currentFocus);
743                 this._currentFocus = evt.currentTarget.index;
744                 dojo.addClass(evt.currentTarget, "dijitPaletteCellHighlight");
745         },
746
747         _onCellBlur: function(/*Event*/ evt){
748                 // summary:
749                 //              needed for Firefox 2 on Mac OS X
750                 this._removeCellHighlight(this._currentFocus);
751         },
752
753         _removeCellHighlight: function(index){
754                 dojo.removeClass(this._cellNodes[index], "dijitPaletteCellHighlight");
755         },
756
757         _selectColor: function(selectNode){     
758                 // summary:
759                 //              This selects a color. It triggers the onChange event
760                 // area:
761                 //              The area node that covers the color being selected.
762                 var img = selectNode.getElementsByTagName("img")[0];
763                 this.onChange(this.value = img.color);
764         },
765
766         _navigateByKey: function(increment, typeCount){
767                 // summary:
768                 //              This is the callback for typematic.
769                 //              It changes the focus and the highlighed color.
770                 // increment:
771                 //              How much the key is navigated.
772                 // typeCount:
773                 //              How many times typematic has fired.
774
775                 // typecount == -1 means the key is released.
776                 if(typeCount == -1){ return; }
777
778                 var newFocusIndex = this._currentFocus + increment;
779                 if(newFocusIndex < this._cellNodes.length && newFocusIndex > -1)
780                 {
781                         var focusNode = this._cellNodes[newFocusIndex];
782                         focusNode.focus();
783                 }
784         }
785 });
786
787 }
788
789 if(!dojo._hasResource["dijit.Declaration"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
790 dojo._hasResource["dijit.Declaration"] = true;
791 dojo.provide("dijit.Declaration");
792
793
794
795 dojo.declare(
796         "dijit.Declaration",
797         dijit._Widget,
798         {
799                 // summary:
800                 //              The Declaration widget allows a user to declare new widget
801                 //              classes directly from a snippet of markup.
802
803                 _noScript: true,
804                 widgetClass: "",
805                 replaceVars: true,
806                 defaults: null,
807                 mixins: [],
808                 buildRendering: function(){
809                         var src = this.srcNodeRef.parentNode.removeChild(this.srcNodeRef);
810                         var preambles = dojo.query("> script[type='dojo/method'][event='preamble']", src).orphan();
811                         var scripts = dojo.query("> script[type^='dojo/']", src).orphan();
812                         var srcType = src.nodeName;
813
814                         var propList = this.defaults||{};
815
816                         // map array of strings like [ "dijit.form.Button" ] to array of mixin objects
817                         // (note that dojo.map(this.mixins, dojo.getObject) doesn't work because it passes
818                         // a bogus third argument to getObject(), confusing it)
819                         this.mixins = this.mixins.length ?
820                                 dojo.map(this.mixins, function(name){ return dojo.getObject(name); } ) :
821                                 [ dijit._Widget, dijit._Templated ];
822
823                         if(preambles.length){
824                                 // we only support one preamble. So be it.
825                                 propList.preamble = dojo.parser._functionFromScript(preambles[0]);
826                         }
827
828                         var parsedScripts = dojo.map(scripts, function(s){
829                                 var evt = s.getAttribute("event")||"postscript";
830                                 return {
831                                         event: evt,
832                                         func: dojo.parser._functionFromScript(s)
833                                 };
834                         });
835
836                         // do the connects for each <script type="dojo/connect" event="foo"> block and make
837                         // all <script type="dojo/method"> tags execute right after construction
838                         this.mixins.push(function(){
839                                 dojo.forEach(parsedScripts, function(s){
840                                         dojo.connect(this, s.event, this, s.func);
841                                 }, this);
842                         });
843
844                         propList.widgetsInTemplate = true;
845                         propList._skipNodeCache = true;
846                         propList.templateString = "<"+srcType+" class='"+src.className+"' dojoAttachPoint='"+(src.getAttribute("dojoAttachPoint")||'')+"' dojoAttachEvent='"+(src.getAttribute("dojoAttachEvent")||'')+"' >"+src.innerHTML.replace(/\%7B/g,"{").replace(/\%7D/g,"}")+"</"+srcType+">";
847                         // console.debug(propList.templateString);
848
849                         // strip things so we don't create stuff under us in the initial setup phase
850                         dojo.query("[dojoType]", src).forEach(function(node){
851                                 node.removeAttribute("dojoType");
852                         });
853
854                         // create the new widget class
855                         dojo.declare(
856                                 this.widgetClass,
857                                 this.mixins,
858                                 propList
859                         );
860                 }
861         }
862 );
863
864 }
865
866 if(!dojo._hasResource["dojo.dnd.common"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
867 dojo._hasResource["dojo.dnd.common"] = true;
868 dojo.provide("dojo.dnd.common");
869
870 dojo.dnd._copyKey = navigator.appVersion.indexOf("Macintosh") < 0 ? "ctrlKey" : "metaKey";
871
872 dojo.dnd.getCopyKeyState = function(e) {
873         // summary: abstracts away the difference between selection on Mac and PC,
874         //      and returns the state of the "copy" key to be pressed.
875         // e: Event: mouse event
876         return e[dojo.dnd._copyKey];    // Boolean
877 };
878
879 dojo.dnd._uniqueId = 0;
880 dojo.dnd.getUniqueId = function(){
881         // summary: returns a unique string for use with any DOM element
882         var id;
883         do{
884                 id = dojo._scopeName + "Unique" + (++dojo.dnd._uniqueId);
885         }while(dojo.byId(id));
886         return id;
887 };
888
889 dojo.dnd._empty = {};
890
891 dojo.dnd.isFormElement = function(/*Event*/ e){
892         // summary: returns true, if user clicked on a form element
893         var t = e.target;
894         if(t.nodeType == 3 /*TEXT_NODE*/){
895                 t = t.parentNode;
896         }
897         return " button textarea input select option ".indexOf(" " + t.tagName.toLowerCase() + " ") >= 0;       // Boolean
898 };
899
900 }
901
902 if(!dojo._hasResource["dojo.dnd.autoscroll"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
903 dojo._hasResource["dojo.dnd.autoscroll"] = true;
904 dojo.provide("dojo.dnd.autoscroll");
905
906 dojo.dnd.getViewport = function(){
907         // summary: returns a viewport size (visible part of the window)
908
909         // FIXME: need more docs!!
910         var d = dojo.doc, dd = d.documentElement, w = window, b = dojo.body();
911         if(dojo.isMozilla){
912                 return {w: dd.clientWidth, h: w.innerHeight};   // Object
913         }else if(!dojo.isOpera && w.innerWidth){
914                 return {w: w.innerWidth, h: w.innerHeight};             // Object
915         }else if (!dojo.isOpera && dd && dd.clientWidth){
916                 return {w: dd.clientWidth, h: dd.clientHeight}; // Object
917         }else if (b.clientWidth){
918                 return {w: b.clientWidth, h: b.clientHeight};   // Object
919         }
920         return null;    // Object
921 };
922
923 dojo.dnd.V_TRIGGER_AUTOSCROLL = 32;
924 dojo.dnd.H_TRIGGER_AUTOSCROLL = 32;
925
926 dojo.dnd.V_AUTOSCROLL_VALUE = 16;
927 dojo.dnd.H_AUTOSCROLL_VALUE = 16;
928
929 dojo.dnd.autoScroll = function(e){
930         // summary:
931         //              a handler for onmousemove event, which scrolls the window, if
932         //              necesary
933         // e: Event:
934         //              onmousemove event
935
936         // FIXME: needs more docs!
937         var v = dojo.dnd.getViewport(), dx = 0, dy = 0;
938         if(e.clientX < dojo.dnd.H_TRIGGER_AUTOSCROLL){
939                 dx = -dojo.dnd.H_AUTOSCROLL_VALUE;
940         }else if(e.clientX > v.w - dojo.dnd.H_TRIGGER_AUTOSCROLL){
941                 dx = dojo.dnd.H_AUTOSCROLL_VALUE;
942         }
943         if(e.clientY < dojo.dnd.V_TRIGGER_AUTOSCROLL){
944                 dy = -dojo.dnd.V_AUTOSCROLL_VALUE;
945         }else if(e.clientY > v.h - dojo.dnd.V_TRIGGER_AUTOSCROLL){
946                 dy = dojo.dnd.V_AUTOSCROLL_VALUE;
947         }
948         window.scrollBy(dx, dy);
949 };
950
951 dojo.dnd._validNodes = {"div": 1, "p": 1, "td": 1};
952 dojo.dnd._validOverflow = {"auto": 1, "scroll": 1};
953
954 dojo.dnd.autoScrollNodes = function(e){
955         // summary:
956         //              a handler for onmousemove event, which scrolls the first avaialble
957         //              Dom element, it falls back to dojo.dnd.autoScroll()
958         // e: Event:
959         //              onmousemove event
960
961         // FIXME: needs more docs!
962         for(var n = e.target; n;){
963                 if(n.nodeType == 1 && (n.tagName.toLowerCase() in dojo.dnd._validNodes)){
964                         var s = dojo.getComputedStyle(n);
965                         if(s.overflow.toLowerCase() in dojo.dnd._validOverflow){
966                                 var b = dojo._getContentBox(n, s), t = dojo._abs(n, true);
967                                 // console.debug(b.l, b.t, t.x, t.y, n.scrollLeft, n.scrollTop);
968                                 b.l += t.x + n.scrollLeft;
969                                 b.t += t.y + n.scrollTop;
970                                 var w = Math.min(dojo.dnd.H_TRIGGER_AUTOSCROLL, b.w / 2), 
971                                         h = Math.min(dojo.dnd.V_TRIGGER_AUTOSCROLL, b.h / 2),
972                                         rx = e.pageX - b.l, ry = e.pageY - b.t, dx = 0, dy = 0;
973                                 if(rx > 0 && rx < b.w){
974                                         if(rx < w){
975                                                 dx = -dojo.dnd.H_AUTOSCROLL_VALUE;
976                                         }else if(rx > b.w - w){
977                                                 dx = dojo.dnd.H_AUTOSCROLL_VALUE;
978                                         }
979                                 }
980                                 //console.debug("ry =", ry, "b.h =", b.h, "h =", h);
981                                 if(ry > 0 && ry < b.h){
982                                         if(ry < h){
983                                                 dy = -dojo.dnd.V_AUTOSCROLL_VALUE;
984                                         }else if(ry > b.h - h){
985                                                 dy = dojo.dnd.V_AUTOSCROLL_VALUE;
986                                         }
987                                 }
988                                 var oldLeft = n.scrollLeft, oldTop = n.scrollTop;
989                                 n.scrollLeft = n.scrollLeft + dx;
990                                 n.scrollTop  = n.scrollTop  + dy;
991                                 // if(dx || dy){ console.debug(oldLeft + ", " + oldTop + "\n" + dx + ", " + dy + "\n" + n.scrollLeft + ", " + n.scrollTop); }
992                                 if(oldLeft != n.scrollLeft || oldTop != n.scrollTop){ return; }
993                         }
994                 }
995                 try{
996                         n = n.parentNode;
997                 }catch(x){
998                         n = null;
999                 }
1000         }
1001         dojo.dnd.autoScroll(e);
1002 };
1003
1004 }
1005
1006 if(!dojo._hasResource["dojo.dnd.Mover"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1007 dojo._hasResource["dojo.dnd.Mover"] = true;
1008 dojo.provide("dojo.dnd.Mover");
1009
1010
1011
1012
1013 dojo.declare("dojo.dnd.Mover", null, {
1014         constructor: function(node, e, host){
1015                 // summary: an object, which makes a node follow the mouse, 
1016                 //      used as a default mover, and as a base class for custom movers
1017                 // node: Node: a node (or node's id) to be moved
1018                 // e: Event: a mouse event, which started the move;
1019                 //      only pageX and pageY properties are used
1020                 // host: Object?: object which implements the functionality of the move,
1021                 //       and defines proper events (onMoveStart and onMoveStop)
1022                 this.node = dojo.byId(node);
1023                 this.marginBox = {l: e.pageX, t: e.pageY};
1024                 this.mouseButton = e.button;
1025                 var h = this.host = host, d = node.ownerDocument, 
1026                         firstEvent = dojo.connect(d, "onmousemove", this, "onFirstMove");
1027                 this.events = [
1028                         dojo.connect(d, "onmousemove", this, "onMouseMove"),
1029                         dojo.connect(d, "onmouseup",   this, "onMouseUp"),
1030                         // cancel text selection and text dragging
1031                         dojo.connect(d, "ondragstart",   dojo, "stopEvent"),
1032                         dojo.connect(d, "onselectstart", dojo, "stopEvent"),
1033                         firstEvent
1034                 ];
1035                 // notify that the move has started
1036                 if(h && h.onMoveStart){
1037                         h.onMoveStart(this);
1038                 }
1039         },
1040         // mouse event processors
1041         onMouseMove: function(e){
1042                 // summary: event processor for onmousemove
1043                 // e: Event: mouse event
1044                 dojo.dnd.autoScroll(e);
1045                 var m = this.marginBox;
1046                 this.host.onMove(this, {l: m.l + e.pageX, t: m.t + e.pageY});
1047         },
1048         onMouseUp: function(e){
1049                 if(this.mouseButton == e.button){
1050                         this.destroy();
1051                 }
1052         },
1053         // utilities
1054         onFirstMove: function(){
1055                 // summary: makes the node absolute; it is meant to be called only once
1056                 var s = this.node.style, l, t;
1057                 switch(s.position){
1058                         case "relative":
1059                         case "absolute":
1060                                 // assume that left and top values are in pixels already
1061                                 l = Math.round(parseFloat(s.left));
1062                                 t = Math.round(parseFloat(s.top));
1063                                 break;
1064                         default:
1065                                 s.position = "absolute";        // enforcing the absolute mode
1066                                 var m = dojo.marginBox(this.node);
1067                                 l = m.l;
1068                                 t = m.t;
1069                                 break;
1070                 }
1071                 this.marginBox.l = l - this.marginBox.l;
1072                 this.marginBox.t = t - this.marginBox.t;
1073                 this.host.onFirstMove(this);
1074                 dojo.disconnect(this.events.pop());
1075         },
1076         destroy: function(){
1077                 // summary: stops the move, deletes all references, so the object can be garbage-collected
1078                 dojo.forEach(this.events, dojo.disconnect);
1079                 // undo global settings
1080                 var h = this.host;
1081                 if(h && h.onMoveStop){
1082                         h.onMoveStop(this);
1083                 }
1084                 // destroy objects
1085                 this.events = this.node = null;
1086         }
1087 });
1088
1089 }
1090
1091 if(!dojo._hasResource["dojo.dnd.Moveable"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1092 dojo._hasResource["dojo.dnd.Moveable"] = true;
1093 dojo.provide("dojo.dnd.Moveable");
1094
1095
1096
1097 dojo.declare("dojo.dnd.Moveable", null, {
1098         // object attributes (for markup)
1099         handle: "",
1100         delay: 0,
1101         skip: false,
1102         
1103         constructor: function(node, params){
1104                 // summary: an object, which makes a node moveable
1105                 // node: Node: a node (or node's id) to be moved
1106                 // params: Object: an optional object with additional parameters;
1107                 //      following parameters are recognized:
1108                 //              handle: Node: a node (or node's id), which is used as a mouse handle
1109                 //                      if omitted, the node itself is used as a handle
1110                 //              delay: Number: delay move by this number of pixels
1111                 //              skip: Boolean: skip move of form elements
1112                 //              mover: Object: a constructor of custom Mover
1113                 this.node = dojo.byId(node);
1114                 if(!params){ params = {}; }
1115                 this.handle = params.handle ? dojo.byId(params.handle) : null;
1116                 if(!this.handle){ this.handle = this.node; }
1117                 this.delay = params.delay > 0 ? params.delay : 0;
1118                 this.skip  = params.skip;
1119                 this.mover = params.mover ? params.mover : dojo.dnd.Mover;
1120                 this.events = [
1121                         dojo.connect(this.handle, "onmousedown", this, "onMouseDown"),
1122                         // cancel text selection and text dragging
1123                         dojo.connect(this.handle, "ondragstart",   this, "onSelectStart"),
1124                         dojo.connect(this.handle, "onselectstart", this, "onSelectStart")
1125                 ];
1126         },
1127
1128         // markup methods
1129         markupFactory: function(params, node){
1130                 return new dojo.dnd.Moveable(node, params);
1131         },
1132
1133         // methods
1134         destroy: function(){
1135                 // summary: stops watching for possible move, deletes all references, so the object can be garbage-collected
1136                 dojo.forEach(this.events, dojo.disconnect);
1137                 this.events = this.node = this.handle = null;
1138         },
1139         
1140         // mouse event processors
1141         onMouseDown: function(e){
1142                 // summary: event processor for onmousedown, creates a Mover for the node
1143                 // e: Event: mouse event
1144                 if(this.skip && dojo.dnd.isFormElement(e)){ return; }
1145                 if(this.delay){
1146                         this.events.push(dojo.connect(this.handle, "onmousemove", this, "onMouseMove"));
1147                         this.events.push(dojo.connect(this.handle, "onmouseup", this, "onMouseUp"));
1148                         this._lastX = e.pageX;
1149                         this._lastY = e.pageY;
1150                 }else{
1151                         new this.mover(this.node, e, this);
1152                 }
1153                 dojo.stopEvent(e);
1154         },
1155         onMouseMove: function(e){
1156                 // summary: event processor for onmousemove, used only for delayed drags
1157                 // e: Event: mouse event
1158                 if(Math.abs(e.pageX - this._lastX) > this.delay || Math.abs(e.pageY - this._lastY) > this.delay){
1159                         this.onMouseUp(e);
1160                         new this.mover(this.node, e, this);
1161                 }
1162                 dojo.stopEvent(e);
1163         },
1164         onMouseUp: function(e){
1165                 // summary: event processor for onmouseup, used only for delayed delayed drags
1166                 // e: Event: mouse event
1167                 dojo.disconnect(this.events.pop());
1168                 dojo.disconnect(this.events.pop());
1169         },
1170         onSelectStart: function(e){
1171                 // summary: event processor for onselectevent and ondragevent
1172                 // e: Event: mouse event
1173                 if(!this.skip || !dojo.dnd.isFormElement(e)){
1174                         dojo.stopEvent(e);
1175                 }
1176         },
1177         
1178         // local events
1179         onMoveStart: function(/* dojo.dnd.Mover */ mover){
1180                 // summary: called before every move operation
1181                 dojo.publish("/dnd/move/start", [mover]);
1182                 dojo.addClass(dojo.body(), "dojoMove"); 
1183                 dojo.addClass(this.node, "dojoMoveItem"); 
1184         },
1185         onMoveStop: function(/* dojo.dnd.Mover */ mover){
1186                 // summary: called after every move operation
1187                 dojo.publish("/dnd/move/stop", [mover]);
1188                 dojo.removeClass(dojo.body(), "dojoMove");
1189                 dojo.removeClass(this.node, "dojoMoveItem");
1190         },
1191         onFirstMove: function(/* dojo.dnd.Mover */ mover){
1192                 // summary: called during the very first move notification,
1193                 //      can be used to initialize coordinates, can be overwritten.
1194                 
1195                 // default implementation does nothing
1196         },
1197         onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
1198                 // summary: called during every move notification,
1199                 //      should actually move the node, can be overwritten.
1200                 this.onMoving(mover, leftTop);
1201                 var s = mover.node.style;
1202                 s.left = leftTop.l + "px";
1203                 s.top  = leftTop.t + "px";
1204                 this.onMoved(mover, leftTop);
1205         },
1206         onMoving: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
1207                 // summary: called before every incremental move,
1208                 //      can be overwritten.
1209                 
1210                 // default implementation does nothing
1211         },
1212         onMoved: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
1213                 // summary: called after every incremental move,
1214                 //      can be overwritten.
1215                 
1216                 // default implementation does nothing
1217         }
1218 });
1219
1220 }
1221
1222 if(!dojo._hasResource["dojo.dnd.TimedMoveable"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1223 dojo._hasResource["dojo.dnd.TimedMoveable"] = true;
1224 dojo.provide("dojo.dnd.TimedMoveable");
1225
1226
1227
1228 (function(){
1229         // precalculate long expressions
1230         var oldOnMove = dojo.dnd.Moveable.prototype.onMove;
1231                 
1232         dojo.declare("dojo.dnd.TimedMoveable", dojo.dnd.Moveable, {
1233                 // summary:
1234                 //      A specialized version of Moveable to support an FPS throttling.
1235                 //      This class puts an upper restriction on FPS, which may reduce 
1236                 //      the CPU load. The additional parameter "timeout" regulates
1237                 //      the delay before actually moving the moveable object.
1238                 
1239                 // object attributes (for markup)
1240                 timeout: 40,    // in ms, 40ms corresponds to 25 fps
1241         
1242                 constructor: function(node, params){
1243                         // summary: an object, which makes a node moveable with a timer
1244                         // node: Node: a node (or node's id) to be moved
1245                         // params: Object: an optional object with additional parameters.
1246                         //      See dojo.dnd.Moveable for details on general parameters.
1247                         //      Following parameters are specific for this class:
1248                         //              timeout: Number: delay move by this number of ms
1249                         //                      accumulating position changes during the timeout
1250                         
1251                         // sanitize parameters
1252                         if(!params){ params = {}; }
1253                         if(params.timeout && typeof params.timeout == "number" && params.timeout >= 0){
1254                                 this.timeout = params.timeout;
1255                         }
1256                 },
1257         
1258                 // markup methods
1259                 markupFactory: function(params, node){
1260                         return new dojo.dnd.TimedMoveable(node, params);
1261                 },
1262         
1263                 onMoveStop: function(/* dojo.dnd.Mover */ mover){
1264                         if(mover._timer){
1265                                 // stop timer
1266                                 clearTimeout(mover._timer)
1267                                 // reflect the last received position
1268                                 oldOnMove.call(this, mover, mover._leftTop)
1269                         }
1270                         dojo.dnd.Moveable.prototype.onMoveStop.apply(this, arguments);
1271                 },
1272                 onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
1273                         mover._leftTop = leftTop;
1274                         if(!mover._timer){
1275                                 var _t = this;  // to avoid using dojo.hitch()
1276                                 mover._timer = setTimeout(function(){
1277                                         // we don't have any pending requests
1278                                         mover._timer = null;
1279                                         // reflect the last received position
1280                                         oldOnMove.call(_t, mover, mover._leftTop);
1281                                 }, this.timeout);
1282                         }
1283                 }
1284         });
1285 })();
1286
1287 }
1288
1289 if(!dojo._hasResource["dojo.fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1290 dojo._hasResource["dojo.fx"] = true;
1291 dojo.provide("dojo.fx");
1292 dojo.provide("dojo.fx.Toggler");
1293
1294 /*=====
1295 dojo.fx = {
1296         // summary: Effects library on top of Base animations
1297 };
1298 =====*/
1299
1300 (function(){
1301         var _baseObj = {
1302                         _fire: function(evt, args){
1303                                 if(this[evt]){
1304                                         this[evt].apply(this, args||[]);
1305                                 }
1306                                 return this;
1307                         }
1308                 };
1309
1310         var _chain = function(animations){
1311                 this._index = -1;
1312                 this._animations = animations||[];
1313                 this._current = this._onAnimateCtx = this._onEndCtx = null;
1314
1315                 this.duration = 0;
1316                 dojo.forEach(this._animations, function(a){
1317                         this.duration += a.duration;
1318                         if(a.delay){ this.duration += a.delay; }
1319                 }, this);
1320         };
1321         dojo.extend(_chain, {
1322                 _onAnimate: function(){
1323                         this._fire("onAnimate", arguments);
1324                 },
1325                 _onEnd: function(){
1326                         dojo.disconnect(this._onAnimateCtx);
1327                         dojo.disconnect(this._onEndCtx);
1328                         this._onAnimateCtx = this._onEndCtx = null;
1329                         if(this._index + 1 == this._animations.length){
1330                                 this._fire("onEnd");
1331                         }else{
1332                                 // switch animations
1333                                 this._current = this._animations[++this._index];
1334                                 this._onAnimateCtx = dojo.connect(this._current, "onAnimate", this, "_onAnimate");
1335                                 this._onEndCtx = dojo.connect(this._current, "onEnd", this, "_onEnd");
1336                                 this._current.play(0, true);
1337                         }
1338                 },
1339                 play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
1340                         if(!this._current){ this._current = this._animations[this._index = 0]; }
1341                         if(!gotoStart && this._current.status() == "playing"){ return this; }
1342                         var beforeBegin = dojo.connect(this._current, "beforeBegin", this, function(){
1343                                         this._fire("beforeBegin");
1344                                 }),
1345                                 onBegin = dojo.connect(this._current, "onBegin", this, function(arg){
1346                                         this._fire("onBegin", arguments);
1347                                 }),
1348                                 onPlay = dojo.connect(this._current, "onPlay", this, function(arg){
1349                                         this._fire("onPlay", arguments);
1350                                         dojo.disconnect(beforeBegin);
1351                                         dojo.disconnect(onBegin);
1352                                         dojo.disconnect(onPlay);
1353                                 });
1354                         if(this._onAnimateCtx){
1355                                 dojo.disconnect(this._onAnimateCtx);
1356                         }
1357                         this._onAnimateCtx = dojo.connect(this._current, "onAnimate", this, "_onAnimate");
1358                         if(this._onEndCtx){
1359                                 dojo.disconnect(this._onEndCtx);
1360                         }
1361                         this._onEndCtx = dojo.connect(this._current, "onEnd", this, "_onEnd");
1362                         this._current.play.apply(this._current, arguments);
1363                         return this;
1364                 },
1365                 pause: function(){
1366                         if(this._current){
1367                                 var e = dojo.connect(this._current, "onPause", this, function(arg){
1368                                                 this._fire("onPause", arguments);
1369                                                 dojo.disconnect(e);
1370                                         });
1371                                 this._current.pause();
1372                         }
1373                         return this;
1374                 },
1375                 gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
1376                         this.pause();
1377                         var offset = this.duration * percent;
1378                         this._current = null;
1379                         dojo.some(this._animations, function(a){
1380                                 if(a.duration <= offset){
1381                                         this._current = a;
1382                                         return true;
1383                                 }
1384                                 offset -= a.duration;
1385                                 return false;
1386                         });
1387                         if(this._current){
1388                                 this._current.gotoPercent(offset / _current.duration, andPlay);
1389                         }
1390                         return this;
1391                 },
1392                 stop: function(/*boolean?*/ gotoEnd){
1393                         if(this._current){
1394                                 if(gotoEnd){
1395                                         for(; this._index + 1 < this._animations.length; ++this._index){
1396                                                 this._animations[this._index].stop(true);
1397                                         }
1398                                         this._current = this._animations[this._index];
1399                                 }
1400                                 var e = dojo.connect(this._current, "onStop", this, function(arg){
1401                                                 this._fire("onStop", arguments);
1402                                                 dojo.disconnect(e);
1403                                         });
1404                                 this._current.stop();
1405                         }
1406                         return this;
1407                 },
1408                 status: function(){
1409                         return this._current ? this._current.status() : "stopped";
1410                 },
1411                 destroy: function(){
1412                         if(this._onAnimateCtx){ dojo.disconnect(this._onAnimateCtx); }
1413                         if(this._onEndCtx){ dojo.disconnect(this._onEndCtx); }
1414                 }
1415         });
1416         dojo.extend(_chain, _baseObj);
1417
1418         dojo.fx.chain = function(/*dojo._Animation[]*/ animations){
1419                 // summary: Chain a list of dojo._Animation s to run in sequence
1420                 // example:
1421                 //      |       dojo.fx.chain([
1422                 //      |               dojo.fadeIn({ node:node }),
1423                 //      |               dojo.fadeOut({ node:otherNode })
1424                 //      |       ]).play();
1425                 //
1426                 return new _chain(animations) // dojo._Animation
1427         };
1428
1429         var _combine = function(animations){
1430                 this._animations = animations||[];
1431                 this._connects = [];
1432                 this._finished = 0;
1433
1434                 this.duration = 0;
1435                 dojo.forEach(animations, function(a){
1436                         var duration = a.duration;
1437                         if(a.delay){ duration += a.delay; }
1438                         if(this.duration < duration){ this.duration = duration; }
1439                         this._connects.push(dojo.connect(a, "onEnd", this, "_onEnd"));
1440                 }, this);
1441                 
1442                 this._pseudoAnimation = new dojo._Animation({curve: [0, 1], duration: this.duration});
1443                 dojo.forEach(["beforeBegin", "onBegin", "onPlay", "onAnimate", "onPause", "onStop"], 
1444                         function(evt){
1445                                 this._connects.push(dojo.connect(this._pseudoAnimation, evt, dojo.hitch(this, "_fire", evt)));
1446                         },
1447                         this
1448                 );
1449         };
1450         dojo.extend(_combine, {
1451                 _doAction: function(action, args){
1452                         dojo.forEach(this._animations, function(a){
1453                                 a[action].apply(a, args);
1454                         });
1455                         return this;
1456                 },
1457                 _onEnd: function(){
1458                         if(++this._finished == this._animations.length){
1459                                 this._fire("onEnd");
1460                         }
1461                 },
1462                 _call: function(action, args){
1463                         var t = this._pseudoAnimation;
1464                         t[action].apply(t, args);
1465                 },
1466                 play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
1467                         this._finished = 0;
1468                         this._doAction("play", arguments);
1469                         this._call("play", arguments);
1470                         return this;
1471                 },
1472                 pause: function(){
1473                         this._doAction("pause", arguments);
1474                         this._call("pause", arguments);
1475                         return this;
1476                 },
1477                 gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
1478                         var ms = this.duration * percent;
1479                         dojo.forEach(this._animations, function(a){
1480                                 a.gotoPercent(a.duration < ms ? 1 : (ms / a.duration), andPlay);
1481                         });
1482                         this._call("gotoProcent", arguments);
1483                         return this;
1484                 },
1485                 stop: function(/*boolean?*/ gotoEnd){
1486                         this._doAction("stop", arguments);
1487                         this._call("stop", arguments);
1488                         return this;
1489                 },
1490                 status: function(){
1491                         return this._pseudoAnimation.status();
1492                 },
1493                 destroy: function(){
1494                         dojo.forEach(this._connects, dojo.disconnect);
1495                 }
1496         });
1497         dojo.extend(_combine, _baseObj);
1498
1499         dojo.fx.combine = function(/*dojo._Animation[]*/ animations){
1500                 // summary: Combine a list of dojo._Animation s to run in parallel
1501                 // example:
1502                 //      |       dojo.fx.combine([
1503                 //      |               dojo.fadeIn({ node:node }),
1504                 //      |               dojo.fadeOut({ node:otherNode })
1505                 //      |       ]).play();
1506                 return new _combine(animations); // dojo._Animation
1507         };
1508 })();
1509
1510 dojo.declare("dojo.fx.Toggler", null, {
1511         // summary:
1512         //              class constructor for an animation toggler. It accepts a packed
1513         //              set of arguments about what type of animation to use in each
1514         //              direction, duration, etc.
1515         //
1516         // example:
1517         //      |       var t = new dojo.fx.Toggler({
1518         //      |               node: "nodeId",
1519         //      |               showDuration: 500,
1520         //      |               // hideDuration will default to "200"
1521         //      |               showFunc: dojo.wipeIn, 
1522         //      |               // hideFunc will default to "fadeOut"
1523         //      |       });
1524         //      |       t.show(100); // delay showing for 100ms
1525         //      |       // ...time passes...
1526         //      |       t.hide();
1527
1528         // FIXME: need a policy for where the toggler should "be" the next
1529         // time show/hide are called if we're stopped somewhere in the
1530         // middle.
1531
1532         constructor: function(args){
1533                 var _t = this;
1534
1535                 dojo.mixin(_t, args);
1536                 _t.node = args.node;
1537                 _t._showArgs = dojo.mixin({}, args);
1538                 _t._showArgs.node = _t.node;
1539                 _t._showArgs.duration = _t.showDuration;
1540                 _t.showAnim = _t.showFunc(_t._showArgs);
1541
1542                 _t._hideArgs = dojo.mixin({}, args);
1543                 _t._hideArgs.node = _t.node;
1544                 _t._hideArgs.duration = _t.hideDuration;
1545                 _t.hideAnim = _t.hideFunc(_t._hideArgs);
1546
1547                 dojo.connect(_t.showAnim, "beforeBegin", dojo.hitch(_t.hideAnim, "stop", true));
1548                 dojo.connect(_t.hideAnim, "beforeBegin", dojo.hitch(_t.showAnim, "stop", true));
1549         },
1550
1551         // node: DomNode
1552         //      the node to toggle
1553         node: null,
1554
1555         // showFunc: Function
1556         //      The function that returns the dojo._Animation to show the node
1557         showFunc: dojo.fadeIn,
1558
1559         // hideFunc: Function   
1560         //      The function that returns the dojo._Animation to hide the node
1561         hideFunc: dojo.fadeOut,
1562
1563         // showDuration:
1564         //      Time in milliseconds to run the show Animation
1565         showDuration: 200,
1566
1567         // hideDuration:
1568         //      Time in milliseconds to run the hide Animation
1569         hideDuration: 200,
1570
1571         /*=====
1572         _showArgs: null,
1573         _showAnim: null,
1574
1575         _hideArgs: null,
1576         _hideAnim: null,
1577
1578         _isShowing: false,
1579         _isHiding: false,
1580         =====*/
1581
1582         show: function(delay){
1583                 // summary: Toggle the node to showing
1584                 return this.showAnim.play(delay || 0);
1585         },
1586
1587         hide: function(delay){
1588                 // summary: Toggle the node to hidden
1589                 return this.hideAnim.play(delay || 0);
1590         }
1591 });
1592
1593 dojo.fx.wipeIn = function(/*Object*/ args){
1594         // summary
1595         //              Returns an animation that will expand the
1596         //              node defined in 'args' object from it's current height to
1597         //              it's natural height (with no scrollbar).
1598         //              Node must have no margin/border/padding.
1599         args.node = dojo.byId(args.node);
1600         var node = args.node, s = node.style;
1601
1602         var anim = dojo.animateProperty(dojo.mixin({
1603                 properties: {
1604                         height: {
1605                                 // wrapped in functions so we wait till the last second to query (in case value has changed)
1606                                 start: function(){
1607                                         // start at current [computed] height, but use 1px rather than 0
1608                                         // because 0 causes IE to display the whole panel
1609                                         s.overflow="hidden";
1610                                         if(s.visibility=="hidden"||s.display=="none"){
1611                                                 s.height="1px";
1612                                                 s.display="";
1613                                                 s.visibility="";
1614                                                 return 1;
1615                                         }else{
1616                                                 var height = dojo.style(node, "height");
1617                                                 return Math.max(height, 1);
1618                                         }
1619                                 },
1620                                 end: function(){
1621                                         return node.scrollHeight;
1622                                 }
1623                         }
1624                 }
1625         }, args));
1626
1627         dojo.connect(anim, "onEnd", function(){ 
1628                 s.height = "auto";
1629         });
1630
1631         return anim; // dojo._Animation
1632 }
1633
1634 dojo.fx.wipeOut = function(/*Object*/ args){
1635         // summary
1636         //              Returns an animation that will shrink node defined in "args"
1637         //              from it's current height to 1px, and then hide it.
1638         var node = args.node = dojo.byId(args.node);
1639         var s = node.style;
1640
1641         var anim = dojo.animateProperty(dojo.mixin({
1642                 properties: {
1643                         height: {
1644                                 end: 1 // 0 causes IE to display the whole panel
1645                         }
1646                 }
1647         }, args));
1648
1649         dojo.connect(anim, "beforeBegin", function(){
1650                 s.overflow = "hidden";
1651                 s.display = "";
1652         });
1653         dojo.connect(anim, "onEnd", function(){
1654                 s.height = "auto";
1655                 s.display = "none";
1656         });
1657
1658         return anim; // dojo._Animation
1659 }
1660
1661 dojo.fx.slideTo = function(/*Object?*/ args){
1662         // summary
1663         //              Returns an animation that will slide "node" 
1664         //              defined in args Object from its current position to
1665         //              the position defined by (args.left, args.top).
1666         // example:
1667         //      |       dojo.fx.slideTo({ node: node, left:"40", top:"50", unit:"px" }).play()
1668
1669         var node = (args.node = dojo.byId(args.node));
1670         
1671         var top = null;
1672         var left = null;
1673         
1674         var init = (function(n){
1675                 return function(){
1676                         var cs = dojo.getComputedStyle(n);
1677                         var pos = cs.position;
1678                         top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0);
1679                         left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0);
1680                         if(pos != 'absolute' && pos != 'relative'){
1681                                 var ret = dojo.coords(n, true);
1682                                 top = ret.y;
1683                                 left = ret.x;
1684                                 n.style.position="absolute";
1685                                 n.style.top=top+"px";
1686                                 n.style.left=left+"px";
1687                         }
1688                 };
1689         })(node);
1690         init();
1691
1692         var anim = dojo.animateProperty(dojo.mixin({
1693                 properties: {
1694                         top: { end: args.top||0 },
1695                         left: { end: args.left||0 }
1696                 }
1697         }, args));
1698         dojo.connect(anim, "beforeBegin", anim, init);
1699
1700         return anim; // dojo._Animation
1701 }
1702
1703 }
1704
1705 if(!dojo._hasResource["dijit.layout.ContentPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1706 dojo._hasResource["dijit.layout.ContentPane"] = true;
1707 dojo.provide("dijit.layout.ContentPane");
1708
1709
1710
1711
1712
1713
1714
1715
1716 dojo.declare(
1717         "dijit.layout.ContentPane",
1718         dijit._Widget,
1719 {
1720         // summary:
1721         //              A widget that acts as a Container for other widgets, and includes a ajax interface
1722         // description:
1723         //              A widget that can be used as a standalone widget
1724         //              or as a baseclass for other widgets
1725         //              Handles replacement of document fragment using either external uri or javascript
1726         //              generated markup or DOM content, instantiating widgets within that content.
1727         //              Don't confuse it with an iframe, it only needs/wants document fragments.
1728         //              It's useful as a child of LayoutContainer, SplitContainer, or TabContainer.
1729         //              But note that those classes can contain any widget as a child.
1730         // example:
1731         //              Some quick samples:
1732         //              To change the innerHTML use .setContent('<b>new content</b>')
1733         //
1734         //              Or you can send it a NodeList, .setContent(dojo.query('div [class=selected]', userSelection))
1735         //              please note that the nodes in NodeList will copied, not moved
1736         //
1737         //              To do a ajax update use .setHref('url')
1738         //
1739         // href: String
1740         //              The href of the content that displays now.
1741         //              Set this at construction if you want to load data externally when the
1742         //              pane is shown.  (Set preload=true to load it immediately.)
1743         //              Changing href after creation doesn't have any effect; see setHref();
1744         href: "",
1745
1746         // extractContent: Boolean
1747         //      Extract visible content from inside of <body> .... </body>
1748         extractContent: false,
1749
1750         // parseOnLoad: Boolean
1751         //      parse content and create the widgets, if any
1752         parseOnLoad:    true,
1753
1754         // preventCache: Boolean
1755         //              Cache content retreived externally
1756         preventCache:   false,
1757
1758         // preload: Boolean
1759         //      Force load of data even if pane is hidden.
1760         preload: false,
1761
1762         // refreshOnShow: Boolean
1763         //              Refresh (re-download) content when pane goes from hidden to shown
1764         refreshOnShow: false,
1765
1766         // loadingMessage: String
1767         //      Message that shows while downloading
1768         loadingMessage: "<span class='dijitContentPaneLoading'>${loadingState}</span>", 
1769
1770         // errorMessage: String
1771         //      Message that shows if an error occurs
1772         errorMessage: "<span class='dijitContentPaneError'>${errorState}</span>", 
1773
1774         // isLoaded: Boolean
1775         //      Tells loading status see onLoad|onUnload for event hooks
1776         isLoaded: false,
1777
1778         // class: String
1779         //      Class name to apply to ContentPane dom nodes
1780         // TODO: this should be called "baseClass" like in the other widgets
1781         "class": "dijitContentPane",
1782
1783         // doLayout: String/Boolean
1784         //      false - don't adjust size of children
1785         //      true - looks for the first sizable child widget (ie, having resize() method) and sets it's size to
1786         //                      however big the ContentPane is (TODO: implement)
1787         //      auto - if there is a single sizable child widget (ie, having resize() method), set it's size to
1788         //                      however big the ContentPane is
1789         doLayout: "auto",
1790
1791         postCreate: function(){
1792                 // remove the title attribute so it doesn't show up when i hover
1793                 // over a node
1794                 this.domNode.title = "";
1795
1796                 if(!this.containerNode){
1797                         // make getDescendants() work
1798                         this.containerNode = this.domNode;
1799                 }
1800
1801                 if(this.preload){
1802                         this._loadCheck();
1803                 }
1804
1805                 var messages = dojo.i18n.getLocalization("dijit", "loading", this.lang);
1806                 this.loadingMessage = dojo.string.substitute(this.loadingMessage, messages);
1807                 this.errorMessage = dojo.string.substitute(this.errorMessage, messages);
1808                 var curRole = dijit.getWaiRole(this.domNode);
1809                 if (!curRole){
1810                         dijit.setWaiRole(this.domNode, "group");
1811                 }
1812
1813                 // for programatically created ContentPane (with <span> tag), need to muck w/CSS
1814                 // or it's as though overflow:visible is set
1815                 dojo.addClass(this.domNode, this["class"]);
1816         },
1817
1818         startup: function(){
1819                 if(this._started){ return; }
1820                 if(this.doLayout != "false" && this.doLayout !== false){
1821                         this._checkIfSingleChild();
1822                         if(this._singleChild){
1823                                 this._singleChild.startup();
1824                         }
1825                 }
1826                 this._loadCheck();
1827                 this.inherited(arguments);
1828         },
1829
1830         _checkIfSingleChild: function(){
1831                 // summary:
1832                 //      Test if we have exactly one widget as a child, and if so assume that we are a container for that widget,
1833                 //      and should propogate startup() and resize() calls to it.
1834
1835                 // TODO: if there are two child widgets (a data store and a TabContainer, for example),
1836                 //      should still find the TabContainer
1837                 var childNodes = dojo.query(">", this.containerNode || this.domNode),
1838                         childWidgets = childNodes.filter("[widgetId]");
1839
1840                 if(childNodes.length == 1 && childWidgets.length == 1){
1841                         this.isContainer = true;
1842                         this._singleChild = dijit.byNode(childWidgets[0]);
1843                 }else{
1844                         delete this.isContainer;
1845                         delete this._singleChild;
1846                 }
1847         },
1848
1849         refresh: function(){
1850                 // summary:
1851                 //      Force a refresh (re-download) of content, be sure to turn off cache
1852
1853                 // we return result of _prepareLoad here to avoid code dup. in dojox.layout.ContentPane
1854                 return this._prepareLoad(true);
1855         },
1856
1857         setHref: function(/*String|Uri*/ href){
1858                 // summary:
1859                 //              Reset the (external defined) content of this pane and replace with new url
1860                 //              Note: It delays the download until widget is shown if preload is false
1861                 //      href:
1862                 //              url to the page you want to get, must be within the same domain as your mainpage
1863                 this.href = href;
1864
1865                 // we return result of _prepareLoad here to avoid code dup. in dojox.layout.ContentPane
1866                 return this._prepareLoad();
1867         },
1868
1869         setContent: function(/*String|DomNode|Nodelist*/data){
1870                 // summary:
1871                 //              Replaces old content with data content, include style classes from old content
1872                 //      data:
1873                 //              the new Content may be String, DomNode or NodeList
1874                 //
1875                 //              if data is a NodeList (or an array of nodes) nodes are copied
1876                 //              so you can import nodes from another document implicitly
1877
1878                 // clear href so we cant run refresh and clear content
1879                 // refresh should only work if we downloaded the content
1880                 if(!this._isDownloaded){
1881                         this.href = "";
1882                         this._onUnloadHandler();
1883                 }
1884
1885                 this._setContent(data || "");
1886
1887                 this._isDownloaded = false; // must be set after _setContent(..), pathadjust in dojox.layout.ContentPane
1888
1889                 if(this.parseOnLoad){
1890                         this._createSubWidgets();
1891                 }
1892
1893                 if(this.doLayout != "false" && this.doLayout !== false){
1894                         this._checkIfSingleChild();
1895                         if(this._singleChild && this._singleChild.resize){
1896                                 this._singleChild.startup();
1897                                 this._singleChild.resize(this._contentBox || dojo.contentBox(this.containerNode || this.domNode));
1898                         }
1899                 }
1900
1901                 this._onLoadHandler();
1902         },
1903
1904         cancel: function(){
1905                 // summary:
1906                 //              Cancels a inflight download of content
1907                 if(this._xhrDfd && (this._xhrDfd.fired == -1)){
1908                         this._xhrDfd.cancel();
1909                 }
1910                 delete this._xhrDfd; // garbage collect
1911         },
1912
1913         destroy: function(){
1914                 // if we have multiple controllers destroying us, bail after the first
1915                 if(this._beingDestroyed){
1916                         return;
1917                 }
1918                 // make sure we call onUnload
1919                 this._onUnloadHandler();
1920                 this._beingDestroyed = true;
1921                 this.inherited("destroy",arguments);
1922         },
1923
1924         resize: function(size){
1925                 dojo.marginBox(this.domNode, size);
1926
1927                 // Compute content box size in case we [later] need to size child
1928                 // If either height or width wasn't specified by the user, then query node for it.
1929                 // But note that setting the margin box and then immediately querying dimensions may return
1930                 // inaccurate results, so try not to depend on it.
1931                 var node = this.containerNode || this.domNode,
1932                         mb = dojo.mixin(dojo.marginBox(node), size||{});
1933
1934                 this._contentBox = dijit.layout.marginBox2contentBox(node, mb);
1935
1936                 // If we have a single widget child then size it to fit snugly within my borders
1937                 if(this._singleChild && this._singleChild.resize){
1938                         this._singleChild.resize(this._contentBox);
1939                 }
1940         },
1941
1942         _prepareLoad: function(forceLoad){
1943                 // sets up for a xhrLoad, load is deferred until widget onShow
1944                 // cancels a inflight download
1945                 this.cancel();
1946                 this.isLoaded = false;
1947                 this._loadCheck(forceLoad);
1948         },
1949
1950         _isShown: function(){
1951                 // summary: returns true if the content is currently shown
1952                 if("open" in this){
1953                         return this.open;               // for TitlePane, etc.
1954                 }else{
1955                         var node = this.domNode;
1956                         return (node.style.display != 'none')  && (node.style.visibility != 'hidden');
1957                 }
1958         },
1959
1960         _loadCheck: function(/*Boolean*/ forceLoad){
1961                 // call this when you change onShow (onSelected) status when selected in parent container
1962                 // it's used as a trigger for href download when this.domNode.display != 'none'
1963
1964                 // sequence:
1965                 // if no href -> bail
1966                 // forceLoad -> always load
1967                 // this.preload -> load when download not in progress, domNode display doesn't matter
1968                 // this.refreshOnShow -> load when download in progress bails, domNode display !='none' AND
1969                 //                                              this.open !== false (undefined is ok), isLoaded doesn't matter
1970                 // else -> load when download not in progress, if this.open !== false (undefined is ok) AND
1971                 //                                              domNode display != 'none', isLoaded must be false
1972
1973                 var displayState = this._isShown();
1974
1975                 if(this.href && 
1976                         (forceLoad ||
1977                                 (this.preload && !this._xhrDfd) ||
1978                                 (this.refreshOnShow && displayState && !this._xhrDfd) ||
1979                                 (!this.isLoaded && displayState && !this._xhrDfd)
1980                         )
1981                 ){
1982                         this._downloadExternalContent();
1983                 }
1984         },
1985
1986         _downloadExternalContent: function(){
1987                 this._onUnloadHandler();
1988
1989                 // display loading message
1990                 this._setContent(
1991                         this.onDownloadStart.call(this)
1992                 );
1993
1994                 var self = this;
1995                 var getArgs = {
1996                         preventCache: (this.preventCache || this.refreshOnShow),
1997                         url: this.href,
1998                         handleAs: "text"
1999                 };
2000                 if(dojo.isObject(this.ioArgs)){
2001                         dojo.mixin(getArgs, this.ioArgs);
2002                 }
2003
2004                 var hand = this._xhrDfd = (this.ioMethod || dojo.xhrGet)(getArgs);
2005
2006                 hand.addCallback(function(html){
2007                         try{
2008                                 self.onDownloadEnd.call(self);
2009                                 self._isDownloaded = true;
2010                                 self.setContent.call(self, html); // onload event is called from here
2011                         }catch(err){
2012                                 self._onError.call(self, 'Content', err); // onContentError
2013                         }
2014                         delete self._xhrDfd;
2015                         return html;
2016                 });
2017
2018                 hand.addErrback(function(err){
2019                         if(!hand.cancelled){
2020                                 // show error message in the pane
2021                                 self._onError.call(self, 'Download', err); // onDownloadError
2022                         }
2023                         delete self._xhrDfd;
2024                         return err;
2025                 });
2026         },
2027
2028         _onLoadHandler: function(){
2029                 this.isLoaded = true;
2030                 try{
2031                         this.onLoad.call(this);
2032                 }catch(e){
2033                         console.error('Error '+this.widgetId+' running custom onLoad code');
2034                 }
2035         },
2036
2037         _onUnloadHandler: function(){
2038                 this.isLoaded = false;
2039                 this.cancel();
2040                 try{
2041                         this.onUnload.call(this);
2042                 }catch(e){
2043                         console.error('Error '+this.widgetId+' running custom onUnload code');
2044                 }
2045         },
2046
2047         _setContent: function(cont){
2048                 this.destroyDescendants();
2049
2050                 try{
2051                         var node = this.containerNode || this.domNode;
2052                         while(node.firstChild){
2053                                 dojo._destroyElement(node.firstChild);
2054                         }
2055                         if(typeof cont == "string"){
2056                                 // dijit.ContentPane does only minimal fixes,
2057                                 // No pathAdjustments, script retrieval, style clean etc
2058                                 // some of these should be available in the dojox.layout.ContentPane
2059                                 if(this.extractContent){
2060                                         match = cont.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
2061                                         if(match){ cont = match[1]; }
2062                                 }
2063                                 node.innerHTML = cont;
2064                         }else{
2065                                 // domNode or NodeList
2066                                 if(cont.nodeType){ // domNode (htmlNode 1 or textNode 3)
2067                                         node.appendChild(cont);
2068                                 }else{// nodelist or array such as dojo.Nodelist
2069                                         dojo.forEach(cont, function(n){
2070                                                 node.appendChild(n.cloneNode(true));
2071                                         });
2072                                 }
2073                         }
2074                 }catch(e){
2075                         // check if a domfault occurs when we are appending this.errorMessage
2076                         // like for instance if domNode is a UL and we try append a DIV
2077                         var errMess = this.onContentError(e);
2078                         try{
2079                                 node.innerHTML = errMess;
2080                         }catch(e){
2081                                 console.error('Fatal '+this.id+' could not change content due to '+e.message, e);
2082                         }
2083                 }
2084         },
2085
2086         _onError: function(type, err, consoleText){
2087                 // shows user the string that is returned by on[type]Error
2088                 // overide on[type]Error and return your own string to customize
2089                 var errText = this['on' + type + 'Error'].call(this, err);
2090                 if(consoleText){
2091                         console.error(consoleText, err);
2092                 }else if(errText){// a empty string won't change current content
2093                         this._setContent.call(this, errText);
2094                 }
2095         },
2096
2097         _createSubWidgets: function(){
2098                 // summary: scan my contents and create subwidgets
2099                 var rootNode = this.containerNode || this.domNode;
2100                 try{
2101                         dojo.parser.parse(rootNode, true);
2102                 }catch(e){
2103                         this._onError('Content', e, "Couldn't create widgets in "+this.id
2104                                 +(this.href ? " from "+this.href : ""));
2105                 }
2106         },
2107
2108         // EVENT's, should be overide-able
2109         onLoad: function(e){
2110                 // summary:
2111                 //              Event hook, is called after everything is loaded and widgetified
2112         },
2113
2114         onUnload: function(e){
2115                 // summary:
2116                 //              Event hook, is called before old content is cleared
2117         },
2118
2119         onDownloadStart: function(){
2120                 // summary:
2121                 //              called before download starts
2122                 //              the string returned by this function will be the html
2123                 //              that tells the user we are loading something
2124                 //              override with your own function if you want to change text
2125                 return this.loadingMessage;
2126         },
2127
2128         onContentError: function(/*Error*/ error){
2129                 // summary:
2130                 //              called on DOM faults, require fault etc in content
2131                 //              default is to display errormessage inside pane
2132         },
2133
2134         onDownloadError: function(/*Error*/ error){
2135                 // summary:
2136                 //              Called when download error occurs, default is to display
2137                 //              errormessage inside pane. Overide function to change that.
2138                 //              The string returned by this function will be the html
2139                 //              that tells the user a error happend
2140                 return this.errorMessage;
2141         },
2142
2143         onDownloadEnd: function(){
2144                 // summary:
2145                 //              called when download is finished
2146         }
2147 });
2148
2149 }
2150
2151 if(!dojo._hasResource["dijit.form.Form"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2152 dojo._hasResource["dijit.form.Form"] = true;
2153 dojo.provide("dijit.form.Form");
2154
2155
2156
2157
2158 dojo.declare("dijit.form._FormMixin", null,
2159         {
2160         //
2161         //      summary:
2162         //              Widget corresponding to HTML form tag, for validation and serialization
2163         //
2164         //      example:
2165         //      |       <form dojoType="dijit.form.Form" id="myForm">
2166         //      |               Name: <input type="text" name="name" />
2167         //      |       </form>
2168         //      |       myObj = {name: "John Doe"};
2169         //      |       dijit.byId('myForm').setValues(myObj);
2170         //      |
2171         //      |       myObj=dijit.byId('myForm').getValues();
2172
2173         //      TODO:
2174         //      * Repeater
2175         //      * better handling for arrays.  Often form elements have names with [] like
2176         //      * people[3].sex (for a list of people [{name: Bill, sex: M}, ...])
2177         //
2178         //      
2179
2180                 reset: function(){
2181                         dojo.forEach(this.getDescendants(), function(widget){
2182                                 if(widget.reset){
2183                                         widget.reset();
2184                                 }
2185                         });
2186                 },
2187
2188                 validate: function(){
2189                         // summary: returns if the form is valid - same as isValid - but
2190                         //                      provides a few additional (ui-specific) features.
2191                         //                      1 - it will highlight any sub-widgets that are not
2192                         //                              valid
2193                         //                      2 - it will call focus() on the first invalid 
2194                         //                              sub-widget
2195                         var didFocus = false;
2196                         return dojo.every(dojo.map(this.getDescendants(), function(widget){
2197                                 // Need to set this so that "required" widgets get their 
2198                                 // state set.
2199                                 widget._hasBeenBlurred = true;
2200                                 var valid = !widget.validate || widget.validate();
2201                                 if (!valid && !didFocus) {
2202                                         // Set focus of the first non-valid widget
2203                                         dijit.scrollIntoView(widget.containerNode||widget.domNode);
2204                                         widget.focus();
2205                                         didFocus = true;
2206                                 }
2207                                 return valid;
2208                         }), "return item;");
2209                 },
2210                 
2211                 setValues: function(/*object*/obj){
2212                         // summary: fill in form values from a JSON structure
2213
2214                         // generate map from name --> [list of widgets with that name]
2215                         var map = { };
2216                         dojo.forEach(this.getDescendants(), function(widget){
2217                                 if(!widget.name){ return; }
2218                                 var entry = map[widget.name] || (map[widget.name] = [] );
2219                                 entry.push(widget);
2220                         });
2221
2222                         // call setValue() or setAttribute('checked') for each widget, according to obj
2223                         for(var name in map){
2224                                 var widgets = map[name],                                                // array of widgets w/this name
2225                                         values = dojo.getObject(name, false, obj);      // list of values for those widgets
2226                                 if(!dojo.isArray(values)){
2227                                         values = [ values ];
2228                                 }
2229                                 if(typeof widgets[0].checked == 'boolean'){
2230                                         // for checkbox/radio, values is a list of which widgets should be checked
2231                                         dojo.forEach(widgets, function(w, i){
2232                                                 w.setValue(dojo.indexOf(values, w.value) != -1);
2233                                         });
2234                                 }else if(widgets[0]._multiValue){
2235                                         // it takes an array (e.g. multi-select)
2236                                         widgets[0].setValue(values);
2237                                 }else{
2238                                         // otherwise, values is a list of values to be assigned sequentially to each widget
2239                                         dojo.forEach(widgets, function(w, i){
2240                                                 w.setValue(values[i]);
2241                                         });                                     
2242                                 }
2243                         }
2244
2245                         /***
2246                          *      TODO: code for plain input boxes (this shouldn't run for inputs that are part of widgets)
2247
2248                         dojo.forEach(this.containerNode.elements, function(element){
2249                                 if (element.name == ''){return};        // like "continue"      
2250                                 var namePath = element.name.split(".");
2251                                 var myObj=obj;
2252                                 var name=namePath[namePath.length-1];
2253                                 for(var j=1,len2=namePath.length;j<len2;++j){
2254                                         var p=namePath[j - 1];
2255                                         // repeater support block
2256                                         var nameA=p.split("[");
2257                                         if (nameA.length > 1){
2258                                                 if(typeof(myObj[nameA[0]]) == "undefined"){
2259                                                         myObj[nameA[0]]=[ ];
2260                                                 } // if
2261
2262                                                 nameIndex=parseInt(nameA[1]);
2263                                                 if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
2264                                                         myObj[nameA[0]][nameIndex] = { };
2265                                                 }
2266                                                 myObj=myObj[nameA[0]][nameIndex];
2267                                                 continue;
2268                                         } // repeater support ends
2269
2270                                         if(typeof(myObj[p]) == "undefined"){
2271                                                 myObj=undefined;
2272                                                 break;
2273                                         };
2274                                         myObj=myObj[p];
2275                                 }
2276
2277                                 if (typeof(myObj) == "undefined"){
2278                                         return;         // like "continue"
2279                                 }
2280                                 if (typeof(myObj[name]) == "undefined" && this.ignoreNullValues){
2281                                         return;         // like "continue"
2282                                 }
2283
2284                                 // TODO: widget values (just call setValue() on the widget)
2285
2286                                 switch(element.type){
2287                                         case "checkbox":
2288                                                 element.checked = (name in myObj) &&
2289                                                         dojo.some(myObj[name], function(val){ return val==element.value; });
2290                                                 break;
2291                                         case "radio":
2292                                                 element.checked = (name in myObj) && myObj[name]==element.value;
2293                                                 break;
2294                                         case "select-multiple":
2295                                                 element.selectedIndex=-1;
2296                                                 dojo.forEach(element.options, function(option){
2297                                                         option.selected = dojo.some(myObj[name], function(val){ return option.value == val; });
2298                                                 });
2299                                                 break;
2300                                         case "select-one":
2301                                                 element.selectedIndex="0";
2302                                                 dojo.forEach(element.options, function(option){
2303                                                         option.selected = option.value == myObj[name];
2304                                                 });
2305                                                 break;
2306                                         case "hidden":
2307                                         case "text":
2308                                         case "textarea":
2309                                         case "password":
2310                                                 element.value = myObj[name] || "";
2311                                                 break;
2312                                 }
2313                         });
2314                         */
2315                 },
2316
2317                 getValues: function(){
2318                         // summary: generate JSON structure from form values
2319
2320                         // get widget values
2321                         var obj = { };
2322                         dojo.forEach(this.getDescendants(), function(widget){
2323                                 var name = widget.name;
2324                                 if(!name){ return; }
2325
2326                                 // Single value widget (checkbox, radio, or plain <input> type widget
2327                                 var value = (widget.getValue && !widget._getValueDeprecated) ? widget.getValue() : widget.value;
2328
2329                                 // Store widget's value(s) as a scalar, except for checkboxes which are automatically arrays
2330                                 if(typeof widget.checked == 'boolean'){
2331                                         if(/Radio/.test(widget.declaredClass)){
2332                                                 // radio button
2333                                                 if(value !== false){
2334                                                         dojo.setObject(name, value, obj);
2335                                                 }
2336                                         }else{
2337                                                 // checkbox/toggle button
2338                                                 var ary=dojo.getObject(name, false, obj);
2339                                                 if(!ary){
2340                                                         ary=[];
2341                                                         dojo.setObject(name, ary, obj);
2342                                                 }
2343                                                 if(value !== false){
2344                                                         ary.push(value);
2345                                                 }
2346                                         }
2347                                 }else{
2348                                         // plain input
2349                                         dojo.setObject(name, value, obj);
2350                                 }
2351                         });
2352
2353                         /***
2354                          * code for plain input boxes (see also dojo.formToObject, can we use that instead of this code?
2355                          * but it doesn't understand [] notation, presumably)
2356                         var obj = { };
2357                         dojo.forEach(this.containerNode.elements, function(elm){
2358                                 if (!elm.name)  {
2359                                         return;         // like "continue"
2360                                 }
2361                                 var namePath = elm.name.split(".");
2362                                 var myObj=obj;
2363                                 var name=namePath[namePath.length-1];
2364                                 for(var j=1,len2=namePath.length;j<len2;++j){
2365                                         var nameIndex = null;
2366                                         var p=namePath[j - 1];
2367                                         var nameA=p.split("[");
2368                                         if (nameA.length > 1){
2369                                                 if(typeof(myObj[nameA[0]]) == "undefined"){
2370                                                         myObj[nameA[0]]=[ ];
2371                                                 } // if
2372                                                 nameIndex=parseInt(nameA[1]);
2373                                                 if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
2374                                                         myObj[nameA[0]][nameIndex] = { };
2375                                                 }
2376                                         } else if(typeof(myObj[nameA[0]]) == "undefined"){
2377                                                 myObj[nameA[0]] = { }
2378                                         } // if
2379
2380                                         if (nameA.length == 1){
2381                                                 myObj=myObj[nameA[0]];
2382                                         } else{
2383                                                 myObj=myObj[nameA[0]][nameIndex];
2384                                         } // if
2385                                 } // for
2386
2387                                 if ((elm.type != "select-multiple" && elm.type != "checkbox" && elm.type != "radio") || (elm.type=="radio" && elm.checked)){
2388                                         if(name == name.split("[")[0]){
2389                                                 myObj[name]=elm.value;
2390                                         } else{
2391                                                 // can not set value when there is no name
2392                                         }
2393                                 } else if (elm.type == "checkbox" && elm.checked){
2394                                         if(typeof(myObj[name]) == 'undefined'){
2395                                                 myObj[name]=[ ];
2396                                         }
2397                                         myObj[name].push(elm.value);
2398                                 } else if (elm.type == "select-multiple"){
2399                                         if(typeof(myObj[name]) == 'undefined'){
2400                                                 myObj[name]=[ ];
2401                                         }
2402                                         for (var jdx=0,len3=elm.options.length; jdx<len3; ++jdx){
2403                                                 if (elm.options[jdx].selected){
2404                                                         myObj[name].push(elm.options[jdx].value);
2405                                                 }
2406                                         }
2407                                 } // if
2408                                 name=undefined;
2409                         }); // forEach
2410                         ***/
2411                         return obj;
2412                 },
2413
2414                 // TODO: ComboBox might need time to process a recently input value.  This should be async?
2415                 isValid: function(){
2416                         // summary: make sure that every widget that has a validator function returns true
2417                         return dojo.every(this.getDescendants(), function(widget){
2418                                 return !widget.isValid || widget.isValid();
2419                         });
2420                 }
2421         });
2422
2423 dojo.declare(
2424         "dijit.form.Form",
2425         [dijit._Widget, dijit._Templated, dijit.form._FormMixin],
2426         {
2427                 // summary:
2428                 // Adds conveniences to regular HTML form
2429
2430                 // HTML <FORM> attributes
2431                 name: "",
2432                 action: "",
2433                 method: "",
2434                 encType: "",
2435                 "accept-charset": "",
2436                 accept: "",
2437                 target: "",
2438
2439                 templateString: "<form dojoAttachPoint='containerNode' dojoAttachEvent='onreset:_onReset,onsubmit:_onSubmit' name='${name}'></form>",
2440
2441                 attributeMap: dojo.mixin(dojo.clone(dijit._Widget.prototype.attributeMap),
2442                         {action: "", method: "", encType: "", "accept-charset": "", accept: "", target: ""}),
2443
2444                 execute: function(/*Object*/ formContents){
2445                         //      summary:
2446                         //              Deprecated: use submit()
2447                 },
2448
2449                 onExecute: function(){
2450                         // summary:
2451                         //              Deprecated: use onSubmit()
2452                 },
2453
2454                 setAttribute: function(/*String*/ attr, /*anything*/ value){
2455                         this.inherited(arguments);
2456                         switch(attr){
2457                                 case "encType":
2458                                         if(dojo.isIE){ this.domNode.encoding = value; }
2459                         }
2460                 },
2461
2462                 postCreate: function(){
2463                         // IE tries to hide encType
2464                         if(dojo.isIE && this.srcNodeRef && this.srcNodeRef.attributes){
2465                                 var item = this.srcNodeRef.attributes.getNamedItem('encType');
2466                                 if(item && !item.specified && (typeof item.value == "string")){
2467                                         this.setAttribute('encType', item.value);
2468                                 }
2469                         }
2470                         this.inherited(arguments);
2471                 },
2472
2473                 onReset: function(/*Event?*/e){ 
2474                         //      summary:
2475                         //              Callback when user resets the form. This method is intended
2476                         //              to be over-ridden. When the `reset` method is called
2477                         //              programmatically, the return value from `onReset` is used
2478                         //              to compute whether or not resetting should proceed
2479                         return true; // Boolean
2480                 },
2481
2482                 _onReset: function(e){
2483                         // create fake event so we can know if preventDefault() is called
2484                         var faux = {
2485                                 returnValue: true, // the IE way
2486                                 preventDefault: function(){  // not IE
2487                                                         this.returnValue = false;
2488                                                 },
2489                                 stopPropagation: function(){}, currentTarget: e.currentTarget, target: e.target
2490                         };
2491                         // if return value is not exactly false, and haven't called preventDefault(), then reset
2492                         if(!(this.onReset(faux) === false) && faux.returnValue){
2493                                 this.reset();
2494                         }
2495                         dojo.stopEvent(e);
2496                         return false;
2497                 },
2498
2499                 _onSubmit: function(e){
2500                         var fp = dijit.form.Form.prototype;
2501                         // TODO: remove ths if statement beginning with 2.0
2502                         if(this.execute != fp.execute || this.onExecute != fp.onExecute){
2503                                 dojo.deprecated("dijit.form.Form:execute()/onExecute() are deprecated. Use onSubmit() instead.", "", "2.0");
2504                                 this.onExecute();
2505                                 this.execute(this.getValues());
2506                         }
2507                         if(this.onSubmit(e) === false){ // only exactly false stops submit
2508                                 dojo.stopEvent(e);
2509                         }
2510                 },
2511                 
2512                 onSubmit: function(/*Event?*/e){ 
2513                         //      summary:
2514                         //              Callback when user submits the form. This method is
2515                         //              intended to be over-ridden, but by default it checks and
2516                         //              returns the validity of form elements. When the `submit`
2517                         //              method is called programmatically, the return value from
2518                         //              `onSubmit` is used to compute whether or not submission
2519                         //              should proceed
2520
2521                         return this.isValid(); // Boolean
2522                 },
2523
2524                 submit: function(){
2525                         // summary:
2526                         //              programmatically submit form if and only if the `onSubmit` returns true
2527                         if(!(this.onSubmit() === false)){
2528                                 this.containerNode.submit();
2529                         }
2530                 }
2531         }
2532 );
2533
2534 }
2535
2536 if(!dojo._hasResource["dijit.Dialog"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2537 dojo._hasResource["dijit.Dialog"] = true;
2538 dojo.provide("dijit.Dialog");
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549 dojo.declare(
2550         "dijit.DialogUnderlay",
2551         [dijit._Widget, dijit._Templated],
2552         {
2553                 // summary: The component that grays out the screen behind the dialog
2554         
2555                 // Template has two divs; outer div is used for fade-in/fade-out, and also to hold background iframe.
2556                 // Inner div has opacity specified in CSS file.
2557                 templateString: "<div class='dijitDialogUnderlayWrapper' id='${id}_wrapper'><div class='dijitDialogUnderlay ${class}' id='${id}' dojoAttachPoint='node'></div></div>",
2558
2559                 attributeMap: {},
2560
2561                 postCreate: function(){
2562                         // summary: Append the underlay to the body
2563                         dojo.body().appendChild(this.domNode);
2564                         this.bgIframe = new dijit.BackgroundIframe(this.domNode);
2565                 },
2566
2567                 layout: function(){
2568                         // summary: Sets the background to the size of the viewport
2569                         //
2570                         // description:
2571                         //      Sets the background to the size of the viewport (rather than the size
2572                         //      of the document) since we need to cover the whole browser window, even
2573                         //      if the document is only a few lines long.
2574
2575                         var viewport = dijit.getViewport();
2576                         var is = this.node.style,
2577                                 os = this.domNode.style;
2578
2579                         os.top = viewport.t + "px";
2580                         os.left = viewport.l + "px";
2581                         is.width = viewport.w + "px";
2582                         is.height = viewport.h + "px";
2583
2584                         // process twice since the scroll bar may have been removed
2585                         // by the previous resizing
2586                         var viewport2 = dijit.getViewport();
2587                         if(viewport.w != viewport2.w){ is.width = viewport2.w + "px"; }
2588                         if(viewport.h != viewport2.h){ is.height = viewport2.h + "px"; }
2589                 },
2590
2591                 show: function(){
2592                         // summary: Show the dialog underlay
2593                         this.domNode.style.display = "block";
2594                         this.layout();
2595                         if(this.bgIframe.iframe){
2596                                 this.bgIframe.iframe.style.display = "block";
2597                         }
2598                         this._resizeHandler = this.connect(window, "onresize", "layout");
2599                 },
2600
2601                 hide: function(){
2602                         // summary: hides the dialog underlay
2603                         this.domNode.style.display = "none";
2604                         if(this.bgIframe.iframe){
2605                                 this.bgIframe.iframe.style.display = "none";
2606                         }
2607                         this.disconnect(this._resizeHandler);
2608                 },
2609
2610                 uninitialize: function(){
2611                         if(this.bgIframe){
2612                                 this.bgIframe.destroy();
2613                         }
2614                 }
2615         }
2616 );
2617
2618
2619 dojo.declare("dijit._DialogMixin", null,
2620         {
2621                 attributeMap: dijit._Widget.prototype.attributeMap,
2622
2623                 // execute: Function
2624                 //      User defined function to do stuff when the user hits the submit button
2625                 execute: function(/*Object*/ formContents){},
2626
2627                 // onCancel: Function
2628                 //      Callback when user has canceled dialog, to notify container
2629                 //      (user shouldn't override)
2630                 onCancel: function(){},
2631
2632                 // onExecute: Function
2633                 //      Callback when user is about to execute dialog, to notify container
2634                 //      (user shouldn't override)
2635                 onExecute: function(){},
2636
2637                 _onSubmit: function(){
2638                         // summary: callback when user hits submit button
2639                         this.onExecute();       // notify container that we are about to execute
2640                         this.execute(this.getValues());
2641                 },
2642
2643                 _getFocusItems: function(/*Node*/ dialogNode){
2644                         // find focusable Items each time a dialog is opened
2645                         var focusItem = dijit.getFirstInTabbingOrder(dialogNode);
2646                         this._firstFocusItem = focusItem ? focusItem : dialogNode;
2647                         focusItem = dijit.getLastInTabbingOrder(dialogNode);
2648                         this._lastFocusItem = focusItem ? focusItem : this._firstFocusItem;
2649                         if(dojo.isMoz && this._firstFocusItem.tagName.toLowerCase() == "input" && dojo.attr(this._firstFocusItem, "type").toLowerCase() == "file"){
2650                                         //FF doesn't behave well when first element is input type=file, set first focusable to dialog container
2651                                         dojo.attr(dialogNode, "tabindex", "0");
2652                                         this._firstFocusItem = dialogNode;
2653                         }
2654                 }
2655         }
2656 );
2657
2658 dojo.declare(
2659         "dijit.Dialog",
2660         [dijit.layout.ContentPane, dijit._Templated, dijit.form._FormMixin, dijit._DialogMixin],
2661         {
2662                 // summary: A modal dialog Widget
2663                 //
2664                 // description:
2665                 //      Pops up a modal dialog window, blocking access to the screen
2666                 //      and also graying out the screen Dialog is extended from
2667                 //      ContentPane so it supports all the same parameters (href, etc.)
2668                 //
2669                 // example:
2670                 // |    <div dojoType="dijit.Dialog" href="test.html"></div>
2671                 //
2672                 // example:
2673                 // |    <div id="test">test content</div>
2674                 // |    ...
2675                 // |    var foo = new dijit.Dialog({ title: "test dialog" },dojo.byId("test"));
2676                 // |    foo.startup();
2677                 
2678                 templateString: null,
2679                 templateString:"<div class=\"dijitDialog\" tabindex=\"-1\" waiRole=\"dialog\" waiState=\"labelledby-${id}_title\">\n\t<div dojoAttachPoint=\"titleBar\" class=\"dijitDialogTitleBar\">\n\t<span dojoAttachPoint=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\">${title}</span>\n\t<span dojoAttachPoint=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" dojoAttachEvent=\"onclick: onCancel\">\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</div>\n",
2680
2681                 // open: Boolean
2682                 //              is True or False depending on state of dialog
2683                 open: false,
2684
2685                 // duration: Integer
2686                 //              The time in milliseconds it takes the dialog to fade in and out
2687                 duration: 400,
2688
2689                 // refocus: Boolean
2690                 //              A Toggle to modify the default focus behavior of a Dialog, which
2691                 //              is to re-focus the element which had focus before being opened.
2692                 //              False will disable refocusing. Default: true
2693                 refocus: true,
2694
2695                 // _firstFocusItem: DomNode
2696                 //              The pointer to the first focusable node in the dialog
2697                 _firstFocusItem:null,
2698                 
2699                 // _lastFocusItem: DomNode
2700                 //              The pointer to which node has focus prior to our dialog
2701                 _lastFocusItem:null,
2702
2703                 // doLayout: Boolean
2704                 //              Don't change this parameter from the default value.
2705                 //              This ContentPane parameter doesn't make sense for Dialog, since Dialog
2706                 //              is never a child of a layout container, nor can you specify the size of
2707                 //              Dialog in order to control the size of an inner widget. 
2708                 doLayout: false,
2709
2710                 attributeMap: dojo.mixin(dojo.clone(dijit._Widget.prototype.attributeMap),
2711                         {title: "titleBar"}),
2712
2713                 postCreate: function(){
2714                         dojo.body().appendChild(this.domNode);
2715                         this.inherited(arguments);
2716                         var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
2717                         if(this.closeButtonNode){
2718                                 this.closeButtonNode.setAttribute("title", _nlsResources.buttonCancel);
2719                         }
2720                         if(this.closeText){
2721                                 this.closeText.setAttribute("title", _nlsResources.buttonCancel);
2722                         }
2723                         var s = this.domNode.style;
2724                         s.visibility = "hidden";
2725                         s.position = "absolute";
2726                         s.display = "";
2727                         s.top = "-9999px";
2728
2729                         this.connect(this, "onExecute", "hide");
2730                         this.connect(this, "onCancel", "hide");
2731                         this._modalconnects = [];
2732                 },
2733
2734                 onLoad: function(){
2735                         // summary: when href is specified we need to reposition the dialog after the data is loaded
2736                         this._position();
2737                         this.inherited(arguments);
2738                 },
2739
2740                 _setup: function(){
2741                         // summary: 
2742                         //              stuff we need to do before showing the Dialog for the first
2743                         //              time (but we defer it until right beforehand, for
2744                         //              performance reasons)
2745
2746                         if(this.titleBar){
2747                                 this._moveable = new dojo.dnd.TimedMoveable(this.domNode, { handle: this.titleBar, timeout: 0 });
2748                         }
2749
2750                         this._underlay = new dijit.DialogUnderlay({
2751                                 id: this.id+"_underlay",
2752                                 "class": dojo.map(this["class"].split(/\s/), function(s){ return s+"_underlay"; }).join(" ")
2753                         });
2754
2755                         var node = this.domNode;
2756                         this._fadeIn = dojo.fx.combine(
2757                                 [dojo.fadeIn({
2758                                         node: node,
2759                                         duration: this.duration
2760                                  }),
2761                                  dojo.fadeIn({
2762                                         node: this._underlay.domNode,
2763                                         duration: this.duration,
2764                                         onBegin: dojo.hitch(this._underlay, "show")
2765                                  })
2766                                 ]
2767                         );
2768
2769                         this._fadeOut = dojo.fx.combine(
2770                                 [dojo.fadeOut({
2771                                         node: node,
2772                                         duration: this.duration,
2773                                         onEnd: function(){
2774                                                 node.style.visibility="hidden";
2775                                                 node.style.top = "-9999px";
2776                                         }
2777                                  }),
2778                                  dojo.fadeOut({
2779                                         node: this._underlay.domNode,
2780                                         duration: this.duration,
2781                                         onEnd: dojo.hitch(this._underlay, "hide")
2782                                  })
2783                                 ]
2784                         );
2785                 },
2786
2787                 uninitialize: function(){
2788                         if(this._fadeIn && this._fadeIn.status() == "playing"){
2789                                 this._fadeIn.stop();
2790                         }
2791                         if(this._fadeOut && this._fadeOut.status() == "playing"){
2792                                 this._fadeOut.stop();
2793                         }
2794                         if(this._underlay){
2795                                 this._underlay.destroy();
2796                         }
2797                 },
2798
2799                 _position: function(){
2800                         // summary: position modal dialog in center of screen
2801                         
2802                         if(dojo.hasClass(dojo.body(),"dojoMove")){ return; }
2803                         var viewport = dijit.getViewport();
2804                         var mb = dojo.marginBox(this.domNode);
2805
2806                         var style = this.domNode.style;
2807                         style.left = Math.floor((viewport.l + (viewport.w - mb.w)/2)) + "px";
2808                         style.top = Math.floor((viewport.t + (viewport.h - mb.h)/2)) + "px";
2809                 },
2810
2811                 _onKey: function(/*Event*/ evt){
2812                         // summary: handles the keyboard events for accessibility reasons
2813                         if(evt.keyCode){
2814                                 var node = evt.target;
2815                                 if (evt.keyCode == dojo.keys.TAB){
2816                                         this._getFocusItems(this.domNode);
2817                                 }
2818                                 var singleFocusItem = (this._firstFocusItem == this._lastFocusItem);
2819                                 // see if we are shift-tabbing from first focusable item on dialog
2820                                 if(node == this._firstFocusItem && evt.shiftKey && evt.keyCode == dojo.keys.TAB){
2821                                         if(!singleFocusItem){
2822                                                 dijit.focus(this._lastFocusItem); // send focus to last item in dialog
2823                                         }
2824                                         dojo.stopEvent(evt);
2825                                 }else if(node == this._lastFocusItem && evt.keyCode == dojo.keys.TAB && !evt.shiftKey){
2826                                         if (!singleFocusItem){
2827                                                 dijit.focus(this._firstFocusItem); // send focus to first item in dialog
2828                                         }
2829                                         dojo.stopEvent(evt);
2830                                 }else{
2831                                         // see if the key is for the dialog
2832                                         while(node){
2833                                                 if(node == this.domNode){
2834                                                         if(evt.keyCode == dojo.keys.ESCAPE){
2835                                                                 this.hide(); 
2836                                                         }else{
2837                                                                 return; // just let it go
2838                                                         }
2839                                                 }
2840                                                 node = node.parentNode;
2841                                         }
2842                                         // this key is for the disabled document window
2843                                         if(evt.keyCode != dojo.keys.TAB){ // allow tabbing into the dialog for a11y
2844                                                 dojo.stopEvent(evt);
2845                                         // opera won't tab to a div
2846                                         }else if(!dojo.isOpera){
2847                                                 try{
2848                                                         this._firstFocusItem.focus();
2849                                                 }catch(e){ /*squelch*/ }
2850                                         }
2851                                 }
2852                         }
2853                 },
2854
2855                 show: function(){
2856                         // summary: display the dialog
2857
2858                         if(this.open){ return; }
2859                         
2860                         // first time we show the dialog, there's some initialization stuff to do                       
2861                         if(!this._alreadyInitialized){
2862                                 this._setup();
2863                                 this._alreadyInitialized=true;
2864                         }
2865
2866                         if(this._fadeOut.status() == "playing"){
2867                                 this._fadeOut.stop();
2868                         }
2869
2870                         this._modalconnects.push(dojo.connect(window, "onscroll", this, "layout"));
2871                         this._modalconnects.push(dojo.connect(dojo.doc.documentElement, "onkeypress", this, "_onKey"));
2872
2873                         dojo.style(this.domNode, "opacity", 0);
2874                         this.domNode.style.visibility="";
2875                         this.open = true;
2876                         this._loadCheck(); // lazy load trigger
2877
2878                         this._position();
2879
2880                         this._fadeIn.play();
2881
2882                         this._savedFocus = dijit.getFocus(this);
2883
2884                         // find focusable Items each time dialog is shown since if dialog contains a widget the 
2885                         // first focusable items can change
2886                         this._getFocusItems(this.domNode);
2887
2888                         // set timeout to allow the browser to render dialog
2889                         setTimeout(dojo.hitch(this, function(){
2890                                 dijit.focus(this._firstFocusItem);
2891                         }), 50);
2892                 },
2893
2894                 hide: function(){
2895                         // summary: Hide the dialog
2896
2897                         // if we haven't been initialized yet then we aren't showing and we can just return             
2898                         if(!this._alreadyInitialized){
2899                                 return;
2900                         }
2901
2902                         if(this._fadeIn.status() == "playing"){
2903                                 this._fadeIn.stop();
2904                         }
2905                         this._fadeOut.play();
2906
2907                         if (this._scrollConnected){
2908                                 this._scrollConnected = false;
2909                         }
2910                         dojo.forEach(this._modalconnects, dojo.disconnect);
2911                         this._modalconnects = [];
2912                         if(this.refocus){
2913                                 this.connect(this._fadeOut,"onEnd",dojo.hitch(dijit,"focus",this._savedFocus));
2914                         }
2915                         this.open = false;
2916                 },
2917
2918                 layout: function() {
2919                         // summary: position the Dialog and the underlay
2920                         if(this.domNode.style.visibility != "hidden"){
2921                                 this._underlay.layout();
2922                                 this._position();
2923                         }
2924                 },
2925                 
2926                 destroy: function(){
2927                         dojo.forEach(this._modalconnects, dojo.disconnect);
2928                         if(this.refocus && this.open){
2929                                 var fo = this._savedFocus;
2930                                 setTimeout(dojo.hitch(dijit,"focus",fo),25);
2931                         }
2932                         this.inherited(arguments);                      
2933                 }
2934         }
2935 );
2936
2937 dojo.declare(
2938         "dijit.TooltipDialog",
2939         [dijit.layout.ContentPane, dijit._Templated, dijit.form._FormMixin, dijit._DialogMixin],
2940         {
2941                 // summary:
2942                 //              Pops up a dialog that appears like a Tooltip
2943                 //
2944                 // title: String
2945                 //              Description of tooltip dialog (required for a11Y)
2946                 title: "",
2947
2948                 // doLayout: Boolean
2949                 //              Don't change this parameter from the default value.
2950                 //              This ContentPane parameter doesn't make sense for TooltipDialog, since TooltipDialog
2951                 //              is never a child of a layout container, nor can you specify the size of
2952                 //              TooltipDialog in order to control the size of an inner widget. 
2953                 doLayout: false,
2954
2955                 // _firstFocusItem: DomNode
2956                 //              The pointer to the first focusable node in the dialog
2957                 _firstFocusItem:null,
2958                 
2959                 // _lastFocusItem: DomNode
2960                 //              The domNode that had focus before we took it.
2961                 _lastFocusItem: null,
2962
2963                 templateString: null,
2964                 templateString:"<div class=\"dijitTooltipDialog\" waiRole=\"presentation\">\n\t<div class=\"dijitTooltipContainer\" waiRole=\"presentation\">\n\t\t<div class =\"dijitTooltipContents dijitTooltipFocusNode\" dojoAttachPoint=\"containerNode\" tabindex=\"-1\" waiRole=\"dialog\"></div>\n\t</div>\n\t<div class=\"dijitTooltipConnector\" waiRole=\"presenation\"></div>\n</div>\n",
2965
2966                 postCreate: function(){
2967                         this.inherited(arguments);
2968                         this.connect(this.containerNode, "onkeypress", "_onKey");
2969                         this.containerNode.title = this.title;
2970                 },
2971
2972                 orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ corner){
2973                         // summary: configure widget to be displayed in given position relative to the button
2974                         this.domNode.className="dijitTooltipDialog " +" dijitTooltipAB"+(corner.charAt(1)=='L'?"Left":"Right")+" dijitTooltip"+(corner.charAt(0)=='T' ? "Below" : "Above");
2975                 },
2976
2977                 onOpen: function(/*Object*/ pos){
2978                         // summary: called when dialog is displayed
2979                 
2980                         this._getFocusItems(this.containerNode);
2981                         this.orient(this.domNode,pos.aroundCorner, pos.corner);
2982                         this._loadCheck(); // lazy load trigger
2983                         dijit.focus(this._firstFocusItem);
2984                 },
2985                 
2986                 _onKey: function(/*Event*/ evt){
2987                         // summary: keep keyboard focus in dialog; close dialog on escape key
2988                         var node = evt.target;
2989                         if (evt.keyCode == dojo.keys.TAB){
2990                                         this._getFocusItems(this.containerNode);
2991                         }
2992                         var singleFocusItem = (this._firstFocusItem == this._lastFocusItem);
2993                         if(evt.keyCode == dojo.keys.ESCAPE){
2994                                 this.onCancel();
2995                         }else if(node == this._firstFocusItem && evt.shiftKey && evt.keyCode == dojo.keys.TAB){
2996                                 if(!singleFocusItem){
2997                                         dijit.focus(this._lastFocusItem); // send focus to last item in dialog
2998                                 }
2999                                 dojo.stopEvent(evt);
3000                         }else if(node == this._lastFocusItem && evt.keyCode == dojo.keys.TAB && !evt.shiftKey){
3001                                 if(!singleFocusItem){
3002                                         dijit.focus(this._firstFocusItem); // send focus to first item in dialog
3003                                 }
3004                                 dojo.stopEvent(evt);
3005                         }else if(evt.keyCode == dojo.keys.TAB){
3006                                 // we want the browser's default tab handling to move focus
3007                                 // but we don't want the tab to propagate upwards
3008                                 evt.stopPropagation();
3009                         }
3010                 }
3011         }       
3012 );
3013
3014
3015 }
3016
3017 if(!dojo._hasResource["dijit._editor.selection"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
3018 dojo._hasResource["dijit._editor.selection"] = true;
3019 dojo.provide("dijit._editor.selection");
3020
3021 // FIXME:
3022 //              all of these methods branch internally for IE. This is probably
3023 //              sub-optimal in terms of runtime performance. We should investigate the
3024 //              size difference for differentiating at definition time.
3025
3026 dojo.mixin(dijit._editor.selection, {
3027         getType: function(){
3028                 // summary: Get the selection type (like dojo.doc.select.type in IE).
3029                 if(dojo.doc.selection){ //IE
3030                         return dojo.doc.selection.type.toLowerCase();
3031                 }else{
3032                         var stype = "text";
3033
3034                         // Check if the actual selection is a CONTROL (IMG, TABLE, HR, etc...).
3035                         var oSel;
3036                         try{
3037                                 oSel = dojo.global.getSelection();
3038                         }catch(e){ /*squelch*/ }
3039
3040                         if(oSel && oSel.rangeCount==1){
3041                                 var oRange = oSel.getRangeAt(0);
3042                                 if(     (oRange.startContainer == oRange.endContainer) &&
3043                                         ((oRange.endOffset - oRange.startOffset) == 1) &&
3044                                         (oRange.startContainer.nodeType != 3 /* text node*/)
3045                                 ){
3046                                         stype = "control";
3047                                 }
3048                         }
3049                         return stype;
3050                 }
3051         },
3052
3053         getSelectedText: function(){
3054                 // summary:
3055                 //              Return the text (no html tags) included in the current selection or null if no text is selected
3056                 if(dojo.doc.selection){ //IE
3057                         if(dijit._editor.selection.getType() == 'control'){
3058                                 return null;
3059                         }
3060                         return dojo.doc.selection.createRange().text;
3061                 }else{
3062                         var selection = dojo.global.getSelection();
3063                         if(selection){
3064                                 return selection.toString();
3065                         }
3066                 }
3067                 return ''
3068         },
3069
3070         getSelectedHtml: function(){
3071                 // summary:
3072                 //              Return the html of the current selection or null if unavailable
3073                 if(dojo.doc.selection){ //IE
3074                         if(dijit._editor.selection.getType() == 'control'){
3075                                 return null;
3076                         }
3077                         return dojo.doc.selection.createRange().htmlText;
3078                 }else{
3079                         var selection = dojo.global.getSelection();
3080                         if(selection && selection.rangeCount){
3081                                 var frag = selection.getRangeAt(0).cloneContents();
3082                                 var div = dojo.doc.createElement("div");
3083                                 div.appendChild(frag);
3084                                 return div.innerHTML;
3085                         }
3086                         return null;
3087                 }
3088         },
3089
3090         getSelectedElement: function(){
3091                 // summary:
3092                 //              Retrieves the selected element (if any), just in the case that
3093                 //              a single element (object like and image or a table) is
3094                 //              selected.
3095                 if(this.getType() == "control"){
3096                         if(dojo.doc.selection){ //IE
3097                                 var range = dojo.doc.selection.createRange();
3098                                 if(range && range.item){
3099                                         return dojo.doc.selection.createRange().item(0);
3100                                 }
3101                         }else{
3102                                 var selection = dojo.global.getSelection();
3103                                 return selection.anchorNode.childNodes[ selection.anchorOffset ];
3104                         }
3105                 }
3106                 return null;
3107         },
3108
3109         getParentElement: function(){
3110                 // summary:
3111                 //              Get the parent element of the current selection
3112                 if(this.getType() == "control"){
3113                         var p = this.getSelectedElement();
3114                         if(p){ return p.parentNode; }
3115                 }else{
3116                         if(dojo.doc.selection){ //IE
3117                                 return dojo.doc.selection.createRange().parentElement();
3118                         }else{
3119                                 var selection = dojo.global.getSelection();
3120                                 if(selection){
3121                                         var node = selection.anchorNode;
3122
3123                                         while(node && (node.nodeType != 1)){ // not an element
3124                                                 node = node.parentNode;
3125                                         }
3126
3127                                         return node;
3128                                 }
3129                         }
3130                 }
3131                 return null;
3132         },
3133
3134         hasAncestorElement: function(/*String*/tagName /* ... */){
3135                 // summary:
3136                 //              Check whether current selection has a  parent element which is
3137                 //              of type tagName (or one of the other specified tagName)
3138                 return this.getAncestorElement.apply(this, arguments) != null;
3139         },
3140
3141         getAncestorElement: function(/*String*/tagName /* ... */){
3142                 // summary:
3143                 //              Return the parent element of the current selection which is of
3144                 //              type tagName (or one of the other specified tagName)
3145
3146                 var node = this.getSelectedElement() || this.getParentElement();
3147                 return this.getParentOfType(node, arguments);
3148         },
3149
3150         isTag: function(/*DomNode*/node, /*Array*/tags){
3151                 if(node && node.tagName){
3152                         var _nlc = node.tagName.toLowerCase();
3153                         for(var i=0; i<tags.length; i++){
3154                                 var _tlc = String(tags[i]).toLowerCase();
3155                                 if(_nlc == _tlc){
3156                                         return _tlc;
3157                                 }
3158                         }
3159                 }
3160                 return "";
3161         },
3162
3163         getParentOfType: function(/*DomNode*/node, /*Array*/tags){
3164                 while(node){
3165                         if(this.isTag(node, tags).length){
3166                                 return node;
3167                         }
3168                         node = node.parentNode;
3169                 }
3170                 return null;
3171         },
3172
3173         collapse: function(/*Boolean*/beginning) {
3174                 // summary: clear current selection
3175           if(window['getSelection']){
3176                   var selection = dojo.global.getSelection();
3177                   if(selection.removeAllRanges){ // Mozilla
3178                           if(beginning){
3179                                   selection.collapseToStart();
3180                           }else{
3181                                   selection.collapseToEnd();
3182                           }
3183                   }else{ // Safari
3184                           // pulled from WebCore/ecma/kjs_window.cpp, line 2536
3185                            selection.collapse(beginning);
3186                   }
3187           }else if(dojo.doc.selection){ // IE
3188                   var range = dojo.doc.selection.createRange();
3189                   range.collapse(beginning);
3190                   range.select();
3191           }
3192         },
3193
3194         remove: function(){
3195                 // summary: delete current selection
3196                 var _s = dojo.doc.selection;
3197                 if(_s){ //IE
3198                         if(_s.type.toLowerCase() != "none"){
3199                                 _s.clear();
3200                         }
3201                         return _s;
3202                 }else{
3203                         _s = dojo.global.getSelection();
3204                         _s.deleteFromDocument();
3205                         return _s;
3206                 }
3207         },
3208
3209         selectElementChildren: function(/*DomNode*/element,/*Boolean?*/nochangefocus){
3210                 // summary:
3211                 //              clear previous selection and select the content of the node
3212                 //              (excluding the node itself)
3213                 var _window = dojo.global;
3214                 var _document = dojo.doc;
3215                 element = dojo.byId(element);
3216                 if(_document.selection && dojo.body().createTextRange){ // IE
3217                         var range = element.ownerDocument.body.createTextRange();
3218                         range.moveToElementText(element);
3219                         if(!nochangefocus){
3220                                 try{
3221                                         range.select(); // IE throws an exception here if the widget is hidden.  See #5439
3222                                 }catch(e){ /* squelch */}
3223                         }
3224                 }else if(_window.getSelection){
3225                         var selection = _window.getSelection();
3226                         if(selection.setBaseAndExtent){ // Safari
3227                                 selection.setBaseAndExtent(element, 0, element, element.innerText.length - 1);
3228                         }else if(selection.selectAllChildren){ // Mozilla
3229                                 selection.selectAllChildren(element);
3230                         }
3231                 }
3232         },
3233
3234         selectElement: function(/*DomNode*/element,/*Boolean?*/nochangefocus){
3235                 // summary:
3236                 //              clear previous selection and select element (including all its children)
3237                 var range, _document = dojo.doc;
3238                 element = dojo.byId(element);
3239                 if(_document.selection && dojo.body().createTextRange){ // IE
3240                         try{
3241                                 range = dojo.body().createControlRange();
3242                                 range.addElement(element);
3243                                 if(!nochangefocus){
3244                                         range.select();
3245                                 }
3246                         }catch(e){
3247                                 this.selectElementChildren(element,nochangefocus);
3248                         }
3249                 }else if(dojo.global.getSelection){
3250                         var selection = dojo.global.getSelection();
3251                         // FIXME: does this work on Safari?
3252                         if(selection.removeAllRanges){ // Mozilla
3253                                 range = _document.createRange();
3254                                 range.selectNode(element);
3255                                 selection.removeAllRanges();
3256                                 selection.addRange(range);
3257                         }
3258                 }
3259         }
3260 });
3261
3262 }
3263
3264 if(!dojo._hasResource["dijit._editor.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
3265 dojo._hasResource["dijit._editor.html"] = true;
3266 dojo.provide("dijit._editor.html");
3267
3268 dijit._editor.escapeXml=function(/*String*/str, /*Boolean*/noSingleQuotes){
3269         //summary:
3270         //              Adds escape sequences for special characters in XML: &<>"'
3271         //              Optionally skips escapes for single quotes
3272         str = str.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
3273         if(!noSingleQuotes){
3274                 str = str.replace(/'/gm, "&#39;");
3275         }
3276         return str; // string
3277 };
3278
3279 dijit._editor.getNodeHtml=function(/* DomNode */node){
3280         var output;
3281         switch(node.nodeType){
3282                 case 1: //element node
3283                         output = '<'+node.nodeName.toLowerCase();
3284
3285                         //store the list of attributes and sort it to have the
3286                         //attributes appear in the dictionary order
3287                         var attrarray = [];
3288                         if(dojo.isIE && node.outerHTML){
3289                                 var s = node.outerHTML;
3290                                 s = s.substr(0,s.indexOf('>'));
3291                                 s = s.replace(/(['"])[^"']*\1/g, '');//to make the following regexp safe
3292                                 var reg = /([^\s=]+)=/g;
3293                                 var m, key;
3294                                 while((m = reg.exec(s))){
3295                                         key=m[1];
3296                                         if(key.substr(0,3) != '_dj'){
3297                                                 if(key == 'src' || key == 'href'){
3298                                                         if(node.getAttribute('_djrealurl')){
3299                                                                 attrarray.push([key,node.getAttribute('_djrealurl')]);
3300                                                                 continue;
3301                                                         }
3302                                                 }
3303                                                 if(key=='style'){
3304                                                         attrarray.push([key, node.style.cssText.toLowerCase()]);
3305                                                 }else{
3306                                                         attrarray.push([key, key=='class'?node.className:node.getAttribute(key)]);
3307                                                 }
3308                                         }
3309                                 }
3310                         }else{
3311                                 var attr, i=0, attrs = node.attributes;
3312                                 while((attr=attrs[i++])){
3313                                         //ignore all attributes starting with _dj which are
3314                                         //internal temporary attributes used by the editor
3315                                         var n=attr.name;
3316                                         if(n.substr(0,3) != '_dj' /*&&
3317                                                 (attr.specified == undefined || attr.specified)*/){
3318                                                 var v = attr.value;
3319                                                 if(n == 'src' || n == 'href'){
3320                                                         if(node.getAttribute('_djrealurl')){
3321                                                                 v = node.getAttribute('_djrealurl');
3322                                                         }
3323                                                 }
3324                                                 attrarray.push([n,v]);
3325                                         }
3326                                 }
3327                         }
3328                         attrarray.sort(function(a,b){
3329                                 return a[0]<b[0]?-1:(a[0]==b[0]?0:1);
3330                         });
3331                         i=0;
3332                         while((attr=attrarray[i++])){
3333                                 output += ' '+attr[0]+'="'+
3334                                         (dojo.isString(attr[1]) ? dijit._editor.escapeXml(attr[1],true) : attr[1])+'"';
3335                         }
3336                         if(node.childNodes.length){
3337                                 output += '>' + dijit._editor.getChildrenHtml(node)+'</'+node.nodeName.toLowerCase()+'>';
3338                         }else{
3339                                 output += ' />';
3340                         }
3341                         break;
3342                 case 3: //text
3343                         // FIXME:
3344                         output = dijit._editor.escapeXml(node.nodeValue,true);
3345                         break;
3346                 case 8: //comment
3347                         // FIXME:
3348                         output = '<!--'+dijit._editor.escapeXml(node.nodeValue,true)+'-->';
3349                         break;
3350                 default:
3351                         output = "Element not recognized - Type: " + node.nodeType + " Name: " + node.nodeName;
3352         }
3353         return output;
3354 };
3355
3356 dijit._editor.getChildrenHtml = function(/* DomNode */dom){
3357         // summary: Returns the html content of a DomNode and children
3358         var out = "";
3359         if(!dom){ return out; }
3360         var nodes = dom["childNodes"]||dom;
3361         var i=0;
3362         var node;
3363         while((node=nodes[i++])){
3364                 out += dijit._editor.getNodeHtml(node);
3365         }
3366         return out; // String
3367 }
3368
3369 }
3370
3371 if(!dojo._hasResource["dijit._editor.RichText"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
3372 dojo._hasResource["dijit._editor.RichText"] = true;
3373 dojo.provide("dijit._editor.RichText");
3374
3375
3376
3377
3378
3379
3380
3381 // used to restore content when user leaves this page then comes back
3382 // but do not try doing dojo.doc.write if we are using xd loading.
3383 // dojo.doc.write will only work if RichText.js is included in the dojo.js
3384 // file. If it is included in dojo.js and you want to allow rich text saving
3385 // for back/forward actions, then set dojo.config.allowXdRichTextSave = true.
3386 if(!dojo.config["useXDomain"] || dojo.config["allowXdRichTextSave"]){
3387         if(dojo._postLoad){
3388                 (function(){
3389                         var savetextarea = dojo.doc.createElement('textarea');
3390                         savetextarea.id = dijit._scopeName + "._editor.RichText.savedContent";
3391                         var s = savetextarea.style;
3392                         s.display='none';
3393                         s.position='absolute';
3394                         s.top="-100px";
3395                         s.left="-100px";
3396                         s.height="3px";
3397                         s.width="3px";
3398                         dojo.body().appendChild(savetextarea);
3399                 })();
3400         }else{
3401                 //dojo.body() is not available before onLoad is fired
3402                 try{
3403                         dojo.doc.write('<textarea id="' + dijit._scopeName + '._editor.RichText.savedContent" ' +
3404                                 'style="display:none;position:absolute;top:-100px;left:-100px;height:3px;width:3px;overflow:hidden;"></textarea>');
3405                 }catch(e){ }
3406         }
3407 }
3408 dojo.declare("dijit._editor.RichText", dijit._Widget, {
3409         constructor: function(){
3410                 // summary:
3411                 //              dijit._editor.RichText is the core of the WYSIWYG editor in dojo, which
3412                 //              provides the basic editing features. It also encapsulates the differences
3413                 //              of different js engines for various browsers
3414                 //
3415                 // contentPreFilters: Array
3416                 //              pre content filter function register array.
3417                 //              these filters will be executed before the actual
3418                 //              editing area get the html content
3419                 this.contentPreFilters = [];
3420
3421                 // contentPostFilters: Array
3422                 //              post content filter function register array.
3423                 //              these will be used on the resulting html
3424                 //              from contentDomPostFilters. The resuling
3425                 //              content is the final html (returned by getValue())
3426                 this.contentPostFilters = [];
3427
3428                 // contentDomPreFilters: Array
3429                 //              pre content dom filter function register array.
3430                 //              these filters are applied after the result from
3431                 //              contentPreFilters are set to the editing area
3432                 this.contentDomPreFilters = [];
3433
3434                 // contentDomPostFilters: Array
3435                 //              post content dom filter function register array.
3436                 //              these filters are executed on the editing area dom
3437                 //              the result from these will be passed to contentPostFilters
3438                 this.contentDomPostFilters = [];
3439
3440                 // editingAreaStyleSheets: Array
3441                 //              array to store all the stylesheets applied to the editing area
3442                 this.editingAreaStyleSheets=[];
3443
3444                 this._keyHandlers = {};
3445                 this.contentPreFilters.push(dojo.hitch(this, "_preFixUrlAttributes"));
3446                 if(dojo.isMoz){
3447                         this.contentPreFilters.push(this._fixContentForMoz);
3448                         this.contentPostFilters.push(this._removeMozBogus);
3449                 }else if(dojo.isSafari){
3450                         this.contentPostFilters.push(this._removeSafariBogus);
3451                 }
3452                 //this.contentDomPostFilters.push(this._postDomFixUrlAttributes);
3453
3454                 this.onLoadDeferred = new dojo.Deferred();
3455         },
3456
3457         // inheritWidth: Boolean
3458         //              whether to inherit the parent's width or simply use 100%
3459         inheritWidth: false,
3460
3461         // focusOnLoad: Boolean
3462         //              whether focusing into this instance of richtext when page onload
3463         focusOnLoad: false,
3464
3465         // name: String
3466         //              If a save name is specified the content is saved and restored when the user
3467         //              leave this page can come back, or if the editor is not properly closed after
3468         //              editing has started.
3469         name: "",
3470
3471         // styleSheets: String
3472         //              semicolon (";") separated list of css files for the editing area
3473         styleSheets: "",
3474
3475         // _content: String
3476         //              temporary content storage
3477         _content: "",
3478
3479         // height: String
3480         //              set height to fix the editor at a specific height, with scrolling.
3481         //              By default, this is 300px. If you want to have the editor always
3482         //              resizes to accommodate the content, use AlwaysShowToolbar plugin
3483         //              and set height=""
3484         height: "300px",
3485
3486         // minHeight: String
3487         //              The minimum height that the editor should have
3488         minHeight: "1em",
3489         
3490         // isClosed: Boolean
3491         isClosed: true,
3492
3493         // isLoaded: Boolean
3494         isLoaded: false,
3495
3496         // _SEPARATOR: String
3497         //              used to concat contents from multiple textareas into a single string
3498         _SEPARATOR: "@@**%%__RICHTEXTBOUNDRY__%%**@@",
3499
3500         // onLoadDeferred: dojo.Deferred
3501         //              deferred which is fired when the editor finishes loading
3502         onLoadDeferred: null,
3503
3504         postCreate: function(){
3505                 // summary: init
3506                 dojo.publish(dijit._scopeName + "._editor.RichText::init", [this]);
3507                 this.open();
3508                 this.setupDefaultShortcuts();
3509         },
3510
3511         setupDefaultShortcuts: function(){
3512                 // summary: add some default key handlers
3513                 // description:
3514                 //              Overwrite this to setup your own handlers. The default
3515                 //              implementation does not use Editor commands, but directly
3516                 //              executes the builtin commands within the underlying browser
3517                 //              support.
3518                 var exec = function(cmd, arg){
3519                         return arguments.length == 1 ? function(){ this.execCommand(cmd); } :
3520                                 function(){ this.execCommand(cmd, arg); };
3521                 };
3522
3523                 var ctrlKeyHandlers = { b: exec("bold"),
3524                         i: exec("italic"),
3525                         u: exec("underline"),
3526                         a: exec("selectall"),
3527                         s: function(){ this.save(true); },
3528
3529                         "1": exec("formatblock", "h1"),
3530                         "2": exec("formatblock", "h2"),
3531                         "3": exec("formatblock", "h3"),
3532                         "4": exec("formatblock", "h4"),
3533
3534                         "\\": exec("insertunorderedlist") };
3535
3536                 if(!dojo.isIE){
3537                         ctrlKeyHandlers.Z = exec("redo"); //FIXME: undo?
3538                 }
3539
3540                 for(var key in ctrlKeyHandlers){
3541                         this.addKeyHandler(key, this.KEY_CTRL, ctrlKeyHandlers[key]);
3542                 }
3543         },
3544
3545         // events: Array
3546         //               events which should be connected to the underlying editing area
3547         events: ["onKeyPress", "onKeyDown", "onKeyUp", "onClick"],
3548
3549         // events: Array
3550         //               events which should be connected to the underlying editing
3551         //               area, events in this array will be addListener with
3552         //               capture=true
3553         captureEvents: [],
3554
3555         _editorCommandsLocalized: false,
3556         _localizeEditorCommands: function(){
3557                 if(this._editorCommandsLocalized){
3558                         return;
3559                 }
3560                 this._editorCommandsLocalized = true;
3561
3562                 //in IE, names for blockformat is locale dependent, so we cache the values here
3563
3564                 //if the normal way fails, we try the hard way to get the list
3565
3566                 //do not use _cacheLocalBlockFormatNames here, as it will
3567                 //trigger security warning in IE7
3568
3569                 //in the array below, ul can not come directly after ol,
3570                 //otherwise the queryCommandValue returns Normal for it
3571                 var formats = ['p', 'pre', 'address', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'div', 'ul'];
3572                 var localhtml = "", format, i=0;
3573                 while((format=formats[i++])){
3574                         if(format.charAt(1) != 'l'){
3575                                 localhtml += "<"+format+"><span>content</span></"+format+">";
3576                         }else{
3577                                 localhtml += "<"+format+"><li>content</li></"+format+">";
3578                         }
3579                 }
3580                 //queryCommandValue returns empty if we hide editNode, so move it out of screen temporary
3581                 var div=dojo.doc.createElement('div');
3582                 div.style.position = "absolute";
3583                 div.style.left = "-2000px";
3584                 div.style.top = "-2000px";
3585                 dojo.doc.body.appendChild(div);
3586                 div.innerHTML = localhtml;
3587                 var node = div.firstChild;
3588                 while(node){
3589                         dijit._editor.selection.selectElement(node.firstChild);
3590                         dojo.withGlobal(this.window, "selectElement", dijit._editor.selection, [node.firstChild]);
3591                         var nativename = node.tagName.toLowerCase();
3592                         this._local2NativeFormatNames[nativename] = dojo.doc.queryCommandValue("formatblock");//this.queryCommandValue("formatblock");
3593                         this._native2LocalFormatNames[this._local2NativeFormatNames[nativename]] = nativename;
3594                         node = node.nextSibling;
3595                 }
3596                 dojo.doc.body.removeChild(div);
3597         },
3598
3599         open: function(/*DomNode?*/element){
3600                 // summary:
3601                 //              Transforms the node referenced in this.domNode into a rich text editing
3602                 //              node. This will result in the creation and replacement with an <iframe>
3603                 //              if designMode(FF)/contentEditable(IE) is used.
3604
3605                 if((!this.onLoadDeferred)||(this.onLoadDeferred.fired >= 0)){
3606                         this.onLoadDeferred = new dojo.Deferred();
3607                 }
3608
3609                 if(!this.isClosed){ this.close(); }
3610                 dojo.publish(dijit._scopeName + "._editor.RichText::open", [ this ]);
3611
3612                 this._content = "";
3613                 if((arguments.length == 1)&&(element["nodeName"])){ this.domNode = element; } // else unchanged
3614
3615                 var html;
3616                 if(     (this.domNode["nodeName"])&&
3617                         (this.domNode.nodeName.toLowerCase() == "textarea")){
3618                         // if we were created from a textarea, then we need to create a
3619                         // new editing harness node.
3620                         this.textarea = this.domNode;
3621                         this.name=this.textarea.name;
3622                         html = this._preFilterContent(this.textarea.value);
3623                         this.domNode = dojo.doc.createElement("div");
3624                         this.domNode.setAttribute('widgetId',this.id);
3625                         this.textarea.removeAttribute('widgetId');
3626                         this.domNode.cssText = this.textarea.cssText;
3627                         this.domNode.className += " "+this.textarea.className;
3628                         dojo.place(this.domNode, this.textarea, "before");
3629                         var tmpFunc = dojo.hitch(this, function(){
3630                                 //some browsers refuse to submit display=none textarea, so
3631                                 //move the textarea out of screen instead
3632                                 dojo.attr(this.textarea, 'tabIndex', '-1');
3633                                 with(this.textarea.style){
3634                                         display = "block";
3635                                         position = "absolute";
3636                                         left = top = "-1000px";
3637
3638                                         if(dojo.isIE){ //nasty IE bug: abnormal formatting if overflow is not hidden
3639                                                 this.__overflow = overflow;
3640                                                 overflow = "hidden";
3641                                         }
3642                                 }
3643                         });
3644                         if(dojo.isIE){
3645                                 setTimeout(tmpFunc, 10);
3646                         }else{
3647                                 tmpFunc();
3648                         }
3649
3650                         // this.domNode.innerHTML = html;
3651
3652 //                              if(this.textarea.form){
3653 //                                      // FIXME: port: this used to be before advice!!!
3654 //                                      dojo.connect(this.textarea.form, "onsubmit", this, function(){
3655 //                                              // FIXME: should we be calling close() here instead?
3656 //                                              this.textarea.value = this.getValue();
3657 //                                      });
3658 //                              }
3659                 }else{
3660                         html = this._preFilterContent(dijit._editor.getChildrenHtml(this.domNode));
3661                         this.domNode.innerHTML = '';
3662                 }
3663                 if(html == ""){ html = "&nbsp;"; }
3664
3665                 var content = dojo.contentBox(this.domNode);
3666                 // var content = dojo.contentBox(this.srcNodeRef);
3667                 this._oldHeight = content.h;
3668                 this._oldWidth = content.w;
3669
3670                 // If we're a list item we have to put in a blank line to force the
3671                 // bullet to nicely align at the top of text
3672                 if(     (this.domNode["nodeName"]) &&
3673                         (this.domNode.nodeName == "LI") ){
3674                         this.domNode.innerHTML = " <br>";
3675                 }
3676
3677                 this.editingArea = dojo.doc.createElement("div");
3678                 this.domNode.appendChild(this.editingArea);
3679
3680                 if(this.name != "" && (!dojo.config["useXDomain"] || dojo.config["allowXdRichTextSave"])){
3681                         var saveTextarea = dojo.byId(dijit._scopeName + "._editor.RichText.savedContent");
3682                         if(saveTextarea.value != ""){
3683                                 var datas = saveTextarea.value.split(this._SEPARATOR), i=0, dat;
3684                                 while((dat=datas[i++])){
3685                                         var data = dat.split(":");
3686                                         if(data[0] == this.name){
3687                                                 html = data[1];
3688                                                 datas.splice(i, 1);
3689                                                 break;
3690                                         }
3691                                 }
3692                         }
3693
3694                         // FIXME: need to do something different for Opera/Safari
3695                         this.connect(window, "onbeforeunload", "_saveContent");
3696                         // dojo.connect(window, "onunload", this, "_saveContent");
3697                 }
3698
3699                 this.isClosed = false;
3700                 // Safari's selections go all out of whack if we do it inline,
3701                 // so for now IE is our only hero
3702                 //if(typeof dojo.doc.body.contentEditable != "undefined"){
3703                 if(dojo.isIE || dojo.isSafari || dojo.isOpera){ // contentEditable, easy                
3704
3705                         if(dojo.config["useXDomain"] && !dojo.config["dojoBlankHtmlUrl"]){
3706                                 console.debug("dijit._editor.RichText: When using cross-domain Dojo builds,"
3707                                 + " please save dojo/resources/blank.html to your domain and set djConfig.dojoBlankHtmlUrl"
3708                                 + " to the path on your domain to blank.html");
3709                         }
3710
3711                         var burl = dojo.config["dojoBlankHtmlUrl"] || (dojo.moduleUrl("dojo", "resources/blank.html")+"");
3712                         var ifr = this.editorObject = this.iframe = dojo.doc.createElement('iframe');
3713                         ifr.id = this.id+"_iframe";
3714                         ifr.src = burl;
3715                         ifr.style.border = "none";
3716                         ifr.style.width = "100%";
3717                         ifr.frameBorder = 0;
3718                         // ifr.style.scrolling = this.height ? "auto" : "vertical";
3719                         this.editingArea.appendChild(ifr);
3720                         var h = null; // set later in non-ie6 branch
3721                         var loadFunc = dojo.hitch( this, function(){
3722                                 if(h){ dojo.disconnect(h); h = null; }
3723                                 this.window = ifr.contentWindow;
3724                                 var d = this.document = this.window.document;
3725                                 d.open();
3726                                 d.write(this._getIframeDocTxt(html));
3727                                 d.close();
3728
3729                                 if(dojo.isIE >= 7){
3730                                         if(this.height){
3731                                                 ifr.style.height = this.height;
3732                                         }
3733                                         if(this.minHeight){
3734                                                 ifr.style.minHeight = this.minHeight;
3735                                         }
3736                                 }else{
3737                                         ifr.style.height = this.height ? this.height : this.minHeight;
3738                                 }
3739
3740                                 if(dojo.isIE){
3741                                         this._localizeEditorCommands();
3742                                 }
3743
3744                                 this.onLoad();
3745                                 this.savedContent = this.getValue(true);
3746                         });
3747                         if(dojo.isIE && dojo.isIE < 7){ // IE 6 is a steaming pile...
3748                                 var t = setInterval(function(){
3749                                         if(ifr.contentWindow.isLoaded){
3750                                                 clearInterval(t);
3751                                                 loadFunc();
3752                                         }
3753                                 }, 100);
3754                         }else{ // blissful sanity!
3755                                 h = dojo.connect(
3756                                         ((dojo.isIE) ? ifr.contentWindow : ifr), "onload", loadFunc
3757                                 );
3758                         }
3759                 }else{ // designMode in iframe
3760                         this._drawIframe(html);
3761                         this.savedContent = this.getValue(true);
3762                 }
3763
3764                 // TODO: this is a guess at the default line-height, kinda works
3765                 if(this.domNode.nodeName == "LI"){ this.domNode.lastChild.style.marginTop = "-1.2em"; }
3766                 this.domNode.className += " RichTextEditable";
3767         },
3768
3769         //static cache variables shared among all instance of this class
3770         _local2NativeFormatNames: {},
3771         _native2LocalFormatNames: {},
3772         _localizedIframeTitles: null,
3773
3774         _getIframeDocTxt: function(/* String */ html){
3775                 var _cs = dojo.getComputedStyle(this.domNode);
3776                 if(dojo.isIE || (!this.height && !dojo.isMoz)){
3777                         html="<div>"+html+"</div>";
3778                 }
3779                 var font = [ _cs.fontWeight, _cs.fontSize, _cs.fontFamily ].join(" ");
3780
3781                 // line height is tricky - applying a units value will mess things up.
3782                 // if we can't get a non-units value, bail out.
3783                 var lineHeight = _cs.lineHeight;
3784                 if(lineHeight.indexOf("px") >= 0){
3785                         lineHeight = parseFloat(lineHeight)/parseFloat(_cs.fontSize);
3786                         // console.debug(lineHeight);
3787                 }else if(lineHeight.indexOf("em")>=0){
3788                         lineHeight = parseFloat(lineHeight);
3789                 }else{
3790                         lineHeight = "1.0";
3791                 }
3792                 return [
3793                         this.isLeftToRight() ? "<html><head>" : "<html dir='rtl'><head>",
3794                         (dojo.isMoz ? "<title>" + this._localizedIframeTitles.iframeEditTitle + "</title>" : ""),
3795                         "<style>",
3796                         "body,html {",
3797                         "       background:transparent;",
3798                         "       font:", font, ";",
3799                         "       padding: 1em 0 0 0;",
3800                         "       margin: -1em 0 0 0;", // remove extraneous vertical scrollbar on safari and firefox
3801                         "       height: 100%;",
3802                         "}",
3803                         // TODO: left positioning will cause contents to disappear out of view
3804                         //         if it gets too wide for the visible area
3805                         "body{",
3806                         "       top:0px; left:0px; right:0px;",
3807                                 ((this.height||dojo.isOpera) ? "" : "position: fixed;"),
3808                         // FIXME: IE 6 won't understand min-height?
3809                         "       min-height:", this.minHeight, ";",
3810                         "       line-height:", lineHeight,
3811                         "}",
3812                         "p{ margin: 1em 0 !important; }",
3813                         (this.height ? // height:auto undoes the height:100%
3814                                 "" : "body,html{height:auto;overflow-y:hidden;/*for IE*/} body > div {overflow-x:auto;/*for FF to show vertical scrollbar*/}"
3815                         ),
3816                         "li > ul:-moz-first-node, li > ol:-moz-first-node{ padding-top: 1.2em; } ",
3817                         "li{ min-height:1.2em; }",
3818                         "</style>",
3819                         this._applyEditingAreaStyleSheets(),
3820                         "</head><body>"+html+"</body></html>"
3821                 ].join(""); // String
3822         },
3823
3824         _drawIframe: function(/*String*/html){
3825                 // summary:
3826                 //              Draws an iFrame using the existing one if one exists.
3827                 //              Used by Mozilla, Safari, and Opera
3828
3829                 if(!this.iframe){
3830                         var ifr = this.iframe = dojo.doc.createElement("iframe");
3831                         ifr.id=this.id;
3832                         // this.iframe.src = "about:blank";
3833                         // dojo.doc.body.appendChild(this.iframe);
3834                         // console.debug(this.iframe.contentDocument.open());
3835                         // dojo.body().appendChild(this.iframe);
3836                         var ifrs = ifr.style;
3837                         // ifrs.border = "1px solid black";
3838                         ifrs.border = "none";
3839                         ifrs.lineHeight = "0"; // squash line height
3840                         ifrs.verticalAlign = "bottom";
3841 //                      ifrs.scrolling = this.height ? "auto" : "vertical";
3842                         this.editorObject = this.iframe;
3843                         // get screen reader text for mozilla here, too
3844                         this._localizedIframeTitles = dojo.i18n.getLocalization("dijit.form", "Textarea");
3845                         // need to find any associated label element and update iframe document title
3846                         var label=dojo.query('label[for="'+this.id+'"]');
3847                         if(label.length){
3848                                 this._localizedIframeTitles.iframeEditTitle = label[0].innerHTML + " " + this._localizedIframeTitles.iframeEditTitle;
3849                         }
3850                 }
3851                 // opera likes this to be outside the with block
3852                 //      this.iframe.src = "javascript:void(0)";//dojo.uri.dojoUri("src/widget/templates/richtextframe.html") + ((dojo.doc.domain != currentDomain) ? ("#"+dojo.doc.domain) : "");
3853                 this.iframe.style.width = this.inheritWidth ? this._oldWidth : "100%";
3854
3855                 if(this.height){
3856                         this.iframe.style.height = this.height;
3857                 }else{
3858                         this.iframe.height = this._oldHeight;
3859                 }
3860
3861                 var tmpContent;
3862                 if(this.textarea){
3863                         tmpContent = this.srcNodeRef;
3864                 }else{
3865                         tmpContent = dojo.doc.createElement('div');
3866                         tmpContent.style.display="none";
3867                         tmpContent.innerHTML = html;
3868                         //append tmpContent to under the current domNode so that the margin
3869                         //calculation below is correct
3870                         this.editingArea.appendChild(tmpContent);
3871                 }
3872
3873                 this.editingArea.appendChild(this.iframe);
3874
3875                 //do we want to show the content before the editing area finish loading here?
3876                 //if external style sheets are used for the editing area, the appearance now
3877                 //and after loading of the editing area won't be the same (and padding/margin
3878                 //calculation above may not be accurate)
3879                 //      tmpContent.style.display = "none";
3880                 //      this.editingArea.appendChild(this.iframe);
3881
3882                 var _iframeInitialized = false;
3883                 // console.debug(this.iframe);
3884                 // var contentDoc = this.iframe.contentWindow.document;
3885
3886
3887                 // note that on Safari lower than 420+, we have to get the iframe
3888                 // by ID in order to get something w/ a contentDocument property
3889
3890                 var contentDoc = this.iframe.contentDocument;
3891                 contentDoc.open();
3892                 if(dojo.isAIR){
3893                         contentDoc.body.innerHTML = html;
3894                 }else{
3895                         contentDoc.write(this._getIframeDocTxt(html));
3896                 }
3897                 contentDoc.close();
3898
3899                 // now we wait for onload. Janky hack!
3900                 var ifrFunc = dojo.hitch(this, function(){
3901                         if(!_iframeInitialized){
3902                                 _iframeInitialized = true;
3903                         }else{ return; }
3904                         if(!this.editNode){
3905                                 try{
3906                                         if(this.iframe.contentWindow){
3907                                                 this.window = this.iframe.contentWindow;
3908                                                 this.document = this.iframe.contentWindow.document
3909                                         }else if(this.iframe.contentDocument){
3910                                                 // for opera
3911                                                 this.window = this.iframe.contentDocument.window;
3912                                                 this.document = this.iframe.contentDocument;
3913                                         }
3914                                         if(!this.document.body){
3915                                                 throw 'Error';
3916                                         }
3917                                 }catch(e){
3918                                         setTimeout(ifrFunc,500);
3919                                         _iframeInitialized = false;
3920                                         return;
3921                                 }
3922
3923                                 dojo._destroyElement(tmpContent);
3924                                 this.onLoad();
3925                         }else{
3926                                 dojo._destroyElement(tmpContent);
3927                                 this.editNode.innerHTML = html;
3928                                 this.onDisplayChanged();
3929                         }
3930                         this._preDomFilterContent(this.editNode);
3931                 });
3932
3933                 ifrFunc();
3934         },
3935
3936         _applyEditingAreaStyleSheets: function(){
3937                 // summary:
3938                 //              apply the specified css files in styleSheets
3939                 var files = [];
3940                 if(this.styleSheets){
3941                         files = this.styleSheets.split(';');
3942                         this.styleSheets = '';
3943                 }
3944
3945                 //empty this.editingAreaStyleSheets here, as it will be filled in addStyleSheet
3946                 files = files.concat(this.editingAreaStyleSheets);
3947                 this.editingAreaStyleSheets = [];
3948
3949                 var text='', i=0, url;
3950                 while((url=files[i++])){
3951                         var abstring = (new dojo._Url(dojo.global.location, url)).toString();
3952                         this.editingAreaStyleSheets.push(abstring);
3953                         text += '<link rel="stylesheet" type="text/css" href="'+abstring+'"/>'
3954                 }
3955                 return text;
3956         },
3957
3958         addStyleSheet: function(/*dojo._Url*/uri){
3959                 // summary:
3960                 //              add an external stylesheet for the editing area
3961                 // uri: a dojo.uri.Uri pointing to the url of the external css file
3962                 var url=uri.toString();
3963
3964                 //if uri is relative, then convert it to absolute so that it can be resolved correctly in iframe
3965                 if(url.charAt(0) == '.' || (url.charAt(0) != '/' && !uri.host)){
3966                         url = (new dojo._Url(dojo.global.location, url)).toString();
3967                 }
3968
3969                 if(dojo.indexOf(this.editingAreaStyleSheets, url) > -1){
3970 //                      console.debug("dijit._editor.RichText.addStyleSheet: Style sheet "+url+" is already applied");
3971                         return;
3972                 }
3973
3974                 this.editingAreaStyleSheets.push(url);
3975                 if(this.document.createStyleSheet){ //IE
3976                         this.document.createStyleSheet(url);
3977                 }else{ //other browser
3978                         var head = this.document.getElementsByTagName("head")[0];
3979                         var stylesheet = this.document.createElement("link");
3980                         with(stylesheet){
3981                                 rel="stylesheet";
3982                                 type="text/css";
3983                                 href=url;
3984                         }
3985                         head.appendChild(stylesheet);
3986                 }
3987         },
3988
3989         removeStyleSheet: function(/*dojo._Url*/uri){
3990                 // summary:
3991                 //              remove an external stylesheet for the editing area
3992                 var url=uri.toString();
3993                 //if uri is relative, then convert it to absolute so that it can be resolved correctly in iframe
3994                 if(url.charAt(0) == '.' || (url.charAt(0) != '/' && !uri.host)){
3995                         url = (new dojo._Url(dojo.global.location, url)).toString();
3996                 }
3997                 var index = dojo.indexOf(this.editingAreaStyleSheets, url);
3998                 if(index == -1){
3999 //                      console.debug("dijit._editor.RichText.removeStyleSheet: Style sheet "+url+" has not been applied");
4000                         return;
4001                 }
4002                 delete this.editingAreaStyleSheets[index];
4003                 dojo.withGlobal(this.window,'query', dojo, ['link:[href="'+url+'"]']).orphan()
4004         },
4005
4006         disabled: true,
4007         _mozSettingProps: ['styleWithCSS','insertBrOnReturn'],
4008         setDisabled: function(/*Boolean*/ disabled){
4009                 if(dojo.isIE || dojo.isSafari || dojo.isOpera){
4010                         if(dojo.isIE){ this.editNode.unselectable = "on"; } // prevent IE from setting focus
4011                         this.editNode.contentEditable = !disabled;
4012                         if(dojo.isIE){
4013                                 var _this = this;
4014                                 setTimeout(function(){ _this.editNode.unselectable = "off"; }, 0);
4015                         }
4016                 }else{ //moz
4017                         if(disabled){
4018                                 //AP: why isn't this set in the constructor, or put in mozSettingProps as a hash?
4019                                 this._mozSettings=[false,this.blockNodeForEnter==='BR'];
4020                         }
4021                         this.document.designMode=(disabled?'off':'on');
4022                         if(!disabled && this._mozSettings){
4023                                 dojo.forEach(this._mozSettingProps, function(s,i){
4024                                         this.document.execCommand(s,false,this._mozSettings[i]);
4025                                 },this);
4026                         }
4027 //                      this.document.execCommand('contentReadOnly', false, disabled);
4028 //                              if(disabled){
4029 //                                      this.blur(); //to remove the blinking caret
4030 //                              }
4031                 }
4032                 this.disabled = disabled;
4033         },
4034
4035 /* Event handlers
4036  *****************/
4037
4038         _isResized: function(){ return false; },
4039
4040         onLoad: function(/* Event */ e){
4041                 // summary: handler after the content of the document finishes loading
4042                 this.isLoaded = true;
4043                 if(!this.window.__registeredWindow){
4044                         this.window.__registeredWindow=true;
4045                         dijit.registerWin(this.window);
4046                 }
4047                 if(!dojo.isIE && (this.height || dojo.isMoz)){
4048                         this.editNode=this.document.body;
4049                 }else{
4050                         this.editNode=this.document.body.firstChild;
4051                         var _this = this;
4052                         if(dojo.isIE){ // #4996 IE wants to focus the BODY tag
4053                                 var tabStop = this.tabStop = dojo.doc.createElement('<div tabIndex=-1>');
4054                                 this.editingArea.appendChild(tabStop);
4055                                 this.iframe.onfocus = function(){ _this.editNode.setActive(); }
4056                         }
4057                 }
4058
4059                 try{
4060                         this.setDisabled(false);
4061                 }catch(e){
4062                         // Firefox throws an exception if the editor is initially hidden
4063                         // so, if this fails, try again onClick by adding "once" advice
4064                         var handle = dojo.connect(this, "onClick", this, function(){
4065                                 this.setDisabled(false);
4066                                 dojo.disconnect(handle);
4067                         });
4068                 }
4069
4070                 this._preDomFilterContent(this.editNode);
4071
4072                 var events=this.events.concat(this.captureEvents),i=0,et;
4073                 while((et=events[i++])){
4074                         this.connect(this.document, et.toLowerCase(), et);
4075                 }
4076                 if(!dojo.isIE){
4077                         try{ // sanity check for Mozilla
4078                         //AP: what's the point of this?
4079 //                                      this.document.execCommand("useCSS", false, true); // old moz call
4080                                 this.document.execCommand("styleWithCSS", false, false); // new moz call
4081                                 //this.document.execCommand("insertBrOnReturn", false, false); // new moz call
4082                         }catch(e2){ }
4083                         // FIXME: when scrollbars appear/disappear this needs to be fired
4084                 }else{ // IE contentEditable
4085                         // give the node Layout on IE
4086                         this.connect(this.document, "onmousedown", "_onMouseDown"); // #4996 fix focus
4087                         this.editNode.style.zoom = 1.0;
4088                 }
4089
4090                 if(this.focusOnLoad){
4091                         setTimeout(dojo.hitch(this, "focus"), 0); // have to wait for IE to set unselectable=off
4092                 }
4093
4094                 this.onDisplayChanged(e);
4095                 if(this.onLoadDeferred){
4096                         this.onLoadDeferred.callback(true);
4097                 }
4098         },
4099
4100         onKeyDown: function(/* Event */ e){
4101                 // summary: Fired on keydown
4102
4103                 // we need this event at the moment to get the events from control keys
4104                 // such as the backspace. It might be possible to add this to Dojo, so that
4105                 // keyPress events can be emulated by the keyDown and keyUp detection.
4106                 if(dojo.isIE){
4107                         if(e.keyCode == dojo.keys.TAB && e.shiftKey && !e.ctrlKey && !e.altKey){
4108                                 // focus the BODY so the browser will tab away from it instead
4109                                 this.iframe.focus();
4110                         }else if(e.keyCode == dojo.keys.TAB && !e.shiftKey && !e.ctrlKey && !e.altKey){
4111                                 // focus the BODY so the browser will tab away from it instead
4112                                 this.tabStop.focus();
4113                         }else if(e.keyCode === dojo.keys.BACKSPACE && this.document.selection.type === "Control"){
4114                                 // IE has a bug where if a non-text object is selected in the editor,
4115                   // hitting backspace would act as if the browser's back button was
4116                   // clicked instead of deleting the object. see #1069
4117                                 dojo.stopEvent(e);
4118                                 this.execCommand("delete");
4119                         }else if((65 <= e.keyCode&&e.keyCode <= 90) ||
4120                                 (e.keyCode>=37&&e.keyCode<=40) // FIXME: get this from connect() instead!
4121                         ){ //arrow keys
4122                                 e.charCode = e.keyCode;
4123                                 this.onKeyPress(e);
4124                         }
4125                 }else if(dojo.isMoz){
4126                         if(e.keyCode == dojo.keys.TAB && !e.shiftKey && !e.ctrlKey && !e.altKey && this.iframe){
4127                                 // update iframe document title for screen reader
4128                                 this.iframe.contentDocument.title = this._localizedIframeTitles.iframeFocusTitle;
4129                                 
4130                                 // Place focus on the iframe. A subsequent tab or shift tab will put focus
4131                                 // on the correct control.
4132                                 this.iframe.focus();  // this.focus(); won't work
4133                                 dojo.stopEvent(e);
4134                         }else if(e.keyCode == dojo.keys.TAB && e.shiftKey){
4135                                 // if there is a toolbar, set focus to it, otherwise ignore
4136                                 if(this.toolbar){
4137                                         this.toolbar.focus();
4138                                 }
4139                                 dojo.stopEvent(e);
4140                         }
4141                 }
4142         },
4143
4144         onKeyUp: function(e){
4145                 // summary: Fired on keyup
4146                 return;
4147         },
4148
4149         KEY_CTRL: 1,
4150         KEY_SHIFT: 2,
4151
4152         onKeyPress: function(e){
4153                 // summary: Fired on keypress
4154
4155                 // handle the various key events
4156                 var modifiers = (e.ctrlKey && !e.altKey) ? this.KEY_CTRL : 0 | e.shiftKey ? this.KEY_SHIFT : 0;
4157
4158                 var key = e.keyChar || e.keyCode;
4159                 if(this._keyHandlers[key]){
4160                         // console.debug("char:", e.key);
4161                         var handlers = this._keyHandlers[key], i = 0, h;
4162                         while((h = handlers[i++])){
4163                                 if(modifiers == h.modifiers){
4164                                         if(!h.handler.apply(this,arguments)){
4165                                                 e.preventDefault();
4166                                         }
4167                                         break;
4168                                 }
4169                         }
4170                 }
4171
4172                 // function call after the character has been inserted
4173                 setTimeout(dojo.hitch(this, function(){
4174                         this.onKeyPressed(e);
4175                 }), 1);
4176         },
4177
4178         addKeyHandler: function(/*String*/key, /*Int*/modifiers, /*Function*/handler){
4179                 // summary: add a handler for a keyboard shortcut
4180                 if(!dojo.isArray(this._keyHandlers[key])){ this._keyHandlers[key] = []; }
4181                 this._keyHandlers[key].push({
4182                         modifiers: modifiers || 0,
4183                         handler: handler
4184                 });
4185         },
4186
4187         onKeyPressed: function(/*Event*/e){
4188                 this.onDisplayChanged(/*e*/); // can't pass in e
4189         },
4190
4191         onClick: function(/*Event*/e){
4192 //              console.info('onClick',this._tryDesignModeOn);
4193                 this.onDisplayChanged(e);
4194         },
4195
4196         _onMouseDown: function(/*Event*/e){ // IE only to prevent 2 clicks to focus
4197                 if(!this._focused && !this.disabled){
4198                         this.focus();
4199                 }
4200         },
4201
4202         _onBlur: function(e){
4203                 this.inherited(arguments);
4204                 var _c=this.getValue(true);
4205                 if(_c!=this.savedContent){
4206                         this.onChange(_c);
4207                         this.savedContent=_c;
4208                 }
4209                 if(dojo.isMoz && this.iframe){
4210                         this.iframe.contentDocument.title = this._localizedIframeTitles.iframeEditTitle;
4211                 } 
4212         },
4213         _initialFocus: true,
4214         _onFocus: function(/*Event*/e){
4215                 // summary: Fired on focus
4216                 this.inherited(arguments);
4217                 if(dojo.isMoz && this._initialFocus){
4218                         this._initialFocus = false;
4219                         if(this.editNode.innerHTML.replace(/^\s+|\s+$/g, "") == "&nbsp;"){
4220                                 this.placeCursorAtStart();
4221 //                                      this.execCommand("selectall");
4222 //                                      this.window.getSelection().collapseToStart();
4223                         }
4224                 }
4225         },
4226
4227         // TODO: why is this needed - should we deprecate this ?
4228         blur: function(){
4229                 // summary: remove focus from this instance
4230                 if(!dojo.isIE && this.window.document.documentElement && this.window.document.documentElement.focus){
4231                         this.window.document.documentElement.focus();
4232                 }else if(dojo.doc.body.focus){
4233                         dojo.doc.body.focus();
4234                 }
4235         },
4236
4237         focus: function(){
4238                 // summary: move focus to this instance
4239                 if(!dojo.isIE){
4240                         dijit.focus(this.iframe);
4241                 }else if(this.editNode && this.editNode.focus){
4242                         // editNode may be hidden in display:none div, lets just punt in this case
4243                         //this.editNode.focus(); -> causes IE to scroll always (strict and quirks mode) to the top the Iframe 
4244                         // if we fire the event manually and let the browser handle the focusing, the latest  
4245                         // cursor position is focused like in FF                         
4246                         this.iframe.fireEvent('onfocus', document.createEventObject()); // createEventObject only in IE 
4247 //              }else{
4248 // TODO: should we throw here?
4249 //                      console.debug("Have no idea how to focus into the editor!");
4250                 }
4251         },
4252
4253 //              _lastUpdate: 0,
4254         updateInterval: 200,
4255         _updateTimer: null,
4256         onDisplayChanged: function(/*Event*/e){
4257                 // summary:
4258                 //              This event will be fired everytime the display context
4259                 //              changes and the result needs to be reflected in the UI.
4260                 // description:
4261                 //              If you don't want to have update too often,
4262                 //              onNormalizedDisplayChanged should be used instead
4263
4264 //                      var _t=new Date();
4265                 if(!this._updateTimer){
4266 //                              this._lastUpdate=_t;
4267                         if(this._updateTimer){
4268                                 clearTimeout(this._updateTimer);
4269                         }
4270                         this._updateTimer=setTimeout(dojo.hitch(this,this.onNormalizedDisplayChanged),this.updateInterval);
4271                 }
4272         },
4273         onNormalizedDisplayChanged: function(){
4274                 // summary:
4275                 //              This event is fired every updateInterval ms or more
4276                 // description:
4277                 //              If something needs to happen immidiately after a
4278                 //              user change, please use onDisplayChanged instead
4279                 this._updateTimer=null;
4280         },
4281         onChange: function(newContent){
4282                 // summary:
4283                 //              this is fired if and only if the editor loses focus and
4284                 //              the content is changed
4285
4286 //                      console.log('onChange',newContent);
4287         },
4288         _normalizeCommand: function(/*String*/cmd){
4289                 // summary:
4290                 //              Used as the advice function by dojo.connect to map our
4291                 //              normalized set of commands to those supported by the target
4292                 //              browser
4293
4294                 var command = cmd.toLowerCase();
4295                 if(command == "hilitecolor" && !dojo.isMoz){
4296                         command = "backcolor";
4297                 }
4298
4299                 return command;
4300         },
4301
4302         queryCommandAvailable: function(/*String*/command){
4303                 // summary:
4304                 //              Tests whether a command is supported by the host. Clients SHOULD check
4305                 //              whether a command is supported before attempting to use it, behaviour
4306                 //              for unsupported commands is undefined.
4307                 // command: The command to test for
4308                 var ie = 1;
4309                 var mozilla = 1 << 1;
4310                 var safari = 1 << 2;
4311                 var opera = 1 << 3;
4312                 var safari420 = 1 << 4;
4313
4314                 var gt420 = dojo.isSafari;
4315
4316                 function isSupportedBy(browsers){
4317                         return {
4318                                 ie: Boolean(browsers & ie),
4319                                 mozilla: Boolean(browsers & mozilla),
4320                                 safari: Boolean(browsers & safari),
4321                                 safari420: Boolean(browsers & safari420),
4322                                 opera: Boolean(browsers & opera)
4323                         }
4324                 }
4325
4326                 var supportedBy = null;
4327
4328                 switch(command.toLowerCase()){
4329                         case "bold": case "italic": case "underline":
4330                         case "subscript": case "superscript":
4331                         case "fontname": case "fontsize":
4332                         case "forecolor": case "hilitecolor":
4333                         case "justifycenter": case "justifyfull": case "justifyleft":
4334                         case "justifyright": case "delete": case "selectall": case "toggledir":
4335                                 supportedBy = isSupportedBy(mozilla | ie | safari | opera);
4336                                 break;
4337
4338                         case "createlink": case "unlink": case "removeformat":
4339                         case "inserthorizontalrule": case "insertimage":
4340                         case "insertorderedlist": case "insertunorderedlist":
4341                         case "indent": case "outdent": case "formatblock":
4342                         case "inserthtml": case "undo": case "redo": case "strikethrough":
4343                                 supportedBy = isSupportedBy(mozilla | ie | opera | safari420);
4344                                 break;
4345
4346                         case "blockdirltr": case "blockdirrtl":
4347                         case "dirltr": case "dirrtl":
4348                         case "inlinedirltr": case "inlinedirrtl":
4349                                 supportedBy = isSupportedBy(ie);
4350                                 break;
4351                         case "cut": case "copy": case "paste":
4352                                 supportedBy = isSupportedBy( ie | mozilla | safari420);
4353                                 break;
4354
4355                         case "inserttable":
4356                                 supportedBy = isSupportedBy(mozilla | ie);
4357                                 break;
4358
4359                         case "insertcell": case "insertcol": case "insertrow":
4360                         case "deletecells": case "deletecols": case "deleterows":
4361                         case "mergecells": case "splitcell":
4362                                 supportedBy = isSupportedBy(ie | mozilla);
4363                                 break;
4364
4365                         default: return false;
4366                 }
4367
4368                 return (dojo.isIE && supportedBy.ie) ||
4369                         (dojo.isMoz && supportedBy.mozilla) ||
4370                         (dojo.isSafari && supportedBy.safari) ||
4371                         (gt420 && supportedBy.safari420) ||
4372                         (dojo.isOpera && supportedBy.opera);  // Boolean return true if the command is supported, false otherwise
4373         },
4374
4375         execCommand: function(/*String*/command, argument){
4376                 // summary: Executes a command in the Rich Text area
4377                 // command: The command to execute
4378                 // argument: An optional argument to the command
4379                 var returnValue;
4380
4381                 //focus() is required for IE to work
4382                 //In addition, focus() makes sure after the execution of
4383                 //the command, the editor receives the focus as expected
4384                 this.focus();
4385
4386                 command = this._normalizeCommand(command);
4387                 if(argument != undefined){
4388                         if(command == "heading"){
4389                                 throw new Error("unimplemented");
4390                         }else if((command == "formatblock") && dojo.isIE){
4391                                 argument = '<'+argument+'>';
4392                         }
4393                 }
4394                 if(command == "inserthtml"){
4395                         argument=this._preFilterContent(argument);
4396                         if(dojo.isIE){
4397                                 var insertRange = this.document.selection.createRange();
4398                                 if(this.document.selection.type.toUpperCase()=='CONTROL'){
4399                                         var n=insertRange.item(0);
4400                                         while(insertRange.length){
4401                                                 insertRange.remove(insertRange.item(0));
4402                                         }
4403                                         n.outerHTML=argument;
4404                                 }else{
4405                                         insertRange.pasteHTML(argument);
4406                                 }
4407                                 insertRange.select();
4408                                 //insertRange.collapse(true);
4409                                 returnValue=true;
4410                         }else if(dojo.isMoz && !argument.length){
4411                                 //mozilla can not inserthtml an empty html to delete current selection
4412                                 //so we delete the selection instead in this case
4413                                 dojo.withGlobal(this.window,'remove',dijit._editor.selection);
4414                                 returnValue=true;
4415                         }else{
4416                                 returnValue=this.document.execCommand(command, false, argument);
4417                         }
4418                 }else if(
4419                         (command == "unlink")&&
4420                         (this.queryCommandEnabled("unlink"))&&
4421                         (dojo.isMoz || dojo.isSafari)
4422                 ){
4423                         // fix up unlink in Mozilla to unlink the link and not just the selection
4424
4425                         // grab selection
4426                         // Mozilla gets upset if we just store the range so we have to
4427                         // get the basic properties and recreate to save the selection
4428                         var selection = this.window.getSelection();
4429                         //      var selectionRange = selection.getRangeAt(0);
4430                         //      var selectionStartContainer = selectionRange.startContainer;
4431                         //      var selectionStartOffset = selectionRange.startOffset;
4432                         //      var selectionEndContainer = selectionRange.endContainer;
4433                         //      var selectionEndOffset = selectionRange.endOffset;
4434
4435                         // select our link and unlink
4436                         var a = dojo.withGlobal(this.window, "getAncestorElement",dijit._editor.selection, ['a']);
4437                         dojo.withGlobal(this.window, "selectElement", dijit._editor.selection, [a]);
4438
4439                         returnValue=this.document.execCommand("unlink", false, null);
4440                 }else if((command == "hilitecolor")&&(dojo.isMoz)){
4441 //                              // mozilla doesn't support hilitecolor properly when useCSS is
4442 //                              // set to false (bugzilla #279330)
4443
4444                         this.document.execCommand("styleWithCSS", false, true);
4445                         returnValue = this.document.execCommand(command, false, argument);
4446                         this.document.execCommand("styleWithCSS", false, false);
4447
4448                 }else if((dojo.isIE)&&( (command == "backcolor")||(command == "forecolor") )){
4449                         // Tested under IE 6 XP2, no problem here, comment out
4450                         // IE weirdly collapses ranges when we exec these commands, so prevent it
4451 //                              var tr = this.document.selection.createRange();
4452                         argument = arguments.length > 1 ? argument : null;
4453                         returnValue = this.document.execCommand(command, false, argument);
4454
4455                         // timeout is workaround for weird IE behavior were the text
4456                         // selection gets correctly re-created, but subsequent input
4457                         // apparently isn't bound to it
4458 //                              setTimeout(function(){tr.select();}, 1);
4459                 }else{
4460                         argument = arguments.length > 1 ? argument : null;
4461 //                              if(dojo.isMoz){
4462 //                                      this.document = this.iframe.contentWindow.document
4463 //                              }
4464
4465                         if(argument || command!="createlink"){
4466                                 returnValue = this.document.execCommand(command, false, argument);
4467                         }
4468                 }
4469
4470                 this.onDisplayChanged();
4471                 return returnValue;
4472         },
4473
4474         queryCommandEnabled: function(/*String*/command){
4475                 // summary: check whether a command is enabled or not
4476
4477                 if(this.disabled){ return false; }
4478                 command = this._normalizeCommand(command);
4479                 if(dojo.isMoz || dojo.isSafari){
4480                         if(command == "unlink"){ // mozilla returns true always
4481                                 // console.debug(dojo.withGlobal(this.window, "hasAncestorElement",dijit._editor.selection, ['a']));
4482                                 return dojo.withGlobal(this.window, "hasAncestorElement",dijit._editor.selection, ['a']);
4483                         }else if(command == "inserttable"){
4484                                 return true;
4485                         }
4486                 }
4487                 //see #4109
4488                 if(dojo.isSafari){
4489                         if(command == "copy"){
4490                                 command = "cut";
4491                         }else if(command == "paste"){
4492                                 return true;
4493                         }
4494                 }
4495
4496                 // return this.document.queryCommandEnabled(command);
4497                 var elem = dojo.isIE ? this.document.selection.createRange() : this.document;
4498                 return elem.queryCommandEnabled(command);
4499         },
4500
4501         queryCommandState: function(command){
4502                 // summary: check the state of a given command
4503
4504                 if(this.disabled){ return false; }
4505                 command = this._normalizeCommand(command);
4506                 return this.document.queryCommandState(command);
4507         },
4508
4509         queryCommandValue: function(command){
4510                 // summary: check the value of a given command
4511
4512                 if(this.disabled){ return false; }
4513                 command = this._normalizeCommand(command);
4514                 if(dojo.isIE && command == "formatblock"){
4515                         return this._local2NativeFormatNames[this.document.queryCommandValue(command)];
4516                 }
4517                 return this.document.queryCommandValue(command);
4518         },
4519
4520         // Misc.
4521
4522         placeCursorAtStart: function(){
4523                 // summary:
4524                 //              place the cursor at the start of the editing area
4525                 this.focus();
4526
4527                 //see comments in placeCursorAtEnd
4528                 var isvalid=false;
4529                 if(dojo.isMoz){
4530                         var first=this.editNode.firstChild;
4531                         while(first){
4532                                 if(first.nodeType == 3){
4533                                         if(first.nodeValue.replace(/^\s+|\s+$/g, "").length>0){
4534                                                 isvalid=true;
4535                                                 dojo.withGlobal(this.window, "selectElement", dijit._editor.selection, [first]);
4536                                                 break;
4537                                         }
4538                                 }else if(first.nodeType == 1){
4539                                         isvalid=true;
4540                                         dojo.withGlobal(this.window, "selectElementChildren",dijit._editor.selection, [first]);
4541                                         break;
4542                                 }
4543                                 first = first.nextSibling;
4544                         }
4545                 }else{
4546                         isvalid=true;
4547                         dojo.withGlobal(this.window, "selectElementChildren",dijit._editor.selection, [this.editNode]);
4548                 }
4549                 if(isvalid){
4550                         dojo.withGlobal(this.window, "collapse", dijit._editor.selection, [true]);
4551                 }
4552         },
4553
4554         placeCursorAtEnd: function(){
4555                 // summary:
4556                 //              place the cursor at the end of the editing area
4557                 this.focus();
4558
4559                 //In mozilla, if last child is not a text node, we have to use selectElementChildren on this.editNode.lastChild
4560                 //otherwise the cursor would be placed at the end of the closing tag of this.editNode.lastChild
4561                 var isvalid=false;
4562                 if(dojo.isMoz){
4563                         var last=this.editNode.lastChild;
4564                         while(last){
4565                                 if(last.nodeType == 3){
4566                                         if(last.nodeValue.replace(/^\s+|\s+$/g, "").length>0){
4567                                                 isvalid=true;
4568                                                 dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [last]);
4569                                                 break;
4570                                         }
4571                                 }else if(last.nodeType == 1){
4572                                         isvalid=true;
4573                                         if(last.lastChild){
4574                                                 dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [last.lastChild]);
4575                                         }else{
4576                                                 dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [last]);
4577                                         }
4578                                         break;
4579                                 }
4580                                 last = last.previousSibling;
4581                         }
4582                 }else{
4583                         isvalid=true;
4584                         dojo.withGlobal(this.window, "selectElementChildren",dijit._editor.selection, [this.editNode]);
4585                 }
4586                 if(isvalid){
4587                         dojo.withGlobal(this.window, "collapse", dijit._editor.selection, [false]);
4588                 }
4589         },
4590
4591         getValue: function(/*Boolean?*/nonDestructive){
4592                 // summary:
4593                 //              return the current content of the editing area (post filters are applied)
4594                 if(this.textarea){
4595                         if(this.isClosed || !this.isLoaded){
4596                                 return this.textarea.value;
4597                         }
4598                 }
4599
4600                 return this._postFilterContent(null, nonDestructive);
4601         },
4602
4603         setValue: function(/*String*/html){
4604                 // summary:
4605                 //              this function set the content. No undo history is preserved
4606
4607                 if(!this.isLoaded){
4608                         // try again after the editor is finished loading
4609                         this.onLoadDeferred.addCallback(dojo.hitch(this, function(){
4610                                 this.setValue(html);
4611                         }));
4612                         return;
4613                 }
4614
4615                 if(this.textarea && (this.isClosed || !this.isLoaded)){
4616                         this.textarea.value=html;
4617                 }else{
4618                         html = this._preFilterContent(html);
4619                         var node = this.isClosed ? this.domNode : this.editNode;
4620                         node.innerHTML = html;
4621                         this._preDomFilterContent(node);
4622                 }
4623
4624                 this.onDisplayChanged();
4625         },
4626
4627         replaceValue: function(/*String*/html){
4628                 // summary:
4629                 //              this function set the content while trying to maintain the undo stack
4630                 //              (now only works fine with Moz, this is identical to setValue in all
4631                 //              other browsers)
4632                 if(this.isClosed){
4633                         this.setValue(html);
4634                 }else if(this.window && this.window.getSelection && !dojo.isMoz){ // Safari
4635                         // look ma! it's a totally f'd browser!
4636                         this.setValue(html);
4637                 }else if(this.window && this.window.getSelection){ // Moz
4638                         html = this._preFilterContent(html);
4639                         this.execCommand("selectall");
4640                         if(dojo.isMoz && !html){ html = "&nbsp;" }
4641                         this.execCommand("inserthtml", html);
4642                         this._preDomFilterContent(this.editNode);
4643                 }else if(this.document && this.document.selection){//IE
4644                         //In IE, when the first element is not a text node, say
4645                         //an <a> tag, when replacing the content of the editing
4646                         //area, the <a> tag will be around all the content
4647                         //so for now, use setValue for IE too
4648                         this.setValue(html);
4649                 }
4650         },
4651
4652         _preFilterContent: function(/*String*/html){
4653                 // summary:
4654                 //              filter the input before setting the content of the editing area
4655                 var ec = html;
4656                 dojo.forEach(this.contentPreFilters, function(ef){ if(ef){ ec = ef(ec); } });
4657                 return ec;
4658         },
4659         _preDomFilterContent: function(/*DomNode*/dom){
4660                 // summary:
4661                 //              filter the input
4662                 dom = dom || this.editNode;
4663                 dojo.forEach(this.contentDomPreFilters, function(ef){
4664                         if(ef && dojo.isFunction(ef)){
4665                                 ef(dom);
4666                         }
4667                 }, this);
4668         },
4669
4670         _postFilterContent: function(/*DomNode|DomNode[]|String?*/dom,/*Boolean?*/nonDestructive){
4671                 // summary:
4672                 //              filter the output after getting the content of the editing area
4673                 var ec;
4674                 if(!dojo.isString(dom)){
4675                         dom = dom || this.editNode;
4676                         if(this.contentDomPostFilters.length){
4677                                 if(nonDestructive && dom['cloneNode']){
4678                                         dom = dom.cloneNode(true);
4679                                 }
4680                                 dojo.forEach(this.contentDomPostFilters, function(ef){
4681                                         dom = ef(dom);
4682                                 });
4683                         }
4684                         ec = dijit._editor.getChildrenHtml(dom);
4685                 }else{
4686                         ec = dom;
4687                 }
4688                 
4689                 if(!ec.replace(/^(?:\s|\xA0)+/g, "").replace(/(?:\s|\xA0)+$/g,"").length){ ec = ""; }
4690
4691                 //      if(dojo.isIE){
4692                 //              //removing appended <P>&nbsp;</P> for IE
4693                 //              ec = ec.replace(/(?:<p>&nbsp;</p>[\n\r]*)+$/i,"");
4694                 //      }
4695                 dojo.forEach(this.contentPostFilters, function(ef){
4696                         ec = ef(ec);
4697                 });
4698
4699                 return ec;
4700         },
4701
4702         _saveContent: function(/*Event*/e){
4703                 // summary:
4704                 //              Saves the content in an onunload event if the editor has not been closed
4705                 var saveTextarea = dojo.byId(dijit._scopeName + "._editor.RichText.savedContent");
4706                 saveTextarea.value += this._SEPARATOR + this.name + ":" + this.getValue();
4707         },
4708
4709         escapeXml: function(/*String*/str, /*Boolean*/noSingleQuotes){
4710                 dojo.deprecated('dijit.Editor::escapeXml is deprecated','use dijit._editor.escapeXml instead', 2);
4711                 return dijit._editor.escapeXml(str,noSingleQuotes);
4712         },
4713
4714         getNodeHtml: function(/* DomNode */node){
4715                 dojo.deprecated('dijit.Editor::getNodeHtml is deprecated','use dijit._editor.getNodeHtml instead', 2);
4716                 return dijit._editor.getNodeHtml(node);
4717         },
4718
4719         getNodeChildrenHtml: function(/* DomNode */dom){
4720                 dojo.deprecated('dijit.Editor::getNodeChildrenHtml is deprecated','use dijit._editor.getChildrenHtml instead', 2);
4721                 return dijit._editor.getChildrenHtml(dom);
4722         },
4723
4724         close: function(/*Boolean*/save, /*Boolean*/force){
4725                 // summary:
4726                 //              Kills the editor and optionally writes back the modified contents to the
4727                 //              element from which it originated.
4728                 // save:
4729                 //              Whether or not to save the changes. If false, the changes are discarded.
4730                 // force:
4731                 if(this.isClosed){return false; }
4732
4733                 if(!arguments.length){ save = true; }
4734                 this._content = this.getValue();
4735                 var changed = (this.savedContent != this._content);
4736
4737                 // line height is squashed for iframes
4738                 // FIXME: why was this here? if(this.iframe){ this.domNode.style.lineHeight = null; }
4739
4740                 if(this.interval){ clearInterval(this.interval); }
4741
4742                 if(this.textarea){
4743                         with(this.textarea.style){
4744                                 position = "";
4745                                 left = top = "";
4746                                 if(dojo.isIE){
4747                                         overflow = this.__overflow;
4748                                         this.__overflow = null;
4749                                 }
4750                         }
4751                         this.textarea.value = save ? this._content : this.savedContent;
4752                         dojo._destroyElement(this.domNode);
4753                         this.domNode = this.textarea;
4754                 }else{
4755 //                      if(save){
4756                                 //why we treat moz differently? comment out to fix #1061
4757 //                                      if(dojo.isMoz){
4758 //                                              var nc = dojo.doc.createElement("span");
4759 //                                              this.domNode.appendChild(nc);
4760 //                                              nc.innerHTML = this.editNode.innerHTML;
4761 //                                      }else{
4762 //                                              this.domNode.innerHTML = this._content;
4763 //                                      }
4764 //                      }
4765                         this.domNode.innerHTML = save ? this._content : this.savedContent;
4766                 }
4767
4768                 dojo.removeClass(this.domNode, "RichTextEditable");
4769                 this.isClosed = true;
4770                 this.isLoaded = false;
4771                 // FIXME: is this always the right thing to do?
4772                 delete this.editNode;
4773
4774                 if(this.window && this.window._frameElement){
4775                         this.window._frameElement = null;
4776                 }
4777
4778                 this.window = null;
4779                 this.document = null;
4780                 this.editingArea = null;
4781                 this.editorObject = null;
4782
4783                 return changed; // Boolean: whether the content has been modified
4784         },
4785
4786         destroyRendering: function(){
4787                 // summary: stub        
4788         }, 
4789
4790         destroy: function(){
4791                 this.destroyRendering();
4792                 if(!this.isClosed){ this.close(false); }
4793                 this.inherited("destroy",arguments);
4794                 //dijit._editor.RichText.superclass.destroy.call(this);
4795         },
4796
4797         _removeMozBogus: function(/* String */ html){
4798                 return html.replace(/\stype="_moz"/gi, '').replace(/\s_moz_dirty=""/gi, ''); // String
4799         },
4800         _removeSafariBogus: function(/* String */ html){
4801                 return html.replace(/\sclass="webkit-block-placeholder"/gi, ''); // String
4802         },
4803         _fixContentForMoz: function(/* String */ html){
4804                 // summary:
4805                 //              Moz can not handle strong/em tags correctly, convert them to b/i
4806                 return html.replace(/<(\/)?strong([ \>])/gi, '<$1b$2')
4807                         .replace(/<(\/)?em([ \>])/gi, '<$1i$2' ); // String
4808         },
4809
4810         _srcInImgRegex  : /(?:(<img(?=\s).*?\ssrc=)("|')(.*?)\2)|(?:(<img\s.*?src=)([^"'][^ >]+))/gi ,
4811         _hrefInARegex   : /(?:(<a(?=\s).*?\shref=)("|')(.*?)\2)|(?:(<a\s.*?href=)([^"'][^ >]+))/gi ,
4812
4813         _preFixUrlAttributes: function(/* String */ html){
4814                 return html.replace(this._hrefInARegex, '$1$4$2$3$5$2 _djrealurl=$2$3$5$2')
4815                         .replace(this._srcInImgRegex, '$1$4$2$3$5$2 _djrealurl=$2$3$5$2'); // String
4816         }
4817 });
4818
4819 }
4820
4821 if(!dojo._hasResource["dijit.Toolbar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
4822 dojo._hasResource["dijit.Toolbar"] = true;
4823 dojo.provide("dijit.Toolbar");
4824
4825
4826
4827
4828
4829 dojo.declare("dijit.Toolbar",
4830         [dijit._Widget, dijit._Templated, dijit._KeyNavContainer],
4831         {
4832         // summary: A Toolbar widget, used to hold things like dijit.Editor buttons
4833
4834         templateString:
4835                 '<div class="dijit dijitToolbar" waiRole="toolbar" tabIndex="${tabIndex}" dojoAttachPoint="containerNode">' +
4836                 //      '<table style="table-layout: fixed" class="dijitReset dijitToolbarTable">' + // factor out style
4837                 //              '<tr class="dijitReset" dojoAttachPoint="containerNode"></tr>'+
4838                 //      '</table>' +
4839                 '</div>',
4840
4841         tabIndex: "0",
4842
4843         postCreate: function(){
4844                 this.connectKeyNavHandlers(
4845                         this.isLeftToRight() ? [dojo.keys.LEFT_ARROW] : [dojo.keys.RIGHT_ARROW],
4846                         this.isLeftToRight() ? [dojo.keys.RIGHT_ARROW] : [dojo.keys.LEFT_ARROW]
4847                 );
4848         },
4849
4850         startup: function(){
4851                 if(this._started){ return; }
4852
4853                 this.startupKeyNavChildren();
4854
4855                 this.inherited(arguments);
4856         }
4857 }
4858 );
4859
4860 // Combine with dijit.MenuSeparator??
4861 dojo.declare("dijit.ToolbarSeparator",
4862         [ dijit._Widget, dijit._Templated ],
4863         {
4864         // summary: A spacer between two Toolbar items
4865         templateString: '<div class="dijitToolbarSeparator dijitInline"></div>',
4866         postCreate: function(){ dojo.setSelectable(this.domNode, false); },
4867         isFocusable: function(){ 
4868                 // summary: This widget isn't focusable, so pass along that fact.
4869                 return false; 
4870         }
4871
4872 });
4873
4874 }
4875
4876 if(!dojo._hasResource["dijit.form.Button"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
4877 dojo._hasResource["dijit.form.Button"] = true;
4878 dojo.provide("dijit.form.Button");
4879
4880
4881
4882
4883 dojo.declare("dijit.form.Button",
4884         dijit.form._FormWidget,
4885         {
4886         // summary:
4887         //      Basically the same thing as a normal HTML button, but with special styling.
4888         //
4889         // example:
4890         // |    <button dojoType="dijit.form.Button" onClick="...">Hello world</button>
4891         // 
4892         // example:
4893         // |    var button1 = new dijit.form.Button({label: "hello world", onClick: foo});
4894         // |    dojo.body().appendChild(button1.domNode);
4895         //
4896         // label: String
4897         //      text to display in button
4898         label: "",
4899
4900         // showLabel: Boolean
4901         //      whether or not to display the text label in button
4902         showLabel: true,
4903
4904         // iconClass: String
4905         //      class to apply to div in button to make it display an icon
4906         iconClass: "",
4907
4908         type: "button",
4909         baseClass: "dijitButton",
4910         templateString:"<div class=\"dijit dijitReset dijitLeft dijitInline\"\n\tdojoAttachEvent=\"onclick:_onButtonClick,onmouseenter:_onMouse,onmouseleave:_onMouse,onmousedown:_onMouse\"\n\twaiRole=\"presentation\"\n\t><button class=\"dijitReset dijitStretch dijitButtonNode dijitButtonContents\" dojoAttachPoint=\"focusNode,titleNode\"\n\t\ttype=\"${type}\" waiRole=\"button\" waiState=\"labelledby-${id}_label\"\n\t\t><span class=\"dijitReset dijitInline ${iconClass}\" dojoAttachPoint=\"iconNode\" \n \t\t\t><span class=\"dijitReset dijitToggleButtonIconChar\">&#10003;</span \n\t\t></span\n\t\t><div class=\"dijitReset dijitInline\"><center class=\"dijitReset dijitButtonText\" id=\"${id}_label\" dojoAttachPoint=\"containerNode\">${label}</center></div\n\t></button\n></div>\n",
4911
4912         _onChangeMonitor: '',
4913         // TODO: set button's title to this.containerNode.innerText
4914
4915         _onClick: function(/*Event*/ e){
4916                 // summary: internal function to handle click actions
4917                 if(this.disabled || this.readOnly){
4918                         dojo.stopEvent(e); // needed for checkbox
4919                         return false;
4920                 }
4921                 this._clicked(); // widget click actions
4922                 return this.onClick(e); // user click actions
4923         },
4924
4925         _onButtonClick: function(/*Event*/ e){
4926                 // summary: callback when the user mouse clicks the button portion
4927                 if(this._onClick(e) === false){ // returning nothing is same as true
4928                         dojo.stopEvent(e);
4929                 }else if(this.type=="submit" && !this.focusNode.form){ // see if a nonform widget needs to be signalled
4930                         for(var node=this.domNode; node.parentNode/*#5935*/; node=node.parentNode){
4931                                 var widget=dijit.byNode(node);
4932                                 if(widget && typeof widget._onSubmit == "function"){
4933                                         widget._onSubmit(e);
4934                                         break;
4935                                 }
4936                         }
4937                 }
4938         },
4939
4940         postCreate: function(){
4941                 // summary:
4942                 //      get label and set as title on button icon if necessary
4943                 if (this.showLabel == false){
4944                         var labelText = "";
4945                         this.label = this.containerNode.innerHTML;
4946                         labelText = dojo.trim(this.containerNode.innerText || this.containerNode.textContent || '');
4947                         // set title attrib on iconNode
4948                         this.titleNode.title=labelText;
4949                         dojo.addClass(this.containerNode,"dijitDisplayNone");
4950                 }
4951                 dojo.setSelectable(this.focusNode, false);
4952                 this.inherited(arguments);
4953         },
4954
4955         onClick: function(/*Event*/ e){
4956                 // summary: user callback for when button is clicked
4957                 //      if type="submit", return true to perform submit
4958                 return true;
4959         },
4960
4961         _clicked: function(/*Event*/ e){
4962                 // summary: internal replaceable function for when the button is clicked
4963         },
4964
4965         setLabel: function(/*String*/ content){
4966                 // summary: reset the label (text) of the button; takes an HTML string
4967                 this.containerNode.innerHTML = this.label = content;
4968                 this._layoutHack();
4969                 if (this.showLabel == false){
4970                         this.titleNode.title=dojo.trim(this.containerNode.innerText || this.containerNode.textContent || '');
4971                 }
4972         }               
4973 });
4974
4975
4976 dojo.declare("dijit.form.DropDownButton", [dijit.form.Button, dijit._Container], {
4977         // summary: A button with a popup
4978         //
4979         // example:
4980         // |    <button dojoType="dijit.form.DropDownButton" label="Hello world">
4981         // |            <div dojotype="dijit.Menu">...</div>
4982         // |    </button>
4983         //
4984         // example:
4985         // |    var button1 = new dijit.form.DropDownButton({ label: "hi", dropDown: new dijit.Menu(...) });
4986         // |    dojo.body().appendChild(button1);
4987         //      
4988         
4989         baseClass : "dijitDropDownButton",
4990
4991         templateString:"<div class=\"dijit dijitReset dijitLeft dijitInline\"\n\tdojoAttachEvent=\"onmouseenter:_onMouse,onmouseleave:_onMouse,onmousedown:_onMouse,onclick:_onDropDownClick,onkeydown:_onDropDownKeydown,onblur:_onDropDownBlur,onkeypress:_onKey\"\n\twaiRole=\"presentation\"\n\t><div class='dijitReset dijitRight' waiRole=\"presentation\"\n\t><button class=\"dijitReset dijitStretch dijitButtonNode dijitButtonContents\" type=\"${type}\"\n\t\tdojoAttachPoint=\"focusNode,titleNode\" waiRole=\"button\" waiState=\"haspopup-true,labelledby-${id}_label\"\n\t\t><div class=\"dijitReset dijitInline ${iconClass}\" dojoAttachPoint=\"iconNode\" waiRole=\"presentation\"></div\n\t\t><div class=\"dijitReset dijitInline dijitButtonText\"  dojoAttachPoint=\"containerNode,popupStateNode\" waiRole=\"presentation\"\n\t\t\tid=\"${id}_label\">${label}</div\n\t\t><div class=\"dijitReset dijitInline dijitArrowButtonInner\" waiRole=\"presentation\">&thinsp;</div\n\t\t><div class=\"dijitReset dijitInline dijitArrowButtonChar\" waiRole=\"presentation\">&#9660;</div\n\t></button\n></div></div>\n",
4992
4993         _fillContent: function(){
4994                 // my inner HTML contains both the button contents and a drop down widget, like
4995                 // <DropDownButton>  <span>push me</span>  <Menu> ... </Menu> </DropDownButton>
4996                 // The first node is assumed to be the button content. The widget is the popup.
4997                 if(this.srcNodeRef){ // programatically created buttons might not define srcNodeRef
4998                         //FIXME: figure out how to filter out the widget and use all remaining nodes as button
4999                         //      content, not just nodes[0]
5000                         var nodes = dojo.query("*", this.srcNodeRef);
5001                         dijit.form.DropDownButton.superclass._fillContent.call(this, nodes[0]);
5002
5003                         // save pointer to srcNode so we can grab the drop down widget after it's instantiated
5004                         this.dropDownContainer = this.srcNodeRef;
5005                 }
5006         },
5007
5008         startup: function(){
5009                 if(this._started){ return; }
5010
5011                 // the child widget from srcNodeRef is the dropdown widget.  Insert it in the page DOM,
5012                 // make it invisible, and store a reference to pass to the popup code.
5013                 if(!this.dropDown){
5014                         var dropDownNode = dojo.query("[widgetId]", this.dropDownContainer)[0];
5015                         this.dropDown = dijit.byNode(dropDownNode);
5016                         delete this.dropDownContainer;
5017                 }
5018                 dijit.popup.prepare(this.dropDown.domNode);
5019
5020                 this.inherited(arguments);
5021         },
5022
5023         destroyDescendants: function(){
5024                 if(this.dropDown){
5025                         this.dropDown.destroyRecursive();
5026                         delete this.dropDown;
5027                 }
5028                 this.inherited(arguments);
5029         },
5030
5031         _onArrowClick: function(/*Event*/ e){
5032                 // summary: callback when the user mouse clicks on menu popup node
5033                 if(this.disabled || this.readOnly){ return; }
5034                 this._toggleDropDown();
5035         },
5036
5037         _onDropDownClick: function(/*Event*/ e){
5038                 // on Firefox 2 on the Mac it is possible to fire onclick
5039                 // by pressing enter down on a second element and transferring
5040                 // focus to the DropDownButton;
5041                 // we want to prevent opening our menu in this situation
5042                 // and only do so if we have seen a keydown on this button;
5043                 // e.detail != 0 means that we were fired by mouse
5044                 var isMacFFlessThan3 = dojo.isFF && dojo.isFF < 3
5045                         && navigator.appVersion.indexOf("Macintosh") != -1;
5046                 if(!isMacFFlessThan3 || e.detail != 0 || this._seenKeydown){
5047                         this._onArrowClick(e);
5048                 }
5049                 this._seenKeydown = false;
5050         },
5051
5052         _onDropDownKeydown: function(/*Event*/ e){
5053                 this._seenKeydown = true;
5054         },
5055
5056         _onDropDownBlur: function(/*Event*/ e){
5057                 this._seenKeydown = false;
5058         },
5059
5060         _onKey: function(/*Event*/ e){
5061                 // summary: callback when the user presses a key on menu popup node
5062                 if(this.disabled || this.readOnly){ return; }
5063                 if(e.keyCode == dojo.keys.DOWN_ARROW){
5064                         if(!this.dropDown || this.dropDown.domNode.style.visibility=="hidden"){
5065                                 dojo.stopEvent(e);
5066                                 this._toggleDropDown();
5067                         }
5068                 }
5069         },
5070
5071         _onBlur: function(){
5072                 // summary: called magically when focus has shifted away from this widget and it's dropdown
5073                 this._closeDropDown();
5074                 // don't focus on button.  the user has explicitly focused on something else.
5075                 this.inherited(arguments);
5076         },
5077
5078         _toggleDropDown: function(){
5079                 // summary: toggle the drop-down widget; if it is up, close it, if not, open it
5080                 if(this.disabled || this.readOnly){ return; }
5081                 dijit.focus(this.popupStateNode);
5082                 var dropDown = this.dropDown;
5083                 if(!dropDown){ return; }
5084                 if(!this._opened){
5085                         // If there's an href, then load that first, so we don't get a flicker
5086                         if(dropDown.href && !dropDown.isLoaded){
5087                                 var self = this;
5088                                 var handler = dojo.connect(dropDown, "onLoad", function(){
5089                                         dojo.disconnect(handler);
5090                                         self._openDropDown();
5091                                 });
5092                                 dropDown._loadCheck(true);
5093                                 return;
5094                         }else{
5095                                 this._openDropDown();
5096                         }
5097                 }else{
5098                         this._closeDropDown();
5099                 }
5100         },
5101
5102         _openDropDown: function(){
5103                 var dropDown = this.dropDown;
5104                 var oldWidth=dropDown.domNode.style.width;
5105                 var self = this;
5106
5107                 dijit.popup.open({
5108                         parent: this,
5109                         popup: dropDown,
5110                         around: this.domNode,
5111                         orient:
5112                                 // TODO: add user-defined positioning option, like in Tooltip.js
5113                                 this.isLeftToRight() ? {'BL':'TL', 'BR':'TR', 'TL':'BL', 'TR':'BR'}
5114                                 : {'BR':'TR', 'BL':'TL', 'TR':'BR', 'TL':'BL'},
5115                         onExecute: function(){
5116                                 self._closeDropDown(true);
5117                         },
5118                         onCancel: function(){
5119                                 self._closeDropDown(true);
5120                         },
5121                         onClose: function(){
5122                                 dropDown.domNode.style.width = oldWidth;
5123                                 self.popupStateNode.removeAttribute("popupActive");
5124                                 this._opened = false;
5125                         }
5126                 });
5127                 if(this.domNode.offsetWidth > dropDown.domNode.offsetWidth){
5128                         var adjustNode = null;
5129                         if(!this.isLeftToRight()){
5130                                 adjustNode = dropDown.domNode.parentNode;
5131                                 var oldRight = adjustNode.offsetLeft + adjustNode.offsetWidth;
5132                         }
5133                         // make menu at least as wide as the button
5134                         dojo.marginBox(dropDown.domNode, {w: this.domNode.offsetWidth});
5135                         if(adjustNode){
5136                                 adjustNode.style.left = oldRight - this.domNode.offsetWidth + "px";
5137                         }
5138                 }
5139                 this.popupStateNode.setAttribute("popupActive", "true");
5140                 this._opened=true;
5141                 if(dropDown.focus){
5142                         dropDown.focus();
5143                 }
5144                 // TODO: set this.checked and call setStateClass(), to affect button look while drop down is shown
5145         },
5146         
5147         _closeDropDown: function(/*Boolean*/ focus){
5148                 if(this._opened){
5149                         dijit.popup.close(this.dropDown);
5150                         if(focus){ this.focus(); }
5151                         this._opened = false;                   
5152                 }
5153         }
5154 });
5155
5156 dojo.declare("dijit.form.ComboButton", dijit.form.DropDownButton, {
5157         // summary: A Normal Button with a DropDown
5158         //
5159         // example:
5160         // |    <button dojoType="dijit.form.ComboButton" onClick="...">
5161         // |            <span>Hello world</span>
5162         // |            <div dojoType="dijit.Menu">...</div>
5163         // |    </button>
5164         //
5165         // example:
5166         // |    var button1 = new dijit.form.ComboButton({label: "hello world", onClick: foo, dropDown: "myMenu"});
5167         // |    dojo.body().appendChild(button1.domNode);
5168         // 
5169
5170         templateString:"<table class='dijit dijitReset dijitInline dijitLeft'\n\tcellspacing='0' cellpadding='0' waiRole=\"presentation\"\n\t><tbody waiRole=\"presentation\"><tr waiRole=\"presentation\"\n\t\t><td\tclass=\"dijitReset dijitStretch dijitButtonContents dijitButtonNode\"\n\t\t\ttabIndex=\"${tabIndex}\"\n\t\t\tdojoAttachEvent=\"ondijitclick:_onButtonClick,onmouseenter:_onMouse,onmouseleave:_onMouse,onmousedown:_onMouse\"  dojoAttachPoint=\"titleNode\"\n\t\t\twaiRole=\"button\" waiState=\"labelledby-${id}_label\"\n\t\t\t><div class=\"dijitReset dijitInline ${iconClass}\" dojoAttachPoint=\"iconNode\" waiRole=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitInline dijitButtonText\" id=\"${id}_label\" dojoAttachPoint=\"containerNode\" waiRole=\"presentation\">${label}</div\n\t\t></td\n\t\t><td class='dijitReset dijitStretch dijitButtonNode dijitArrowButton dijitDownArrowButton'\n\t\t\tdojoAttachPoint=\"popupStateNode,focusNode\"\n\t\t\tdojoAttachEvent=\"ondijitclick:_onArrowClick, onkeypress:_onKey,onmouseenter:_onMouse,onmouseleave:_onMouse\"\n\t\t\tstateModifier=\"DownArrow\"\n\t\t\ttitle=\"${optionsTitle}\" name=\"${name}\"\n\t\t\twaiRole=\"button\" waiState=\"haspopup-true\"\n\t\t\t><div class=\"dijitReset dijitArrowButtonInner\" waiRole=\"presentation\">&thinsp;</div\n\t\t\t><div class=\"dijitReset dijitArrowButtonChar\" waiRole=\"presentation\">&#9660;</div\n\t\t></td\n\t></tr></tbody\n></table>\n",
5171
5172         attributeMap: dojo.mixin(dojo.clone(dijit.form._FormWidget.prototype.attributeMap),
5173                 {id:"", name:""}),
5174
5175         // optionsTitle: String
5176         //  text that describes the options menu (accessibility)
5177         optionsTitle: "",
5178
5179         baseClass: "dijitComboButton",
5180
5181         _focusedNode: null,
5182
5183         postCreate: function(){
5184                 this.inherited(arguments);
5185                 this._focalNodes = [this.titleNode, this.popupStateNode];
5186                 dojo.forEach(this._focalNodes, dojo.hitch(this, function(node){
5187                         if(dojo.isIE){
5188                                 this.connect(node, "onactivate", this._onNodeFocus);
5189                                 this.connect(node, "ondeactivate", this._onNodeBlur);
5190                         }else{
5191                                 this.connect(node, "onfocus", this._onNodeFocus);
5192                                 this.connect(node, "onblur", this._onNodeBlur);
5193                         }
5194                 }));
5195         },
5196
5197         focusFocalNode: function(node){
5198                 // summary: Focus the focal node node.
5199                 this._focusedNode = node;
5200                 dijit.focus(node);
5201         },
5202
5203         hasNextFocalNode: function(){
5204                 // summary: Returns true if this widget has no node currently
5205                 //              focused or if there is a node following the focused one.
5206                 //              False is returned if the last node has focus.
5207                 return this._focusedNode !== this.getFocalNodes()[1];
5208         },
5209
5210         focusNext: function(){
5211                 // summary: Focus the focal node following the current node with focus
5212                 //              or the first one if no node currently has focus.
5213                 this._focusedNode = this.getFocalNodes()[this._focusedNode ? 1 : 0];
5214                 dijit.focus(this._focusedNode);
5215         },
5216
5217         hasPrevFocalNode: function(){
5218                 // summary: Returns true if this widget has no node currently
5219                 //              focused or if there is a node before the focused one.
5220                 //              False is returned if the first node has focus.
5221                 return this._focusedNode !== this.getFocalNodes()[0];
5222         },
5223
5224         focusPrev: function(){
5225                 // summary: Focus the focal node before the current node with focus
5226                 //              or the last one if no node currently has focus.
5227                 this._focusedNode = this.getFocalNodes()[this._focusedNode ? 0 : 1];
5228                 dijit.focus(this._focusedNode);
5229         },
5230
5231         getFocalNodes: function(){
5232                 // summary: Returns an array of focal nodes for this widget.
5233                 return this._focalNodes;
5234         },
5235
5236         _onNodeFocus: function(evt){
5237                 this._focusedNode = evt.currentTarget;
5238                 var fnc = this._focusedNode == this.focusNode ? "dijitDownArrowButtonFocused" : "dijitButtonContentsFocused";
5239                 dojo.addClass(this._focusedNode, fnc);
5240         },
5241
5242         _onNodeBlur: function(evt){
5243                 var fnc = evt.currentTarget == this.focusNode ? "dijitDownArrowButtonFocused" : "dijitButtonContentsFocused";
5244                 dojo.removeClass(evt.currentTarget, fnc);
5245         },
5246
5247         _onBlur: function(){
5248                 this.inherited(arguments);
5249                 this._focusedNode = null;
5250         }
5251 });
5252
5253 dojo.declare("dijit.form.ToggleButton", dijit.form.Button, {
5254         // summary:
5255         //      A button that can be in two states (checked or not).
5256         //      Can be base class for things like tabs or checkbox or radio buttons
5257
5258         baseClass: "dijitToggleButton",
5259
5260         // checked: Boolean
5261         //              Corresponds to the native HTML <input> element's attribute.
5262         //              In markup, specified as "checked='checked'" or just "checked".
5263         //              True if the button is depressed, or the checkbox is checked,
5264         //              or the radio button is selected, etc.
5265         checked: false,
5266
5267         _onChangeMonitor: 'checked',
5268
5269         attributeMap: dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap),
5270                 {checked:"focusNode"}),
5271
5272         _clicked: function(/*Event*/ evt){
5273                 this.setAttribute('checked', !this.checked);
5274         },
5275
5276         setAttribute: function(/*String*/ attr, /*anything*/ value){
5277                 this.inherited(arguments);
5278                 switch(attr){
5279                         case "checked":
5280                                 dijit.setWaiState(this.focusNode || this.domNode, "pressed", this.checked);
5281                                 this._setStateClass();          
5282                                 this._handleOnChange(this.checked, true);
5283                 }
5284         },
5285
5286
5287         setChecked: function(/*Boolean*/ checked){
5288                 // summary:
5289                 //      Programatically deselect the button
5290                 dojo.deprecated("setChecked("+checked+") is deprecated. Use setAttribute('checked',"+checked+") instead.", "", "2.0");
5291                 this.setAttribute('checked', checked);
5292         },
5293         
5294         postCreate: function(){
5295                 this.inherited(arguments);
5296                 this.setAttribute('checked', this.checked); //to initially set wai pressed state 
5297         }
5298 });
5299
5300 }
5301
5302 if(!dojo._hasResource["dijit._editor._Plugin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
5303 dojo._hasResource["dijit._editor._Plugin"] = true;
5304 dojo.provide("dijit._editor._Plugin");
5305
5306
5307
5308
5309 dojo.declare("dijit._editor._Plugin", null, {
5310         // summary
5311         //              This represents a "plugin" to the editor, which is basically
5312         //              a single button on the Toolbar and some associated code
5313         constructor: function(/*Object?*/args, /*DomNode?*/node){
5314                 if(args){
5315                         dojo.mixin(this, args);
5316                 }
5317                 this._connects=[];
5318         },
5319
5320         editor: null,
5321         iconClassPrefix: "dijitEditorIcon",
5322         button: null,
5323         queryCommand: null,
5324         command: "",
5325         commandArg: null,
5326         useDefaultCommand: true,
5327         buttonClass: dijit.form.Button,
5328         getLabel: function(key){
5329                 return this.editor.commands[key];
5330         },
5331         _initButton: function(props){
5332                 if(this.command.length){
5333                         var label = this.getLabel(this.command);
5334                         var className = this.iconClassPrefix+" "+this.iconClassPrefix + this.command.charAt(0).toUpperCase() + this.command.substr(1);
5335                         if(!this.button){
5336                                 props = dojo.mixin({
5337                                         label: label,
5338                                         showLabel: false,
5339                                         iconClass: className,
5340                                         dropDown: this.dropDown,
5341                                         tabIndex: "-1"
5342                                 }, props || {});
5343                                 this.button = new this.buttonClass(props);
5344                         }
5345                 }
5346         },
5347         destroy: function(f){
5348                 dojo.forEach(this._connects, dojo.disconnect);
5349         },
5350         connect: function(o, f, tf){
5351                 this._connects.push(dojo.connect(o, f, this, tf));
5352         },
5353         updateState: function(){
5354                 var _e = this.editor;
5355                 var _c = this.command;
5356                 if(!_e){ return; }
5357                 if(!_e.isLoaded){ return; }
5358                 if(!_c.length){ return; }
5359                 if(this.button){
5360                         try{
5361                                 var enabled = _e.queryCommandEnabled(_c);
5362                                 this.button.setAttribute('disabled', !enabled);
5363                                 if(typeof this.button.checked == 'boolean'){
5364                                         this.button.setAttribute('checked', _e.queryCommandState(_c));
5365                                 }
5366                         }catch(e){
5367                                 console.debug(e);
5368                         }
5369                 }
5370         },
5371         setEditor: function(/*Widget*/editor){
5372                 // FIXME: detatch from previous editor!!
5373                 this.editor = editor;
5374
5375                 // FIXME: prevent creating this if we don't need to (i.e., editor can't handle our command)
5376                 this._initButton();
5377
5378                 // FIXME: wire up editor to button here!
5379                 if(this.command.length &&
5380                         !this.editor.queryCommandAvailable(this.command)
5381                 ){
5382                         // console.debug("hiding:", this.command);
5383                         if(this.button){
5384                                 this.button.domNode.style.display = "none";
5385                         }
5386                 }
5387                 if(this.button && this.useDefaultCommand){
5388                         this.connect(this.button, "onClick",
5389                                 dojo.hitch(this.editor, "execCommand", this.command, this.commandArg)
5390                         );
5391                 }
5392                 this.connect(this.editor, "onNormalizedDisplayChanged", "updateState");
5393         },
5394         setToolbar: function(/*Widget*/toolbar){
5395                 if(this.button){
5396                         toolbar.addChild(this.button);
5397                 }
5398                 // console.debug("adding", this.button, "to:", toolbar);
5399         }
5400 });
5401
5402 }
5403
5404 if(!dojo._hasResource["dijit.Editor"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
5405 dojo._hasResource["dijit.Editor"] = true;
5406 dojo.provide("dijit.Editor");
5407
5408
5409
5410
5411
5412
5413
5414 dojo.declare(
5415         "dijit.Editor",
5416         dijit._editor.RichText,
5417         {
5418         // summary: A rich-text Editing widget
5419
5420                 // plugins: Array
5421                 //              a list of plugin names (as strings) or instances (as objects)
5422                 //              for this widget.
5423                 plugins: null,
5424
5425                 // extraPlugins: Array
5426                 //              a list of extra plugin names which will be appended to plugins array
5427                 extraPlugins: null,
5428
5429                 constructor: function(){
5430                         if(!dojo.isArray(this.plugins)){
5431                                 this.plugins=["undo","redo","|","cut","copy","paste","|","bold","italic","underline","strikethrough","|",
5432                                 "insertOrderedList","insertUnorderedList","indent","outdent","|","justifyLeft","justifyRight","justifyCenter","justifyFull"/*"createLink"*/];
5433                         }
5434
5435                         this._plugins=[];
5436                         this._editInterval = this.editActionInterval * 1000;
5437                 },
5438
5439                 postCreate: function(){
5440                         //for custom undo/redo
5441                         if(this.customUndo){
5442                                 dojo['require']("dijit._editor.range");
5443                                 this._steps=this._steps.slice(0);
5444                                 this._undoedSteps=this._undoedSteps.slice(0);
5445 //                              this.addKeyHandler('z',this.KEY_CTRL,this.undo);
5446 //                              this.addKeyHandler('y',this.KEY_CTRL,this.redo);
5447                         }
5448                         if(dojo.isArray(this.extraPlugins)){
5449                                 this.plugins=this.plugins.concat(this.extraPlugins);
5450                         }
5451
5452 //                      try{
5453                         this.inherited(arguments);
5454 //                      dijit.Editor.superclass.postCreate.apply(this, arguments);
5455
5456                         this.commands = dojo.i18n.getLocalization("dijit._editor", "commands", this.lang);
5457
5458                         if(!this.toolbar){
5459                                 // if we haven't been assigned a toolbar, create one
5460                                 this.toolbar = new dijit.Toolbar({});
5461                                 dojo.place(this.toolbar.domNode, this.editingArea, "before");
5462                         }
5463
5464                         dojo.forEach(this.plugins, this.addPlugin, this);
5465                         this.onNormalizedDisplayChanged(); //update toolbar button status
5466 //                      }catch(e){ console.debug(e); }
5467                 },
5468                 destroy: function(){
5469                         dojo.forEach(this._plugins, function(p){
5470                                 if(p && p.destroy){
5471                                         p.destroy();
5472                                 }
5473                         });
5474                         this._plugins=[];
5475                         this.toolbar.destroy(); delete this.toolbar;
5476                         this.inherited(arguments);
5477                 },
5478                 addPlugin: function(/*String||Object*/plugin, /*Integer?*/index){
5479                         //      summary:
5480                         //              takes a plugin name as a string or a plugin instance and
5481                         //              adds it to the toolbar and associates it with this editor
5482                         //              instance. The resulting plugin is added to the Editor's
5483                         //              plugins array. If index is passed, it's placed in the plugins
5484                         //              array at that index. No big magic, but a nice helper for
5485                         //              passing in plugin names via markup.
5486                         //      plugin: String, args object or plugin instance. Required.
5487                         //      args: This object will be passed to the plugin constructor.
5488                         //      index:  
5489                         //              Integer, optional. Used when creating an instance from
5490                         //              something already in this.plugins. Ensures that the new
5491                         //              instance is assigned to this.plugins at that index.
5492                         var args=dojo.isString(plugin)?{name:plugin}:plugin;
5493                         if(!args.setEditor){
5494                                 var o={"args":args,"plugin":null,"editor":this};
5495                                 dojo.publish(dijit._scopeName + ".Editor.getPlugin",[o]);
5496                                 if(!o.plugin){
5497                                         var pc = dojo.getObject(args.name);
5498                                         if(pc){
5499                                                 o.plugin=new pc(args);
5500                                         }
5501                                 }
5502                                 if(!o.plugin){
5503                                         console.warn('Cannot find plugin',plugin);
5504                                         return;
5505                                 }
5506                                 plugin=o.plugin;
5507                         }
5508                         if(arguments.length > 1){
5509                                 this._plugins[index] = plugin;
5510                         }else{
5511                                 this._plugins.push(plugin);
5512                         }
5513                         plugin.setEditor(this);
5514                         if(dojo.isFunction(plugin.setToolbar)){
5515                                 plugin.setToolbar(this.toolbar);
5516                         }
5517                 },
5518                 /* beginning of custom undo/redo support */
5519
5520                 // customUndo: Boolean
5521                 //              Whether we shall use custom undo/redo support instead of the native
5522                 //              browser support. By default, we only enable customUndo for IE, as it
5523                 //              has broken native undo/redo support. Note: the implementation does
5524                 //              support other browsers which have W3C DOM2 Range API.
5525                 customUndo: dojo.isIE,
5526
5527                 //      editActionInterval: Integer
5528                 //              When using customUndo, not every keystroke will be saved as a step.
5529                 //              Instead typing (including delete) will be grouped together: after
5530                 //              a user stop typing for editActionInterval seconds, a step will be
5531                 //              saved; if a user resume typing within editActionInterval seconds,
5532                 //              the timeout will be restarted. By default, editActionInterval is 3
5533                 //              seconds.
5534                 editActionInterval: 3,
5535                 beginEditing: function(cmd){
5536                         if(!this._inEditing){
5537                                 this._inEditing=true;
5538                                 this._beginEditing(cmd);
5539                         }
5540                         if(this.editActionInterval>0){
5541                                 if(this._editTimer){
5542                                         clearTimeout(this._editTimer);
5543                                 }
5544                                 this._editTimer = setTimeout(dojo.hitch(this, this.endEditing), this._editInterval);
5545                         }
5546                 },
5547                 _steps:[],
5548                 _undoedSteps:[],
5549                 execCommand: function(cmd){
5550                         if(this.customUndo && (cmd=='undo' || cmd=='redo')){
5551                                 return this[cmd]();
5552                         }else{
5553                                 try{
5554                                         if(this.customUndo){
5555                                                 this.endEditing();
5556                                                 this._beginEditing();
5557                                         }
5558                                         var r = this.inherited('execCommand',arguments);
5559                                         if(this.customUndo){
5560                                                 this._endEditing();
5561                                         }
5562                                         return r;
5563                                 }catch(e){
5564                                         if(dojo.isMoz && /copy|cut|paste/.test(cmd)){
5565                                                 // Warn user of platform limitation.  Cannot programmatically access keyboard. See ticket #4136
5566                                                 var sub = dojo.string.substitute,
5567                                                         accel = {cut:'X', copy:'C', paste:'V'},
5568                                                         isMac = navigator.userAgent.indexOf("Macintosh") != -1;
5569                                                 alert(sub(this.commands.systemShortcutFF,
5570                                                         [this.commands[cmd], sub(this.commands[isMac ? 'appleKey' : 'ctrlKey'], [accel[cmd]])]));
5571                                         }
5572                                         return false;
5573                                 }
5574                         }
5575                 },
5576                 queryCommandEnabled: function(cmd){
5577                         if(this.customUndo && (cmd=='undo' || cmd=='redo')){
5578                                 return cmd=='undo'?(this._steps.length>1):(this._undoedSteps.length>0);
5579                         }else{
5580                                 return this.inherited('queryCommandEnabled',arguments);
5581                         }
5582                 },
5583                 _moveToBookmark: function(b){
5584                         var bookmark=b;
5585                         if(dojo.isIE){
5586                                 if(dojo.isArray(b)){//IE CONTROL
5587                                         bookmark=[];
5588                                         dojo.forEach(b,function(n){
5589                                                 bookmark.push(dijit.range.getNode(n,this.editNode));
5590                                         },this);
5591                                 }
5592                         }else{//w3c range
5593                                 var r=dijit.range.create();
5594                                 r.setStart(dijit.range.getNode(b.startContainer,this.editNode),b.startOffset);
5595                                 r.setEnd(dijit.range.getNode(b.endContainer,this.editNode),b.endOffset);
5596                                 bookmark=r;
5597                         }
5598                         dojo.withGlobal(this.window,'moveToBookmark',dijit,[bookmark]);
5599                 },
5600                 _changeToStep: function(from,to){
5601                         this.setValue(to.text);
5602                         var b=to.bookmark;
5603                         if(!b){ return; }
5604                         this._moveToBookmark(b);
5605                 },
5606                 undo: function(){
5607 //                      console.log('undo');
5608                         this.endEditing(true);
5609                         var s=this._steps.pop();
5610                         if(this._steps.length>0){
5611                                 this.focus();
5612                                 this._changeToStep(s,this._steps[this._steps.length-1]);
5613                                 this._undoedSteps.push(s);
5614                                 this.onDisplayChanged();
5615                                 return true;
5616                         }
5617                         return false;
5618                 },
5619                 redo: function(){
5620 //                      console.log('redo');
5621                         this.endEditing(true);
5622                         var s=this._undoedSteps.pop();
5623                         if(s && this._steps.length>0){
5624                                 this.focus();
5625                                 this._changeToStep(this._steps[this._steps.length-1],s);
5626                                 this._steps.push(s);
5627                                 this.onDisplayChanged();
5628                                 return true;
5629                         }
5630                         return false;
5631                 },
5632                 endEditing: function(ignore_caret){
5633                         if(this._editTimer){
5634                                 clearTimeout(this._editTimer);
5635                         }
5636                         if(this._inEditing){
5637                                 this._endEditing(ignore_caret);
5638                                 this._inEditing=false;
5639                         }
5640                 },
5641                 _getBookmark: function(){
5642                         var b=dojo.withGlobal(this.window,dijit.getBookmark);
5643                         var tmp=[];
5644                         if(dojo.isIE){
5645                                 if(dojo.isArray(b)){//CONTROL
5646                                         dojo.forEach(b,function(n){
5647                                                 tmp.push(dijit.range.getIndex(n,this.editNode).o);
5648                                         },this);
5649                                         b=tmp;
5650                                 }
5651                         }else{//w3c range
5652                                 tmp=dijit.range.getIndex(b.startContainer,this.editNode).o;
5653                                 b={startContainer:tmp,
5654                                         startOffset:b.startOffset,
5655                                         endContainer:b.endContainer===b.startContainer?tmp:dijit.range.getIndex(b.endContainer,this.editNode).o,
5656                                         endOffset:b.endOffset};
5657                         }
5658                         return b;
5659                 },
5660                 _beginEditing: function(cmd){
5661                         if(this._steps.length===0){
5662                                 this._steps.push({'text':this.savedContent,'bookmark':this._getBookmark()});
5663                         }
5664                 },
5665                 _endEditing: function(ignore_caret){
5666                         var v=this.getValue(true);
5667
5668                         this._undoedSteps=[];//clear undoed steps
5669                         this._steps.push({text: v, bookmark: this._getBookmark()});
5670                 },
5671                 onKeyDown: function(e){
5672                         if(!this.customUndo){
5673                                 this.inherited('onKeyDown',arguments);
5674                                 return;
5675                         }
5676                         var k = e.keyCode, ks = dojo.keys;
5677                         if(e.ctrlKey && !e.altKey){//undo and redo only if the special right Alt + z/y are not pressed #5892
5678                                 if(k == 90 || k == 122){ //z
5679                                         dojo.stopEvent(e);
5680                                         this.undo();
5681                                         return;
5682                                 }else if(k == 89 || k == 121){ //y
5683                                         dojo.stopEvent(e);
5684                                         this.redo();
5685                                         return;
5686                                 }
5687                         }
5688                         this.inherited('onKeyDown',arguments);
5689
5690                         switch(k){
5691                                         case ks.ENTER:
5692                                         case ks.BACKSPACE:
5693                                         case ks.DELETE:
5694                                                 this.beginEditing();
5695                                                 break;
5696                                         case 88: //x
5697                                         case 86: //v
5698                                                 if(e.ctrlKey && !e.altKey && !e.metaKey){
5699                                                         this.endEditing();//end current typing step if any
5700                                                         if(e.keyCode == 88){
5701                                                                 this.beginEditing('cut');
5702                                                                 //use timeout to trigger after the cut is complete
5703                                                                 setTimeout(dojo.hitch(this, this.endEditing), 1);
5704                                                         }else{
5705                                                                 this.beginEditing('paste');
5706                                                                 //use timeout to trigger after the paste is complete
5707                                                                 setTimeout(dojo.hitch(this, this.endEditing), 1);
5708                                                         }
5709                                                         break;
5710                                                 }
5711                                                 //pass through
5712                                         default:
5713                                                 if(!e.ctrlKey && !e.altKey && !e.metaKey && (e.keyCode<dojo.keys.F1 || e.keyCode>dojo.keys.F15)){
5714                                                         this.beginEditing();
5715                                                         break;
5716                                                 }
5717                                                 //pass through
5718                                         case ks.ALT:
5719                                                 this.endEditing();
5720                                                 break;
5721                                         case ks.UP_ARROW:
5722                                         case ks.DOWN_ARROW:
5723                                         case ks.LEFT_ARROW:
5724                                         case ks.RIGHT_ARROW:
5725                                         case ks.HOME:
5726                                         case ks.END:
5727                                         case ks.PAGE_UP:
5728                                         case ks.PAGE_DOWN:
5729                                                 this.endEditing(true);
5730                                                 break;
5731                                         //maybe ctrl+backspace/delete, so don't endEditing when ctrl is pressed
5732                                         case ks.CTRL:
5733                                         case ks.SHIFT:
5734                                         case ks.TAB:
5735                                                 break;
5736                                 }       
5737                 },
5738                 _onBlur: function(){
5739                         this.inherited('_onBlur',arguments);
5740                         this.endEditing(true);
5741                 },
5742                 onClick: function(){
5743                         this.endEditing(true);
5744                         this.inherited('onClick',arguments);
5745                 }
5746                 /* end of custom undo/redo support */
5747         }
5748 );
5749
5750 /* the following code is to registered a handler to get default plugins */
5751 dojo.subscribe(dijit._scopeName + ".Editor.getPlugin",null,function(o){
5752         if(o.plugin){ return; }
5753         var args = o.args, p;
5754         var _p = dijit._editor._Plugin;
5755         var name = args.name;
5756         switch(name){
5757                 case "undo": case "redo": case "cut": case "copy": case "paste": case "insertOrderedList":
5758                 case "insertUnorderedList": case "indent": case "outdent": case "justifyCenter":
5759                 case "justifyFull": case "justifyLeft": case "justifyRight": case "delete":
5760                 case "selectAll": case "removeFormat":
5761                 case "insertHorizontalRule":
5762                         p = new _p({ command: name });
5763                         break;
5764
5765                 case "bold": case "italic": case "underline": case "strikethrough":
5766                 case "subscript": case "superscript":
5767                         p = new _p({ buttonClass: dijit.form.ToggleButton, command: name });
5768                         break;
5769                 case "|":
5770                         p = new _p({ button: new dijit.ToolbarSeparator() });
5771         }
5772 //      console.log('name',name,p);
5773         o.plugin=p;
5774 });
5775
5776 }
5777
5778 if(!dojo._hasResource["dijit.Menu"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
5779 dojo._hasResource["dijit.Menu"] = true;
5780 dojo.provide("dijit.Menu");
5781
5782
5783
5784
5785
5786 dojo.declare("dijit.Menu",
5787         [dijit._Widget, dijit._Templated, dijit._KeyNavContainer],
5788         {
5789         // summary
5790         //      A context menu you can assign to multiple elements
5791
5792         constructor: function(){
5793                 this._bindings = [];
5794         },
5795
5796         templateString:
5797                         '<table class="dijit dijitMenu dijitReset dijitMenuTable" waiRole="menu" dojoAttachEvent="onkeypress:_onKeyPress">' +
5798                                 '<tbody class="dijitReset" dojoAttachPoint="containerNode"></tbody>'+
5799                         '</table>',
5800
5801         // targetNodeIds: String[]
5802         //      Array of dom node ids of nodes to attach to.
5803         //      Fill this with nodeIds upon widget creation and it becomes context menu for those nodes.
5804         targetNodeIds: [],
5805
5806         // contextMenuForWindow: Boolean
5807         //      if true, right clicking anywhere on the window will cause this context menu to open;
5808         //      if false, must specify targetNodeIds
5809         contextMenuForWindow: false,
5810
5811         // leftClickToOpen: Boolean
5812         //      If true, menu will open on left click instead of right click, similiar to a file menu.
5813         leftClickToOpen: false,
5814         
5815         // parentMenu: Widget
5816         // pointer to menu that displayed me
5817         parentMenu: null,
5818
5819         // popupDelay: Integer
5820         //      number of milliseconds before hovering (without clicking) causes the popup to automatically open
5821         popupDelay: 500,
5822
5823         // _contextMenuWithMouse: Boolean
5824         //      used to record mouse and keyboard events to determine if a context
5825         //      menu is being opened with the keyboard or the mouse
5826         _contextMenuWithMouse: false,
5827
5828         postCreate: function(){
5829                 if(this.contextMenuForWindow){
5830                         this.bindDomNode(dojo.body());
5831                 }else{
5832                         dojo.forEach(this.targetNodeIds, this.bindDomNode, this);
5833                 }
5834                 this.connectKeyNavHandlers([dojo.keys.UP_ARROW], [dojo.keys.DOWN_ARROW]);
5835         },
5836
5837         startup: function(){
5838                 if(this._started){ return; }
5839
5840                 dojo.forEach(this.getChildren(), function(child){ child.startup(); });
5841                 this.startupKeyNavChildren();
5842
5843                 this.inherited(arguments);
5844         },
5845
5846         onExecute: function(){
5847                 // summary: attach point for notification about when a menu item has been executed
5848         },
5849
5850         onCancel: function(/*Boolean*/ closeAll){
5851                 // summary: attach point for notification about when the user cancels the current menu
5852         },
5853
5854         _moveToPopup: function(/*Event*/ evt){
5855                 if(this.focusedChild && this.focusedChild.popup && !this.focusedChild.disabled){
5856                         this.focusedChild._onClick(evt);
5857                 }
5858         },
5859
5860         _onKeyPress: function(/*Event*/ evt){
5861                 // summary: Handle keyboard based menu navigation.
5862                 if(evt.ctrlKey || evt.altKey){ return; }
5863
5864                 switch(evt.keyCode){
5865                         case dojo.keys.RIGHT_ARROW:
5866                                 this._moveToPopup(evt);
5867                                 dojo.stopEvent(evt);
5868                                 break;
5869                         case dojo.keys.LEFT_ARROW:
5870                                 if(this.parentMenu){
5871                                         this.onCancel(false);
5872                                 }else{
5873                                         dojo.stopEvent(evt);
5874                                 }
5875                                 break;
5876                 }
5877         },
5878
5879         onItemHover: function(/*MenuItem*/ item){
5880                 // summary: Called when cursor is over a MenuItem
5881                 this.focusChild(item);
5882
5883                 if(this.focusedChild.popup && !this.focusedChild.disabled && !this.hover_timer){
5884                         this.hover_timer = setTimeout(dojo.hitch(this, "_openPopup"), this.popupDelay);
5885                 }
5886         },
5887
5888         _onChildBlur: function(item){
5889                 // summary: Close all popups that are open and descendants of this menu
5890                 dijit.popup.close(item.popup);
5891                 item._blur();
5892                 this._stopPopupTimer();
5893         },
5894
5895         onItemUnhover: function(/*MenuItem*/ item){
5896                 // summary: Callback fires when mouse exits a MenuItem
5897         },
5898
5899         _stopPopupTimer: function(){
5900                 if(this.hover_timer){
5901                         clearTimeout(this.hover_timer);
5902                         this.hover_timer = null;
5903                 }
5904         },
5905
5906         _getTopMenu: function(){
5907                 for(var top=this; top.parentMenu; top=top.parentMenu);
5908                 return top;
5909         },
5910
5911         onItemClick: function(/*Widget*/ item, /*Event*/ evt){
5912                 // summary: user defined function to handle clicks on an item
5913                 if(item.disabled){ return false; }
5914
5915                 if(item.popup){
5916                         if(!this.is_open){
5917                                 this._openPopup();
5918                         }
5919                 }else{
5920                         // before calling user defined handler, close hierarchy of menus
5921                         // and restore focus to place it was when menu was opened
5922                         this.onExecute();
5923
5924                         // user defined handler for click
5925                         item.onClick(evt);
5926                 }
5927         },
5928
5929         // thanks burstlib!
5930         _iframeContentWindow: function(/* HTMLIFrameElement */iframe_el){
5931                 // summary:
5932                 //      Returns the window reference of the passed iframe
5933                 var win = dijit.getDocumentWindow(dijit.Menu._iframeContentDocument(iframe_el)) ||
5934                         // Moz. TODO: is this available when defaultView isn't?
5935                         dijit.Menu._iframeContentDocument(iframe_el)['__parent__'] ||
5936                         (iframe_el.name && dojo.doc.frames[iframe_el.name]) || null;
5937                 return win;     //      Window
5938         },
5939
5940         _iframeContentDocument: function(/* HTMLIFrameElement */iframe_el){
5941                 // summary:
5942                 //      Returns a reference to the document object inside iframe_el
5943                 var doc = iframe_el.contentDocument // W3
5944                         || (iframe_el.contentWindow && iframe_el.contentWindow.document) // IE
5945                         || (iframe_el.name && dojo.doc.frames[iframe_el.name] && dojo.doc.frames[iframe_el.name].document)
5946                         || null;
5947                 return doc;     //      HTMLDocument
5948         },
5949
5950         bindDomNode: function(/*String|DomNode*/ node){
5951                 // summary: attach menu to given node
5952                 node = dojo.byId(node);
5953
5954                 //TODO: this is to support context popups in Editor.  Maybe this shouldn't be in dijit.Menu
5955                 var win = dijit.getDocumentWindow(node.ownerDocument);
5956                 if(node.tagName.toLowerCase()=="iframe"){
5957                         win = this._iframeContentWindow(node);
5958                         node = dojo.withGlobal(win, dojo.body);
5959                 }
5960
5961                 // to capture these events at the top level,
5962                 // attach to document, not body
5963                 var cn = (node == dojo.body() ? dojo.doc : node);
5964
5965                 node[this.id] = this._bindings.push([
5966                         dojo.connect(cn, (this.leftClickToOpen)?"onclick":"oncontextmenu", this, "_openMyself"),
5967                         dojo.connect(cn, "onkeydown", this, "_contextKey"),
5968                         dojo.connect(cn, "onmousedown", this, "_contextMouse")
5969                 ]);
5970         },
5971
5972         unBindDomNode: function(/*String|DomNode*/ nodeName){
5973                 // summary: detach menu from given node
5974                 var node = dojo.byId(nodeName);
5975                 if(node){
5976                         var bid = node[this.id]-1, b = this._bindings[bid];
5977                         dojo.forEach(b, dojo.disconnect);
5978                         delete this._bindings[bid];
5979                 }
5980         },
5981
5982         _contextKey: function(e){
5983                 this._contextMenuWithMouse = false;
5984                 if(e.keyCode == dojo.keys.F10){
5985                         dojo.stopEvent(e);
5986                         if(e.shiftKey && e.type=="keydown"){
5987                                 // FF: copying the wrong property from e will cause the system
5988                                 // context menu to appear in spite of stopEvent. Don't know
5989                                 // exactly which properties cause this effect.
5990                                 var _e = { target: e.target, pageX: e.pageX, pageY: e.pageY };
5991                                 _e.preventDefault = _e.stopPropagation = function(){};
5992                                 // IE: without the delay, focus work in "open" causes the system
5993                                 // context menu to appear in spite of stopEvent.
5994                                 window.setTimeout(dojo.hitch(this, function(){ this._openMyself(_e); }), 1);
5995                         }
5996                 }
5997         },
5998
5999         _contextMouse: function(e){
6000                 this._contextMenuWithMouse = true;
6001         },
6002
6003         _openMyself: function(/*Event*/ e){
6004                 // summary:
6005                 //              Internal function for opening myself when the user
6006                 //              does a right-click or something similar
6007
6008                 if(this.leftClickToOpen&&e.button>0){
6009                         return;
6010                 }
6011                 dojo.stopEvent(e);
6012
6013                 // Get coordinates.
6014                 // if we are opening the menu with the mouse or on safari open
6015                 // the menu at the mouse cursor
6016                 // (Safari does not have a keyboard command to open the context menu
6017                 // and we don't currently have a reliable way to determine
6018                 // _contextMenuWithMouse on Safari)
6019                 var x,y;
6020                 if(dojo.isSafari || this._contextMenuWithMouse){
6021                         x=e.pageX;
6022                         y=e.pageY;
6023                 }else{
6024                         // otherwise open near e.target
6025                         var coords = dojo.coords(e.target, true);
6026                         x = coords.x + 10;
6027                         y = coords.y + 10;
6028                 }
6029
6030                 var self=this;
6031                 var savedFocus = dijit.getFocus(this);
6032                 function closeAndRestoreFocus(){
6033                         // user has clicked on a menu or popup
6034                         dijit.focus(savedFocus);
6035                         dijit.popup.close(self);
6036                 }
6037                 dijit.popup.open({
6038                         popup: this,
6039                         x: x,
6040                         y: y,
6041                         onExecute: closeAndRestoreFocus,
6042                         onCancel: closeAndRestoreFocus,
6043                         orient: this.isLeftToRight() ? 'L' : 'R'
6044                 });
6045                 this.focus();
6046
6047                 this._onBlur = function(){
6048                         this.inherited('_onBlur', arguments);
6049                         // Usually the parent closes the child widget but if this is a context
6050                         // menu then there is no parent
6051                         dijit.popup.close(this);
6052                         // don't try to restore focus; user has clicked another part of the screen
6053                         // and set focus there
6054                 }
6055         },
6056
6057         onOpen: function(/*Event*/ e){
6058                 // summary: Open menu relative to the mouse
6059                 this.isShowingNow = true;
6060         },
6061
6062         onClose: function(){
6063                 // summary: callback when this menu is closed
6064                 this._stopPopupTimer();
6065                 this.parentMenu = null;
6066                 this.isShowingNow = false;
6067                 this.currentPopup = null;
6068                 if(this.focusedChild){
6069                         this._onChildBlur(this.focusedChild);
6070                         this.focusedChild = null;
6071                 }
6072         },
6073
6074         _openPopup: function(){
6075                 // summary: open the popup to the side of the current menu item
6076                 this._stopPopupTimer();
6077                 var from_item = this.focusedChild;
6078                 var popup = from_item.popup;
6079
6080                 if(popup.isShowingNow){ return; }
6081                 popup.parentMenu = this;
6082                 var self = this;
6083                 dijit.popup.open({
6084                         parent: this,
6085                         popup: popup,
6086                         around: from_item.arrowCell,
6087                         orient: this.isLeftToRight() ? {'TR': 'TL', 'TL': 'TR'} : {'TL': 'TR', 'TR': 'TL'},
6088                         onCancel: function(){
6089                                 // called when the child menu is canceled
6090                                 dijit.popup.close(popup);
6091                                 from_item.focus();      // put focus back on my node
6092                                 self.currentPopup = null;
6093                         }
6094                 });
6095
6096                 this.currentPopup = popup;
6097
6098                 if(popup.focus){
6099                         popup.focus();
6100                 }
6101         },
6102         
6103         uninitialize: function(){
6104                 dojo.forEach(this.targetNodeIds, this.unBindDomNode, this);
6105                 this.inherited(arguments);
6106         }
6107 }
6108 );
6109
6110 dojo.declare("dijit.MenuItem",
6111         [dijit._Widget, dijit._Templated, dijit._Contained],
6112         {
6113         // summary: A line item in a Menu Widget
6114
6115         // Make 3 columns
6116         //   icon, label, and expand arrow (BiDi-dependent) indicating sub-menu
6117         templateString:
6118                  '<tr class="dijitReset dijitMenuItem" '
6119                 +'dojoAttachEvent="onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick">'
6120                 +'<td class="dijitReset"><div class="dijitMenuItemIcon ${iconClass}" dojoAttachPoint="iconNode"></div></td>'
6121                 +'<td tabIndex="-1" class="dijitReset dijitMenuItemLabel" dojoAttachPoint="containerNode,focusNode" waiRole="menuitem"></td>'
6122                 +'<td class="dijitReset" dojoAttachPoint="arrowCell">'
6123                         +'<div class="dijitMenuExpand" dojoAttachPoint="expand" style="display:none">'
6124                         +'<span class="dijitInline dijitArrowNode dijitMenuExpandInner">+</span>'
6125                         +'</div>'
6126                 +'</td>'
6127                 +'</tr>',
6128
6129         // label: String
6130         //      menu text
6131         label: '',
6132
6133         // iconClass: String
6134         //      class to apply to div in button to make it display an icon
6135         iconClass: "",
6136
6137         // disabled: Boolean
6138         //  if true, the menu item is disabled
6139         //  if false, the menu item is enabled
6140         disabled: false,
6141
6142         postCreate: function(){
6143                 dojo.setSelectable(this.domNode, false);
6144                 this.setDisabled(this.disabled);
6145                 if(this.label){
6146                         this.setLabel(this.label);
6147                 }
6148         },
6149
6150         _onHover: function(){
6151                 // summary: callback when mouse is moved onto menu item
6152                 this.getParent().onItemHover(this);
6153         },
6154
6155         _onUnhover: function(){
6156                 // summary: callback when mouse is moved off of menu item
6157
6158                 // if we are unhovering the currently selected item
6159                 // then unselect it
6160                 this.getParent().onItemUnhover(this);
6161         },
6162
6163         _onClick: function(evt){
6164                 this.getParent().onItemClick(this, evt);
6165                 dojo.stopEvent(evt);
6166         },
6167
6168         onClick: function(/*Event*/ evt){
6169                 // summary: User defined function to handle clicks
6170         },
6171
6172         focus: function(){
6173                 dojo.addClass(this.domNode, 'dijitMenuItemHover');
6174                 try{
6175                         dijit.focus(this.containerNode);
6176                 }catch(e){
6177                         // this throws on IE (at least) in some scenarios
6178                 }
6179         },
6180
6181         _blur: function(){
6182                 dojo.removeClass(this.domNode, 'dijitMenuItemHover');
6183         },
6184         
6185         setLabel: function(/*String*/ value){
6186                 this.containerNode.innerHTML=this.label=value;
6187         },
6188
6189         setDisabled: function(/*Boolean*/ value){
6190                 // summary: enable or disable this menu item
6191                 this.disabled = value;
6192                 dojo[value ? "addClass" : "removeClass"](this.domNode, 'dijitMenuItemDisabled');
6193                 dijit.setWaiState(this.containerNode, 'disabled', value ? 'true' : 'false');
6194         }
6195 });
6196
6197 dojo.declare("dijit.PopupMenuItem",
6198         dijit.MenuItem,
6199         {
6200         _fillContent: function(){
6201                 // summary: The innerHTML contains both the menu item text and a popup widget
6202                 // description: the first part holds the menu item text and the second part is the popup
6203                 // example: 
6204                 // |    <div dojoType="dijit.PopupMenuItem">
6205                 // |            <span>pick me</span>
6206                 // |            <popup> ... </popup>
6207                 // |    </div>
6208                 if(this.srcNodeRef){
6209                         var nodes = dojo.query("*", this.srcNodeRef);
6210                         dijit.PopupMenuItem.superclass._fillContent.call(this, nodes[0]);
6211
6212                         // save pointer to srcNode so we can grab the drop down widget after it's instantiated
6213                         this.dropDownContainer = this.srcNodeRef;
6214                 }
6215         },
6216
6217         startup: function(){
6218                 if(this._started){ return; }
6219                 this.inherited(arguments);
6220
6221                 // we didn't copy the dropdown widget from the this.srcNodeRef, so it's in no-man's
6222                 // land now.  move it to dojo.doc.body.
6223                 if(!this.popup){
6224                         var node = dojo.query("[widgetId]", this.dropDownContainer)[0];
6225                         this.popup = dijit.byNode(node);
6226                 }
6227                 dojo.body().appendChild(this.popup.domNode);
6228
6229                 this.popup.domNode.style.display="none";
6230                 dojo.addClass(this.expand, "dijitMenuExpandEnabled");
6231                 dojo.style(this.expand, "display", "");
6232                 dijit.setWaiState(this.containerNode, "haspopup", "true");
6233         },
6234         
6235         destroyDescendants: function(){
6236                 if(this.popup){
6237                         this.popup.destroyRecursive();
6238                         delete this.popup;
6239                 }
6240                 this.inherited(arguments);
6241         }
6242 });
6243
6244 dojo.declare("dijit.MenuSeparator",
6245         [dijit._Widget, dijit._Templated, dijit._Contained],
6246         {
6247         // summary: A line between two menu items
6248
6249         templateString: '<tr class="dijitMenuSeparator"><td colspan=3>'
6250                         +'<div class="dijitMenuSeparatorTop"></div>'
6251                         +'<div class="dijitMenuSeparatorBottom"></div>'
6252                         +'</td></tr>',
6253
6254         postCreate: function(){
6255                 dojo.setSelectable(this.domNode, false);
6256         },
6257         
6258         isFocusable: function(){
6259                 // summary: over ride to always return false
6260                 return false; // Boolean
6261         }
6262 });
6263
6264 }
6265
6266 if(!dojo._hasResource["dojo.regexp"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
6267 dojo._hasResource["dojo.regexp"] = true;
6268 dojo.provide("dojo.regexp");
6269
6270 /*=====
6271 dojo.regexp = {
6272         // summary: Regular expressions and Builder resources
6273 };
6274 =====*/
6275
6276 dojo.regexp.escapeString = function(/*String*/str, /*String?*/except){
6277         //      summary:
6278         //              Adds escape sequences for special characters in regular expressions
6279         // except:
6280         //              a String with special characters to be left unescaped
6281
6282 //      return str.replace(/([\f\b\n\t\r[\^$|?*+(){}])/gm, "\\$1"); // string
6283         return str.replace(/([\.$?*!=:|{}\(\)\[\]\\\/^])/g, function(ch){
6284                 if(except && except.indexOf(ch) != -1){
6285                         return ch;
6286                 }
6287                 return "\\" + ch;
6288         }); // String
6289 }
6290
6291 dojo.regexp.buildGroupRE = function(/*Object|Array*/arr, /*Function*/re, /*Boolean?*/nonCapture){
6292         //      summary:
6293         //              Builds a regular expression that groups subexpressions
6294         //      description:
6295         //              A utility function used by some of the RE generators. The
6296         //              subexpressions are constructed by the function, re, in the second
6297         //              parameter.  re builds one subexpression for each elem in the array
6298         //              a, in the first parameter. Returns a string for a regular
6299         //              expression that groups all the subexpressions.
6300         // arr:
6301         //              A single value or an array of values.
6302         // re:
6303         //              A function. Takes one parameter and converts it to a regular
6304         //              expression. 
6305         // nonCapture:
6306         //              If true, uses non-capturing match, otherwise matches are retained
6307         //              by regular expression. Defaults to false
6308
6309         // case 1: a is a single value.
6310         if(!(arr instanceof Array)){
6311                 return re(arr); // String
6312         }
6313
6314         // case 2: a is an array
6315         var b = [];
6316         for(var i = 0; i < arr.length; i++){
6317                 // convert each elem to a RE
6318                 b.push(re(arr[i]));
6319         }
6320
6321          // join the REs as alternatives in a RE group.
6322         return dojo.regexp.group(b.join("|"), nonCapture); // String
6323 }
6324
6325 dojo.regexp.group = function(/*String*/expression, /*Boolean?*/nonCapture){
6326         // summary:
6327         //              adds group match to expression
6328         // nonCapture:
6329         //              If true, uses non-capturing match, otherwise matches are retained
6330         //              by regular expression. 
6331         return "(" + (nonCapture ? "?:":"") + expression + ")"; // String
6332 }
6333
6334 }
6335
6336 if(!dojo._hasResource["dojo.number"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
6337 dojo._hasResource["dojo.number"] = true;
6338 dojo.provide("dojo.number");
6339
6340
6341
6342
6343
6344
6345
6346 /*=====
6347 dojo.number = {
6348         // summary: localized formatting and parsing routines for Number
6349 }
6350
6351 dojo.number.__FormatOptions = function(){
6352         //      pattern: String?
6353         //              override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
6354         //              with this string
6355         //      type: String?
6356         //              choose a format type based on the locale from the following:
6357         //              decimal, scientific, percent, currency. decimal by default.
6358         //      places: Number?
6359         //              fixed number of decimal places to show.  This overrides any
6360         //              information in the provided pattern.
6361         //      round: Number?
6362         //              5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
6363         //              means don't round.
6364         //      currency: String?
6365         //              an [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code, a three letter sequence like "USD"
6366         //      symbol: String?
6367         //              localized currency symbol
6368         //      locale: String?
6369         //              override the locale used to determine formatting rules
6370         this.pattern = pattern;
6371         this.type = type;
6372         this.places = places;
6373         this.round = round;
6374         this.currency = currency;
6375         this.symbol = symbol;
6376         this.locale = locale;
6377 }
6378 =====*/
6379
6380 dojo.number.format = function(/*Number*/value, /*dojo.number.__FormatOptions?*/options){
6381         // summary:
6382         //              Format a Number as a String, using locale-specific settings
6383         // description:
6384         //              Create a string from a Number using a known localized pattern.
6385         //              Formatting patterns appropriate to the locale are chosen from the
6386         //              [CLDR](http://unicode.org/cldr) as well as the appropriate symbols and
6387         //              delimiters.  See <http://www.unicode.org/reports/tr35/#Number_Elements>
6388         // value:
6389         //              the number to be formatted.  If not a valid JavaScript number,
6390         //              return null.
6391
6392         options = dojo.mixin({}, options || {});
6393         var locale = dojo.i18n.normalizeLocale(options.locale);
6394         var bundle = dojo.i18n.getLocalization("dojo.cldr", "number", locale);
6395         options.customs = bundle;
6396         var pattern = options.pattern || bundle[(options.type || "decimal") + "Format"];
6397         if(isNaN(value)){ return null; } // null
6398         return dojo.number._applyPattern(value, pattern, options); // String
6399 };
6400
6401 //dojo.number._numberPatternRE = /(?:[#0]*,?)*[#0](?:\.0*#*)?/; // not precise, but good enough
6402 dojo.number._numberPatternRE = /[#0,]*[#0](?:\.0*#*)?/; // not precise, but good enough
6403
6404 dojo.number._applyPattern = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatOptions?*/options){
6405         // summary:
6406         //              Apply pattern to format value as a string using options. Gives no
6407         //              consideration to local customs.
6408         // value:
6409         //              the number to be formatted.
6410         // pattern:
6411         //              a pattern string as described by
6412         //              [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
6413         // options: dojo.number.__FormatOptions?
6414         //              _applyPattern is usually called via `dojo.number.format()` which
6415         //              populates an extra property in the options parameter, "customs".
6416         //              The customs object specifies group and decimal parameters if set.
6417
6418         //TODO: support escapes
6419         options = options || {};
6420         var group = options.customs.group;
6421         var decimal = options.customs.decimal;
6422
6423         var patternList = pattern.split(';');
6424         var positivePattern = patternList[0];
6425         pattern = patternList[(value < 0) ? 1 : 0] || ("-" + positivePattern);
6426
6427         //TODO: only test against unescaped
6428         if(pattern.indexOf('%') != -1){
6429                 value *= 100;
6430         }else if(pattern.indexOf('\u2030') != -1){
6431                 value *= 1000; // per mille
6432         }else if(pattern.indexOf('\u00a4') != -1){
6433                 group = options.customs.currencyGroup || group;//mixins instead?
6434                 decimal = options.customs.currencyDecimal || decimal;// Should these be mixins instead?
6435                 pattern = pattern.replace(/\u00a4{1,3}/, function(match){
6436                         var prop = ["symbol", "currency", "displayName"][match.length-1];
6437                         return options[prop] || options.currency || "";
6438                 });
6439         }else if(pattern.indexOf('E') != -1){
6440                 throw new Error("exponential notation not supported");
6441         }
6442         
6443         //TODO: support @ sig figs?
6444         var numberPatternRE = dojo.number._numberPatternRE;
6445         var numberPattern = positivePattern.match(numberPatternRE);
6446         if(!numberPattern){
6447                 throw new Error("unable to find a number expression in pattern: "+pattern);
6448         }
6449         return pattern.replace(numberPatternRE,
6450                 dojo.number._formatAbsolute(value, numberPattern[0], {decimal: decimal, group: group, places: options.places}));
6451 }
6452
6453 dojo.number.round = function(/*Number*/value, /*Number*/places, /*Number?*/multiple){
6454         //      summary:
6455         //              Rounds the number at the given number of places
6456         //      value:
6457         //              the number to round
6458         //      places:
6459         //              the number of decimal places where rounding takes place
6460         //      multiple:
6461         //              rounds next place to nearest multiple
6462
6463         var pieces = String(value).split(".");
6464         var length = (pieces[1] && pieces[1].length) || 0;
6465         if(length > places){
6466                 var factor = Math.pow(10, places);
6467                 if(multiple > 0){factor *= 10/multiple;places++;} //FIXME
6468                 value = Math.round(value * factor)/factor;
6469
6470                 // truncate to remove any residual floating point values
6471                 pieces = String(value).split(".");
6472                 length = (pieces[1] && pieces[1].length) || 0;
6473                 if(length > places){
6474                         pieces[1] = pieces[1].substr(0, places);
6475                         value = Number(pieces.join("."));
6476                 }
6477         }
6478         return value; //Number
6479 }
6480
6481 /*=====
6482 dojo.number.__FormatAbsoluteOptions = function(){
6483         //      decimal: String?
6484         //              the decimal separator
6485         //      group: String?
6486         //              the group separator
6487         //      places: Integer?
6488         //              number of decimal places
6489         //      round: Number?
6490         //              5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
6491         //              means don't round.
6492         this.decimal = decimal;
6493         this.group = group;
6494         this.places = places;
6495         this.round = round;
6496 }
6497 =====*/
6498
6499 dojo.number._formatAbsolute = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatAbsoluteOptions?*/options){
6500         // summary: 
6501         //              Apply numeric pattern to absolute value using options. Gives no
6502         //              consideration to local customs.
6503         // value:
6504         //              the number to be formatted, ignores sign
6505         // pattern:
6506         //              the number portion of a pattern (e.g. `#,##0.00`)
6507         options = options || {};
6508         if(options.places === true){options.places=0;}
6509         if(options.places === Infinity){options.places=6;} // avoid a loop; pick a limit
6510
6511         var patternParts = pattern.split(".");
6512         var maxPlaces = (options.places >= 0) ? options.places : (patternParts[1] && patternParts[1].length) || 0;
6513         if(!(options.round < 0)){
6514                 value = dojo.number.round(value, maxPlaces, options.round);
6515         }
6516
6517         var valueParts = String(Math.abs(value)).split(".");
6518         var fractional = valueParts[1] || "";
6519         if(options.places){
6520                 valueParts[1] = dojo.string.pad(fractional.substr(0, options.places), options.places, '0', true);
6521         }else if(patternParts[1] && options.places !== 0){
6522                 // Pad fractional with trailing zeros
6523                 var pad = patternParts[1].lastIndexOf("0") + 1;
6524                 if(pad > fractional.length){
6525                         valueParts[1] = dojo.string.pad(fractional, pad, '0', true);
6526                 }
6527
6528                 // Truncate fractional
6529                 var places = patternParts[1].length;
6530                 if(places < fractional.length){
6531                         valueParts[1] = fractional.substr(0, places);
6532                 }
6533         }else{
6534                 if(valueParts[1]){ valueParts.pop(); }
6535         }
6536
6537         // Pad whole with leading zeros
6538         var patternDigits = patternParts[0].replace(',', '');
6539         pad = patternDigits.indexOf("0");
6540         if(pad != -1){
6541                 pad = patternDigits.length - pad;
6542                 if(pad > valueParts[0].length){
6543                         valueParts[0] = dojo.string.pad(valueParts[0], pad);
6544                 }
6545
6546                 // Truncate whole
6547                 if(patternDigits.indexOf("#") == -1){
6548                         valueParts[0] = valueParts[0].substr(valueParts[0].length - pad);
6549                 }
6550         }
6551
6552         // Add group separators
6553         var index = patternParts[0].lastIndexOf(',');
6554         var groupSize, groupSize2;
6555         if(index != -1){
6556                 groupSize = patternParts[0].length - index - 1;
6557                 var remainder = patternParts[0].substr(0, index);
6558                 index = remainder.lastIndexOf(',');
6559                 if(index != -1){
6560                         groupSize2 = remainder.length - index - 1;
6561                 }
6562         }
6563         var pieces = [];
6564         for(var whole = valueParts[0]; whole;){
6565                 var off = whole.length - groupSize;
6566                 pieces.push((off > 0) ? whole.substr(off) : whole);
6567                 whole = (off > 0) ? whole.slice(0, off) : "";
6568                 if(groupSize2){
6569                         groupSize = groupSize2;
6570                         delete groupSize2;
6571                 }
6572         }
6573         valueParts[0] = pieces.reverse().join(options.group || ",");
6574
6575         return valueParts.join(options.decimal || ".");
6576 };
6577
6578 /*=====
6579 dojo.number.__RegexpOptions = function(){
6580         //      pattern: String?
6581         //              override pattern with this string.  Default is provided based on
6582         //              locale.
6583         //      type: String?
6584         //              choose a format type based on the locale from the following:
6585         //              decimal, scientific, percent, currency. decimal by default.
6586         //      locale: String?
6587         //              override the locale used to determine formatting rules
6588         //      strict: Boolean?
6589         //              strict parsing, false by default
6590         //      places: Number|String?
6591         //              number of decimal places to accept: Infinity, a positive number, or
6592         //              a range "n,m".  By default, defined by pattern.
6593         this.pattern = pattern;
6594         this.type = type;
6595         this.locale = locale;
6596         this.strict = strict;
6597         this.places = places;
6598 }
6599 =====*/
6600 dojo.number.regexp = function(/*dojo.number.__RegexpOptions?*/options){
6601         //      summary:
6602         //              Builds the regular needed to parse a number
6603         //      description:
6604         //              Returns regular expression with positive and negative match, group
6605         //              and decimal separators
6606         return dojo.number._parseInfo(options).regexp; // String
6607 }
6608
6609 dojo.number._parseInfo = function(/*Object?*/options){
6610         options = options || {};
6611         var locale = dojo.i18n.normalizeLocale(options.locale);
6612         var bundle = dojo.i18n.getLocalization("dojo.cldr", "number", locale);
6613         var pattern = options.pattern || bundle[(options.type || "decimal") + "Format"];
6614 //TODO: memoize?
6615         var group = bundle.group;
6616         var decimal = bundle.decimal;
6617         var factor = 1;
6618
6619         if(pattern.indexOf('%') != -1){
6620                 factor /= 100;
6621         }else if(pattern.indexOf('\u2030') != -1){
6622                 factor /= 1000; // per mille
6623         }else{
6624                 var isCurrency = pattern.indexOf('\u00a4') != -1;
6625                 if(isCurrency){
6626                         group = bundle.currencyGroup || group;
6627                         decimal = bundle.currencyDecimal || decimal;
6628                 }
6629         }
6630
6631         //TODO: handle quoted escapes
6632         var patternList = pattern.split(';');
6633         if(patternList.length == 1){
6634                 patternList.push("-" + patternList[0]);
6635         }
6636
6637         var re = dojo.regexp.buildGroupRE(patternList, function(pattern){
6638                 pattern = "(?:"+dojo.regexp.escapeString(pattern, '.')+")";
6639                 return pattern.replace(dojo.number._numberPatternRE, function(format){
6640                         var flags = {
6641                                 signed: false,
6642                                 separator: options.strict ? group : [group,""],
6643                                 fractional: options.fractional,
6644                                 decimal: decimal,
6645                                 exponent: false};
6646                         var parts = format.split('.');
6647                         var places = options.places;
6648                         if(parts.length == 1 || places === 0){flags.fractional = false;}
6649                         else{
6650                                 if(places === undefined){ places = parts[1].lastIndexOf('0')+1; }
6651                                 if(places && options.fractional == undefined){flags.fractional = true;} // required fractional, unless otherwise specified
6652                                 if(!options.places && (places < parts[1].length)){ places += "," + parts[1].length; }
6653                                 flags.places = places;
6654                         }
6655                         var groups = parts[0].split(',');
6656                         if(groups.length>1){
6657                                 flags.groupSize = groups.pop().length;
6658                                 if(groups.length>1){
6659                                         flags.groupSize2 = groups.pop().length;
6660                                 }
6661                         }
6662                         return "("+dojo.number._realNumberRegexp(flags)+")";
6663                 });
6664         }, true);
6665
6666         if(isCurrency){
6667                 // substitute the currency symbol for the placeholder in the pattern
6668                 re = re.replace(/(\s*)(\u00a4{1,3})(\s*)/g, function(match, before, target, after){
6669                         var prop = ["symbol", "currency", "displayName"][target.length-1];
6670                         var symbol = dojo.regexp.escapeString(options[prop] || options.currency || "");
6671                         before = before ? "\\s" : "";
6672                         after = after ? "\\s" : "";
6673                         if(!options.strict){
6674                                 if(before){before += "*";}
6675                                 if(after){after += "*";}
6676                                 return "(?:"+before+symbol+after+")?";
6677                         }
6678                         return before+symbol+after;
6679                 });
6680         }
6681
6682 //TODO: substitute localized sign/percent/permille/etc.?
6683
6684         // normalize whitespace and return
6685         return {regexp: re.replace(/[\xa0 ]/g, "[\\s\\xa0]"), group: group, decimal: decimal, factor: factor}; // Object
6686 }
6687
6688 /*=====
6689 dojo.number.__ParseOptions = function(){
6690         //      pattern: String
6691         //              override pattern with this string.  Default is provided based on
6692         //              locale.
6693         //      type: String?
6694         //              choose a format type based on the locale from the following:
6695         //              decimal, scientific, percent, currency. decimal by default.
6696         //      locale: String
6697         //              override the locale used to determine formatting rules
6698         //      strict: Boolean?
6699         //              strict parsing, false by default
6700         //      currency: Object
6701         //              object with currency information
6702         this.pattern = pattern;
6703         this.type = type;
6704         this.locale = locale;
6705         this.strict = strict;
6706         this.currency = currency;
6707 }
6708 =====*/
6709 dojo.number.parse = function(/*String*/expression, /*dojo.number.__ParseOptions?*/options){
6710         // summary:
6711         //              Convert a properly formatted string to a primitive Number, using
6712         //              locale-specific settings.
6713         // description:
6714         //              Create a Number from a string using a known localized pattern.
6715         //              Formatting patterns are chosen appropriate to the locale
6716         //              and follow the syntax described by
6717         //              [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
6718         // expression:
6719         //              A string representation of a Number
6720         var info = dojo.number._parseInfo(options);
6721         var results = (new RegExp("^"+info.regexp+"$")).exec(expression);
6722         if(!results){
6723                 return NaN; //NaN
6724         }
6725         var absoluteMatch = results[1]; // match for the positive expression
6726         if(!results[1]){
6727                 if(!results[2]){
6728                         return NaN; //NaN
6729                 }
6730                 // matched the negative pattern
6731                 absoluteMatch =results[2];
6732                 info.factor *= -1;
6733         }
6734
6735         // Transform it to something Javascript can parse as a number.  Normalize
6736         // decimal point and strip out group separators or alternate forms of whitespace
6737         absoluteMatch = absoluteMatch.
6738                 replace(new RegExp("["+info.group + "\\s\\xa0"+"]", "g"), "").
6739                 replace(info.decimal, ".");
6740         // Adjust for negative sign, percent, etc. as necessary
6741         return Number(absoluteMatch) * info.factor; //Number
6742 };
6743
6744 /*=====
6745 dojo.number.__RealNumberRegexpFlags = function(){
6746         //      places: Number?
6747         //              The integer number of decimal places or a range given as "n,m".  If
6748         //              not given, the decimal part is optional and the number of places is
6749         //              unlimited.
6750         //      decimal: String?
6751         //              A string for the character used as the decimal point.  Default
6752         //              is ".".
6753         //      fractional: Boolean|Array?
6754         //              Whether decimal places are allowed.  Can be true, false, or [true,
6755         //              false].  Default is [true, false]
6756         //      exponent: Boolean|Array?
6757         //              Express in exponential notation.  Can be true, false, or [true,
6758         //              false]. Default is [true, false], (i.e. will match if the
6759         //              exponential part is present are not).
6760         //      eSigned: Boolean|Array?
6761         //              The leading plus-or-minus sign on the exponent.  Can be true,
6762         //              false, or [true, false].  Default is [true, false], (i.e. will
6763         //              match if it is signed or unsigned).  flags in regexp.integer can be
6764         //              applied.
6765         this.places = places;
6766         this.decimal = decimal;
6767         this.fractional = fractional;
6768         this.exponent = exponent;
6769         this.eSigned = eSigned;
6770 }
6771 =====*/
6772
6773 dojo.number._realNumberRegexp = function(/*dojo.number.__RealNumberRegexpFlags?*/flags){
6774         // summary:
6775         //              Builds a regular expression to match a real number in exponential
6776         //              notation
6777
6778         // assign default values to missing paramters
6779         flags = flags || {};
6780         //TODO: use mixin instead?
6781         if(!("places" in flags)){ flags.places = Infinity; }
6782         if(typeof flags.decimal != "string"){ flags.decimal = "."; }
6783         if(!("fractional" in flags) || /^0/.test(flags.places)){ flags.fractional = [true, false]; }
6784         if(!("exponent" in flags)){ flags.exponent = [true, false]; }
6785         if(!("eSigned" in flags)){ flags.eSigned = [true, false]; }
6786
6787         // integer RE
6788         var integerRE = dojo.number._integerRegexp(flags);
6789
6790         // decimal RE
6791         var decimalRE = dojo.regexp.buildGroupRE(flags.fractional,
6792                 function(q){
6793                         var re = "";
6794                         if(q && (flags.places!==0)){
6795                                 re = "\\" + flags.decimal;
6796                                 if(flags.places == Infinity){ 
6797                                         re = "(?:" + re + "\\d+)?"; 
6798                                 }else{
6799                                         re += "\\d{" + flags.places + "}"; 
6800                                 }
6801                         }
6802                         return re;
6803                 },
6804                 true
6805         );
6806
6807         // exponent RE
6808         var exponentRE = dojo.regexp.buildGroupRE(flags.exponent,
6809                 function(q){ 
6810                         if(q){ return "([eE]" + dojo.number._integerRegexp({ signed: flags.eSigned}) + ")"; }
6811                         return ""; 
6812                 }
6813         );
6814
6815         // real number RE
6816         var realRE = integerRE + decimalRE;
6817         // allow for decimals without integers, e.g. .25
6818         if(decimalRE){realRE = "(?:(?:"+ realRE + ")|(?:" + decimalRE + "))";}
6819         return realRE + exponentRE; // String
6820 };
6821
6822 /*=====
6823 dojo.number.__IntegerRegexpFlags = function(){
6824         //      signed: Boolean?
6825         //              The leading plus-or-minus sign. Can be true, false, or `[true,false]`.
6826         //              Default is `[true, false]`, (i.e. will match if it is signed
6827         //              or unsigned).
6828         //      separator: String?
6829         //              The character used as the thousands separator. Default is no
6830         //              separator. For more than one symbol use an array, e.g. `[",", ""]`,
6831         //              makes ',' optional.
6832         //      groupSize: Number?
6833         //              group size between separators
6834         //      groupSize2: Number?
6835         //              second grouping, where separators 2..n have a different interval than the first separator (for India)
6836         this.signed = signed;
6837         this.separator = separator;
6838         this.groupSize = groupSize;
6839         this.groupSize2 = groupSize2;
6840 }
6841 =====*/
6842
6843 dojo.number._integerRegexp = function(/*dojo.number.__IntegerRegexpFlags?*/flags){
6844         // summary: 
6845         //              Builds a regular expression that matches an integer
6846
6847         // assign default values to missing paramters
6848         flags = flags || {};
6849         if(!("signed" in flags)){ flags.signed = [true, false]; }
6850         if(!("separator" in flags)){
6851                 flags.separator = "";
6852         }else if(!("groupSize" in flags)){
6853                 flags.groupSize = 3;
6854         }
6855         // build sign RE
6856         var signRE = dojo.regexp.buildGroupRE(flags.signed,
6857                 function(q) { return q ? "[-+]" : ""; },
6858                 true
6859         );
6860
6861         // number RE
6862         var numberRE = dojo.regexp.buildGroupRE(flags.separator,
6863                 function(sep){
6864                         if(!sep){
6865                                 return "(?:0|[1-9]\\d*)";
6866                         }
6867
6868                         sep = dojo.regexp.escapeString(sep);
6869                         if(sep == " "){ sep = "\\s"; }
6870                         else if(sep == "\xa0"){ sep = "\\s\\xa0"; }
6871
6872                         var grp = flags.groupSize, grp2 = flags.groupSize2;
6873                         if(grp2){
6874                                 var grp2RE = "(?:0|[1-9]\\d{0," + (grp2-1) + "}(?:[" + sep + "]\\d{" + grp2 + "})*[" + sep + "]\\d{" + grp + "})";
6875                                 return ((grp-grp2) > 0) ? "(?:" + grp2RE + "|(?:0|[1-9]\\d{0," + (grp-1) + "}))" : grp2RE;
6876                         }
6877                         return "(?:0|[1-9]\\d{0," + (grp-1) + "}(?:[" + sep + "]\\d{" + grp + "})*)";
6878                 },
6879                 true
6880         );
6881
6882         // integer RE
6883         return signRE + numberRE; // String
6884 }
6885
6886 }
6887
6888 if(!dojo._hasResource["dijit.ProgressBar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
6889 dojo._hasResource["dijit.ProgressBar"] = true;
6890 dojo.provide("dijit.ProgressBar");
6891
6892
6893
6894
6895
6896
6897
6898 dojo.declare("dijit.ProgressBar", [dijit._Widget, dijit._Templated], {
6899         // summary: A progress indication widget
6900         //
6901         // example:
6902         // |    <div dojoType="ProgressBar"
6903         // |             places="0"
6904         // |             progress="..." maximum="...">
6905         // |    </div>
6906         //
6907         // progress: String (Percentage or Number)
6908         //      initial progress value.
6909         //      with "%": percentage value, 0% <= progress <= 100%
6910         //      or without "%": absolute value, 0 <= progress <= maximum
6911         progress: "0",
6912
6913         // maximum: Float
6914         //      max sample number
6915         maximum: 100,
6916
6917         // places: Number
6918         //      number of places to show in values; 0 by default
6919         places: 0,
6920
6921         // indeterminate: Boolean
6922         //      If false: show progress.
6923         //      If true: show that a process is underway but that the progress is unknown
6924         indeterminate: false,
6925
6926         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",
6927
6928         _indeterminateHighContrastImagePath:
6929                 dojo.moduleUrl("dijit", "themes/a11y/indeterminate_progress.gif"),
6930
6931         // public functions
6932         postCreate: function(){
6933                 this.inherited("postCreate",arguments);
6934                 this.inteterminateHighContrastImage.setAttribute("src",
6935                         this._indeterminateHighContrastImagePath);
6936                 this.update();
6937         },
6938
6939         update: function(/*Object?*/attributes){
6940                 // summary: update progress information
6941                 //
6942                 // attributes: may provide progress and/or maximum properties on this parameter,
6943                 //      see attribute specs for details.
6944                 dojo.mixin(this, attributes||{});
6945                 var percent = 1, classFunc;
6946                 if(this.indeterminate){
6947                         classFunc = "addClass";
6948                         dijit.removeWaiState(this.internalProgress, "valuenow");
6949                         dijit.removeWaiState(this.internalProgress, "valuemin");
6950                         dijit.removeWaiState(this.internalProgress, "valuemax");
6951                 }else{
6952                         classFunc = "removeClass";
6953                         if(String(this.progress).indexOf("%") != -1){
6954                                 percent = Math.min(parseFloat(this.progress)/100, 1);
6955                                 this.progress = percent * this.maximum;
6956                         }else{
6957                                 this.progress = Math.min(this.progress, this.maximum);
6958                                 percent = this.progress / this.maximum;
6959                         }
6960                         var text = this.report(percent);
6961                         this.label.firstChild.nodeValue = text;
6962                         dijit.setWaiState(this.internalProgress, "describedby", this.label.id);
6963                         dijit.setWaiState(this.internalProgress, "valuenow", this.progress);
6964                         dijit.setWaiState(this.internalProgress, "valuemin", 0);
6965                         dijit.setWaiState(this.internalProgress, "valuemax", this.maximum);
6966                 }
6967                 dojo[classFunc](this.domNode, "dijitProgressBarIndeterminate");
6968                 this.internalProgress.style.width = (percent * 100) + "%";
6969                 this.onChange();
6970         },
6971
6972         report: function(/*float*/percent){
6973                 // Generates message to show; may be overridden by user
6974                 return dojo.number.format(percent, {type: "percent", places: this.places, locale: this.lang});
6975         },
6976
6977         onChange: function(){
6978                 // summary: User definable function fired when progress updates.
6979         }
6980 });
6981
6982 }
6983
6984 if(!dojo._hasResource["dijit.TitlePane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
6985 dojo._hasResource["dijit.TitlePane"] = true;
6986 dojo.provide("dijit.TitlePane");
6987
6988
6989
6990
6991
6992
6993 dojo.declare(
6994         "dijit.TitlePane",
6995         [dijit.layout.ContentPane, dijit._Templated],
6996 {
6997         // summary: A pane with a title on top, that can be opened or collapsed.
6998         //
6999         // description: An accessible container with a Title Heading, and a content
7000         //      section that slides open and closed. TitlePane is an extension to 
7001         //      ContentPane, providing all the usesful content-control aspects from.
7002         //
7003         // example:
7004         // |    // load a TitlePane from remote file:
7005         // |    var foo = new dijit.TitlePane({ href: "foobar.html", title:"Title" });
7006         // |    foo.startup();
7007         //
7008         // example:
7009         // |    <!-- markup href example: -->
7010         // |    <div dojoType="dijit.TitlePane" href="foobar.html" title="Title"></div>
7011         // 
7012         // example:
7013         // |    <!-- markup with inline data -->
7014         // |    <div dojoType="dijit.TitlePane" title="Title">
7015         // |            <p>I am content</p>
7016         // |    </div>
7017         //
7018         // title: String
7019         //              Title of the pane
7020         title: "",
7021
7022         // open: Boolean
7023         //              Whether pane is opened or closed.
7024         open: true,
7025
7026         // duration: Integer
7027         //              Time in milliseconds to fade in/fade out
7028         duration: 250,
7029
7030         // baseClass: String
7031         //      The root className to use for the various states of this widget
7032         baseClass: "dijitTitlePane",
7033
7034         templateString:"<div class=\"${baseClass}\">\n\t<div dojoAttachEvent=\"onclick:toggle,onkeypress: _onTitleKey,onfocus:_handleFocus,onblur:_handleFocus\" tabindex=\"0\"\n\t\t\twaiRole=\"button\" class=\"dijitTitlePaneTitle\" dojoAttachPoint=\"titleBarNode,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",
7035
7036         postCreate: function(){
7037                 this.setTitle(this.title);
7038                 if(!this.open){
7039                         this.hideNode.style.display = this.wipeNode.style.display = "none";
7040                 }
7041                 this._setCss();
7042                 dojo.setSelectable(this.titleNode, false);
7043                 this.inherited(arguments);
7044                 dijit.setWaiState(this.containerNode, "labelledby", this.titleNode.id);
7045                 dijit.setWaiState(this.focusNode, "haspopup", "true");
7046
7047                 // setup open/close animations
7048                 var hideNode = this.hideNode, wipeNode = this.wipeNode;
7049                 this._wipeIn = dojo.fx.wipeIn({
7050                         node: this.wipeNode,
7051                         duration: this.duration,
7052                         beforeBegin: function(){
7053                                 hideNode.style.display="";
7054                         }
7055                 });
7056                 this._wipeOut = dojo.fx.wipeOut({
7057                         node: this.wipeNode,
7058                         duration: this.duration,
7059                         onEnd: function(){
7060                                 hideNode.style.display="none";
7061                         }
7062                 });
7063         },
7064
7065         setContent: function(content){
7066                 // summary:
7067                 //              Typically called when an href is loaded.  Our job is to make the animation smooth
7068                 if(!this.open || this._wipeOut.status() == "playing"){
7069                         // we are currently *closing* the pane (or the pane is closed), so just let that continue
7070                         this.inherited(arguments);
7071                 }else{
7072                         if(this._wipeIn.status() == "playing"){
7073                                 this._wipeIn.stop();
7074                         }
7075
7076                         // freeze container at current height so that adding new content doesn't make it jump
7077                         dojo.marginBox(this.wipeNode, { h: dojo.marginBox(this.wipeNode).h });
7078
7079                         // add the new content (erasing the old content, if any)
7080                         this.inherited(arguments);
7081
7082                         // call _wipeIn.play() to animate from current height to new height
7083                         this._wipeIn.play();
7084                 }
7085         },
7086
7087         toggle: function(){
7088                 // summary: switches between opened and closed state
7089                 dojo.forEach([this._wipeIn, this._wipeOut], function(animation){
7090                         if(animation.status() == "playing"){
7091                                 animation.stop();
7092                         }
7093                 });
7094
7095                 this[this.open ? "_wipeOut" : "_wipeIn"].play();
7096                 this.open =! this.open;
7097
7098                 // load content (if this is the first time we are opening the TitlePane
7099                 // and content is specified as an href, or we have setHref when hidden)
7100                 this._loadCheck();
7101
7102                 this._setCss();
7103         },
7104
7105         _setCss: function(){
7106                 // summary: set the open/close css state for the TitlePane
7107                 var classes = ["dijitClosed", "dijitOpen"];
7108                 var boolIndex = this.open;
7109                 var node = this.titleBarNode || this.focusNode
7110                 dojo.removeClass(node, classes[!boolIndex+0]);
7111                 node.className += " " + classes[boolIndex+0];
7112
7113                 // provide a character based indicator for images-off mode
7114                 this.arrowNodeInner.innerHTML = this.open ? "-" : "+";
7115         },
7116
7117         _onTitleKey: function(/*Event*/ e){
7118                 // summary: callback when user hits a key
7119                 if(e.keyCode == dojo.keys.ENTER || e.charCode == dojo.keys.SPACE){
7120                         this.toggle();
7121                 }else if(e.keyCode == dojo.keys.DOWN_ARROW && this.open){
7122                         this.containerNode.focus();
7123                         e.preventDefault();
7124                 }
7125         },
7126         
7127         _handleFocus: function(/*Event*/ e){
7128                 // summary: handle blur and focus for this widget
7129                 
7130                 // add/removeClass is safe to call without hasClass in this case
7131                 dojo[(e.type == "focus" ? "addClass" : "removeClass")](this.focusNode, this.baseClass + "Focused");
7132         },
7133
7134         setTitle: function(/*String*/ title){
7135                 // summary: sets the text of the title
7136                 this.titleNode.innerHTML = title;
7137         }
7138 });
7139
7140 }
7141
7142 if(!dojo._hasResource["dijit.Tooltip"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
7143 dojo._hasResource["dijit.Tooltip"] = true;
7144 dojo.provide("dijit.Tooltip");
7145
7146
7147
7148
7149 dojo.declare(
7150         "dijit._MasterTooltip",
7151         [dijit._Widget, dijit._Templated],
7152         {
7153                 // summary
7154                 //              Internal widget that holds the actual tooltip markup,
7155                 //              which occurs once per page.
7156                 //              Called by Tooltip widgets which are just containers to hold
7157                 //              the markup
7158
7159                 // duration: Integer
7160                 //              Milliseconds to fade in/fade out
7161                 duration: 200,
7162
7163                 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",
7164
7165                 postCreate: function(){
7166                         dojo.body().appendChild(this.domNode);
7167
7168                         this.bgIframe = new dijit.BackgroundIframe(this.domNode);
7169
7170                         // Setup fade-in and fade-out functions.
7171                         this.fadeIn = dojo.fadeIn({ node: this.domNode, duration: this.duration, onEnd: dojo.hitch(this, "_onShow") });
7172                         this.fadeOut = dojo.fadeOut({ node: this.domNode, duration: this.duration, onEnd: dojo.hitch(this, "_onHide") });
7173
7174                 },
7175
7176                 show: function(/*String*/ innerHTML, /*DomNode*/ aroundNode, /*String[]?*/ position){
7177                         // summary:
7178                         //      Display tooltip w/specified contents to right specified node
7179                         //      (To left if there's no space on the right, or if LTR==right)
7180
7181                         if(this.aroundNode && this.aroundNode === aroundNode){
7182                                 return;
7183                         }
7184
7185                         if(this.fadeOut.status() == "playing"){
7186                                 // previous tooltip is being hidden; wait until the hide completes then show new one
7187                                 this._onDeck=arguments;
7188                                 return;
7189                         }
7190                         this.containerNode.innerHTML=innerHTML;
7191
7192                         // Firefox bug. when innerHTML changes to be shorter than previous
7193                         // one, the node size will not be updated until it moves.
7194                         this.domNode.style.top = (this.domNode.offsetTop + 1) + "px";
7195
7196                         // position the element and change CSS according to position[] (a list of positions to try)
7197                         var align = {};
7198                         var ltr = this.isLeftToRight();
7199                         dojo.forEach( (position && position.length) ? position : dijit.Tooltip.defaultPosition, function(pos){
7200                                 switch(pos){
7201                                         case "after":                           
7202                                                 align[ltr ? "BR" : "BL"] = ltr ? "BL" : "BR";
7203                                                 break;
7204                                         case "before":
7205                                                 align[ltr ? "BL" : "BR"] = ltr ? "BR" : "BL";
7206                                                 break;
7207                                         case "below":
7208                                                 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
7209                                                 align[ltr ? "BL" : "BR"] = ltr ? "TL" : "TR";
7210                                                 align[ltr ? "BR" : "BL"] = ltr ? "TR" : "TL";
7211                                                 break;
7212                                         case "above":
7213                                         default:
7214                                                 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
7215                                                 align[ltr ? "TL" : "TR"] = ltr ? "BL" : "BR";
7216                                                 align[ltr ? "TR" : "TL"] = ltr ? "BR" : "BL";
7217                                                 break;
7218                                 }
7219                         });
7220                         var pos = dijit.placeOnScreenAroundElement(this.domNode, aroundNode, align, dojo.hitch(this, "orient"));
7221
7222                         // show it
7223                         dojo.style(this.domNode, "opacity", 0);
7224                         this.fadeIn.play();
7225                         this.isShowingNow = true;
7226                         this.aroundNode = aroundNode;
7227                 },
7228
7229                 orient: function(/* DomNode */ node, /* String */ aroundCorner, /* String */ tooltipCorner){
7230                         // summary: private function to set CSS for tooltip node based on which position it's in
7231                         node.className = "dijitTooltip " +
7232                                 {
7233                                         "BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
7234                                         "TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
7235                                         "BR-TR": "dijitTooltipBelow dijitTooltipABRight",
7236                                         "TR-BR": "dijitTooltipAbove dijitTooltipABRight",
7237                                         "BR-BL": "dijitTooltipRight",
7238                                         "BL-BR": "dijitTooltipLeft"
7239                                 }[aroundCorner + "-" + tooltipCorner];
7240                 },
7241
7242                 _onShow: function(){
7243                         if(dojo.isIE){
7244                                 // the arrow won't show up on a node w/an opacity filter
7245                                 this.domNode.style.filter="";
7246                         }
7247                 },
7248
7249                 hide: function(aroundNode){
7250                         // summary: hide the tooltip
7251                         if(!this.aroundNode || this.aroundNode !== aroundNode){
7252                                 return;
7253                         }
7254                         if(this._onDeck){
7255                                 // this hide request is for a show() that hasn't even started yet;
7256                                 // just cancel the pending show()
7257                                 this._onDeck=null;
7258                                 return;
7259                         }
7260                         this.fadeIn.stop();
7261                         this.isShowingNow = false;
7262                         this.aroundNode = null;
7263                         this.fadeOut.play();
7264                 },
7265
7266                 _onHide: function(){
7267                         this.domNode.style.cssText="";  // to position offscreen again
7268                         if(this._onDeck){
7269                                 // a show request has been queued up; do it now
7270                                 this.show.apply(this, this._onDeck);
7271                                 this._onDeck=null;
7272                         }
7273                 }
7274
7275         }
7276 );
7277
7278 dijit.showTooltip = function(/*String*/ innerHTML, /*DomNode*/ aroundNode, /*String[]?*/ position){
7279         // summary:
7280         //      Display tooltip w/specified contents in specified position.
7281         //      See description of dijit.Tooltip.defaultPosition for details on position parameter.
7282         //      If position is not specified then dijit.Tooltip.defaultPosition is used.
7283         if(!dijit._masterTT){ dijit._masterTT = new dijit._MasterTooltip(); }
7284         return dijit._masterTT.show(innerHTML, aroundNode, position);
7285 };
7286
7287 dijit.hideTooltip = function(aroundNode){
7288         // summary: hide the tooltip
7289         if(!dijit._masterTT){ dijit._masterTT = new dijit._MasterTooltip(); }
7290         return dijit._masterTT.hide(aroundNode);
7291 };
7292
7293 dojo.declare(
7294         "dijit.Tooltip",
7295         dijit._Widget,
7296         {
7297                 // summary
7298                 //              Pops up a tooltip (a help message) when you hover over a node.
7299
7300                 // label: String
7301                 //              Text to display in the tooltip.
7302                 //              Specified as innerHTML when creating the widget from markup.
7303                 label: "",
7304
7305                 // showDelay: Integer
7306                 //              Number of milliseconds to wait after hovering over/focusing on the object, before
7307                 //              the tooltip is displayed.
7308                 showDelay: 400,
7309
7310                 // connectId: String[]
7311                 //              Id(s) of domNodes to attach the tooltip to.
7312                 //              When user hovers over any of the specified dom nodes, the tooltip will appear.
7313                 connectId: [],
7314
7315                 //      position: String[]
7316                 //              See description of dijit.Tooltip.defaultPosition for details on position parameter.
7317                 position: [],
7318
7319                 postCreate: function(){
7320                         if(this.srcNodeRef){
7321                                 this.srcNodeRef.style.display = "none";
7322                         }
7323
7324                         this._connectNodes = [];
7325                         
7326                         dojo.forEach(this.connectId, function(id) {
7327                                 var node = dojo.byId(id);
7328                                 if (node) {
7329                                         this._connectNodes.push(node);
7330                                         dojo.forEach(["onMouseOver", "onMouseOut", "onFocus", "onBlur", "onHover", "onUnHover"], function(event){
7331                                                 this.connect(node, event.toLowerCase(), "_"+event);
7332                                         }, this);
7333                                         if(dojo.isIE){
7334                                                 // BiDi workaround
7335                                                 node.style.zoom = 1;
7336                                         }
7337                                 }
7338                         }, this);
7339                 },
7340
7341                 _onMouseOver: function(/*Event*/ e){
7342                         this._onHover(e);
7343                 },
7344
7345                 _onMouseOut: function(/*Event*/ e){
7346                         if(dojo.isDescendant(e.relatedTarget, e.target)){
7347                                 // false event; just moved from target to target child; ignore.
7348                                 return;
7349                         }
7350                         this._onUnHover(e);
7351                 },
7352
7353                 _onFocus: function(/*Event*/ e){
7354                         this._focus = true;
7355                         this._onHover(e);
7356                         this.inherited(arguments);
7357                 },
7358                 
7359                 _onBlur: function(/*Event*/ e){
7360                         this._focus = false;
7361                         this._onUnHover(e);
7362                         this.inherited(arguments);
7363                 },
7364
7365                 _onHover: function(/*Event*/ e){
7366                         if(!this._showTimer){
7367                                 var target = e.target;
7368                                 this._showTimer = setTimeout(dojo.hitch(this, function(){this.open(target)}), this.showDelay);
7369                         }
7370                 },
7371
7372                 _onUnHover: function(/*Event*/ e){
7373                         // keep a tooltip open if the associated element has focus
7374                         if(this._focus){ return; }
7375                         if(this._showTimer){
7376                                 clearTimeout(this._showTimer);
7377                                 delete this._showTimer;
7378                         }
7379                         this.close();
7380                 },
7381
7382                 open: function(/*DomNode*/ target){
7383                         // summary: display the tooltip; usually not called directly.
7384                         target = target || this._connectNodes[0];
7385                         if(!target){ return; }
7386
7387                         if(this._showTimer){
7388                                 clearTimeout(this._showTimer);
7389                                 delete this._showTimer;
7390                         }
7391                         dijit.showTooltip(this.label || this.domNode.innerHTML, target, this.position);
7392                         
7393                         this._connectNode = target;
7394                 },
7395
7396                 close: function(){
7397                         // summary: hide the tooltip; usually not called directly.
7398                         dijit.hideTooltip(this._connectNode);
7399                         delete this._connectNode;
7400                         if(this._showTimer){
7401                                 clearTimeout(this._showTimer);
7402                                 delete this._showTimer;
7403                         }
7404                 },
7405
7406                 uninitialize: function(){
7407                         this.close();
7408                 }
7409         }
7410 );
7411
7412 // dijit.Tooltip.defaultPosition: String[]
7413 //              This variable controls the position of tooltips, if the position is not specified to
7414 //              the Tooltip widget or *TextBox widget itself.  It's an array of strings with the following values:
7415 //
7416 //                      * before: places tooltip to the left of the target node/widget, or to the right in
7417 //                        the case of RTL scripts like Hebrew and Arabic
7418 //                      * after: places tooltip to the right of the target node/widget, or to the left in
7419 //                        the case of RTL scripts like Hebrew and Arabic
7420 //                      * above: tooltip goes above target node
7421 //                      * below: tooltip goes below target node
7422 //
7423 //              The list is positions is tried, in order, until a position is found where the tooltip fits
7424 //              within the viewport.
7425 //
7426 //              Be careful setting this parameter.  A value of "above" may work fine until the user scrolls
7427 //              the screen so that there's no room above the target node.   Nodes with drop downs, like
7428 //              DropDownButton or FilteringSelect, are especially problematic, in that you need to be sure
7429 //              that the drop down and tooltip don't overlap, even when the viewport is scrolled so that there
7430 //              is only room below (or above) the target node, but not both.
7431 dijit.Tooltip.defaultPosition = ["after", "before"];
7432
7433 }
7434
7435 if(!dojo._hasResource["dojo.cookie"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
7436 dojo._hasResource["dojo.cookie"] = true;
7437 dojo.provide("dojo.cookie");
7438
7439
7440
7441 /*=====
7442 dojo.__cookieProps = function(){
7443         //      expires: Date|String|Number?
7444         //              If a number, the number of days from today at which the cookie
7445         //              will expire. If a date, the date past which the cookie will expire.
7446         //              If expires is in the past, the cookie will be deleted.
7447         //              If expires is omitted or is 0, the cookie will expire when the browser closes. << FIXME: 0 seems to disappear right away? FF3.
7448         //      path: String?
7449         //              The path to use for the cookie.
7450         //      domain: String?
7451         //              The domain to use for the cookie.
7452         //      secure: Boolean?
7453         //              Whether to only send the cookie on secure connections
7454         this.expires = expires;
7455         this.path = path;
7456         this.domain = domain;
7457         this.secure = secure;
7458 }
7459 =====*/
7460
7461
7462 dojo.cookie = function(/*String*/name, /*String?*/value, /*dojo.__cookieProps?*/props){
7463         //      summary: 
7464         //              Get or set a cookie.
7465         //      description:
7466         //              If one argument is passed, returns the value of the cookie
7467         //              For two or more arguments, acts as a setter.
7468         //      name:
7469         //              Name of the cookie
7470         //      value:
7471         //              Value for the cookie
7472         //      props: 
7473         //              Properties for the cookie
7474         //      example:
7475         //              set a cookie with the JSON-serialized contents of an object which
7476         //              will expire 5 days from now:
7477         //      |       dojo.cookie("configObj", dojo.toJson(config), { expires: 5 });
7478         //      
7479         //      example:
7480         //              de-serialize a cookie back into a JavaScript object:
7481         //      |       var config = dojo.fromJson(dojo.cookie("configObj"));
7482         //      
7483         //      example:
7484         //              delete a cookie:
7485         //      |       dojo.cookie("configObj", null, {expires: -1});
7486         var c = document.cookie;
7487         if(arguments.length == 1){
7488                 var matches = c.match(new RegExp("(?:^|; )" + dojo.regexp.escapeString(name) + "=([^;]*)"));
7489                 return matches ? decodeURIComponent(matches[1]) : undefined; // String or undefined
7490         }else{
7491                 props = props || {};
7492 // FIXME: expires=0 seems to disappear right away, not on close? (FF3)  Change docs?
7493                 var exp = props.expires;
7494                 if(typeof exp == "number"){ 
7495                         var d = new Date();
7496                         d.setTime(d.getTime() + exp*24*60*60*1000);
7497                         exp = props.expires = d;
7498                 }
7499                 if(exp && exp.toUTCString){ props.expires = exp.toUTCString(); }
7500
7501                 value = encodeURIComponent(value);
7502                 var updatedCookie = name + "=" + value;
7503                 for(propName in props){
7504                         updatedCookie += "; " + propName;
7505                         var propValue = props[propName];
7506                         if(propValue !== true){ updatedCookie += "=" + propValue; }
7507                 }
7508                 document.cookie = updatedCookie;
7509         }
7510 };
7511
7512 dojo.cookie.isSupported = function(){
7513         //      summary:
7514         //              Use to determine if the current browser supports cookies or not.
7515         //              
7516         //              Returns true if user allows cookies.
7517         //              Returns false if user doesn't allow cookies.
7518
7519         if(!("cookieEnabled" in navigator)){
7520                 this("__djCookieTest__", "CookiesAllowed");
7521                 navigator.cookieEnabled = this("__djCookieTest__") == "CookiesAllowed";
7522                 if(navigator.cookieEnabled){
7523                         this("__djCookieTest__", "", {expires: -1});
7524                 }
7525         }
7526         return navigator.cookieEnabled;
7527 };
7528
7529 }
7530
7531 if(!dojo._hasResource["dijit.Tree"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
7532 dojo._hasResource["dijit.Tree"] = true;
7533 dojo.provide("dijit.Tree");
7534
7535
7536
7537
7538
7539
7540
7541
7542 dojo.declare(
7543         "dijit._TreeNode",
7544         [dijit._Widget, dijit._Templated, dijit._Container, dijit._Contained],
7545 {
7546         // summary
7547         //              Single node within a tree
7548
7549         // item: dojo.data.Item
7550         //              the dojo.data entry this tree represents
7551         item: null,     
7552
7553         isTreeNode: true,
7554
7555         // label: String
7556         //              Text of this tree node
7557         label: "",
7558         
7559         isExpandable: null, // show expando node
7560         
7561         isExpanded: false,
7562
7563         // state: String
7564         //              dynamic loading-related stuff.
7565         //              When an empty folder node appears, it is "UNCHECKED" first,
7566         //              then after dojo.data query it becomes "LOADING" and, finally "LOADED"   
7567         state: "UNCHECKED",
7568         
7569         templateString:"<div class=\"dijitTreeNode\" waiRole=\"presentation\"\n\t><div dojoAttachPoint=\"rowNode\" waiRole=\"presentation\"\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><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=\"-1\" waiState=\"selected-false\" dojoAttachEvent=\"onfocus:_onNodeFocus\"></span>\n\t\t</div\n\t></div>\n</div>\n",          
7570
7571         postCreate: function(){
7572                 // set label, escaping special characters
7573                 this.setLabelNode(this.label);
7574
7575                 // set expand icon for leaf
7576                 this._setExpando();
7577
7578                 // set icon and label class based on item
7579                 this._updateItemClasses(this.item);
7580
7581                 if(this.isExpandable){
7582                         dijit.setWaiState(this.labelNode, "expanded", this.isExpanded);
7583                 }
7584         },
7585
7586         markProcessing: function(){
7587                 // summary: visually denote that tree is loading data, etc.
7588                 this.state = "LOADING";
7589                 this._setExpando(true); 
7590         },
7591
7592         unmarkProcessing: function(){
7593                 // summary: clear markup from markProcessing() call
7594                 this._setExpando(false);        
7595         },
7596
7597         _updateItemClasses: function(item){
7598                 // summary: set appropriate CSS classes for icon and label dom node (used to allow for item updates to change respective CSS)
7599                 var tree = this.tree, model = tree.model;
7600                 if(tree._v10Compat && item === model.root){
7601                         // For back-compat with 1.0, need to use null to specify root item (TODO: remove in 2.0)
7602                         item = null;
7603                 }
7604                 this.iconNode.className = "dijitInline dijitTreeIcon " + tree.getIconClass(item, this.isExpanded);
7605                 this.labelNode.className = "dijitTreeLabel " + tree.getLabelClass(item, this.isExpanded);
7606         },
7607
7608         _updateLayout: function(){
7609                 // summary: set appropriate CSS classes for this.domNode
7610                 var parent = this.getParent();
7611                 if(!parent || parent.rowNode.style.display == "none"){
7612                         /* if we are hiding the root node then make every first level child look like a root node */
7613                         dojo.addClass(this.domNode, "dijitTreeIsRoot");
7614                 }else{
7615                         dojo.toggleClass(this.domNode, "dijitTreeIsLast", !this.getNextSibling());
7616                 }
7617         },
7618
7619         _setExpando: function(/*Boolean*/ processing){
7620                 // summary: set the right image for the expando node
7621
7622                 // apply the appropriate class to the expando node
7623                 var styles = ["dijitTreeExpandoLoading", "dijitTreeExpandoOpened",
7624                         "dijitTreeExpandoClosed", "dijitTreeExpandoLeaf"];
7625                 var idx = processing ? 0 : (this.isExpandable ? (this.isExpanded ? 1 : 2) : 3);
7626                 dojo.forEach(styles,
7627                         function(s){
7628                                 dojo.removeClass(this.expandoNode, s);
7629                         }, this
7630                 );
7631                 dojo.addClass(this.expandoNode, styles[idx]);
7632
7633                 // provide a non-image based indicator for images-off mode
7634                 this.expandoNodeText.innerHTML =
7635                         processing ? "*" :
7636                                 (this.isExpandable ?
7637                                         (this.isExpanded ? "-" : "+") : "*");
7638         },      
7639
7640         expand: function(){
7641                 // summary: show my children
7642                 if(this.isExpanded){ return; }
7643                 // cancel in progress collapse operation
7644                 if(this._wipeOut.status() == "playing"){
7645                         this._wipeOut.stop();
7646                 }
7647
7648                 this.isExpanded = true;
7649                 dijit.setWaiState(this.labelNode, "expanded", "true");
7650                 dijit.setWaiRole(this.containerNode, "group");
7651                 this.contentNode.className = "dijitTreeContent dijitTreeContentExpanded";
7652                 this._setExpando();
7653                 this._updateItemClasses(this.item);
7654
7655                 this._wipeIn.play();
7656         },
7657
7658         collapse: function(){                                   
7659                 if(!this.isExpanded){ return; }
7660
7661                 // cancel in progress expand operation
7662                 if(this._wipeIn.status() == "playing"){
7663                         this._wipeIn.stop();
7664                 }
7665
7666                 this.isExpanded = false;
7667                 dijit.setWaiState(this.labelNode, "expanded", "false");
7668                 this.contentNode.className = "dijitTreeContent";
7669                 this._setExpando();
7670                 this._updateItemClasses(this.item);
7671
7672                 this._wipeOut.play();
7673         },
7674
7675         setLabelNode: function(label){
7676                 this.labelNode.innerHTML="";
7677                 this.labelNode.appendChild(dojo.doc.createTextNode(label));
7678         },
7679
7680         setChildItems: function(/* Object[] */ items){
7681                 // summary:
7682                 //              Sets the child items of this node, removing/adding nodes
7683                 //              from current children to match specified items[] array.
7684
7685                 var tree = this.tree,
7686                         model = tree.model;
7687
7688                 // Orphan all my existing children.
7689                 // If items contains some of the same items as before then we will reattach them.
7690                 // Don't call this.removeChild() because that will collapse the tree etc.
7691                 this.getChildren().forEach(function(child){
7692                         dijit._Container.prototype.removeChild.call(this, child);
7693                 }, this);
7694
7695                 this.state = "LOADED";
7696
7697                 if(items && items.length > 0){
7698                         this.isExpandable = true;
7699                         if(!this.containerNode){ // maybe this node was unfolderized and still has container
7700                                 this.containerNode = this.tree.containerNodeTemplate.cloneNode(true);
7701                                 this.domNode.appendChild(this.containerNode);
7702                         }
7703
7704                         // Create _TreeNode widget for each specified tree node, unless one already
7705                         // exists and isn't being used (presumably it's from a DnD move and was recently
7706                         // released
7707                         dojo.forEach(items, function(item){
7708                                 var id = model.getIdentity(item),
7709                                         existingNode = tree._itemNodeMap[id],
7710                                         node = 
7711                                                 ( existingNode && !existingNode.getParent() ) ?
7712                                                 existingNode :
7713                                                 new dijit._TreeNode({
7714                                                         item: item,
7715                                                         tree: tree,
7716                                                         isExpandable: model.mayHaveChildren(item),
7717                                                         label: tree.getLabel(item)
7718                                                 });
7719                                 this.addChild(node);
7720                                 // note: this won't work if there are two nodes for one item (multi-parented items); will be fixed later
7721                                 tree._itemNodeMap[id] = node;
7722                                 if(this.tree.persist){
7723                                         if(tree._openedItemIds[id]){
7724                                                 tree._expandNode(node);
7725                                         }
7726                                 }
7727                         }, this);
7728
7729                         // note that updateLayout() needs to be called on each child after
7730                         // _all_ the children exist
7731                         dojo.forEach(this.getChildren(), function(child, idx){
7732                                 child._updateLayout();
7733                         });
7734                 }else{
7735                         this.isExpandable=false;
7736                 }
7737
7738                 if(this._setExpando){
7739                         // change expando to/from dot or + icon, as appropriate
7740                         this._setExpando(false);
7741                 }
7742
7743                 // On initial tree show, put focus on either the root node of the tree,
7744                 // or the first child, if the root node is hidden
7745                 if(!this.parent){
7746                         var fc = this.tree.showRoot ? this : this.getChildren()[0],
7747                                 tabnode = fc ? fc.labelNode : this.domNode;
7748                         tabnode.setAttribute("tabIndex", "0");
7749                 }
7750
7751                 // create animations for showing/hiding the children (if children exist)
7752                 if(this.containerNode && !this._wipeIn){
7753                         this._wipeIn = dojo.fx.wipeIn({node: this.containerNode, duration: 150});
7754                         this._wipeOut = dojo.fx.wipeOut({node: this.containerNode, duration: 150});
7755                 }
7756         },
7757
7758         removeChild: function(/* treeNode */ node){
7759                 this.inherited(arguments);
7760
7761                 var children = this.getChildren();              
7762                 if(children.length == 0){
7763                         this.isExpandable = false;
7764                         this.collapse();
7765                 }
7766
7767                 dojo.forEach(children, function(child){
7768                                 child._updateLayout();
7769                 });
7770         },
7771
7772         makeExpandable: function(){
7773                 //summary
7774                 //              if this node wasn't already showing the expando node,
7775                 //              turn it into one and call _setExpando()
7776                 this.isExpandable = true;
7777                 this._setExpando(false);
7778         },
7779
7780         _onNodeFocus: function(evt){
7781                 var node = dijit.getEnclosingWidget(evt.target);
7782                 this.tree._onTreeFocus(node);
7783         }
7784 });
7785
7786 dojo.declare(
7787         "dijit.Tree",
7788         [dijit._Widget, dijit._Templated],
7789 {
7790         // summary
7791         //      This widget displays hierarchical data from a store.  A query is specified
7792         //      to get the "top level children" from a data store, and then those items are
7793         //      queried for their children and so on (but lazily, as the user clicks the expand node).
7794         //
7795         //      Thus in the default mode of operation this widget is technically a forest, not a tree,
7796         //      in that there can be multiple "top level children".  However, if you specify label,
7797         //      then a special top level node (not corresponding to any item in the datastore) is
7798         //      created, to father all the top level children.
7799
7800         // store: String||dojo.data.Store
7801         //      The store to get data to display in the tree.
7802         //      May remove for 2.0 in favor of "model".
7803         store: null,
7804
7805         // model: dijit.Tree.model
7806         //      Alternate interface from store to access data (and changes to data) in the tree
7807         model: null,
7808
7809         // query: anything
7810         //      Specifies datastore query to return the root item for the tree.
7811         //
7812         //      Deprecated functionality: if the query returns multiple items, the tree is given
7813         //      a fake root node (not corresponding to any item in the data store), 
7814         //      whose children are the items that match this query.
7815         //
7816         //      The root node is shown or hidden based on whether a label is specified.
7817         //
7818         //      Having a query return multiple items is deprecated.
7819         //      If your store doesn't have a root item, wrap the store with
7820         //      dijit.tree.ForestStoreModel, and specify model=myModel
7821         //
7822         // example:
7823         //              {type:'continent'}
7824         query: null,
7825
7826         // label: String
7827         //      Deprecated.  Use dijit.tree.ForestStoreModel directly instead.
7828         //      Used in conjunction with query parameter.
7829         //      If a query is specified (rather than a root node id), and a label is also specified,
7830         //      then a fake root node is created and displayed, with this label.
7831         label: "",
7832
7833         // showRoot: Boolean
7834         //      Should the root node be displayed, or hidden?
7835         showRoot: true,
7836
7837         // childrenAttr: String[]
7838         //              one ore more attributes that holds children of a tree node
7839         childrenAttr: ["children"],
7840
7841         // openOnClick: Boolean
7842         //              If true, clicking a folder node's label will open it, rather than calling onClick()
7843         openOnClick: false,
7844
7845         templateString:"<div class=\"dijitTreeContainer\" waiRole=\"tree\"\n\tdojoAttachEvent=\"onclick:_onClick,onkeypress:_onKeyPress\">\n</div>\n",          
7846
7847         isExpandable: true,
7848
7849         isTree: true,
7850
7851         // persist: Boolean
7852         //      enables/disables use of cookies for state saving.
7853         persist: true,
7854         
7855         // dndController: String
7856         //      class name to use as as the dnd controller
7857         dndController: null,
7858
7859         //parameters to pull off of the tree and pass on to the dndController as its params
7860         dndParams: ["onDndDrop","itemCreator","onDndCancel","checkAcceptance", "checkItemAcceptance"],
7861
7862         //declare the above items so they can be pulled from the tree's markup
7863         onDndDrop:null,
7864         itemCreator:null,
7865         onDndCancel:null,
7866         checkAcceptance:null,   
7867         checkItemAcceptance:null,
7868
7869         _publish: function(/*String*/ topicName, /*Object*/ message){
7870                 // summary:
7871                 //              Publish a message for this widget/topic
7872                 dojo.publish(this.id, [dojo.mixin({tree: this, event: topicName}, message||{})]);
7873         },
7874
7875         postMixInProperties: function(){
7876                 this.tree = this;
7877
7878                 this._itemNodeMap={};
7879
7880                 if(!this.cookieName){
7881                         this.cookieName = this.id + "SaveStateCookie";
7882                 }
7883         },
7884
7885         postCreate: function(){
7886                 // load in which nodes should be opened automatically
7887                 if(this.persist){
7888                         var cookie = dojo.cookie(this.cookieName);
7889                         this._openedItemIds = {};
7890                         if(cookie){
7891                                 dojo.forEach(cookie.split(','), function(item){
7892                                         this._openedItemIds[item] = true;
7893                                 }, this);
7894                         }
7895                 }
7896                 
7897                 // make template for container node (we will clone this and insert it into
7898                 // any nodes that have children)
7899                 var div = dojo.doc.createElement('div');
7900                 div.style.display = 'none';
7901                 div.className = "dijitTreeContainer";   
7902                 dijit.setWaiRole(div, "presentation");
7903                 this.containerNodeTemplate = div;
7904
7905                 // Create glue between store and Tree, if not specified directly by user
7906                 if(!this.model){
7907                         this._store2model();
7908                 }
7909
7910                 // monitor changes to items
7911                 this.connect(this.model, "onChange", "_onItemChange");
7912                 this.connect(this.model, "onChildrenChange", "_onItemChildrenChange");
7913                 // TODO: monitor item deletes so we don't end up w/orphaned nodes?
7914
7915                 this._load();
7916
7917                 this.inherited("postCreate", arguments);
7918
7919                 if(this.dndController){
7920                         if(dojo.isString(this.dndController)){
7921                                 this.dndController= dojo.getObject(this.dndController);
7922                         }       
7923                         var params={};
7924                         for (var i=0; i<this.dndParams.length;i++){
7925                                 if(this[this.dndParams[i]]){
7926                                         params[this.dndParams[i]]=this[this.dndParams[i]];
7927                                 }
7928                         }
7929                         this.dndController= new this.dndController(this, params);
7930                 }
7931         },
7932
7933         _store2model: function(){
7934                 // summary: user specified a store&query rather than model, so create model from store/query
7935                 this._v10Compat = true;
7936                 dojo.deprecated("Tree: from version 2.0, should specify a model object rather than a store/query");
7937
7938                 var modelParams = {
7939                         id: this.id + "_ForestStoreModel",
7940                         store: this.store,
7941                         query: this.query,
7942                         childrenAttrs: this.childrenAttr
7943                 };
7944
7945                 // Only override the model's mayHaveChildren() method if the user has specified an override
7946                 if(this.params.mayHaveChildren){
7947                         modelParams.mayHaveChildren = dojo.hitch(this, "mayHaveChildren");
7948                 }
7949                                         
7950                 if(this.params.getItemChildren){
7951                         modelParams.getChildren = dojo.hitch(this, function(item, onComplete, onError){
7952                                 this.getItemChildren((this._v10Compat && item === this.model.root) ? null : item, onComplete, onError);
7953                         });
7954                 }
7955                 this.model = new dijit.tree.ForestStoreModel(modelParams);
7956                 
7957                 // For backwards compatibility, the visibility of the root node is controlled by
7958                 // whether or not the user has specified a label
7959                 this.showRoot = Boolean(this.label);
7960         },
7961
7962         _load: function(){
7963                 // summary: initial load of the tree
7964                 // load root node (possibly hidden) and it's children
7965                 this.model.getRoot(
7966                         dojo.hitch(this, function(item){
7967                                 var rn = this.rootNode = new dijit._TreeNode({
7968                                         item: item,
7969                                         tree: this,
7970                                         isExpandable: true,
7971                                         label: this.label || this.getLabel(item)
7972                                 });
7973                                 if(!this.showRoot){
7974                                         rn.rowNode.style.display="none";
7975                                 }
7976                                 this.domNode.appendChild(rn.domNode);
7977                                 this._itemNodeMap[this.model.getIdentity(item)] = rn;
7978
7979                                 rn._updateLayout();             // sets "dijitTreeIsRoot" CSS classname
7980
7981                                 // load top level children
7982                                 this._expandNode(rn);
7983                         }),
7984                         function(err){
7985                                 console.error(this, ": error loading root: ", err);
7986                         }
7987                 );
7988         },
7989
7990         ////////////// Data store related functions //////////////////////
7991         // These just get passed to the model; they are here for back-compat
7992
7993         mayHaveChildren: function(/*dojo.data.Item*/ item){
7994                 // summary
7995                 //              User overridable function to tell if an item has or may have children.
7996                 //              Controls whether or not +/- expando icon is shown.
7997                 //              (For efficiency reasons we may not want to check if an element actually
7998                 //              has children until user clicks the expando node)
7999         },
8000
8001         getItemChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete){
8002                 // summary
8003                 //              User overridable function that return array of child items of given parent item,
8004                 //              or if parentItem==null then return top items in tree
8005         },
8006
8007         ///////////////////////////////////////////////////////
8008         // Functions for converting an item to a TreeNode
8009         getLabel: function(/*dojo.data.Item*/ item){
8010                 // summary: user overridable function to get the label for a tree node (given the item)
8011                 return this.model.getLabel(item);       // String
8012         },
8013
8014         getIconClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
8015                 // summary: user overridable function to return CSS class name to display icon
8016                 return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "dijitLeaf"
8017         },
8018
8019         getLabelClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
8020                 // summary: user overridable function to return CSS class name to display label
8021         },
8022
8023         /////////// Keyboard and Mouse handlers ////////////////////
8024
8025         _onKeyPress: function(/*Event*/ e){
8026                 // summary: translates keypress events into commands for the controller
8027                 if(e.altKey){ return; }
8028                 var treeNode = dijit.getEnclosingWidget(e.target);
8029                 if(!treeNode){ return; }
8030
8031                 // Note: On IE e.keyCode is not 0 for printables so check e.charCode.
8032                 // In dojo charCode is universally 0 for non-printables.
8033                 if(e.charCode){  // handle printables (letter navigation)
8034                         // Check for key navigation.
8035                         var navKey = e.charCode;
8036                         if(!e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey){
8037                                 navKey = (String.fromCharCode(navKey)).toLowerCase();
8038                                 this._onLetterKeyNav( { node: treeNode, key: navKey } );
8039                                 dojo.stopEvent(e);
8040                         }
8041                 }else{  // handle non-printables (arrow keys)
8042                         var map = this._keyHandlerMap;
8043                         if(!map){
8044                                 // setup table mapping keys to events
8045                                 map = {};
8046                                 map[dojo.keys.ENTER]="_onEnterKey";
8047                                 map[this.isLeftToRight() ? dojo.keys.LEFT_ARROW : dojo.keys.RIGHT_ARROW]="_onLeftArrow";
8048                                 map[this.isLeftToRight() ? dojo.keys.RIGHT_ARROW : dojo.keys.LEFT_ARROW]="_onRightArrow";
8049                                 map[dojo.keys.UP_ARROW]="_onUpArrow";
8050                                 map[dojo.keys.DOWN_ARROW]="_onDownArrow";
8051                                 map[dojo.keys.HOME]="_onHomeKey";
8052                                 map[dojo.keys.END]="_onEndKey";
8053                                 this._keyHandlerMap = map;
8054                         }
8055                         if(this._keyHandlerMap[e.keyCode]){
8056                                 this[this._keyHandlerMap[e.keyCode]]( { node: treeNode, item: treeNode.item } );        
8057                                 dojo.stopEvent(e);
8058                         }
8059                 }
8060         },
8061
8062         _onEnterKey: function(/*Object*/ message){
8063                 this._publish("execute", { item: message.item, node: message.node} );
8064                 this.onClick(message.item, message.node);
8065         },
8066
8067         _onDownArrow: function(/*Object*/ message){
8068                 // summary: down arrow pressed; get next visible node, set focus there
8069                 var node = this._getNextNode(message.node);
8070                 if(node && node.isTreeNode){
8071                         this.focusNode(node);
8072                 }       
8073         },
8074
8075         _onUpArrow: function(/*Object*/ message){
8076                 // summary: up arrow pressed; move to previous visible node
8077
8078                 var node = message.node;
8079
8080                 // if younger siblings          
8081                 var previousSibling = node.getPreviousSibling();
8082                 if(previousSibling){
8083                         node = previousSibling;
8084                         // if the previous node is expanded, dive in deep
8085                         while(node.isExpandable && node.isExpanded && node.hasChildren()){
8086                                 // move to the last child
8087                                 var children = node.getChildren();
8088                                 node = children[children.length-1];
8089                         }
8090                 }else{
8091                         // if this is the first child, return the parent
8092                         // unless the parent is the root of a tree with a hidden root
8093                         var parent = node.getParent();
8094                         if(!(!this.showRoot && parent === this.rootNode)){
8095                                 node = parent;
8096                         }
8097                 }
8098
8099                 if(node && node.isTreeNode){
8100                         this.focusNode(node);
8101                 }
8102         },
8103
8104         _onRightArrow: function(/*Object*/ message){
8105                 // summary: right arrow pressed; go to child node
8106                 var node = message.node;
8107
8108                 // if not expanded, expand, else move to 1st child
8109                 if(node.isExpandable && !node.isExpanded){
8110                         this._expandNode(node);
8111                 }else if(node.hasChildren()){
8112                         node = node.getChildren()[0];
8113                         if(node && node.isTreeNode){
8114                                 this.focusNode(node);
8115                         }
8116                 }
8117         },
8118
8119         _onLeftArrow: function(/*Object*/ message){
8120                 // summary:
8121                 //              Left arrow pressed.
8122                 //              If not collapsed, collapse, else move to parent.
8123
8124                 var node = message.node;
8125
8126                 if(node.isExpandable && node.isExpanded){
8127                         this._collapseNode(node);
8128                 }else{
8129                         node = node.getParent();
8130                         if(node && node.isTreeNode){
8131                                 this.focusNode(node);
8132                         }
8133                 }
8134         },
8135
8136         _onHomeKey: function(){
8137                 // summary: home pressed; get first visible node, set focus there
8138                 var node = this._getRootOrFirstNode();
8139                 if(node){
8140                         this.focusNode(node);
8141                 }
8142         },
8143
8144         _onEndKey: function(/*Object*/ message){
8145                 // summary: end pressed; go to last visible node
8146
8147                 var node = this;
8148                 while(node.isExpanded){
8149                         var c = node.getChildren();
8150                         node = c[c.length - 1];
8151                 }
8152
8153                 if(node && node.isTreeNode){
8154                         this.focusNode(node);
8155                 }
8156         },
8157
8158         _onLetterKeyNav: function(message){
8159                 // summary: letter key pressed; search for node starting with first char = key
8160                 var node = startNode = message.node,
8161                         key = message.key;
8162                 do{
8163                         node = this._getNextNode(node);
8164                         //check for last node, jump to first node if necessary
8165                         if(!node){
8166                                 node = this._getRootOrFirstNode();
8167                         }
8168                 }while(node !== startNode && (node.label.charAt(0).toLowerCase() != key));
8169                 if(node && node.isTreeNode){
8170                         // no need to set focus if back where we started
8171                         if(node !== startNode){
8172                                 this.focusNode(node);
8173                         }
8174                 }
8175         },
8176
8177         _onClick: function(/*Event*/ e){
8178                 // summary: translates click events into commands for the controller to process
8179                 var domElement = e.target;
8180
8181                 // find node
8182                 var nodeWidget = dijit.getEnclosingWidget(domElement);  
8183                 if(!nodeWidget || !nodeWidget.isTreeNode){
8184                         return;
8185                 }
8186
8187                 if( (this.openOnClick && nodeWidget.isExpandable) ||
8188                         (domElement == nodeWidget.expandoNode || domElement == nodeWidget.expandoNodeText) ){
8189                         // expando node was clicked, or label of a folder node was clicked; open it
8190                         if(nodeWidget.isExpandable){
8191                                 this._onExpandoClick({node:nodeWidget});
8192                         }
8193                 }else{
8194                         this._publish("execute", { item: nodeWidget.item, node: nodeWidget} );
8195                         this.onClick(nodeWidget.item, nodeWidget);
8196                         this.focusNode(nodeWidget);
8197                 }
8198                 dojo.stopEvent(e);
8199         },
8200
8201         _onExpandoClick: function(/*Object*/ message){
8202                 // summary: user clicked the +/- icon; expand or collapse my children.
8203                 var node = message.node;
8204                 
8205                 // If we are collapsing, we might be hiding the currently focused node.
8206                 // Also, clicking the expando node might have erased focus from the current node.
8207                 // For simplicity's sake just focus on the node with the expando.
8208                 this.focusNode(node);
8209
8210                 if(node.isExpanded){
8211                         this._collapseNode(node);
8212                 }else{
8213                         this._expandNode(node);
8214                 }
8215         },
8216
8217         onClick: function(/* dojo.data */ item, /*TreeNode*/ node){
8218                 // summary: user overridable function for executing a tree item
8219         },
8220
8221         _getNextNode: function(node){
8222                 // summary: get next visible node
8223
8224                 if(node.isExpandable && node.isExpanded && node.hasChildren()){
8225                         // if this is an expanded node, get the first child
8226                         return node.getChildren()[0];           // _TreeNode    
8227                 }else{
8228                         // find a parent node with a sibling
8229                         while(node && node.isTreeNode){
8230                                 var returnNode = node.getNextSibling();
8231                                 if(returnNode){
8232                                         return returnNode;              // _TreeNode
8233                                 }
8234                                 node = node.getParent();
8235                         }
8236                         return null;
8237                 }
8238         },
8239
8240         _getRootOrFirstNode: function(){
8241                 // summary: get first visible node
8242                 return this.showRoot ? this.rootNode : this.rootNode.getChildren()[0];
8243         },
8244
8245         _collapseNode: function(/*_TreeNode*/ node){
8246                 // summary: called when the user has requested to collapse the node
8247
8248                 if(node.isExpandable){
8249                         if(node.state == "LOADING"){
8250                                 // ignore clicks while we are in the process of loading data
8251                                 return;
8252                         }
8253
8254                         node.collapse();
8255                         if(this.persist && node.item){
8256                                 delete this._openedItemIds[this.model.getIdentity(node.item)];
8257                                 this._saveState();
8258                         }
8259                 }
8260         },
8261
8262         _expandNode: function(/*_TreeNode*/ node){
8263                 // summary: called when the user has requested to expand the node
8264
8265                 if(!node.isExpandable){
8266                         return;
8267                 }
8268
8269                 var model = this.model,
8270                         item = node.item;
8271
8272                 switch(node.state){
8273                         case "LOADING":
8274                                 // ignore clicks while we are in the process of loading data
8275                                 return;
8276
8277                         case "UNCHECKED":
8278                                 // need to load all the children, and then expand
8279                                 node.markProcessing();
8280                                 var _this = this;
8281                                 model.getChildren(item, function(items){
8282                                                 node.unmarkProcessing();
8283                                                 node.setChildItems(items);
8284                                                 _this._expandNode(node);
8285                                         },
8286                                         function(err){
8287                                                 console.error(_this, ": error loading root children: ", err);
8288                                         });
8289                                 break;
8290
8291                         default:
8292                                 // data is already loaded; just proceed
8293                                 node.expand();
8294                                 if(this.persist && item){
8295                                         this._openedItemIds[model.getIdentity(item)] = true;
8296                                         this._saveState();
8297                                 }
8298                 }
8299         },
8300
8301         ////////////////// Miscellaneous functions ////////////////
8302
8303         blurNode: function(){
8304                 // summary
8305                 //      Removes focus from the currently focused node (which must be visible).
8306                 //      Usually not called directly (just call focusNode() on another node instead)
8307                 var node = this.lastFocused;
8308                 if(!node){ return; }
8309                 var labelNode = node.labelNode;
8310                 dojo.removeClass(labelNode, "dijitTreeLabelFocused");
8311                 labelNode.setAttribute("tabIndex", "-1");
8312                 dijit.setWaiState(labelNode, "selected", false);
8313                 this.lastFocused = null;
8314         },
8315
8316         focusNode: function(/* _tree.Node */ node){
8317                 // summary
8318                 //      Focus on the specified node (which must be visible)
8319
8320                 // set focus so that the label will be voiced using screen readers
8321                 node.labelNode.focus();
8322         },
8323
8324         _onBlur: function(){
8325                 // summary:
8326                 //              We've moved away from the whole tree.  The currently "focused" node
8327                 //              (see focusNode above) should remain as the lastFocused node so we can
8328                 //              tab back into the tree.  Just change CSS to get rid of the dotted border
8329                 //              until that time
8330
8331                 this.inherited(arguments);
8332                 if(this.lastFocused){
8333                         var labelNode = this.lastFocused.labelNode;
8334                         dojo.removeClass(labelNode, "dijitTreeLabelFocused");   
8335                 }
8336         },
8337
8338         _onTreeFocus: function(/*Widget*/ node){
8339                 // summary:
8340                 //              called from onFocus handler of treeitem labelNode to set styles, wai state and tabindex
8341                 //              for currently focused treeitem.
8342                 
8343                 if (node){
8344                         if(node != this.lastFocused){
8345                                 this.blurNode();
8346                         }
8347                         var labelNode = node.labelNode;
8348                         // set tabIndex so that the tab key can find this node
8349                         labelNode.setAttribute("tabIndex", "0");
8350                         dijit.setWaiState(labelNode, "selected", true);
8351                         dojo.addClass(labelNode, "dijitTreeLabelFocused");
8352                         this.lastFocused = node;
8353                 }
8354         },
8355
8356         //////////////// Events from the model //////////////////////////
8357         
8358         _onItemDelete: function(/*Object*/ item){
8359                 //summary: delete event from the store
8360                 // TODO: currently this isn't called, and technically doesn't need to be,
8361                 // but it would help with garbage collection
8362
8363                 var identity = this.model.getIdentity(item);
8364                 var node = this._itemNodeMap[identity];
8365
8366                 if(node){
8367                         var parent = node.getParent();
8368                         if(parent){
8369                                 // if node has not already been orphaned from a _onSetItem(parent, "children", ..) call...
8370                                 parent.removeChild(node);
8371                         }
8372                         delete this._itemNodeMap[identity];
8373                         node.destroyRecursive();
8374                 }
8375         },
8376
8377         _onItemChange: function(/*Item*/ item){
8378                 //summary: set data event on an item in the store
8379                 var model = this.model,
8380                         identity = model.getIdentity(item),
8381                         node = this._itemNodeMap[identity];
8382
8383                 if(node){
8384                         node.setLabelNode(this.getLabel(item));
8385                         node._updateItemClasses(item);
8386                 }
8387         },
8388
8389         _onItemChildrenChange: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
8390                 //summary: set data event on an item in the store
8391                 var model = this.model,
8392                         identity = model.getIdentity(parent),
8393                         parentNode = this._itemNodeMap[identity];
8394
8395                 if(parentNode){
8396                         parentNode.setChildItems(newChildrenList);
8397                 }
8398         },
8399
8400         /////////////// Miscellaneous funcs
8401         
8402         _saveState: function(){
8403                 //summary: create and save a cookie with the currently expanded nodes identifiers
8404                 if(!this.persist){
8405                         return;
8406                 }
8407                 var ary = [];
8408                 for(var id in this._openedItemIds){
8409                         ary.push(id);
8410                 }
8411                 dojo.cookie(this.cookieName, ary.join(","));
8412         },
8413
8414         destroy: function(){
8415                 if(this.rootNode){
8416                         this.rootNode.destroyRecursive();
8417                 }
8418                 this.rootNode = null;
8419                 this.inherited(arguments);
8420         },
8421         
8422         destroyRecursive: function(){
8423                 // A tree is treated as a leaf, not as a node with children (like a grid),
8424                 // but defining destroyRecursive for back-compat.
8425                 this.destroy();
8426         }
8427 });
8428
8429
8430 dojo.declare(
8431         "dijit.tree.TreeStoreModel",
8432         null,
8433 {
8434         // summary
8435         //              Implements dijit.Tree.model connecting to a store with a single
8436         //              root item.  Any methods passed into the constructor will override
8437         //              the ones defined here.
8438
8439         // store: dojo.data.Store
8440         //              Underlying store
8441         store: null,
8442
8443         // childrenAttrs: String[]
8444         //              one ore more attributes that holds children of a tree node
8445         childrenAttrs: ["children"],
8446         
8447         // root: dojo.data.Item
8448         //              Pointer to the root item (read only, not a parameter)
8449         root: null,
8450
8451         // query: anything
8452         //              Specifies datastore query to return the root item for the tree.
8453         //              Must only return a single item.   Alternately can just pass in pointer
8454         //              to root item.
8455         // example:
8456         //              {id:'ROOT'}
8457         query: null,
8458
8459         constructor: function(/* Object */ args){
8460                 // summary: passed the arguments listed above (store, etc)
8461                 dojo.mixin(this, args);
8462
8463                 this.connects = [];
8464
8465                 var store = this.store;
8466                 if(!store.getFeatures()['dojo.data.api.Identity']){
8467                         throw new Error("dijit.Tree: store must support dojo.data.Identity");                   
8468                 }
8469
8470                 // if the store supports Notification, subscribe to the notification events
8471                 if(store.getFeatures()['dojo.data.api.Notification']){
8472                         this.connects = this.connects.concat([
8473                                 dojo.connect(store, "onNew", this, "_onNewItem"),
8474                                 dojo.connect(store, "onDelete", this, "_onDeleteItem"),
8475                                 dojo.connect(store, "onSet", this, "_onSetItem")
8476                         ]);
8477                 }
8478         },
8479
8480         destroy: function(){
8481                 dojo.forEach(this.connects, dojo.disconnect);
8482         },
8483
8484         // =======================================================================
8485         // Methods for traversing hierarchy
8486
8487         getRoot: function(onItem, onError){
8488                 // summary:
8489                 //              Calls onItem with the root item for the tree, possibly a fabricated item.
8490                 //              Calls onError on error.
8491                 if(this.root){
8492                         onItem(this.root);
8493                 }else{
8494                         this.store.fetch({
8495                                 query: this.query,
8496                                 onComplete: dojo.hitch(this, function(items){
8497                                         if(items.length != 1){
8498                                                 throw new Error(this.declaredClass + ": query " + query + " returned " + items.length +
8499                                                         " items, but must return exactly one item");
8500                                         }
8501                                         this.root = items[0];
8502                                         onItem(this.root);
8503                                 }),
8504                                 onError: onError
8505                         });
8506                 }
8507         },
8508
8509         mayHaveChildren: function(/*dojo.data.Item*/ item){
8510                 // summary
8511                 //              Tells if an item has or may have children.  Implementing logic here
8512                 //              avoids showing +/- expando icon for nodes that we know don't have children.
8513                 //              (For efficiency reasons we may not want to check if an element actually
8514                 //              has children until user clicks the expando node)
8515                 return dojo.some(this.childrenAttrs, function(attr){
8516                         return this.store.hasAttribute(item, attr);
8517                 }, this);
8518         },
8519
8520         getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete, /*function*/ onError){
8521                 // summary
8522                 //              Calls onComplete() with array of child items of given parent item, all loaded.
8523
8524                 var store = this.store;
8525
8526                 // get children of specified item
8527                 var childItems = [];
8528                 for (var i=0; i<this.childrenAttrs.length; i++){
8529                         var vals = store.getValues(parentItem, this.childrenAttrs[i]);
8530                         childItems = childItems.concat(vals);
8531                 }
8532
8533                 // count how many items need to be loaded
8534                 var _waitCount = 0;
8535                 dojo.forEach(childItems, function(item){ if(!store.isItemLoaded(item)){ _waitCount++; } });
8536
8537                 if(_waitCount == 0){
8538                         // all items are already loaded.  proceed...
8539                         onComplete(childItems);
8540                 }else{
8541                         // still waiting for some or all of the items to load
8542                         var onItem = function onItem(item){
8543                                 if(--_waitCount == 0){
8544                                         // all nodes have been loaded, send them to the tree
8545                                         onComplete(childItems);
8546                                 }
8547                         }
8548                         dojo.forEach(childItems, function(item){
8549                                 if(!store.isItemLoaded(item)){
8550                                         store.loadItem({
8551                                                 item: item,
8552                                                 onItem: onItem,
8553                                                 onError: onError
8554                                         });
8555                                 }
8556                         });
8557                 }
8558         },
8559
8560         // =======================================================================
8561         // Inspecting items
8562
8563         getIdentity: function(/* item */ item){
8564                 return this.store.getIdentity(item);    // Object
8565         },
8566
8567         getLabel: function(/*dojo.data.Item*/ item){
8568                 // summary: get the label for an item
8569                 return this.store.getLabel(item);       // String
8570         },
8571
8572         // =======================================================================
8573         // Write interface
8574
8575         newItem: function(/* Object? */ args, /*Item*/ parent){
8576                 // summary
8577                 //              Creates a new item.   See dojo.data.api.Write for details on args.
8578                 //              Used in drag & drop when item from external source dropped onto tree.
8579                 var pInfo = {parent: parent, attribute: this.childrenAttrs[0]};
8580                 return this.store.newItem(args, pInfo);
8581         },
8582
8583         pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy){
8584                 // summary
8585                 //              Move or copy an item from one parent item to another.
8586                 //              Used in drag & drop
8587                 var store = this.store,
8588                         parentAttr = this.childrenAttrs[0];     // name of "children" attr in parent item
8589
8590                 // remove child from source item, and record the attributee that child occurred in      
8591                 if(oldParentItem){
8592                         dojo.forEach(this.childrenAttrs, function(attr){
8593                                 if(store.containsValue(oldParentItem, attr, childItem)){
8594                                         if(!bCopy){
8595                                                 var values = dojo.filter(store.getValues(oldParentItem, attr), function(x){
8596                                                         return x != childItem;
8597                                                 });
8598                                                 store.setValues(oldParentItem, attr, values);
8599                                         }
8600                                         parentAttr = attr;
8601                                 }
8602                         });
8603                 }
8604
8605                 // modify target item's children attribute to include this item
8606                 if(newParentItem){
8607                         store.setValues(newParentItem, parentAttr,
8608                                 store.getValues(newParentItem, parentAttr).concat(childItem));
8609                 }
8610         },
8611
8612         // =======================================================================
8613         // Callbacks
8614         
8615         onChange: function(/*dojo.data.Item*/ item){
8616                 // summary
8617                 //              Callback whenever an item has changed, so that Tree
8618                 //              can update the label, icon, etc.   Note that changes
8619                 //              to an item's children or parent(s) will trigger an
8620                 //              onChildrenChange() so you can ignore those changes here.
8621         },
8622
8623         onChildrenChange: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
8624                 // summary
8625                 //              Callback to do notifications about new, updated, or deleted items.
8626         },
8627
8628         // =======================================================================
8629         ///Events from data store
8630
8631         _onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){
8632                 // summary: handler for when new items appear in the store.
8633
8634                 //      In this case there's no correspond onSet() call on the parent of this
8635                 //      item, so need to get the new children list of the parent manually somehow.
8636                 if(!parentInfo){
8637                         return;
8638                 }
8639                 this.getChildren(parentInfo.item, dojo.hitch(this, function(children){
8640                         // NOTE: maybe can be optimized since parentInfo contains the new and old attribute value
8641                         this.onChildrenChange(parentInfo.item, children);
8642                 }));
8643         },
8644         
8645         _onDeleteItem: function(/*Object*/ item){
8646                 // summary: handler for delete notifications from underlying store
8647         },
8648
8649         _onSetItem: function(/* item */ item, 
8650                                         /* attribute-name-string */ attribute, 
8651                                         /* object | array */ oldValue,
8652                                         /* object | array */ newValue){
8653                 //summary: set data event on an item in the store
8654         
8655                 if(dojo.indexOf(this.childrenAttrs, attribute) != -1){
8656                         // item's children list changed
8657                         this.getChildren(item, dojo.hitch(this, function(children){
8658                                 // NOTE: maybe can be optimized since parentInfo contains the new and old attribute value
8659                                 this.onChildrenChange(item, children);
8660                         }));
8661                 }else{
8662                         // item's label/icon/etc. changed.
8663                         this.onChange(item);
8664                 }
8665         }
8666 });
8667
8668 dojo.declare("dijit.tree.ForestStoreModel", dijit.tree.TreeStoreModel, {
8669         // summary
8670         //              Interface between Tree and a dojo.store that doesn't have a root item, ie,
8671         //              has multiple "top level" items.
8672         //
8673         // description
8674         //              Use this class to wrap a dojo.store, making all the items matching the specified query
8675         //              appear as children of a fabricated "root item".  If no query is specified then all the
8676         //              items returned by fetch() on the underlying store become children of the root item.
8677         //              It allows dijit.Tree to assume a single root item, even if the store doesn't have one.
8678
8679         // Parameters to constructor
8680
8681         // rootId: String
8682         //      ID of fabricated root item
8683         rootId: "$root$",
8684
8685         // rootLabel: String
8686         //      Label of fabricated root item
8687         rootLabel: "ROOT",
8688
8689         // query: String
8690         //      Specifies the set of children of the root item.
8691         // example:
8692         //              {type:'continent'}
8693         query: null,
8694
8695         // End of parameters to constructor
8696
8697         constructor: function(params){
8698                 // Make dummy root item
8699                 this.root = {
8700                         store: this,
8701                         root: true,
8702                         id: params.rootId,
8703                         label: params.rootLabel,
8704                         children: params.rootChildren   // optional param
8705                 };
8706         },
8707
8708         // =======================================================================
8709         // Methods for traversing hierarchy
8710
8711         mayHaveChildren: function(/*dojo.data.Item*/ item){
8712                 // summary
8713                 //              Tells if an item has or may have children.  Implementing logic here
8714                 //              avoids showing +/- expando icon for nodes that we know don't have children.
8715                 //              (For efficiency reasons we may not want to check if an element actually
8716                 //              has children until user clicks the expando node)
8717                 return item === this.root || this.inherited(arguments);
8718         },
8719
8720         getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ callback, /*function*/ onError){
8721                 // summary
8722                 //              Calls onComplete() with array of child items of given parent item, all loaded.
8723                 if(parentItem === this.root){
8724                         if(this.root.children){
8725                                 // already loaded, just return
8726                                 callback(this.root.children);
8727                         }else{
8728                                 this.store.fetch({
8729                                         query: this.query,
8730                                         onComplete: dojo.hitch(this, function(items){
8731                                                 this.root.children = items;
8732                                                 callback(items);
8733                                         }),
8734                                         onError: onError
8735                                 });
8736                         }
8737                 }else{
8738                         this.inherited(arguments);
8739                 }
8740         },
8741
8742         // =======================================================================
8743         // Inspecting items
8744
8745         getIdentity: function(/* item */ item){
8746                 return (item === this.root) ? this.root.id : this.inherited(arguments);
8747         },
8748
8749         getLabel: function(/* item */ item){
8750                 return  (item === this.root) ? this.root.label : this.inherited(arguments);
8751         },
8752
8753         // =======================================================================
8754         // Write interface
8755
8756         newItem: function(/* Object? */ args, /*Item*/ parent){
8757                 // summary
8758                 //              Creates a new item.   See dojo.data.api.Write for details on args.
8759                 //              Used in drag & drop when item from external source dropped onto tree.
8760                 if(parent===this.root){
8761                         this.onNewRootItem(args);
8762                         return this.store.newItem(args);
8763                 }else{
8764                         return this.inherited(arguments);
8765                 }
8766         },
8767  
8768         onNewRootItem: function(args){
8769                 // summary:
8770                 //              User can override this method to modify a new element that's being
8771                 //              added to the root of the tree, for example to add a flag like root=true
8772         },
8773
8774         pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy){
8775                 // summary
8776                 //              Move or copy an item from one parent item to another.
8777                 //              Used in drag & drop
8778                 if(oldParentItem === this.root){
8779                         if(!bCopy){
8780                                 // It's onLeaveRoot()'s responsibility to modify the item so it no longer matches
8781                                 // this.query... thus triggering an onChildrenChange() event to notify the Tree
8782                                 // that this element is no longer a child of the root node
8783                                 this.onLeaveRoot(childItem);
8784                         }
8785                 }
8786                 dijit.tree.TreeStoreModel.prototype.pasteItem.call(this, childItem,
8787                         oldParentItem === this.root ? null : oldParentItem,
8788                         newParentItem === this.root ? null : newParentItem
8789                 );
8790                 if(newParentItem === this.root){
8791                         // It's onAddToRoot()'s responsibility to modify the item so it matches
8792                         // this.query... thus triggering an onChildrenChange() event to notify the Tree
8793                         // that this element is now a child of the root node
8794                         this.onAddToRoot(childItem);
8795                 }
8796         },
8797
8798         // =======================================================================
8799         // Callbacks
8800         
8801         onAddToRoot: function(/* item */ item){
8802                 // summary
8803                 //              Called when item added to root of tree; user must override
8804                 //              to modify the item so that it matches the query for top level items
8805                 // example
8806                 //      |       store.setValue(item, "root", true);
8807                 console.log(this, ": item ", item, " added to root");
8808         },
8809
8810         onLeaveRoot: function(/* item */ item){
8811                 // summary
8812                 //              Called when item removed from root of tree; user must override
8813                 //              to modify the item so it doesn't match the query for top level items
8814                 // example
8815                 //      |       store.unsetAttribute(item, "root");
8816                 console.log(this, ": item ", item, " removed from root");
8817         },
8818         
8819         // =======================================================================
8820         // Events from data store
8821
8822         _requeryTop: function(){
8823                 // reruns the query for the children of the root node,
8824                 // sending out an onSet notification if those children have changed
8825                 var _this = this,
8826                         oldChildren = this.root.children;
8827                 this.store.fetch({
8828                         query: this.query,
8829                         onComplete: function(newChildren){
8830                                 _this.root.children = newChildren;
8831
8832                                 // If the list of children or the order of children has changed...      
8833                                 if(oldChildren.length != newChildren.length ||
8834                                         dojo.some(oldChildren, function(item, idx){ return newChildren[idx] != item;})){
8835                                         _this.onChildrenChange(_this.root, newChildren);
8836                                 }
8837                         }
8838                 });
8839         },
8840
8841         _onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){
8842                 // summary: handler for when new items appear in the store.
8843
8844                 //              In theory, any new item could be a top level item.
8845                 //              Do the safe but inefficient thing by requerying the top
8846                 //              level items.   User can override this function to do something
8847                 //              more efficient.
8848                 this._requeryTop();
8849
8850                 this.inherited(arguments);
8851         },
8852
8853         _onDeleteItem: function(/*Object*/ item){
8854                 // summary: handler for delete notifications from underlying store
8855
8856                 // check if this was a child of root, and if so send notification that root's children
8857                 // have changed
8858                 if(dojo.indexOf(this.root.children, item) != -1){
8859                         this._requeryTop();
8860                 }
8861
8862                 this.inherited(arguments);
8863         }
8864 });
8865
8866 }
8867
8868 if(!dojo._hasResource["dijit.form.TextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8869 dojo._hasResource["dijit.form.TextBox"] = true;
8870 dojo.provide("dijit.form.TextBox");
8871
8872
8873
8874 dojo.declare(
8875         "dijit.form.TextBox",
8876         dijit.form._FormValueWidget,
8877         {
8878                 //      summary:
8879                 //              A base class for textbox form inputs
8880                 //
8881                 //      trim: Boolean
8882                 //              Removes leading and trailing whitespace if true.  Default is false.
8883                 trim: false,
8884
8885                 //      uppercase: Boolean
8886                 //              Converts all characters to uppercase if true.  Default is false.
8887                 uppercase: false,
8888
8889                 //      lowercase: Boolean
8890                 //              Converts all characters to lowercase if true.  Default is false.
8891                 lowercase: false,
8892
8893                 //      propercase: Boolean
8894                 //              Converts the first character of each word to uppercase if true.
8895                 propercase: false,
8896
8897                 //      maxLength: String
8898                 //              HTML INPUT tag maxLength declaration.
8899                 maxLength: "",
8900
8901                 templateString:"<input class=\"dijit dijitReset dijitLeft\" dojoAttachPoint='textbox,focusNode' name=\"${name}\"\n\tdojoAttachEvent='onmouseenter:_onMouse,onmouseleave:_onMouse,onfocus:_onMouse,onblur:_onMouse,onkeypress:_onKeyPress,onkeyup'\n\tautocomplete=\"off\" type=\"${type}\"\n\t/>\n",
8902                 baseClass: "dijitTextBox",
8903
8904                 attributeMap: dojo.mixin(dojo.clone(dijit.form._FormValueWidget.prototype.attributeMap),
8905                         {maxLength:"focusNode"}),
8906
8907                 getDisplayedValue: function(){
8908                         //      summary:
8909                         //              Returns the formatted value that the user sees in the textbox, which may be different
8910                         //              from the serialized value that's actually sent to the server (see dijit.form.ValidationTextBox.serialize)
8911                         return this.filter(this.textbox.value);
8912                 },
8913
8914                 getValue: function(){
8915                         return this.parse(this.getDisplayedValue(), this.constraints);
8916                 },
8917
8918                 setValue: function(value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
8919                         //      summary: 
8920                         //              Sets the value of the widget to "value" which can be of
8921                         //              any type as determined by the widget.
8922                         //
8923                         //      value:
8924                         //              The visual element value is also set to a corresponding,
8925                         //              but not necessarily the same, value.
8926                         //
8927                         //      formattedValue:
8928                         //              If specified, used to set the visual element value,
8929                         //              otherwise a computed visual value is used.
8930                         //
8931                         //      priorityChange:
8932                         //              If true, an onChange event is fired immediately instead of 
8933                         //              waiting for the next blur event.
8934
8935                         var filteredValue = this.filter(value);
8936                         if((((typeof filteredValue == typeof value) && (value !== undefined/*#5317*/)) || (value === null/*#5329*/)) && (formattedValue == null || formattedValue == undefined)){
8937                                 formattedValue = this.format(filteredValue, this.constraints);
8938                         }
8939                         if(formattedValue != null && formattedValue != undefined){
8940                                 this.textbox.value = formattedValue;
8941                         }
8942                         dijit.form.TextBox.superclass.setValue.call(this, filteredValue, priorityChange);
8943                 },
8944
8945                 setDisplayedValue: function(/*String*/value, /*Boolean?*/ priorityChange){
8946                         //      summary: 
8947                         //              Sets the value of the visual element to the string "value".
8948                         //              The widget value is also set to a corresponding,
8949                         //              but not necessarily the same, value.
8950                         //
8951                         //      priorityChange:
8952                         //              If true, an onChange event is fired immediately instead of 
8953                         //              waiting for the next blur event.
8954
8955                         this.textbox.value = value;
8956                         this.setValue(this.getValue(), priorityChange);
8957                 },
8958
8959                 format: function(/* String */ value, /* Object */ constraints){
8960                         //      summary:
8961                         //              Replacable function to convert a value to a properly formatted string
8962                         return ((value == null || value == undefined) ? "" : (value.toString ? value.toString() : value));
8963                 },
8964
8965                 parse: function(/* String */ value, /* Object */ constraints){
8966                         //      summary:
8967                         //              Replacable function to convert a formatted string to a value
8968                         return value;
8969                 },
8970
8971                 postCreate: function(){
8972                         // setting the value here is needed since value="" in the template causes "undefined"
8973                         // and setting in the DOM (instead of the JS object) helps with form reset actions
8974                         this.textbox.setAttribute("value", this.getDisplayedValue());
8975                         this.inherited(arguments);
8976
8977                         /*#5297:if(this.srcNodeRef){
8978                                 dojo.style(this.textbox, "cssText", this.style);
8979                                 this.textbox.className += " " + this["class"];
8980                         }*/
8981                         this._layoutHack();
8982                 },
8983
8984                 filter: function(val){
8985                         //      summary:
8986                         //              Apply specified filters to textbox value
8987                         if(val === null || val === undefined){ return ""; }
8988                         else if(typeof val != "string"){ return val; }
8989                         if(this.trim){
8990                                 val = dojo.trim(val);
8991                         }
8992                         if(this.uppercase){
8993                                 val = val.toUpperCase();
8994                         }
8995                         if(this.lowercase){
8996                                 val = val.toLowerCase();
8997                         }
8998                         if(this.propercase){
8999                                 val = val.replace(/[^\s]+/g, function(word){
9000                                         return word.substring(0,1).toUpperCase() + word.substring(1);
9001                                 });
9002                         }
9003                         return val;
9004                 },
9005
9006                 _setBlurValue: function(){
9007                         this.setValue(this.getValue(), (this.isValid ? this.isValid() : true));
9008                 },
9009
9010                 _onBlur: function(){
9011                         this._setBlurValue();
9012                         this.inherited(arguments);
9013                 },
9014
9015                 onkeyup: function(){
9016                         //      summary:
9017                         //              User replaceable keyup event handler
9018                 }
9019         }
9020 );
9021
9022 dijit.selectInputText = function(/*DomNode*/element, /*Number?*/ start, /*Number?*/ stop){
9023         //      summary:
9024         //              Select text in the input element argument, from start (default 0), to stop (default end).
9025
9026         // TODO: use functions in _editor/selection.js?
9027         var _window = dojo.global;
9028         var _document = dojo.doc;
9029         element = dojo.byId(element);
9030         if(isNaN(start)){ start = 0; }
9031         if(isNaN(stop)){ stop = element.value ? element.value.length : 0; }
9032         element.focus();
9033         if(_document["selection"] && dojo.body()["createTextRange"]){ // IE
9034                 if(element.createTextRange){
9035                         var range = element.createTextRange();
9036                         with(range){
9037                                 collapse(true);
9038                                 moveStart("character", start);
9039                                 moveEnd("character", stop);
9040                                 select();
9041                         }
9042                 }
9043         }else if(_window["getSelection"]){
9044                 var selection = _window.getSelection();
9045                 // FIXME: does this work on Safari?
9046                 if(element.setSelectionRange){
9047                         element.setSelectionRange(start, stop);
9048                 }
9049         }
9050 }
9051
9052 }
9053
9054 if(!dojo._hasResource["dijit.InlineEditBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9055 dojo._hasResource["dijit.InlineEditBox"] = true;
9056 dojo.provide("dijit.InlineEditBox");
9057
9058
9059
9060
9061
9062
9063
9064
9065
9066
9067 dojo.declare("dijit.InlineEditBox",
9068         dijit._Widget,
9069         {
9070         // summary: An element with in-line edit capabilitites
9071         //
9072         // description:
9073         //              Behavior for an existing node (`<p>`, `<div>`, `<span>`, etc.) so that
9074         //              when you click it, an editor shows up in place of the original
9075         //              text.  Optionally, Save and Cancel button are displayed below the edit widget.
9076         //              When Save is clicked, the text is pulled from the edit
9077         //              widget and redisplayed and the edit widget is again hidden.
9078         //              By default a plain Textarea widget is used as the editor (or for
9079         //              inline values a TextBox), but you can specify an editor such as
9080         //              dijit.Editor (for editing HTML) or a Slider (for adjusting a number).
9081         //              An edit widget must support the following API to be used:
9082         //              String getDisplayedValue() OR String getValue()
9083         //              void setDisplayedValue(String) OR void setValue(String)
9084         //              void focus()
9085         //
9086         // editing: Boolean
9087         //              Is the node currently in edit mode?
9088         editing: false,
9089
9090         // autoSave: Boolean
9091         //              Changing the value automatically saves it; don't have to push save button
9092         //              (and save button isn't even displayed)
9093         autoSave: true,
9094
9095         // buttonSave: String
9096         //              Save button label
9097         buttonSave: "",
9098
9099         // buttonCancel: String
9100         //              Cancel button label
9101         buttonCancel: "",
9102
9103         // renderAsHtml: Boolean
9104         //              Set this to true if the specified Editor's value should be interpreted as HTML
9105         //              rather than plain text (ie, dijit.Editor)
9106         renderAsHtml: false,
9107
9108         // editor: String
9109         //              Class name for Editor widget
9110         editor: "dijit.form.TextBox",
9111
9112         // editorParams: Object
9113         //              Set of parameters for editor, like {required: true}
9114         editorParams: {},
9115
9116         onChange: function(value){
9117                 // summary: User should set this handler to be notified of changes to value
9118         },
9119
9120         // width: String
9121         //              Width of editor.  By default it's width=100% (ie, block mode)
9122         width: "100%",
9123
9124         // value: String
9125         //              The display value of the widget in read-only mode
9126         value: "",
9127
9128         // noValueIndicator: String
9129         //              The text that gets displayed when there is no value (so that the user has a place to click to edit)
9130         noValueIndicator: "<span style='font-family: wingdings; text-decoration: underline;'>&nbsp;&nbsp;&nbsp;&nbsp;&#x270d;&nbsp;&nbsp;&nbsp;&nbsp;</span>",
9131
9132         postMixInProperties: function(){
9133                 this.inherited('postMixInProperties', arguments);
9134
9135                 // save pointer to original source node, since Widget nulls-out srcNodeRef
9136                 this.displayNode = this.srcNodeRef;
9137
9138                 // connect handlers to the display node
9139                 var events = {
9140                         ondijitclick: "_onClick",
9141                         onmouseover: "_onMouseOver",
9142                         onmouseout: "_onMouseOut",
9143                         onfocus: "_onMouseOver",
9144                         onblur: "_onMouseOut"                   
9145                 };
9146                 for(var name in events){
9147                         this.connect(this.displayNode, name, events[name]);
9148                 }
9149                 dijit.setWaiRole(this.displayNode, "button");
9150                 if(!this.displayNode.getAttribute("tabIndex")){
9151                         this.displayNode.setAttribute("tabIndex", 0);
9152                 }
9153
9154                 this.setValue(this.value || this.displayNode.innerHTML);
9155         },
9156
9157         setDisabled: function(/*Boolean*/ disabled){
9158                 // summary:
9159                 //              Set disabled state of widget.
9160
9161                 this.disabled = disabled;
9162                 dijit.setWaiState(this.focusNode || this.domNode, "disabled", disabled);
9163         },
9164
9165         _onMouseOver: function(){
9166                 dojo.addClass(this.displayNode, this.disabled ? "dijitDisabledClickableRegion" : "dijitClickableRegion");
9167         },
9168
9169         _onMouseOut: function(){
9170                 dojo.removeClass(this.displayNode, this.disabled ? "dijitDisabledClickableRegion" : "dijitClickableRegion");
9171         },
9172
9173         _onClick: function(/*Event*/ e){
9174                 if(this.disabled){ return; }
9175                 if(e){ dojo.stopEvent(e); }
9176                 this._onMouseOut();
9177
9178                 // Since FF gets upset if you move a node while in an event handler for that node...
9179                 setTimeout(dojo.hitch(this, "_edit"), 0);
9180         },
9181
9182         _edit: function(){
9183                 // summary: display the editor widget in place of the original (read only) markup
9184
9185                 this.editing = true;
9186
9187                 var editValue = 
9188                                 (this.renderAsHtml ?
9189                                 this.value :
9190                                 this.value.replace(/\s*\r?\n\s*/g,"").replace(/<br\/?>/gi, "\n").replace(/&gt;/g,">").replace(/&lt;/g,"<").replace(/&amp;/g,"&"));
9191
9192                 // Placeholder for edit widget
9193                 // Put place holder (and eventually editWidget) before the display node so that it's positioned correctly
9194                 // when Calendar dropdown appears, which happens automatically on focus.
9195                 var placeholder = dojo.doc.createElement("span");
9196                 dojo.place(placeholder, this.domNode, "before");
9197
9198                 var ew = this.editWidget = new dijit._InlineEditor({
9199                         value: dojo.trim(editValue),
9200                         autoSave: this.autoSave,
9201                         buttonSave: this.buttonSave,
9202                         buttonCancel: this.buttonCancel,
9203                         renderAsHtml: this.renderAsHtml,
9204                         editor: this.editor,
9205                         editorParams: this.editorParams,
9206                         style: dojo.getComputedStyle(this.displayNode),
9207                         save: dojo.hitch(this, "save"),
9208                         cancel: dojo.hitch(this, "cancel"),
9209                         width: this.width
9210                 }, placeholder);
9211
9212                 // to avoid screen jitter, we first create the editor with position:absolute, visibility:hidden,
9213                 // and then when it's finished rendering, we switch from display mode to editor
9214                 var ews = ew.domNode.style;
9215                 this.displayNode.style.display="none";
9216                 ews.position = "static";
9217                 ews.visibility = "visible";
9218
9219                 // Replace the display widget with edit widget, leaving them both displayed for a brief time so that
9220                 // focus can be shifted without incident.  (browser may needs some time to render the editor.)
9221                 this.domNode = ew.domNode;
9222                 setTimeout(function(){
9223                         ew.focus();
9224                 }, 100);
9225         },
9226
9227         _showText: function(/*Boolean*/ focus){
9228                 // summary: revert to display mode, and optionally focus on display node
9229
9230                 // display the read-only text and then quickly hide the editor (to avoid screen jitter)
9231                 this.displayNode.style.display="";
9232                 var ew = this.editWidget;
9233                 var ews = ew.domNode.style;
9234                 ews.position="absolute";
9235                 ews.visibility="hidden";
9236
9237                 this.domNode = this.displayNode;
9238
9239                 if(focus){
9240                         dijit.focus(this.displayNode);
9241                 }
9242                 ews.display = "none";
9243                 // give the browser some time to render the display node and then shift focus to it
9244                 // and hide the edit widget before garbage collecting the edit widget
9245                 setTimeout(function(){
9246                         ew.destroy();
9247                         delete ew;
9248                         if(dojo.isIE){
9249                                 // messing with the DOM tab order can cause IE to focus the body - so restore
9250                                 dijit.focus(dijit.getFocus());
9251                         }
9252                 }, 1000); // no hurry - wait for things to quiesce
9253         },
9254
9255         save: function(/*Boolean*/ focus){
9256                 // summary:
9257                 //              Save the contents of the editor and revert to display mode.
9258                 // focus: Boolean
9259                 //              Focus on the display mode text
9260                 this.editing = false;
9261
9262                 var value = this.editWidget.getValue() + "";
9263                 if(!this.renderAsHtml){
9264                         value = value.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;")
9265                                 .replace(/\n/g, "<br>");
9266                 }
9267                 this.setValue(value);
9268
9269                 // tell the world that we have changed
9270                 this.onChange(value);
9271
9272                 this._showText(focus);  
9273         },
9274
9275         setValue: function(/*String*/ val){
9276                 // summary: inserts specified HTML value into this node, or an "input needed" character if node is blank
9277                 this.value = val;
9278                 this.displayNode.innerHTML = dojo.trim(val) || this.noValueIndicator;
9279         },
9280
9281         getValue: function(){
9282                 return this.value;
9283         },
9284
9285         cancel: function(/*Boolean*/ focus){
9286                 // summary:
9287                 //              Revert to display mode, discarding any changes made in the editor
9288                 this.editing = false;
9289                 this._showText(focus);
9290         }
9291 });
9292
9293 dojo.declare(
9294         "dijit._InlineEditor",
9295          [dijit._Widget, dijit._Templated],
9296 {
9297         // summary:
9298         //              internal widget used by InlineEditBox, displayed when in editing mode
9299         //              to display the editor and maybe save/cancel buttons.  Calling code should
9300         //              connect to save/cancel methods to detect when editing is finished
9301         //
9302         //              Has mainly the same parameters as InlineEditBox, plus these values:
9303         //
9304         // style: Object
9305         //              Set of CSS attributes of display node, to replicate in editor
9306         //
9307         // value: String
9308         //              Value as an HTML string or plain text string, depending on renderAsHTML flag
9309
9310         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\" disabled=\"true\">${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",
9311         widgetsInTemplate: true,
9312
9313         postMixInProperties: function(){
9314                 this.inherited('postMixInProperties', arguments);
9315                 this.messages = dojo.i18n.getLocalization("dijit", "common", this.lang);
9316                 dojo.forEach(["buttonSave", "buttonCancel"], function(prop){
9317                         if(!this[prop]){ this[prop] = this.messages[prop]; }
9318                 }, this);
9319         },
9320
9321         postCreate: function(){
9322                 // Create edit widget in place in the template
9323                 var cls = dojo.getObject(this.editor);
9324                 var ew = this.editWidget = new cls(this.editorParams, this.editorPlaceholder);
9325
9326                 // Copy the style from the source
9327                 // Don't copy ALL properties though, just the necessary/applicable ones
9328                 var srcStyle = this.style;
9329                 dojo.forEach(["fontWeight","fontFamily","fontSize","fontStyle"], function(prop){
9330                         ew.focusNode.style[prop]=srcStyle[prop];
9331                 }, this);
9332                 dojo.forEach(["marginTop","marginBottom","marginLeft", "marginRight"], function(prop){
9333                         this.domNode.style[prop]=srcStyle[prop];
9334                 }, this);
9335                 if(this.width=="100%"){
9336                         // block mode
9337                         ew.domNode.style.width = "100%";        // because display: block doesn't work for table widgets
9338                         this.domNode.style.display="block";
9339                 }else{
9340                         // inline-block mode
9341                         ew.domNode.style.width = this.width + (Number(this.width)==this.width ? "px" : "");                     
9342                 }
9343
9344                 this.connect(ew, "onChange", "_onChange");
9345
9346                 // Monitor keypress on the edit widget.   Note that edit widgets do a stopEvent() on ESC key (to
9347                 // prevent Dialog from closing when the user just wants to revert the value in the edit widget),
9348                 // so this is the only way we can see the key press event.
9349                 this.connect(ew.focusNode || ew.domNode, "onkeypress", "_onKeyPress");
9350
9351                 // priorityChange=false will prevent bogus onChange event
9352                 (this.editWidget.setDisplayedValue||this.editWidget.setValue).call(this.editWidget, this.value, false);
9353
9354                 this._initialText = this.getValue();
9355
9356                 if(this.autoSave){
9357                         this.buttonContainer.style.display="none";
9358                 }
9359         },
9360
9361         destroy: function(){
9362                 this.editWidget.destroy();
9363                 this.inherited(arguments);
9364         },
9365
9366         getValue: function(){
9367                 var ew = this.editWidget;
9368                 return ew.getDisplayedValue ? ew.getDisplayedValue() : ew.getValue();
9369         },
9370
9371         _onKeyPress: function(e){
9372                 // summary: Callback when keypress in the edit box (see template).
9373                 // description:
9374                 //              For autoSave widgets, if Esc/Enter, call cancel/save.
9375                 //              For non-autoSave widgets, enable save button if the text value is
9376                 //              different than the original value.
9377                 if(this._exitInProgress){
9378                         return;
9379                 }
9380                 if(this.autoSave){
9381                         if(e.altKey || e.ctrlKey){ return; }
9382                         // If Enter/Esc pressed, treat as save/cancel.
9383                         if(e.keyCode == dojo.keys.ESCAPE){
9384                                 dojo.stopEvent(e);
9385                                 this._exitInProgress = true;
9386                                 this.cancel(true);
9387                         }else if(e.keyCode == dojo.keys.ENTER){
9388                                 dojo.stopEvent(e);
9389                                 this._exitInProgress = true;
9390                                 this.save(true);
9391                         }else if(e.keyCode == dojo.keys.TAB){
9392                                 this._exitInProgress = true;
9393                                 // allow the TAB to change focus before we mess with the DOM: #6227
9394                                 // Expounding by request:
9395                                 //      The current focus is on the edit widget input field.
9396                                 //      save() will hide and destroy this widget.
9397                                 //      We want the focus to jump from the currently hidden
9398                                 //      displayNode, but since it's hidden, it's impossible to
9399                                 //      unhide it, focus it, and then have the browser focus
9400                                 //      away from it to the next focusable element since each
9401                                 //      of these events is asynchronous and the focus-to-next-element
9402                                 //      is already queued.
9403                                 //      So we allow the browser time to unqueue the move-focus event 
9404                                 //      before we do all the hide/show stuff.
9405                                 setTimeout(dojo.hitch(this, "save", false), 0);
9406                         }
9407                 }else{
9408                         var _this = this;
9409                         // Delay before calling getValue().
9410                         // The delay gives the browser a chance to update the Textarea.
9411                         setTimeout(
9412                                 function(){
9413                                         _this.saveButton.setAttribute("disabled", _this.getValue() == _this._initialText);
9414                                 }, 100);
9415                 }
9416         },
9417
9418         _onBlur: function(){
9419                 // summary:
9420                 //      Called when focus moves outside the editor
9421                 this.inherited(arguments);
9422                 if(this._exitInProgress){
9423                         // when user clicks the "save" button, focus is shifted back to display text, causing this
9424                         // function to be called, but in that case don't do anything
9425                         return;
9426                 }
9427                 if(this.autoSave){
9428                         this._exitInProgress = true;
9429                         if(this.getValue() == this._initialText){
9430                                 this.cancel(false);
9431                         }else{
9432                                 this.save(false);
9433                         }
9434                 }
9435         },
9436
9437         enableSave: function(){
9438                 // summary: User replacable function returning a Boolean to indicate
9439                 //      if the Save button should be enabled or not - usually due to invalid conditions
9440                 return this.editWidget.isValid ? this.editWidget.isValid() : true; // Boolean
9441         },
9442
9443         _onChange: function(){
9444                 // summary:
9445                 //      Called when the underlying widget fires an onChange event,
9446                 //      which means that the user has finished entering the value
9447                 if(this._exitInProgress){
9448                         // TODO: the onChange event might happen after the return key for an async widget
9449                         // like FilteringSelect.  Shouldn't be deleting the edit widget on end-of-edit
9450                         return;
9451                 }
9452                 if(this.autoSave){
9453                         this._exitInProgress = true;
9454                         this.save(true);
9455                 }else{
9456                         // in case the keypress event didn't get through (old problem with Textarea that has been fixed
9457                         // in theory) or if the keypress event comes too quickly and the value inside the Textarea hasn't
9458                         // been updated yet)
9459                         this.saveButton.setAttribute("disabled", (this.getValue() == this._initialText) || !this.enableSave());
9460                 }
9461         },
9462         
9463         enableSave: function(){
9464                 // summary: User replacable function returning a Boolean to indicate
9465                 //      if the Save button should be enabled or not - usually due to invalid conditions
9466                 return this.editWidget.isValid ? this.editWidget.isValid() : true;
9467         },
9468
9469         focus: function(){
9470                 this.editWidget.focus();
9471                 dijit.selectInputText(this.editWidget.focusNode);
9472         }
9473 });
9474
9475 }
9476
9477 if(!dojo._hasResource["dijit.form.CheckBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9478 dojo._hasResource["dijit.form.CheckBox"] = true;
9479 dojo.provide("dijit.form.CheckBox");
9480
9481
9482
9483 dojo.declare(
9484         "dijit.form.CheckBox",
9485         dijit.form.ToggleButton,
9486         {
9487                 // summary:
9488                 //              Same as an HTML checkbox, but with fancy styling.
9489                 //
9490                 // description:
9491                 // User interacts with real html inputs.
9492                 // On onclick (which occurs by mouse click, space-bar, or
9493                 // using the arrow keys to switch the selected radio button),
9494                 // we update the state of the checkbox/radio.
9495                 //
9496                 // There are two modes:
9497                 //   1. High contrast mode
9498                 //   2. Normal mode
9499                 // In case 1, the regular html inputs are shown and used by the user.
9500                 // In case 2, the regular html inputs are invisible but still used by
9501                 // the user. They are turned quasi-invisible and overlay the background-image.
9502
9503                 templateString:"<div class=\"dijitReset dijitInline\" waiRole=\"presentation\"\n\t><input\n\t \ttype=\"${type}\" name=\"${name}\"\n\t\tclass=\"dijitReset dijitCheckBoxInput\"\n\t\tdojoAttachPoint=\"focusNode\"\n\t \tdojoAttachEvent=\"onmouseover:_onMouse,onmouseout:_onMouse,onclick:_onClick\"\n/></div>\n",
9504
9505                 baseClass: "dijitCheckBox",
9506
9507                 //      Value of "type" attribute for <input>
9508                 type: "checkbox",
9509
9510                 // value: Value
9511                 //      equivalent to value field on normal checkbox (if checked, the value is passed as
9512                 //      the value when form is submitted)
9513                 value: "on",
9514
9515                 setValue: function(/*String or Boolean*/ newValue){
9516                         // summary:
9517                         //              When passed a boolean, controls whether or not the CheckBox is checked.
9518                         //              If passed a string, changes the value attribute of the CheckBox (the one
9519                         //              specified as "value" when the CheckBox was constructed (ex: <input
9520                         //              dojoType="dijit.CheckBox" value="chicken">)
9521                         if(typeof newValue == "string"){
9522                                 this.setAttribute('value', newValue);
9523                                 newValue = true;
9524                         }
9525                         this.setAttribute('checked', newValue);
9526                 },
9527
9528                 _getValueDeprecated: false, // remove when _FormWidget:_getValueDeprecated is removed
9529                 getValue: function(){
9530                         // summary:
9531                         //              If the CheckBox is checked, returns the value attribute.
9532                         //              Otherwise returns false.
9533                         return (this.checked ? this.value : false);
9534                 },
9535
9536                 reset: function(){
9537                         this.inherited(arguments);
9538                         this.setAttribute('value', this._resetValueAttr);
9539                 },
9540
9541                 postCreate: function(){
9542                         this.inherited(arguments);
9543                         this._resetValueAttr = this.value;
9544                 }
9545         }
9546 );
9547
9548 dojo.declare(
9549         "dijit.form.RadioButton",
9550         dijit.form.CheckBox,
9551         {
9552                 // summary:
9553                 //              Same as an HTML radio, but with fancy styling.
9554                 //
9555                 // description:
9556                 // Implementation details
9557                 //
9558                 // Specialization:
9559                 // We keep track of dijit radio groups so that we can update the state
9560                 // of all the siblings (the "context") in a group based on input
9561                 // events. We don't rely on browser radio grouping.
9562
9563                 type: "radio",
9564                 baseClass: "dijitRadio",
9565
9566                 // This shared object keeps track of all widgets, grouped by name
9567                 _groups: {},
9568
9569                 postCreate: function(){
9570                         // add this widget to _groups
9571                         (this._groups[this.name] = this._groups[this.name] || []).push(this);
9572
9573                         this.inherited(arguments);
9574                 },
9575
9576                 uninitialize: function(){
9577                         // remove this widget from _groups
9578                         dojo.forEach(this._groups[this.name], function(widget, i, arr){
9579                                 if(widget === this){
9580                                         arr.splice(i, 1);
9581                                         return;
9582                                 }
9583                         }, this);
9584                 },
9585
9586                 setAttribute: function(/*String*/ attr, /*anything*/ value){
9587                         // If I am being checked then have to deselect currently checked radio button
9588                         this.inherited(arguments);
9589                         switch(attr){
9590                                 case "checked":
9591                                         if(this.checked){
9592                                                 dojo.forEach(this._groups[this.name], function(widget){
9593                                                         if(widget != this && widget.checked){
9594                                                                 widget.setAttribute('checked', false);
9595                                                         }
9596                                                 }, this);
9597                                         }
9598                         }
9599                 },
9600
9601                 _clicked: function(/*Event*/ e){
9602                         if(!this.checked){
9603                                 this.setAttribute('checked', true);
9604                         }
9605                 }
9606         }
9607 );
9608
9609 }
9610
9611 if(!dojo._hasResource["dijit.form.ValidationTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9612 dojo._hasResource["dijit.form.ValidationTextBox"] = true;
9613 dojo.provide("dijit.form.ValidationTextBox");
9614
9615
9616
9617
9618
9619
9620
9621
9622 /*=====
9623         dijit.form.ValidationTextBox.__Constraints = function(){
9624                 // locale: String
9625                 //              locale used for validation, picks up value from this widget's lang attribute
9626                 // _flags_: anything
9627                 //              various flags passed to regExpGen function
9628                 this.locale = "";
9629                 this._flags_ = "";
9630         }
9631 =====*/
9632
9633 dojo.declare(
9634         "dijit.form.ValidationTextBox",
9635         dijit.form.TextBox,
9636         {
9637                 // summary:
9638                 //              A TextBox subclass with the ability to validate content of various types and provide user feedback.
9639
9640                 templateString:"<div class=\"dijit dijitReset dijitInlineTable dijitLeft\"\n\tid=\"widget_${id}\"\n\tdojoAttachEvent=\"onmouseenter:_onMouse,onmouseleave:_onMouse,onmousedown:_onMouse\" waiRole=\"presentation\"\n\t><div style=\"overflow:hidden;\"\n\t\t><div class=\"dijitReset dijitValidationIcon\"><br></div\n\t\t><div class=\"dijitReset dijitValidationIconText\">&Chi;</div\n\t\t><div class=\"dijitReset dijitInputField\"\n\t\t\t><input class=\"dijitReset\" dojoAttachPoint='textbox,focusNode' dojoAttachEvent='onfocus:_update,onkeyup:_onkeyup,onblur:_onMouse,onkeypress:_onKeyPress' autocomplete=\"off\"\n\t\t\ttype='${type}' name='${name}'\n\t\t/></div\n\t></div\n></div>\n",
9641                 baseClass: "dijitTextBox",
9642
9643                 // default values for new subclass properties
9644                 // required: Boolean
9645                 //              Can be true or false, default is false.
9646                 required: false,
9647
9648                 // promptMessage: String
9649                 //              Hint string
9650                 promptMessage: "",
9651
9652                 // invalidMessage: String
9653                 //              The message to display if value is invalid.
9654                 invalidMessage: "$_unset_$", // read from the message file if not overridden
9655
9656                 // constraints: dijit.form.ValidationTextBox.__Constraints
9657                 //              user-defined object needed to pass parameters to the validator functions
9658                 constraints: {},
9659
9660                 // regExp: String
9661                 //              regular expression string used to validate the input
9662                 //              Do not specify both regExp and regExpGen
9663                 regExp: ".*",
9664
9665                 // regExpGen: Function
9666                 //              user replaceable function used to generate regExp when dependent on constraints
9667                 //              Do not specify both regExp and regExpGen
9668                 regExpGen: function(/*dijit.form.ValidationTextBox.__Constraints*/constraints){ return this.regExp; },
9669
9670                 // state: String
9671                 //              Shows current state (ie, validation result) of input (Normal, Warning, or Error)
9672                 state: "",
9673
9674                 //      tooltipPosition: String[]
9675                 //              See description of dijit.Tooltip.defaultPosition for details on this parameter.
9676                 tooltipPosition: [],
9677
9678                 setValue: function(){
9679                         this.inherited(arguments);
9680                         this.validate(this._focused);
9681                 },
9682
9683                 validator: function(/*anything*/value, /*dijit.form.ValidationTextBox.__Constraints*/constraints){
9684                         // summary: user replaceable function used to validate the text input against the regular expression.
9685                         return (new RegExp("^(" + this.regExpGen(constraints) + ")"+(this.required?"":"?")+"$")).test(value) &&
9686                                 (!this.required || !this._isEmpty(value)) &&
9687                                 (this._isEmpty(value) || this.parse(value, constraints) !== undefined); // Boolean
9688                 },
9689
9690                 isValid: function(/*Boolean*/ isFocused){
9691                         // summary: Need to over-ride with your own validation code in subclasses
9692                         return this.validator(this.textbox.value, this.constraints);
9693                 },
9694
9695                 _isEmpty: function(value){
9696                         // summary: Checks for whitespace
9697                         return /^\s*$/.test(value); // Boolean
9698                 },
9699
9700                 getErrorMessage: function(/*Boolean*/ isFocused){
9701                         // summary: return an error message to show if appropriate
9702                         return this.invalidMessage; // String
9703                 },
9704
9705                 getPromptMessage: function(/*Boolean*/ isFocused){
9706                         // summary: return a hint to show if appropriate
9707                         return this.promptMessage; // String
9708                 },
9709
9710                 validate: function(/*Boolean*/ isFocused){
9711                         // summary:
9712                         //              Called by oninit, onblur, and onkeypress.
9713                         // description:
9714                         //              Show missing or invalid messages if appropriate, and highlight textbox field.
9715                         var message = "";
9716                         var isValid = this.isValid(isFocused);
9717                         var isEmpty = this._isEmpty(this.textbox.value);
9718                         this.state = (isValid || (!this._hasBeenBlurred && isEmpty)) ? "" : "Error";
9719                         this._setStateClass();
9720                         dijit.setWaiState(this.focusNode, "invalid", isValid ? "false" : "true");
9721                         if(isFocused){
9722                                 if(isEmpty){
9723                                         message = this.getPromptMessage(true);
9724                                 }
9725                                 if(!message && this.state == "Error"){
9726                                         message = this.getErrorMessage(true);
9727                                 }
9728                         }
9729                         this.displayMessage(message);
9730                         return isValid;
9731                 },
9732
9733                 // currently displayed message
9734                 _message: "",
9735
9736                 displayMessage: function(/*String*/ message){
9737                         // summary:
9738                         //              User overridable method to display validation errors/hints.
9739                         //              By default uses a tooltip.
9740                         if(this._message == message){ return; }
9741                         this._message = message;
9742                         dijit.hideTooltip(this.domNode);
9743                         if(message){
9744                                 dijit.showTooltip(message, this.domNode, this.tooltipPosition);
9745                         }
9746                 },
9747
9748                 _refreshState: function(){
9749                         this.validate(this._focused);
9750                 },
9751
9752                 _update: function(/*Event*/e){
9753                         this._refreshState();
9754                         this._onMouse(e);       // update CSS classes
9755                 },
9756
9757                 _onkeyup: function(/*Event*/e){
9758                         this._update(e);
9759                         this.onkeyup(e);
9760                 },
9761
9762                 //////////// INITIALIZATION METHODS ///////////////////////////////////////
9763
9764                 constructor: function(){
9765                         this.constraints = {};
9766                 },
9767
9768                 postMixInProperties: function(){
9769                         this.inherited(arguments);
9770                         this.constraints.locale = this.lang;
9771                         this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
9772                         if(this.invalidMessage == "$_unset_$"){ this.invalidMessage = this.messages.invalidMessage; }
9773                         var p = this.regExpGen(this.constraints);
9774                         this.regExp = p;
9775                 }
9776         }
9777 );
9778
9779 dojo.declare(
9780         "dijit.form.MappedTextBox",
9781         dijit.form.ValidationTextBox,
9782         {
9783                 // summary:
9784                 //              A dijit.form.ValidationTextBox subclass which provides a visible formatted display and a serializable
9785                 //              value in a hidden input field which is actually sent to the server.  The visible display may
9786                 //              be locale-dependent and interactive.  The value sent to the server is stored in a hidden
9787                 //              input field which uses the `name` attribute declared by the original widget.  That value sent
9788                 //              to the serveris defined by the dijit.form.MappedTextBox.serialize method and is typically
9789                 //              locale-neutral.
9790
9791                 serialize: function(/*anything*/val, /*Object?*/options){
9792                         // summary: user replaceable function used to convert the getValue() result to a String
9793                         return val.toString ? val.toString() : ""; // String
9794                 },
9795
9796                 toString: function(){
9797                         // summary: display the widget as a printable string using the widget's value
9798                         var val = this.filter(this.getValue());
9799                         return val != null ? (typeof val == "string" ? val : this.serialize(val, this.constraints)) : ""; // String
9800                 },
9801
9802                 validate: function(){
9803                         this.valueNode.value = this.toString();
9804                         return this.inherited(arguments);
9805                 },
9806
9807                 setAttribute: function(/*String*/ attr, /*anything*/ value){
9808                         this.inherited(arguments);
9809                         switch(attr){
9810                                 case "disabled":
9811                                         if(this.valueNode){
9812                                                 this.valueNode.disabled = this.disabled;
9813                                         }
9814                         }
9815                 },
9816
9817                 postCreate: function(){
9818                         var textbox = this.textbox;
9819                         var valueNode = (this.valueNode = dojo.doc.createElement("input"));
9820                         valueNode.setAttribute("type", textbox.type);
9821                         valueNode.setAttribute("value", this.toString());
9822                         dojo.style(valueNode, "display", "none");
9823                         valueNode.name = this.textbox.name;
9824                         valueNode.disabled = this.textbox.disabled;
9825                         this.textbox.name = this.textbox.name + "_displayed_";
9826                         this.textbox.removeAttribute("name");
9827                         dojo.place(valueNode, textbox, "after");
9828
9829                         this.inherited(arguments);
9830                 }
9831         }
9832 );
9833
9834 /*=====
9835         dijit.form.RangeBoundTextBox.__Constraints = function(){
9836                 // min: Number
9837                 //              Minimum signed value.  Default is -Infinity
9838                 // max: Number
9839                 //              Maximum signed value.  Default is +Infinity
9840                 this.min = min;
9841                 this.max = max;
9842         }
9843 =====*/
9844
9845 dojo.declare(
9846         "dijit.form.RangeBoundTextBox",
9847         dijit.form.MappedTextBox,
9848         {
9849                 // summary:
9850                 //              A dijit.form.MappedTextBox subclass which defines a range of valid values
9851                 //
9852                 // constraints: dijit.form.RangeBoundTextBox.__Constraints
9853                 //
9854                 // rangeMessage: String
9855                 //              The message to display if value is out-of-range
9856
9857                 /*=====
9858                 constraints: {},
9859                 ======*/
9860                 rangeMessage: "",
9861
9862                 compare: function(/*anything*/val1, /*anything*/val2){
9863                         // summary: compare 2 values
9864                         return val1 - val2; // anything
9865                 },
9866
9867                 rangeCheck: function(/*Number*/ primitive, /*dijit.form.RangeBoundTextBox.__Constraints*/ constraints){
9868                         // summary: user replaceable function used to validate the range of the numeric input value
9869                         var isMin = "min" in constraints;
9870                         var isMax = "max" in constraints;
9871                         if(isMin || isMax){
9872                                 return (!isMin || this.compare(primitive,constraints.min) >= 0) &&
9873                                         (!isMax || this.compare(primitive,constraints.max) <= 0);
9874                         }
9875                         return true; // Boolean
9876                 },
9877
9878                 isInRange: function(/*Boolean*/ isFocused){
9879                         // summary: Need to over-ride with your own validation code in subclasses
9880                         return this.rangeCheck(this.getValue(), this.constraints);
9881                 },
9882
9883                 isValid: function(/*Boolean*/ isFocused){
9884                         return this.inherited(arguments) &&
9885                                 ((this._isEmpty(this.textbox.value) && !this.required) || this.isInRange(isFocused)); // Boolean
9886                 },
9887
9888                 getErrorMessage: function(/*Boolean*/ isFocused){
9889                         if(dijit.form.RangeBoundTextBox.superclass.isValid.call(this, false) && !this.isInRange(isFocused)){ return this.rangeMessage; } // String
9890                         return this.inherited(arguments);
9891                 },
9892
9893                 postMixInProperties: function(){
9894                         this.inherited(arguments);
9895                         if(!this.rangeMessage){
9896                                 this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
9897                                 this.rangeMessage = this.messages.rangeMessage;
9898                         }
9899                 },
9900
9901                 postCreate: function(){
9902                         this.inherited(arguments);
9903                         if(this.constraints.min !== undefined){
9904                                 dijit.setWaiState(this.focusNode, "valuemin", this.constraints.min);
9905                         }
9906                         if(this.constraints.max !== undefined){
9907                                 dijit.setWaiState(this.focusNode, "valuemax", this.constraints.max);
9908                         }
9909                 },
9910                 
9911                 setValue: function(/*Number*/ value, /*Boolean?*/ priorityChange){
9912                         dijit.setWaiState(this.focusNode, "valuenow", value);
9913                         this.inherited('setValue', arguments);
9914                 }
9915         }
9916 );
9917
9918 }
9919
9920 if(!dojo._hasResource["dijit.form.ComboBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9921 dojo._hasResource["dijit.form.ComboBox"] = true;
9922 dojo.provide("dijit.form.ComboBox");
9923
9924
9925
9926
9927 dojo.declare(
9928         "dijit.form.ComboBoxMixin",
9929         null,
9930         {
9931                 // item: Object
9932                 //              This is the item returned by the dojo.data.store implementation that
9933                 //              provides the data for this cobobox, it's the currently selected item.
9934                 item: null,
9935
9936                 // pageSize: Integer
9937                 //              Argument to data provider.
9938                 //              Specifies number of search results per page (before hitting "next" button)
9939                 pageSize: Infinity,
9940
9941                 // store: Object
9942                 //              Reference to data provider object used by this ComboBox
9943                 store: null,
9944
9945                 // query: Object
9946                 //              A query that can be passed to 'store' to initially filter the items,
9947                 //              before doing further filtering based on `searchAttr` and the key.
9948                 //              Any reference to the `searchAttr` is ignored.
9949                 query: {},
9950
9951                 // autoComplete: Boolean
9952                 //              If you type in a partial string, and then tab out of the `<input>` box,
9953                 //              automatically copy the first entry displayed in the drop down list to
9954                 //              the `<input>` field
9955                 autoComplete: true,
9956
9957                 // searchDelay: Integer
9958                 //              Delay in milliseconds between when user types something and we start
9959                 //              searching based on that value
9960                 searchDelay: 100,
9961
9962                 // searchAttr: String
9963                 //              Searches pattern match against this field
9964                 searchAttr: "name",
9965
9966                 // queryExpr: String
9967                 //              dojo.data query expression pattern.
9968                 //              `${0}` will be substituted for the user text.
9969                 //              `*` is used for wildcards.
9970                 //              `${0}*` means "starts with", `*${0}*` means "contains", `${0}` means "is"
9971                 queryExpr: "${0}*",
9972
9973                 // ignoreCase: Boolean
9974                 //              Set true if the ComboBox should ignore case when matching possible items
9975                 ignoreCase: true,
9976
9977                 // hasDownArrow: Boolean
9978                 //              Set this textbox to have a down arrow button.
9979                 //              Defaults to true.
9980                 hasDownArrow:true,
9981
9982                 templateString:"<div class=\"dijit dijitReset dijitInlineTable dijitLeft\"\n\tid=\"widget_${id}\"\n\tdojoAttachEvent=\"onmouseenter:_onMouse,onmouseleave:_onMouse,onmousedown:_onMouse\" dojoAttachPoint=\"comboNode\" waiRole=\"combobox\" tabIndex=\"-1\"\n\t><div style=\"overflow:hidden;\"\n\t\t><div class='dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton'\n\t\t\tdojoAttachPoint=\"downArrowNode\" waiRole=\"presentation\"\n\t\t\tdojoAttachEvent=\"onmousedown:_onArrowMouseDown,onmouseup:_onMouse,onmouseenter:_onMouse,onmouseleave:_onMouse\"\n\t\t\t><div class=\"dijitArrowButtonInner\">&thinsp;</div\n\t\t\t><div class=\"dijitArrowButtonChar\">&#9660;</div\n\t\t></div\n\t\t><div class=\"dijitReset dijitValidationIcon\"><br></div\n\t\t><div class=\"dijitReset dijitValidationIconText\">&Chi;</div\n\t\t><div class=\"dijitReset dijitInputField\"\n\t\t\t><input type=\"text\" autocomplete=\"off\" name=\"${name}\" class='dijitReset'\n\t\t\tdojoAttachEvent=\"onkeypress:_onKeyPress, onfocus:_update, compositionend,onkeyup\"\n\t\t\tdojoAttachPoint=\"textbox,focusNode\" waiRole=\"textbox\" waiState=\"haspopup-true,autocomplete-list\"\n\t\t/></div\n\t></div\n></div>\n",
9983
9984                 baseClass:"dijitComboBox",
9985
9986                 _getCaretPos: function(/*DomNode*/ element){
9987                         // khtml 3.5.2 has selection* methods as does webkit nightlies from 2005-06-22
9988                         var pos = 0;
9989                         if(typeof(element.selectionStart)=="number"){
9990                                 // FIXME: this is totally borked on Moz < 1.3. Any recourse?
9991                                 pos = element.selectionStart;
9992                         }else if(dojo.isIE){
9993                                 // in the case of a mouse click in a popup being handled,
9994                                 // then the dojo.doc.selection is not the textarea, but the popup
9995                                 // var r = dojo.doc.selection.createRange();
9996                                 // hack to get IE 6 to play nice. What a POS browser.
9997                                 var tr = dojo.doc.selection.createRange().duplicate();
9998                                 var ntr = element.createTextRange();
9999                                 tr.move("character",0);
10000                                 ntr.move("character",0);
10001                                 try{
10002                                         // If control doesnt have focus, you get an exception.
10003                                         // Seems to happen on reverse-tab, but can also happen on tab (seems to be a race condition - only happens sometimes).
10004                                         // There appears to be no workaround for this - googled for quite a while.
10005                                         ntr.setEndPoint("EndToEnd", tr);
10006                                         pos = String(ntr.text).replace(/\r/g,"").length;
10007                                 }catch(e){
10008                                         // If focus has shifted, 0 is fine for caret pos.
10009                                 }
10010                         }
10011                         return pos;
10012                 },
10013
10014                 _setCaretPos: function(/*DomNode*/ element, /*Number*/ location){
10015                         location = parseInt(location);
10016                         dijit.selectInputText(element, location, location);
10017                 },
10018
10019                 _setAttribute: function(/*String*/ attr, /*anything*/ value){
10020                         // summary: additional code to set disablbed state of combobox node
10021                         if (attr == "disabled"){
10022                                 dijit.setWaiState(this.comboNode, "disabled", value);
10023                         }
10024                 },      
10025                 
10026                 _onKeyPress: function(/*Event*/ evt){
10027                         // summary: handles keyboard events
10028
10029                         //except for pasting case - ctrl + v(118)
10030                         if(evt.altKey || (evt.ctrlKey && evt.charCode != 118)){
10031                                 return;
10032                         }
10033                         var doSearch = false;
10034                         var pw = this._popupWidget;
10035                         var dk = dojo.keys;
10036                         if(this._isShowingNow){
10037                                 pw.handleKey(evt);
10038                         }
10039                         switch(evt.keyCode){
10040                                 case dk.PAGE_DOWN:
10041                                 case dk.DOWN_ARROW:
10042                                         if(!this._isShowingNow||this._prev_key_esc){
10043                                                 this._arrowPressed();
10044                                                 doSearch=true;
10045                                         }else{
10046                                                 this._announceOption(pw.getHighlightedOption());
10047                                         }
10048                                         dojo.stopEvent(evt);
10049                                         this._prev_key_backspace = false;
10050                                         this._prev_key_esc = false;
10051                                         break;
10052
10053                                 case dk.PAGE_UP:
10054                                 case dk.UP_ARROW:
10055                                         if(this._isShowingNow){
10056                                                 this._announceOption(pw.getHighlightedOption());
10057                                         }
10058                                         dojo.stopEvent(evt);
10059                                         this._prev_key_backspace = false;
10060                                         this._prev_key_esc = false;
10061                                         break;
10062
10063                                 case dk.ENTER:
10064                                         // prevent submitting form if user presses enter. Also
10065                                         // prevent accepting the value if either Next or Previous
10066                                         // are selected
10067                                         var highlighted;
10068                                         if(     this._isShowingNow && 
10069                                                 (highlighted = pw.getHighlightedOption())
10070                                         ){
10071                                                 // only stop event on prev/next
10072                                                 if(highlighted == pw.nextButton){
10073                                                         this._nextSearch(1);
10074                                                         dojo.stopEvent(evt);
10075                                                         break;
10076                                                 }else if(highlighted == pw.previousButton){
10077                                                         this._nextSearch(-1);
10078                                                         dojo.stopEvent(evt);
10079                                                         break;
10080                                                 }
10081                                         }else{
10082                                                 this.setDisplayedValue(this.getDisplayedValue());
10083                                         }
10084                                         // default case:
10085                                         // prevent submit, but allow event to bubble
10086                                         evt.preventDefault();
10087                                         // fall through
10088
10089                                 case dk.TAB:
10090                                         var newvalue = this.getDisplayedValue();
10091                                         // #4617: 
10092                                         //              if the user had More Choices selected fall into the
10093                                         //              _onBlur handler
10094                                         if(pw && (
10095                                                 newvalue == pw._messages["previousMessage"] ||
10096                                                 newvalue == pw._messages["nextMessage"])
10097                                         ){
10098                                                 break;
10099                                         }
10100                                         if(this._isShowingNow){
10101                                                 this._prev_key_backspace = false;
10102                                                 this._prev_key_esc = false;
10103                                                 if(pw.getHighlightedOption()){
10104                                                         pw.setValue({ target: pw.getHighlightedOption() }, true);
10105                                                 }
10106                                                 this._hideResultList();
10107                                         }
10108                                         break;
10109
10110                                 case dk.SPACE:
10111                                         this._prev_key_backspace = false;
10112                                         this._prev_key_esc = false;
10113                                         if(this._isShowingNow && pw.getHighlightedOption()){
10114                                                 dojo.stopEvent(evt);
10115                                                 this._selectOption();
10116                                                 this._hideResultList();
10117                                         }else{
10118                                                 doSearch = true;
10119                                         }
10120                                         break;
10121
10122                                 case dk.ESCAPE:
10123                                         this._prev_key_backspace = false;
10124                                         this._prev_key_esc = true;
10125                                         if(this._isShowingNow){
10126                                                 dojo.stopEvent(evt);
10127                                                 this._hideResultList();
10128                                         }
10129                                         this.inherited(arguments);
10130                                         break;
10131
10132                                 case dk.DELETE:
10133                                 case dk.BACKSPACE:
10134                                         this._prev_key_esc = false;
10135                                         this._prev_key_backspace = true;
10136                                         doSearch = true;
10137                                         break;
10138
10139                                 case dk.RIGHT_ARROW: // fall through
10140                                 case dk.LEFT_ARROW: 
10141                                         this._prev_key_backspace = false;
10142                                         this._prev_key_esc = false;
10143                                         break;
10144
10145                                 default: // non char keys (F1-F12 etc..)  shouldn't open list
10146                                         this._prev_key_backspace = false;
10147                                         this._prev_key_esc = false;
10148                                         if(dojo.isIE || evt.charCode != 0){
10149                                                 doSearch = true;
10150                                         }
10151                         }
10152                         if(this.searchTimer){
10153                                 clearTimeout(this.searchTimer);
10154                         }
10155                         if(doSearch){
10156                                 // need to wait a tad before start search so that the event
10157                                 // bubbles through DOM and we have value visible
10158                                 setTimeout(dojo.hitch(this, "_startSearchFromInput"),1);
10159                         }
10160                 },
10161
10162                 _autoCompleteText: function(/*String*/ text){
10163                         // summary:
10164                         //              Fill in the textbox with the first item from the drop down
10165                         //              list, and highlight the characters that were
10166                         //              auto-completed. For example, if user typed "CA" and the
10167                         //              drop down list appeared, the textbox would be changed to
10168                         //              "California" and "ifornia" would be highlighted.
10169
10170                         var fn = this.focusNode;
10171
10172                         // IE7: clear selection so next highlight works all the time
10173                         dijit.selectInputText(fn, fn.value.length);
10174                         // does text autoComplete the value in the textbox?
10175                         var caseFilter = this.ignoreCase? 'toLowerCase' : 'substr';
10176                         if(text[caseFilter](0).indexOf(this.focusNode.value[caseFilter](0)) == 0){
10177                                 var cpos = this._getCaretPos(fn);
10178                                 // only try to extend if we added the last character at the end of the input
10179                                 if((cpos+1) > fn.value.length){
10180                                         // only add to input node as we would overwrite Capitalisation of chars
10181                                         // actually, that is ok
10182                                         fn.value = text;//.substr(cpos);
10183                                         // visually highlight the autocompleted characters
10184                                         dijit.selectInputText(fn, cpos);
10185                                 }
10186                         }else{
10187                                 // text does not autoComplete; replace the whole value and highlight
10188                                 fn.value = text;
10189                                 dijit.selectInputText(fn);
10190                         }
10191                 },
10192
10193                 _openResultList: function(/*Object*/ results, /*Object*/ dataObject){
10194                         if(     this.disabled || 
10195                                 this.readOnly || 
10196                                 (dataObject.query[this.searchAttr] != this._lastQuery)
10197                         ){
10198                                 return;
10199                         }
10200                         this._popupWidget.clearResultList();
10201                         if(!results.length){
10202                                 this._hideResultList();
10203                                 return;
10204                         }
10205
10206                         // Fill in the textbox with the first item from the drop down list,
10207                         // and highlight the characters that were auto-completed. For
10208                         // example, if user typed "CA" and the drop down list appeared, the
10209                         // textbox would be changed to "California" and "ifornia" would be
10210                         // highlighted.
10211
10212                         var zerothvalue = new String(this.store.getValue(results[0], this.searchAttr));
10213                         if(zerothvalue && this.autoComplete && !this._prev_key_backspace &&
10214                                 (dataObject.query[this.searchAttr] != "*")){
10215                                 // when the user clicks the arrow button to show the full list,
10216                                 // startSearch looks for "*".
10217                                 // it does not make sense to autocomplete
10218                                 // if they are just previewing the options available.
10219                                 this._autoCompleteText(zerothvalue);
10220                         }
10221                         this._popupWidget.createOptions(
10222                                 results, 
10223                                 dataObject, 
10224                                 dojo.hitch(this, "_getMenuLabelFromItem")
10225                         );
10226
10227                         // show our list (only if we have content, else nothing)
10228                         this._showResultList();
10229
10230                         // #4091:
10231                         //              tell the screen reader that the paging callback finished by
10232                         //              shouting the next choice
10233                         if(dataObject.direction){
10234                                 if(1 == dataObject.direction){
10235                                         this._popupWidget.highlightFirstOption();
10236                                 }else if(-1 == dataObject.direction){
10237                                         this._popupWidget.highlightLastOption();
10238                                 }
10239                                 this._announceOption(this._popupWidget.getHighlightedOption());
10240                         }
10241                 },
10242
10243                 _showResultList: function(){
10244                         this._hideResultList();
10245                         var items = this._popupWidget.getItems(),
10246                                 visibleCount = Math.min(items.length,this.maxListLength);
10247                         this._arrowPressed();
10248                         // hide the tooltip
10249                         this.displayMessage("");
10250                         
10251                         // Position the list and if it's too big to fit on the screen then
10252                         // size it to the maximum possible height
10253                         // Our dear friend IE doesnt take max-height so we need to
10254                         // calculate that on our own every time
10255
10256                         // TODO: want to redo this, see 
10257                         //              http://trac.dojotoolkit.org/ticket/3272
10258                         //      and
10259                         //              http://trac.dojotoolkit.org/ticket/4108
10260
10261                         with(this._popupWidget.domNode.style){
10262                                 // natural size of the list has changed, so erase old
10263                                 // width/height settings, which were hardcoded in a previous
10264                                 // call to this function (via dojo.marginBox() call) 
10265                                 width = "";
10266                                 height = "";
10267                         }
10268                         var best = this.open();
10269                         // #3212:
10270                         //              only set auto scroll bars if necessary prevents issues with
10271                         //              scroll bars appearing when they shouldn't when node is made
10272                         //              wider (fractional pixels cause this)
10273                         var popupbox = dojo.marginBox(this._popupWidget.domNode);
10274                         this._popupWidget.domNode.style.overflow = 
10275                                 ((best.h==popupbox.h)&&(best.w==popupbox.w)) ? "hidden" : "auto";
10276                         // #4134:
10277                         //              borrow TextArea scrollbar test so content isn't covered by
10278                         //              scrollbar and horizontal scrollbar doesn't appear
10279                         var newwidth = best.w;
10280                         if(best.h < this._popupWidget.domNode.scrollHeight){
10281                                 newwidth += 16;
10282                         }
10283                         dojo.marginBox(this._popupWidget.domNode, {
10284                                 h: best.h,
10285                                 w: Math.max(newwidth, this.domNode.offsetWidth)
10286                         });
10287                         dijit.setWaiState(this.comboNode, "expanded", "true");
10288                 },
10289
10290                 _hideResultList: function(){
10291                         if(this._isShowingNow){
10292                                 dijit.popup.close(this._popupWidget);
10293                                 this._arrowIdle();
10294                                 this._isShowingNow=false;
10295                                 dijit.setWaiState(this.comboNode, "expanded", "false");
10296                                 dijit.removeWaiState(this.focusNode,"activedescendant");
10297                         }
10298                 },
10299
10300                 _setBlurValue: function(){
10301                         // if the user clicks away from the textbox OR tabs away, set the
10302                         // value to the textbox value
10303                         // #4617: 
10304                         //              if value is now more choices or previous choices, revert
10305                         //              the value
10306                         var newvalue=this.getDisplayedValue();
10307                         var pw = this._popupWidget;
10308                         if(pw && (
10309                                 newvalue == pw._messages["previousMessage"] ||
10310                                 newvalue == pw._messages["nextMessage"]
10311                                 )
10312                         ){
10313                                 this.setValue(this._lastValueReported, true);
10314                         }else{
10315                                 this.setDisplayedValue(newvalue);
10316                         }
10317                 },
10318
10319                 _onBlur: function(){
10320                         // summary: called magically when focus has shifted away from this widget and it's dropdown
10321                         this._hideResultList();
10322                         this._arrowIdle();
10323                         this.inherited(arguments);
10324                 },
10325
10326                 _announceOption: function(/*Node*/ node){
10327                         // summary:
10328                         //              a11y code that puts the highlighted option in the textbox
10329                         //              This way screen readers will know what is happening in the
10330                         //              menu
10331
10332                         if(node == null){
10333                                 return;
10334                         }
10335                         // pull the text value from the item attached to the DOM node
10336                         var newValue;
10337                         if( node == this._popupWidget.nextButton ||
10338                                 node == this._popupWidget.previousButton){
10339                                 newValue = node.innerHTML;
10340                         }else{
10341                                 newValue = this.store.getValue(node.item, this.searchAttr);
10342                         }
10343                         // get the text that the user manually entered (cut off autocompleted text)
10344                         this.focusNode.value = this.focusNode.value.substring(0, this._getCaretPos(this.focusNode));
10345                         //set up ARIA activedescendant
10346                         dijit.setWaiState(this.focusNode, "activedescendant", dojo.attr(node, "id")); 
10347                         // autocomplete the rest of the option to announce change
10348                         this._autoCompleteText(newValue);
10349                 },
10350
10351                 _selectOption: function(/*Event*/ evt){
10352                         var tgt = null;
10353                         if(!evt){
10354                                 evt ={ target: this._popupWidget.getHighlightedOption()};
10355                         }
10356                                 // what if nothing is highlighted yet?
10357                         if(!evt.target){
10358                                 // handle autocompletion where the the user has hit ENTER or TAB
10359                                 this.setDisplayedValue(this.getDisplayedValue());
10360                                 return;
10361                         // otherwise the user has accepted the autocompleted value
10362                         }else{
10363                                 tgt = evt.target;
10364                         }
10365                         if(!evt.noHide){
10366                                 this._hideResultList();
10367                                 this._setCaretPos(this.focusNode, this.store.getValue(tgt.item, this.searchAttr).length);
10368                         }
10369                         this._doSelect(tgt);
10370                 },
10371
10372                 _doSelect: function(tgt){
10373                         this.item = tgt.item;
10374                         this.setValue(this.store.getValue(tgt.item, this.searchAttr), true);
10375                 },
10376
10377                 _onArrowMouseDown: function(evt){
10378                         // summary: callback when arrow is clicked
10379                         if(this.disabled || this.readOnly){
10380                                 return;
10381                         }
10382                         dojo.stopEvent(evt);
10383                         this.focus();
10384                         if(this._isShowingNow){
10385                                 this._hideResultList();
10386                         }else{
10387                                 // forces full population of results, if they click
10388                                 // on the arrow it means they want to see more options
10389                                 this._startSearch("");
10390                         }
10391                 },
10392
10393                 _startSearchFromInput: function(){
10394                         this._startSearch(this.focusNode.value);
10395                 },
10396
10397                 _getQueryString: function(/*String*/ text){
10398                         return dojo.string.substitute(this.queryExpr, [text]);
10399                 },
10400
10401                 _startSearch: function(/*String*/ key){
10402                         if(!this._popupWidget){
10403                                 var popupId = this.id + "_popup";
10404                                 this._popupWidget = new dijit.form._ComboBoxMenu({
10405                                         onChange: dojo.hitch(this, this._selectOption),
10406                                         id:popupId
10407                                 });
10408                                 dijit.removeWaiState(this.focusNode,"activedescendant");
10409                                 dijit.setWaiState(this.textbox,"owns",popupId); // associate popup with textbox
10410                         }
10411                         // create a new query to prevent accidentally querying for a hidden
10412                         // value from FilteringSelect's keyField
10413                         this.item = null; // #4872
10414                         var query = dojo.clone(this.query); // #5970
10415                         this._lastQuery = query[this.searchAttr] = this._getQueryString(key);
10416                         // #5970: set _lastQuery, *then* start the timeout
10417                         // otherwise, if the user types and the last query returns before the timeout,
10418                         // _lastQuery won't be set and their input gets rewritten
10419                         this.searchTimer=setTimeout(dojo.hitch(this, function(query, _this){
10420                                 var dataObject = this.store.fetch({
10421                                         queryOptions: {
10422                                                 ignoreCase: this.ignoreCase, 
10423                                                 deep: true
10424                                         },
10425                                         query: query,
10426                                         onComplete: dojo.hitch(this, "_openResultList"), 
10427                                         onError: function(errText){
10428                                                 console.error('dijit.form.ComboBox: ' + errText);
10429                                                 dojo.hitch(_this, "_hideResultList")();
10430                                         },
10431                                         start:0,
10432                                         count:this.pageSize
10433                                 });
10434
10435                                 var nextSearch = function(dataObject, direction){
10436                                         dataObject.start += dataObject.count*direction;
10437                                         // #4091:
10438                                         //              tell callback the direction of the paging so the screen
10439                                         //              reader knows which menu option to shout
10440                                         dataObject.direction = direction;
10441                                         this.store.fetch(dataObject);
10442                                 }
10443                                 this._nextSearch = this._popupWidget.onPage = dojo.hitch(this, nextSearch, dataObject);
10444                         }, query, this), this.searchDelay);
10445                 },
10446
10447                 _getValueField:function(){
10448                         return this.searchAttr;
10449                 },
10450
10451                 /////////////// Event handlers /////////////////////
10452
10453                 _arrowPressed: function(){
10454                         if(!this.disabled && !this.readOnly && this.hasDownArrow){
10455                                 dojo.addClass(this.downArrowNode, "dijitArrowButtonActive");
10456                         }
10457                 },
10458
10459                 _arrowIdle: function(){
10460                         if(!this.disabled && !this.readOnly && this.hasDownArrow){
10461                                 dojo.removeClass(this.downArrowNode, "dojoArrowButtonPushed");
10462                         }
10463                 },
10464
10465                 // FIXME: 
10466                 //              this is public so we can't remove until 2.0, but the name
10467                 //              SHOULD be "compositionEnd"
10468
10469                 compositionend: function(/*Event*/ evt){
10470                         //      summary:
10471                         //              When inputting characters using an input method, such as
10472                         //              Asian languages, it will generate this event instead of
10473                         //              onKeyDown event Note: this event is only triggered in FF
10474                         //              (not in IE)
10475                         this.onkeypress({charCode:-1});
10476                 },
10477
10478                 //////////// INITIALIZATION METHODS ///////////////////////////////////////
10479
10480                 constructor: function(){
10481                         this.query={};
10482                 },
10483
10484                 postMixInProperties: function(){
10485                         if(!this.hasDownArrow){
10486                                 this.baseClass = "dijitTextBox";
10487                         }
10488                         if(!this.store){
10489                                 var srcNodeRef = this.srcNodeRef;
10490
10491                                 // if user didn't specify store, then assume there are option tags
10492                                 this.store = new dijit.form._ComboBoxDataStore(srcNodeRef);
10493
10494                                 // if there is no value set and there is an option list, set
10495                                 // the value to the first value to be consistent with native
10496                                 // Select
10497
10498                                 // Firefox and Safari set value
10499                                 // IE6 and Opera set selectedIndex, which is automatically set
10500                                 // by the selected attribute of an option tag
10501                                 // IE6 does not set value, Opera sets value = selectedIndex
10502                                 if(     !this.value || (
10503                                                 (typeof srcNodeRef.selectedIndex == "number") && 
10504                                                 srcNodeRef.selectedIndex.toString() === this.value)
10505                                 ){
10506                                         var item = this.store.fetchSelectedItem();
10507                                         if(item){
10508                                                 this.value = this.store.getValue(item, this._getValueField());
10509                                         }
10510                                 }
10511                         }
10512                 },
10513                 
10514                 _postCreate:function(){
10515                         //find any associated label element and add to combobox node.
10516                         var label=dojo.query('label[for="'+this.id+'"]');
10517                         if(label.length){
10518                                 label[0].id = (this.id+"_label");
10519                                 var cn=this.comboNode;
10520                                 dijit.setWaiState(cn, "labelledby", label[0].id);
10521                                 dijit.setWaiState(cn, "disabled", this.disabled);
10522                                 
10523                         }
10524                 },
10525
10526                 uninitialize:function(){
10527                         if(this._popupWidget){
10528                                 this._hideResultList();
10529                                 this._popupWidget.destroy()
10530                         }
10531                 },
10532
10533                 _getMenuLabelFromItem:function(/*Item*/ item){
10534                         return {
10535                                 html: false, 
10536                                 label: this.store.getValue(item, this.searchAttr)
10537                         };
10538                 },
10539
10540                 open:function(){
10541                         this._isShowingNow=true;
10542                         return dijit.popup.open({
10543                                 popup: this._popupWidget,
10544                                 around: this.domNode,
10545                                 parent: this
10546                         });
10547                 },
10548                 
10549                 reset:function(){
10550                         //      summary:
10551                         //              Additionally reset the .item (to clean up).
10552                         this.item = null;
10553                         this.inherited(arguments);
10554                 }
10555                 
10556         }
10557 );
10558
10559 dojo.declare(
10560         "dijit.form._ComboBoxMenu",
10561         [dijit._Widget, dijit._Templated],
10562
10563         {
10564                 //      summary:
10565                 //              Focus-less div based menu for internal use in ComboBox
10566
10567                 templateString: "<ul class='dijitMenu' dojoAttachEvent='onmousedown:_onMouseDown,onmouseup:_onMouseUp,onmouseover:_onMouseOver,onmouseout:_onMouseOut' tabIndex='-1' style='overflow:\"auto\";'>"
10568                                 +"<li class='dijitMenuItem dijitMenuPreviousButton' dojoAttachPoint='previousButton'></li>"
10569                                 +"<li class='dijitMenuItem dijitMenuNextButton' dojoAttachPoint='nextButton'></li>"
10570                         +"</ul>",
10571                 _messages: null,
10572
10573                 postMixInProperties: function(){
10574                         this._messages = dojo.i18n.getLocalization("dijit.form", "ComboBox", this.lang);
10575                         this.inherited("postMixInProperties", arguments);
10576                 },
10577
10578                 setValue: function(/*Object*/ value){
10579                         this.value = value;
10580                         this.onChange(value);
10581                 },
10582
10583                 // stubs
10584                 onChange: function(/*Object*/ value){},
10585                 onPage: function(/*Number*/ direction){},
10586
10587                 postCreate:function(){
10588                         // fill in template with i18n messages
10589                         this.previousButton.innerHTML = this._messages["previousMessage"];
10590                         this.nextButton.innerHTML = this._messages["nextMessage"];
10591                         this.inherited("postCreate", arguments);
10592                 },
10593
10594                 onClose:function(){
10595                         this._blurOptionNode();
10596                 },
10597
10598                 _createOption:function(/*Object*/ item, labelFunc){
10599                         //      summary: 
10600                         //              creates an option to appear on the popup menu subclassed by
10601                         //              FilteringSelect
10602
10603                         var labelObject = labelFunc(item);
10604                         var menuitem = dojo.doc.createElement("li");
10605                         dijit.setWaiRole(menuitem, "option");
10606                         if(labelObject.html){
10607                                 menuitem.innerHTML = labelObject.label;
10608                         }else{
10609                                 menuitem.appendChild(
10610                                         dojo.doc.createTextNode(labelObject.label)
10611                                 );
10612                         }
10613                         // #3250: in blank options, assign a normal height
10614                         if(menuitem.innerHTML == ""){
10615                                 menuitem.innerHTML = "&nbsp;";
10616                         }
10617                         menuitem.item=item;
10618                         return menuitem;
10619                 },
10620
10621                 createOptions: function(results, dataObject, labelFunc){
10622                         //this._dataObject=dataObject;
10623                         //this._dataObject.onComplete=dojo.hitch(comboBox, comboBox._openResultList);
10624                         // display "Previous . . ." button
10625                         this.previousButton.style.display = (dataObject.start == 0) ? "none" : "";
10626                         dojo.attr(this.previousButton, "id", this.id + "_prev");
10627                         // create options using _createOption function defined by parent
10628                         // ComboBox (or FilteringSelect) class
10629                         // #2309:
10630                         //              iterate over cache nondestructively
10631                         dojo.forEach(results, function(item, i){
10632                                 var menuitem = this._createOption(item, labelFunc);
10633                                 menuitem.className = "dijitMenuItem";
10634                                 dojo.attr(menuitem, "id", this.id + i);
10635                                 this.domNode.insertBefore(menuitem, this.nextButton);
10636                         }, this);
10637                         // display "Next . . ." button
10638                         this.nextButton.style.display = (dataObject.count == results.length) ? "" : "none";
10639                         dojo.attr(this.nextButton,"id", this.id + "_next")
10640                 },
10641
10642                 clearResultList: function(){
10643                         // keep the previous and next buttons of course
10644                         while(this.domNode.childNodes.length>2){
10645                                 this.domNode.removeChild(this.domNode.childNodes[this.domNode.childNodes.length-2]);
10646                         }
10647                 },
10648
10649                 // these functions are called in showResultList
10650                 getItems: function(){
10651                         return this.domNode.childNodes;
10652                 },
10653
10654                 getListLength: function(){
10655                         return this.domNode.childNodes.length-2;
10656                 },
10657
10658                 _onMouseDown: function(/*Event*/ evt){
10659                         dojo.stopEvent(evt);
10660                 },
10661
10662                 _onMouseUp: function(/*Event*/ evt){
10663                         if(evt.target === this.domNode){
10664                                 return;
10665                         }else if(evt.target==this.previousButton){
10666                                 this.onPage(-1);
10667                         }else if(evt.target==this.nextButton){
10668                                 this.onPage(1);
10669                         }else{
10670                                 var tgt = evt.target;
10671                                 // while the clicked node is inside the div
10672                                 while(!tgt.item){
10673                                         // recurse to the top
10674                                         tgt = tgt.parentNode;
10675                                 }
10676                                 this.setValue({ target: tgt }, true);
10677                         }
10678                 },
10679
10680                 _onMouseOver: function(/*Event*/ evt){
10681                         if(evt.target === this.domNode){ return; }
10682                         var tgt = evt.target;
10683                         if(!(tgt == this.previousButton || tgt == this.nextButton)){
10684                                 // while the clicked node is inside the div
10685                                 while(!tgt.item){
10686                                         // recurse to the top
10687                                         tgt = tgt.parentNode;
10688                                 }
10689                         }
10690                         this._focusOptionNode(tgt);
10691                 },
10692
10693                 _onMouseOut:function(/*Event*/ evt){
10694                         if(evt.target === this.domNode){ return; }
10695                         this._blurOptionNode();
10696                 },
10697
10698                 _focusOptionNode:function(/*DomNode*/ node){
10699                         // summary:
10700                         //      does the actual highlight
10701                         if(this._highlighted_option != node){
10702                                 this._blurOptionNode();
10703                                 this._highlighted_option = node;
10704                                 dojo.addClass(this._highlighted_option, "dijitMenuItemHover");
10705                         }
10706                 },
10707
10708                 _blurOptionNode:function(){
10709                         // summary:
10710                         //      removes highlight on highlighted option
10711                         if(this._highlighted_option){
10712                                 dojo.removeClass(this._highlighted_option, "dijitMenuItemHover");
10713                                 this._highlighted_option = null;
10714                         }
10715                 },
10716
10717                 _highlightNextOption:function(){
10718                         //      summary:
10719                         //              Highlight the item just below the current selection.
10720                         //              If nothing selected, highlight first option
10721
10722                         // because each press of a button clears the menu,
10723                         // the highlighted option sometimes becomes detached from the menu!
10724                         // test to see if the option has a parent to see if this is the case.
10725                         var fc = this.domNode.firstChild;
10726                         if(!this.getHighlightedOption()){
10727                                 this._focusOptionNode(fc.style.display=="none" ? fc.nextSibling : fc);
10728                         }else{
10729                                 var ns = this._highlighted_option.nextSibling;
10730                                 if(ns && ns.style.display!="none"){
10731                                         this._focusOptionNode(ns);
10732                                 }
10733                         }
10734                         // scrollIntoView is called outside of _focusOptionNode because in IE putting it inside causes the menu to scroll up on mouseover
10735                         dijit.scrollIntoView(this._highlighted_option);
10736                 },
10737
10738                 highlightFirstOption:function(){
10739                         //      summary:
10740                         //              Highlight the first real item in the list (not Previous Choices).
10741                         this._focusOptionNode(this.domNode.firstChild.nextSibling);
10742                         dijit.scrollIntoView(this._highlighted_option);
10743                 },
10744
10745                 highlightLastOption:function(){
10746                         //      summary:
10747                         //              Highlight the last real item in the list (not More Choices).
10748                         this._focusOptionNode(this.domNode.lastChild.previousSibling);
10749                         dijit.scrollIntoView(this._highlighted_option);
10750                 },
10751
10752                 _highlightPrevOption:function(){
10753                         //      summary:
10754                         //              Highlight the item just above the current selection.
10755                         //              If nothing selected, highlight last option (if
10756                         //              you select Previous and try to keep scrolling up the list)
10757                         var lc = this.domNode.lastChild;
10758                         if(!this.getHighlightedOption()){
10759                                 this._focusOptionNode(lc.style.display == "none" ? lc.previousSibling : lc);
10760                         }else{
10761                                 var ps = this._highlighted_option.previousSibling;
10762                                 if(ps && ps.style.display != "none"){
10763                                         this._focusOptionNode(ps);
10764                                 }
10765                         }
10766                         dijit.scrollIntoView(this._highlighted_option);
10767                 },
10768
10769                 _page:function(/*Boolean*/ up){
10770                         var scrollamount = 0;
10771                         var oldscroll = this.domNode.scrollTop;
10772                         var height = dojo.style(this.domNode, "height");
10773                         // if no item is highlighted, highlight the first option
10774                         if(!this.getHighlightedOption()){
10775                                 this._highlightNextOption();
10776                         }
10777                         while(scrollamount<height){
10778                                 if(up){
10779                                         // stop at option 1
10780                                         if(!this.getHighlightedOption().previousSibling ||
10781                                                 this._highlighted_option.previousSibling.style.display == "none"){
10782                                                 break;
10783                                         }
10784                                         this._highlightPrevOption();
10785                                 }else{
10786                                         // stop at last option
10787                                         if(!this.getHighlightedOption().nextSibling ||
10788                                                 this._highlighted_option.nextSibling.style.display == "none"){
10789                                                 break;
10790                                         }
10791                                         this._highlightNextOption();
10792                                 }
10793                                 // going backwards
10794                                 var newscroll=this.domNode.scrollTop;
10795                                 scrollamount+=(newscroll-oldscroll)*(up ? -1:1);
10796                                 oldscroll=newscroll;
10797                         }
10798                 },
10799
10800                 pageUp: function(){ this._page(true); },
10801
10802                 pageDown: function(){ this._page(false); },
10803
10804                 getHighlightedOption: function(){
10805                         //      summary:
10806                         //              Returns the highlighted option.
10807                         var ho = this._highlighted_option;
10808                         return (ho && ho.parentNode) ? ho : null;
10809                 },
10810
10811                 handleKey: function(evt){
10812                         switch(evt.keyCode){
10813                                 case dojo.keys.DOWN_ARROW:
10814                                         this._highlightNextOption();
10815                                         break;
10816                                 case dojo.keys.PAGE_DOWN:
10817                                         this.pageDown();
10818                                         break;  
10819                                 case dojo.keys.UP_ARROW:
10820                                         this._highlightPrevOption();
10821                                         break;
10822                                 case dojo.keys.PAGE_UP:
10823                                         this.pageUp();
10824                                         break;  
10825                         }
10826                 }
10827         }
10828 );
10829
10830 dojo.declare(
10831         "dijit.form.ComboBox",
10832         [dijit.form.ValidationTextBox, dijit.form.ComboBoxMixin],
10833         {
10834                 //      summary:
10835                 //              Auto-completing text box, and base class for dijit.form.FilteringSelect.
10836                 // 
10837                 //      description:
10838                 //              The drop down box's values are populated from an class called
10839                 //              a data provider, which returns a list of values based on the characters
10840                 //              that the user has typed into the input box.
10841                 // 
10842                 //              Some of the options to the ComboBox are actually arguments to the data
10843                 //              provider.
10844                 // 
10845                 //              You can assume that all the form widgets (and thus anything that mixes
10846                 //              in dijit.formComboBoxMixin) will inherit from dijit.form._FormWidget and thus the `this`
10847                 //              reference will also "be a" _FormWidget.
10848
10849                 postMixInProperties: function(){
10850                         // this.inherited(arguments); // ??
10851                         dijit.form.ComboBoxMixin.prototype.postMixInProperties.apply(this, arguments);
10852                         dijit.form.ValidationTextBox.prototype.postMixInProperties.apply(this, arguments);
10853                 },
10854
10855                 postCreate: function(){
10856                         dijit.form.ComboBoxMixin.prototype._postCreate.apply(this, arguments);
10857                         dijit.form.ValidationTextBox.prototype.postCreate.apply(this, arguments);
10858                 },
10859                 setAttribute: function(/*String*/ attr, /*anything*/ value){
10860                         dijit.form.ValidationTextBox.prototype.setAttribute.apply(this, arguments);
10861                         dijit.form.ComboBoxMixin.prototype._setAttribute.apply(this, arguments);
10862                 }
10863                 
10864         }
10865 );
10866
10867 dojo.declare("dijit.form._ComboBoxDataStore", null, {
10868         //      summary:
10869         //              Inefficient but small data store specialized for inlined ComboBox data
10870         //
10871         //      description:
10872         //              Provides a store for inlined data like:
10873         //
10874         //      |       <select>
10875         //      |               <option value="AL">Alabama</option>
10876         //      |               ...
10877         //
10878         //              Actually. just implements the subset of dojo.data.Read/Notification
10879         //              needed for ComboBox and FilteringSelect to work.
10880         //
10881         //              Note that an item is just a pointer to the <option> DomNode.
10882
10883         constructor: function( /*DomNode*/ root){
10884                 this.root = root;
10885 /*
10886                 //      TODO: this was added in #3858 but unclear why/if it's needed;  doesn't seem to be.
10887                 //      If it is needed then can we just hide the select itself instead?
10888                 dojo.query("> option", root).forEach(function(node){
10889                         node.style.display="none";
10890                 });
10891 */
10892         },
10893
10894         getValue: function(     /* item */ item, 
10895                                                 /* attribute-name-string */ attribute, 
10896                                                 /* value? */ defaultValue){
10897                 return (attribute == "value") ? item.value : (item.innerText || item.textContent || '');
10898         },
10899
10900         isItemLoaded: function(/* anything */ something) {
10901                 return true;
10902         },
10903
10904         fetch: function(/* Object */ args){
10905                 //      summary:
10906                 //              Given a query and set of defined options, such as a start and count of items to return,
10907                 //              this method executes the query and makes the results available as data items.
10908                 //              Refer to dojo.data.api.Read.fetch() more details.
10909                 //
10910                 //      description:
10911                 //              Given a query like
10912                 //
10913                 //      |       {
10914                 //      |               query: {name: "Cal*"},
10915                 //      |               start: 30,
10916                 //      |               count: 20,
10917                 //      |               ignoreCase: true,
10918                 //      |               onComplete: function(/* item[] */ items, /* Object */ args){...}
10919                 //      |       }
10920                 //
10921                 //              will call `onComplete()` with the results of the query (and the argument to this method)
10922
10923                 // convert query to regex (ex: convert "first\last*" to /^first\\last.*$/i) and get matching vals
10924                 var query = "^" + args.query.name
10925                                 .replace(/([\\\|\(\)\[\{\^\$\+\?\.\<\>])/g, "\\$1")
10926                                 .replace("*", ".*") + "$",
10927                         matcher = new RegExp(query, args.queryOptions.ignoreCase ? "i" : ""),
10928                         items = dojo.query("> option", this.root).filter(function(option){
10929                                 return (option.innerText || option.textContent || '').match(matcher);
10930                         } );
10931
10932                 var start = args.start || 0,
10933                         end = ("count" in args && args.count != Infinity) ? (start + args.count) : items.length ;
10934                 args.onComplete(items.slice(start, end), args);
10935                 return args; // Object
10936                 // TODO: I don't need to return the length?
10937         },
10938
10939         close: function(/*dojo.data.api.Request || args || null */ request){
10940                 return;
10941         },
10942
10943         getLabel: function(/* item */ item){
10944                 return item.innerHTML;
10945         },
10946
10947         getIdentity: function(/* item */ item){
10948                 return dojo.attr(item, "value");
10949         },
10950
10951         fetchItemByIdentity: function(/* Object */ args){
10952                 //      summary:
10953                 //              Given the identity of an item, this method returns the item that has
10954                 //              that identity through the onItem callback.
10955                 //              Refer to dojo.data.api.Identity.fetchItemByIdentity() for more details.
10956                 //
10957                 //      description:
10958                 //              Given arguments like:
10959                 //
10960                 //      |               {identity: "CA", onItem: function(item){...}
10961                 //
10962                 //              Call `onItem()` with the DOM node `<option value="CA">California</option>`
10963                 var item = dojo.query("option[value='" + args.identity + "']", this.root)[0];
10964                 args.onItem(item);
10965         },
10966         
10967         fetchSelectedItem: function(){
10968                 //      summary:
10969                 //              Get the option marked as selected, like `<option selected>`.
10970                 //              Not part of dojo.data API.
10971                 var root = this.root,
10972                         si = root.selectedIndex;
10973                 return dojo.query("> option:nth-child(" +
10974                         (si != -1 ? si+1 : 1) + ")",
10975                         root)[0];       // dojo.data.Item
10976         }
10977 });
10978
10979 }
10980
10981 if(!dojo._hasResource["dojo.cldr.monetary"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
10982 dojo._hasResource["dojo.cldr.monetary"] = true;
10983 dojo.provide("dojo.cldr.monetary");
10984
10985 dojo.cldr.monetary.getData = function(/*String*/code){
10986 // summary: A mapping of currency code to currency-specific formatting information. Returns a unique object with properties: places, round.
10987 // code: an [ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) currency code
10988
10989 // from http://www.unicode.org/cldr/data/common/supplemental/supplementalData.xml:supplementalData/currencyData/fractions
10990
10991         var placesData = {
10992                 ADP:0,BHD:3,BIF:0,BYR:0,CLF:0,CLP:0,DJF:0,ESP:0,GNF:0,
10993                 IQD:3,ITL:0,JOD:3,JPY:0,KMF:0,KRW:0,KWD:3,LUF:0,LYD:3,
10994                 MGA:0,MGF:0,OMR:3,PYG:0,RWF:0,TND:3,TRL:0,VUV:0,XAF:0,
10995                 XOF:0,XPF:0
10996         };
10997
10998         var roundingData = {CHF:5};
10999
11000         var places = placesData[code], round = roundingData[code];
11001         if(typeof places == "undefined"){ places = 2; }
11002         if(typeof round == "undefined"){ round = 0; }
11003
11004         return {places: places, round: round}; // Object
11005 };
11006
11007 }
11008
11009 if(!dojo._hasResource["dojo.currency"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
11010 dojo._hasResource["dojo.currency"] = true;
11011 dojo.provide("dojo.currency");
11012
11013
11014
11015
11016
11017
11018 /*=====
11019 dojo.currency = {
11020         // summary: localized formatting and parsing routines for currencies
11021 }
11022 =====*/
11023
11024 dojo.currency._mixInDefaults = function(options){
11025         options = options || {};
11026         options.type = "currency";
11027
11028         // Get locale-depenent currency data, like the symbol
11029         var bundle = dojo.i18n.getLocalization("dojo.cldr", "currency", options.locale) || {};
11030
11031         // Mixin locale-independent currency data, like # of places
11032         var iso = options.currency;
11033         var data = dojo.cldr.monetary.getData(iso);
11034
11035         dojo.forEach(["displayName","symbol","group","decimal"], function(prop){
11036                 data[prop] = bundle[iso+"_"+prop];
11037         });
11038
11039         data.fractional = [true, false];
11040
11041         // Mixin with provided options
11042         return dojo.mixin(data, options);
11043 }
11044
11045 dojo.currency.format = function(/*Number*/value, /*dojo.number.__FormatOptions?*/options){
11046 // summary:
11047 //              Format a Number as a currency, using locale-specific settings
11048 //
11049 // description:
11050 //              Create a string from a Number using a known, localized pattern.
11051 //              [Formatting patterns](http://www.unicode.org/reports/tr35/#Number_Elements) appropriate to the locale are chosen from the [CLDR](http://unicode.org/cldr)
11052 //              as well as the appropriate symbols and delimiters.
11053 //
11054 // value:
11055 //              the number to be formatted.
11056
11057         return dojo.number.format(value, dojo.currency._mixInDefaults(options));
11058 }
11059
11060 dojo.currency.regexp = function(/*dojo.number.__RegexpOptions?*/options){
11061 //
11062 // summary:
11063 //              Builds the regular needed to parse a currency value
11064 //
11065 // description:
11066 //              Returns regular expression with positive and negative match, group and decimal separators
11067 //              Note: the options.places default, the number of decimal places to accept, is defined by the currency type.
11068         return dojo.number.regexp(dojo.currency._mixInDefaults(options)); // String
11069 }
11070
11071 /*=====
11072 dojo.declare("dojo.currency.__ParseOptions", [dojo.number.__ParseOptions], {
11073         //      type: String?
11074         //              currency, set by default.
11075         //      symbol: String?
11076         //              override currency symbol. Normally, will be looked up in table of supported currencies,
11077         //              and ISO currency code will be used if not found.  See dojo.i18n.cldr.nls->currency.js
11078         //      places: Number?
11079         //              number of decimal places to accept.  Default is defined by currency.
11080         //      fractional: Boolean?|Array?
11081         //              where places are implied by pattern or explicit 'places' parameter, whether to include the fractional portion.
11082         //              By default for currencies, it the fractional portion is optional.
11083         type: "",
11084         symbol: "",
11085         places: "",
11086         fractional: ""
11087 });
11088 =====*/
11089
11090 dojo.currency.parse = function(/*String*/expression, /*dojo.currency.__ParseOptions?*/options){
11091         //
11092         // summary:
11093         //              Convert a properly formatted currency string to a primitive Number,
11094         //              using locale-specific settings.
11095         //
11096         // description:
11097         //              Create a Number from a string using a known, localized pattern.
11098         //              [Formatting patterns](http://www.unicode.org/reports/tr35/#Number_Format_Patterns) are chosen appropriate to the locale.
11099         //
11100         // expression: A string representation of a Number
11101
11102         return dojo.number.parse(expression, dojo.currency._mixInDefaults(options));
11103 }
11104
11105 }
11106
11107 if(!dojo._hasResource["dijit.form.NumberTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
11108 dojo._hasResource["dijit.form.NumberTextBox"] = true;
11109 dojo.provide("dijit.form.NumberTextBox");
11110
11111
11112
11113
11114 /*=====
11115 dojo.declare(
11116         "dijit.form.NumberTextBox.__Constraints",
11117         [dijit.form.RangeBoundTextBox.__Constraints, dojo.number.__FormatOptions, dojo.number.__ParseOptions]
11118 );
11119 =====*/
11120
11121 dojo.declare(
11122         "dijit.form.NumberTextBoxMixin",
11123         null,
11124         {
11125                 // summary:
11126                 //              A mixin for all number textboxes
11127
11128                 regExpGen: dojo.number.regexp,
11129
11130                 /*=====
11131                 // constraints: dijit.form.NumberTextBox.__Constraints 
11132                 constraints: {},
11133                 ======*/
11134
11135                 // editOptions: Object
11136                 //              properties to mix into constraints when the value is being edited
11137                 editOptions: { pattern: '#.######' },
11138
11139                 _onFocus: function(){
11140                         this.setValue(this.getValue(), false);  
11141                         this.inherited(arguments);
11142                 },
11143
11144                 _formatter: dojo.number.format,
11145
11146                 format: function(/*Number*/ value, /*dojo.number.__FormatOptions*/ constraints){
11147                         //      summary: formats the value as a Number, according to constraints
11148
11149                         if(typeof value == "string") { return value; }
11150                         if(isNaN(value)){ return ""; }
11151                         if(this.editOptions && this._focused){
11152                                 constraints = dojo.mixin(dojo.mixin({}, this.editOptions), this.constraints);
11153                         }
11154                         return this._formatter(value, constraints);
11155                 },
11156
11157                 parse: dojo.number.parse,
11158                 /*=====
11159                 parse: function(value, constraints){
11160                         //      summary: parses the value as a Number, according to constraints
11161                         //      value: String
11162                         //
11163                         //      constraints: dojo.number.__ParseOptions
11164                 },
11165                 =====*/
11166
11167                 filter: function(/*Number*/ value){
11168                         if(typeof value == "string"){ return this.inherited('filter', arguments); }
11169                         return isNaN(value) ? '' : value;
11170                 },
11171
11172                 value: NaN
11173         }
11174 );
11175
11176 dojo.declare(
11177         "dijit.form.NumberTextBox",
11178         [dijit.form.RangeBoundTextBox,dijit.form.NumberTextBoxMixin],
11179         {
11180                 // summary:
11181                 //              A validating, serializable, range-bound text box.
11182         }
11183 );
11184
11185 }
11186
11187 if(!dojo._hasResource["dijit.form.CurrencyTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
11188 dojo._hasResource["dijit.form.CurrencyTextBox"] = true;
11189 dojo.provide("dijit.form.CurrencyTextBox");
11190
11191 //FIXME: dojo.experimental throws an unreadable exception?
11192 //dojo.experimental("dijit.form.CurrencyTextBox");
11193
11194
11195
11196
11197 dojo.declare(
11198         "dijit.form.CurrencyTextBox",
11199         dijit.form.NumberTextBox,
11200         {
11201                 // summary:
11202                 //              A validating currency textbox
11203                 //
11204                 // constraints: dijit.form._DateTimeTextBox.__Constraints 
11205                 //
11206                 // currency: String
11207                 //              the [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code, a three letter sequence like "USD"
11208                 currency: "",
11209
11210                 /*=====
11211                 constraints: {},
11212                 ======*/
11213
11214                 regExpGen: dojo.currency.regexp,
11215                 _formatter: dojo.currency.format,
11216 /*=====
11217                 parse: function(value, constraints){
11218                         //      summary: parses the value as a Currency, according to constraints
11219                         //      value: String
11220                         //
11221                         //      constraints: dojo.currency.__ParseOptions
11222                 },
11223 =====*/
11224                 parse: dojo.currency.parse,
11225
11226                 postMixInProperties: function(){
11227                         if(this.constraints === dijit.form.ValidationTextBox.prototype.constraints){
11228                                 // declare a constraints property on 'this' so we don't overwrite the shared default object in 'prototype'
11229                                 this.constraints = {};
11230                         }
11231                         this.constraints.currency = this.currency;
11232                         dijit.form.CurrencyTextBox.superclass.postMixInProperties.apply(this, arguments);
11233                 }
11234         }
11235 );
11236
11237 }
11238
11239 if(!dojo._hasResource["dojo.cldr.supplemental"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
11240 dojo._hasResource["dojo.cldr.supplemental"] = true;
11241 dojo.provide("dojo.cldr.supplemental");
11242
11243
11244
11245 dojo.cldr.supplemental.getFirstDayOfWeek = function(/*String?*/locale){
11246 // summary: Returns a zero-based index for first day of the week
11247 // description:
11248 //              Returns a zero-based index for first day of the week, as used by the local (Gregorian) calendar.
11249 //              e.g. Sunday (returns 0), or Monday (returns 1)
11250
11251         // from http://www.unicode.org/cldr/data/common/supplemental/supplementalData.xml:supplementalData/weekData/firstDay
11252         var firstDay = {/*default is 1=Monday*/
11253                 mv:5,
11254                 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,
11255                 sd:6,so:6,tn:6,ye:6,
11256                 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,
11257                 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,
11258                 et:0,mw:0,ng:0,tj:0,
11259 // variant. do not use?         gb:0,
11260                 sy:4
11261         };
11262
11263         var country = dojo.cldr.supplemental._region(locale);
11264         var dow = firstDay[country];
11265         return (dow === undefined) ? 1 : dow; /*Number*/
11266 };
11267
11268 dojo.cldr.supplemental._region = function(/*String?*/locale){
11269         locale = dojo.i18n.normalizeLocale(locale);
11270         var tags = locale.split('-');
11271         var region = tags[1];
11272         if(!region){
11273                 // IE often gives language only (#2269)
11274                 // Arbitrary mappings of language-only locales to a country:
11275         region = {de:"de", en:"us", es:"es", fi:"fi", fr:"fr", hu:"hu", it:"it",
11276         ja:"jp", ko:"kr", nl:"nl", pt:"br", sv:"se", zh:"cn"}[tags[0]];
11277         }else if(region.length == 4){
11278                 // The ISO 3166 country code is usually in the second position, unless a
11279                 // 4-letter script is given. See http://www.ietf.org/rfc/rfc4646.txt
11280                 region = tags[2];
11281         }
11282         return region;
11283 }
11284
11285 dojo.cldr.supplemental.getWeekend = function(/*String?*/locale){
11286 // summary: Returns a hash containing the start and end days of the weekend
11287 // description:
11288 //              Returns a hash containing the start and end days of the weekend according to local custom using locale,
11289 //              or by default in the user's locale.
11290 //              e.g. {start:6, end:0}
11291
11292         // from http://www.unicode.org/cldr/data/common/supplemental/supplementalData.xml:supplementalData/weekData/weekend{Start,End}
11293         var weekendStart = {/*default is 6=Saturday*/
11294                 eg:5,il:5,sy:5,
11295                 'in':0,
11296                 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              
11297         };
11298
11299         var weekendEnd = {/*default is 0=Sunday*/
11300                 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,
11301                 eg:6,il:6,sy:6
11302         };
11303
11304         var country = dojo.cldr.supplemental._region(locale);
11305         var start = weekendStart[country];
11306         var end = weekendEnd[country];
11307         if(start === undefined){start=6;}
11308         if(end === undefined){end=0;}
11309         return {start:start, end:end}; /*Object {start,end}*/
11310 };
11311
11312 }
11313
11314 if(!dojo._hasResource["dojo.date"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
11315 dojo._hasResource["dojo.date"] = true;
11316 dojo.provide("dojo.date");
11317
11318 /*=====
11319 dojo.date = {
11320         // summary: Date manipulation utilities
11321 }
11322 =====*/
11323
11324 dojo.date.getDaysInMonth = function(/*Date*/dateObject){
11325         //      summary:
11326         //              Returns the number of days in the month used by dateObject
11327         var month = dateObject.getMonth();
11328         var days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
11329         if(month == 1 && dojo.date.isLeapYear(dateObject)){ return 29; } // Number
11330         return days[month]; // Number
11331 }
11332
11333 dojo.date.isLeapYear = function(/*Date*/dateObject){
11334         //      summary:
11335         //              Determines if the year of the dateObject is a leap year
11336         //      description:
11337         //              Leap years are years with an additional day YYYY-02-29, where the
11338         //              year number is a multiple of four with the following exception: If
11339         //              a year is a multiple of 100, then it is only a leap year if it is
11340         //              also a multiple of 400. For example, 1900 was not a leap year, but
11341         //              2000 is one.
11342
11343         var year = dateObject.getFullYear();
11344         return !(year%400) || (!(year%4) && !!(year%100)); // Boolean
11345 }
11346
11347 // FIXME: This is not localized
11348 dojo.date.getTimezoneName = function(/*Date*/dateObject){
11349         //      summary:
11350         //              Get the user's time zone as provided by the browser
11351         // dateObject:
11352         //              Needed because the timezone may vary with time (daylight savings)
11353         //      description:
11354         //              Try to get time zone info from toString or toLocaleString method of
11355         //              the Date object -- UTC offset is not a time zone.  See
11356         //              http://www.twinsun.com/tz/tz-link.htm Note: results may be
11357         //              inconsistent across browsers.
11358
11359         var str = dateObject.toString(); // Start looking in toString
11360         var tz = ''; // The result -- return empty string if nothing found
11361         var match;
11362
11363         // First look for something in parentheses -- fast lookup, no regex
11364         var pos = str.indexOf('(');
11365         if(pos > -1){
11366                 tz = str.substring(++pos, str.indexOf(')'));
11367         }else{
11368                 // If at first you don't succeed ...
11369                 // If IE knows about the TZ, it appears before the year
11370                 // Capital letters or slash before a 4-digit year 
11371                 // at the end of string
11372                 var pat = /([A-Z\/]+) \d{4}$/;
11373                 if((match = str.match(pat))){
11374                         tz = match[1];
11375                 }else{
11376                 // Some browsers (e.g. Safari) glue the TZ on the end
11377                 // of toLocaleString instead of putting it in toString
11378                         str = dateObject.toLocaleString();
11379                         // Capital letters or slash -- end of string, 
11380                         // after space
11381                         pat = / ([A-Z\/]+)$/;
11382                         if((match = str.match(pat))){
11383                                 tz = match[1];
11384                         }
11385                 }
11386         }
11387
11388         // Make sure it doesn't somehow end up return AM or PM
11389         return (tz == 'AM' || tz == 'PM') ? '' : tz; // String
11390 }
11391
11392 // Utility methods to do arithmetic calculations with Dates
11393
11394 dojo.date.compare = function(/*Date*/date1, /*Date?*/date2, /*String?*/portion){
11395         //      summary:
11396         //              Compare two date objects by date, time, or both.
11397         //      description:
11398         //      Returns 0 if equal, positive if a > b, else negative.
11399         //      date1:
11400         //              Date object
11401         //      date2:
11402         //              Date object.  If not specified, the current Date is used.
11403         //      portion:
11404         //              A string indicating the "date" or "time" portion of a Date object.
11405         //              Compares both "date" and "time" by default.  One of the following:
11406         //              "date", "time", "datetime"
11407
11408         // Extra step required in copy for IE - see #3112
11409         date1 = new Date(Number(date1));
11410         date2 = new Date(Number(date2 || new Date()));
11411
11412         if(portion !== "undefined"){
11413                 if(portion == "date"){
11414                         // Ignore times and compare dates.
11415                         date1.setHours(0, 0, 0, 0);
11416                         date2.setHours(0, 0, 0, 0);
11417                 }else if(portion == "time"){
11418                         // Ignore dates and compare times.
11419                         date1.setFullYear(0, 0, 0);
11420                         date2.setFullYear(0, 0, 0);
11421                 }
11422         }
11423         
11424         if(date1 > date2){ return 1; } // int
11425         if(date1 < date2){ return -1; } // int
11426         return 0; // int
11427 };
11428
11429 dojo.date.add = function(/*Date*/date, /*String*/interval, /*int*/amount){
11430         //      summary:
11431         //              Add to a Date in intervals of different size, from milliseconds to years
11432         //      date: Date
11433         //              Date object to start with
11434         //      interval:
11435         //              A string representing the interval.  One of the following:
11436         //                      "year", "month", "day", "hour", "minute", "second",
11437         //                      "millisecond", "quarter", "week", "weekday"
11438         //      amount:
11439         //              How much to add to the date.
11440
11441         var sum = new Date(Number(date)); // convert to Number before copying to accomodate IE (#3112)
11442         var fixOvershoot = false;
11443         var property = "Date";
11444
11445         switch(interval){
11446                 case "day":
11447                         break;
11448                 case "weekday":
11449                         //i18n FIXME: assumes Saturday/Sunday weekend, but this is not always true.  see dojo.cldr.supplemental
11450
11451                         // Divide the increment time span into weekspans plus leftover days
11452                         // e.g., 8 days is one 5-day weekspan / and two leftover days
11453                         // Can't have zero leftover days, so numbers divisible by 5 get
11454                         // a days value of 5, and the remaining days make up the number of weeks
11455                         var days, weeks;
11456                         var mod = amount % 5;
11457                         if(!mod){
11458                                 days = (amount > 0) ? 5 : -5;
11459                                 weeks = (amount > 0) ? ((amount-5)/5) : ((amount+5)/5);
11460                         }else{
11461                                 days = mod;
11462                                 weeks = parseInt(amount/5);
11463                         }
11464                         // Get weekday value for orig date param
11465                         var strt = date.getDay();
11466                         // Orig date is Sat / positive incrementer
11467                         // Jump over Sun
11468                         var adj = 0;
11469                         if(strt == 6 && amount > 0){
11470                                 adj = 1;
11471                         }else if(strt == 0 && amount < 0){
11472                         // Orig date is Sun / negative incrementer
11473                         // Jump back over Sat
11474                                 adj = -1;
11475                         }
11476                         // Get weekday val for the new date
11477                         var trgt = strt + days;
11478                         // New date is on Sat or Sun
11479                         if(trgt == 0 || trgt == 6){
11480                                 adj = (amount > 0) ? 2 : -2;
11481                         }
11482                         // Increment by number of weeks plus leftover days plus
11483                         // weekend adjustments
11484                         amount = (7 * weeks) + days + adj;
11485                         break;
11486                 case "year":
11487                         property = "FullYear";
11488                         // Keep increment/decrement from 2/29 out of March
11489                         fixOvershoot = true;
11490                         break;
11491                 case "week":
11492                         amount *= 7;
11493                         break;
11494                 case "quarter":
11495                         // Naive quarter is just three months
11496                         amount *= 3;
11497                         // fallthrough...
11498                 case "month":
11499                         // Reset to last day of month if you overshoot
11500                         fixOvershoot = true;
11501                         property = "Month";
11502                         break;
11503                 case "hour":
11504                 case "minute":
11505                 case "second":
11506                 case "millisecond":
11507                         property = "UTC"+interval.charAt(0).toUpperCase() + interval.substring(1) + "s";
11508         }
11509
11510         if(property){
11511                 sum["set"+property](sum["get"+property]()+amount);
11512         }
11513
11514         if(fixOvershoot && (sum.getDate() < date.getDate())){
11515                 sum.setDate(0);
11516         }
11517
11518         return sum; // Date
11519 };
11520
11521 dojo.date.difference = function(/*Date*/date1, /*Date?*/date2, /*String?*/interval){
11522         //      summary:
11523         //              Get the difference in a specific unit of time (e.g., number of
11524         //              months, weeks, days, etc.) between two dates, rounded to the
11525         //              nearest integer.
11526         //      date1:
11527         //              Date object
11528         //      date2:
11529         //              Date object.  If not specified, the current Date is used.
11530         //      interval:
11531         //              A string representing the interval.  One of the following:
11532         //                      "year", "month", "day", "hour", "minute", "second",
11533         //                      "millisecond", "quarter", "week", "weekday"
11534         //              Defaults to "day".
11535
11536         date2 = date2 || new Date();
11537         interval = interval || "day";
11538         var yearDiff = date2.getFullYear() - date1.getFullYear();
11539         var delta = 1; // Integer return value
11540
11541         switch(interval){
11542                 case "quarter":
11543                         var m1 = date1.getMonth();
11544                         var m2 = date2.getMonth();
11545                         // Figure out which quarter the months are in
11546                         var q1 = Math.floor(m1/3) + 1;
11547                         var q2 = Math.floor(m2/3) + 1;
11548                         // Add quarters for any year difference between the dates
11549                         q2 += (yearDiff * 4);
11550                         delta = q2 - q1;
11551                         break;
11552                 case "weekday":
11553                         var days = Math.round(dojo.date.difference(date1, date2, "day"));
11554                         var weeks = parseInt(dojo.date.difference(date1, date2, "week"));
11555                         var mod = days % 7;
11556
11557                         // Even number of weeks
11558                         if(mod == 0){
11559                                 days = weeks*5;
11560                         }else{
11561                                 // Weeks plus spare change (< 7 days)
11562                                 var adj = 0;
11563                                 var aDay = date1.getDay();
11564                                 var bDay = date2.getDay();
11565
11566                                 weeks = parseInt(days/7);
11567                                 mod = days % 7;
11568                                 // Mark the date advanced by the number of
11569                                 // round weeks (may be zero)
11570                                 var dtMark = new Date(date1);
11571                                 dtMark.setDate(dtMark.getDate()+(weeks*7));
11572                                 var dayMark = dtMark.getDay();
11573
11574                                 // Spare change days -- 6 or less
11575                                 if(days > 0){
11576                                         switch(true){
11577                                                 // Range starts on Sat
11578                                                 case aDay == 6:
11579                                                         adj = -1;
11580                                                         break;
11581                                                 // Range starts on Sun
11582                                                 case aDay == 0:
11583                                                         adj = 0;
11584                                                         break;
11585                                                 // Range ends on Sat
11586                                                 case bDay == 6:
11587                                                         adj = -1;
11588                                                         break;
11589                                                 // Range ends on Sun
11590                                                 case bDay == 0:
11591                                                         adj = -2;
11592                                                         break;
11593                                                 // Range contains weekend
11594                                                 case (dayMark + mod) > 5:
11595                                                         adj = -2;
11596                                         }
11597                                 }else if(days < 0){
11598                                         switch(true){
11599                                                 // Range starts on Sat
11600                                                 case aDay == 6:
11601                                                         adj = 0;
11602                                                         break;
11603                                                 // Range starts on Sun
11604                                                 case aDay == 0:
11605                                                         adj = 1;
11606                                                         break;
11607                                                 // Range ends on Sat
11608                                                 case bDay == 6:
11609                                                         adj = 2;
11610                                                         break;
11611                                                 // Range ends on Sun
11612                                                 case bDay == 0:
11613                                                         adj = 1;
11614                                                         break;
11615                                                 // Range contains weekend
11616                                                 case (dayMark + mod) < 0:
11617                                                         adj = 2;
11618                                         }
11619                                 }
11620                                 days += adj;
11621                                 days -= (weeks*2);
11622                         }
11623                         delta = days;
11624                         break;
11625                 case "year":
11626                         delta = yearDiff;
11627                         break;
11628                 case "month":
11629                         delta = (date2.getMonth() - date1.getMonth()) + (yearDiff * 12);
11630                         break;
11631                 case "week":
11632                         // Truncate instead of rounding
11633                         // Don't use Math.floor -- value may be negative
11634                         delta = parseInt(dojo.date.difference(date1, date2, "day")/7);
11635                         break;
11636                 case "day":
11637                         delta /= 24;
11638                         // fallthrough
11639                 case "hour":
11640                         delta /= 60;
11641                         // fallthrough
11642                 case "minute":
11643                         delta /= 60;
11644                         // fallthrough
11645                 case "second":
11646                         delta /= 1000;
11647                         // fallthrough
11648                 case "millisecond":
11649                         delta *= date2.getTime() - date1.getTime();
11650         }
11651
11652         // Round for fractional values and DST leaps
11653         return Math.round(delta); // Number (integer)
11654 };
11655
11656 }
11657
11658 if(!dojo._hasResource["dojo.date.locale"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
11659 dojo._hasResource["dojo.date.locale"] = true;
11660 dojo.provide("dojo.date.locale");
11661
11662 // Localization methods for Date.   Honor local customs using locale-dependent dojo.cldr data.
11663
11664
11665
11666
11667
11668
11669
11670 // Load the bundles containing localization information for
11671 // names and formats
11672
11673
11674 //NOTE: Everything in this module assumes Gregorian calendars.
11675 // Other calendars will be implemented in separate modules.
11676
11677 (function(){
11678         // Format a pattern without literals
11679         function formatPattern(dateObject, bundle, fullYear, pattern){
11680                 return pattern.replace(/([a-z])\1*/ig, function(match){
11681                         var s, pad;
11682                         var c = match.charAt(0);
11683                         var l = match.length;
11684                         var widthList = ["abbr", "wide", "narrow"];
11685                         switch(c){
11686                                 case 'G':
11687                                         s = bundle[(l < 4) ? "eraAbbr" : "eraNames"][dateObject.getFullYear() < 0 ? 0 : 1];
11688                                         break;
11689                                 case 'y':
11690                                         s = dateObject.getFullYear();
11691                                         switch(l){
11692                                                 case 1:
11693                                                         break;
11694                                                 case 2:
11695                                                         if(!fullYear){
11696                                                                 s = String(s); s = s.substr(s.length - 2);
11697                                                                 break;
11698                                                         }
11699                                                         // fallthrough
11700                                                 default:
11701                                                         pad = true;
11702                                         }
11703                                         break;
11704                                 case 'Q':
11705                                 case 'q':
11706                                         s = Math.ceil((dateObject.getMonth()+1)/3);
11707 //                                      switch(l){
11708 //                                              case 1: case 2:
11709                                                         pad = true;
11710 //                                                      break;
11711 //                                              case 3: case 4: // unimplemented
11712 //                                      }
11713                                         break;
11714                                 case 'M':
11715                                 case 'L':
11716                                         var m = dateObject.getMonth();
11717                                         var widthM;
11718                                         switch(l){
11719                                                 case 1: case 2:
11720                                                         s = m+1; pad = true;
11721                                                         break;
11722                                                 case 3: case 4: case 5:
11723                                                         widthM = widthList[l-3];
11724                                                         break;
11725                                         }
11726                                         if(widthM){
11727                                                 var typeM = (c == "L") ? "standalone" : "format";
11728                                                 var propM = ["months", typeM, widthM].join("-");
11729                                                 s = bundle[propM][m];
11730                                         }
11731                                         break;
11732                                 case 'w':
11733                                         var firstDay = 0;
11734                                         s = dojo.date.locale._getWeekOfYear(dateObject, firstDay); pad = true;
11735                                         break;
11736                                 case 'd':
11737                                         s = dateObject.getDate(); pad = true;
11738                                         break;
11739                                 case 'D':
11740                                         s = dojo.date.locale._getDayOfYear(dateObject); pad = true;
11741                                         break;
11742                                 case 'E':
11743                                 case 'e':
11744                                 case 'c': // REVIEW: don't see this in the spec?
11745                                         var d = dateObject.getDay();
11746                                         var widthD;
11747                                         switch(l){
11748                                                 case 1: case 2:
11749                                                         if(c == 'e'){
11750                                                                 var first = dojo.cldr.supplemental.getFirstDayOfWeek(options.locale);
11751                                                                 d = (d-first+7)%7;
11752                                                         }
11753                                                         if(c != 'c'){
11754                                                                 s = d+1; pad = true;
11755                                                                 break;
11756                                                         }
11757                                                         // else fallthrough...
11758                                                 case 3: case 4: case 5:
11759                                                         widthD = widthList[l-3];
11760                                                         break;
11761                                         }
11762                                         if(widthD){
11763                                                 var typeD = (c == "c") ? "standalone" : "format";
11764                                                 var propD = ["days", typeD, widthD].join("-");
11765                                                 s = bundle[propD][d];
11766                                         }
11767                                         break;
11768                                 case 'a':
11769                                         var timePeriod = (dateObject.getHours() < 12) ? 'am' : 'pm';
11770                                         s = bundle[timePeriod];
11771                                         break;
11772                                 case 'h':
11773                                 case 'H':
11774                                 case 'K':
11775                                 case 'k':
11776                                         var h = dateObject.getHours();
11777                                         // strange choices in the date format make it impossible to write this succinctly
11778                                         switch (c){
11779                                                 case 'h': // 1-12
11780                                                         s = (h % 12) || 12;
11781                                                         break;
11782                                                 case 'H': // 0-23
11783                                                         s = h;
11784                                                         break;
11785                                                 case 'K': // 0-11
11786                                                         s = (h % 12);
11787                                                         break;
11788                                                 case 'k': // 1-24
11789                                                         s = h || 24;
11790                                                         break;
11791                                         }
11792                                         pad = true;
11793                                         break;
11794                                 case 'm':
11795                                         s = dateObject.getMinutes(); pad = true;
11796                                         break;
11797                                 case 's':
11798                                         s = dateObject.getSeconds(); pad = true;
11799                                         break;
11800                                 case 'S':
11801                                         s = Math.round(dateObject.getMilliseconds() * Math.pow(10, l-3)); pad = true;
11802                                         break;
11803                                 case 'v': // FIXME: don't know what this is. seems to be same as z?
11804                                 case 'z':
11805                                         // We only have one timezone to offer; the one from the browser
11806                                         s = dojo.date.getTimezoneName(dateObject);
11807                                         if(s){break;}
11808                                         l=4;
11809                                         // fallthrough... use GMT if tz not available
11810                                 case 'Z':
11811                                         var offset = dateObject.getTimezoneOffset();
11812                                         var tz = [
11813                                                 (offset<=0 ? "+" : "-"),
11814                                                 dojo.string.pad(Math.floor(Math.abs(offset)/60), 2),
11815                                                 dojo.string.pad(Math.abs(offset)% 60, 2)
11816                                         ];
11817                                         if(l==4){
11818                                                 tz.splice(0, 0, "GMT");
11819                                                 tz.splice(3, 0, ":");
11820                                         }
11821                                         s = tz.join("");
11822                                         break;
11823 //                              case 'Y': case 'u': case 'W': case 'F': case 'g': case 'A':
11824 //                                      console.debug(match+" modifier unimplemented");
11825                                 default:
11826                                         throw new Error("dojo.date.locale.format: invalid pattern char: "+pattern);
11827                         }
11828                         if(pad){ s = dojo.string.pad(s, l); }
11829                         return s;
11830                 });
11831         }
11832
11833 /*=====
11834         dojo.date.locale.__FormatOptions = function(){
11835         //      selector: String
11836         //              choice of 'time','date' (default: date and time)
11837         //      formatLength: String
11838         //              choice of long, short, medium or full (plus any custom additions).  Defaults to 'short'
11839         //      datePattern:String
11840         //              override pattern with this string
11841         //      timePattern:String
11842         //              override pattern with this string
11843         //      am: String
11844         //              override strings for am in times
11845         //      pm: String
11846         //              override strings for pm in times
11847         //      locale: String
11848         //              override the locale used to determine formatting rules
11849         //      fullYear: Boolean
11850         //              (format only) use 4 digit years whenever 2 digit years are called for
11851         //      strict: Boolean
11852         //              (parse only) strict parsing, off by default
11853                 this.selector = selector;
11854                 this.formatLength = formatLength;
11855                 this.datePattern = datePattern;
11856                 this.timePattern = timePattern;
11857                 this.am = am;
11858                 this.pm = pm;
11859                 this.locale = locale;
11860                 this.fullYear = fullYear;
11861                 this.strict = strict;
11862         }
11863 =====*/
11864
11865 dojo.date.locale.format = function(/*Date*/dateObject, /*dojo.date.locale.__FormatOptions?*/options){
11866         // summary:
11867         //              Format a Date object as a String, using locale-specific settings.
11868         //
11869         // description:
11870         //              Create a string from a Date object using a known localized pattern.
11871         //              By default, this method formats both date and time from dateObject.
11872         //              Formatting patterns are chosen appropriate to the locale.  Different
11873         //              formatting lengths may be chosen, with "full" used by default.
11874         //              Custom patterns may be used or registered with translations using
11875         //              the dojo.date.locale.addCustomFormats method.
11876         //              Formatting patterns are implemented using [the syntax described at
11877         //              unicode.org](http://www.unicode.org/reports/tr35/tr35-4.html#Date_Format_Patterns)
11878         //
11879         // dateObject:
11880         //              the date and/or time to be formatted.  If a time only is formatted,
11881         //              the values in the year, month, and day fields are irrelevant.  The
11882         //              opposite is true when formatting only dates.
11883
11884         options = options || {};
11885
11886         var locale = dojo.i18n.normalizeLocale(options.locale);
11887         var formatLength = options.formatLength || 'short';
11888         var bundle = dojo.date.locale._getGregorianBundle(locale);
11889         var str = [];
11890         var sauce = dojo.hitch(this, formatPattern, dateObject, bundle, options.fullYear);
11891         if(options.selector == "year"){
11892                 // Special case as this is not yet driven by CLDR data
11893                 var year = dateObject.getFullYear();
11894                 if(locale.match(/^zh|^ja/)){
11895                         year += "\u5E74";
11896                 }
11897                 return year;
11898         }
11899         if(options.selector != "time"){
11900                 var datePattern = options.datePattern || bundle["dateFormat-"+formatLength];
11901                 if(datePattern){str.push(_processPattern(datePattern, sauce));}
11902         }
11903         if(options.selector != "date"){
11904                 var timePattern = options.timePattern || bundle["timeFormat-"+formatLength];
11905                 if(timePattern){str.push(_processPattern(timePattern, sauce));}
11906         }
11907         var result = str.join(" "); //TODO: use locale-specific pattern to assemble date + time
11908         return result; // String
11909 };
11910
11911 dojo.date.locale.regexp = function(/*dojo.date.locale.__FormatOptions?*/options){
11912         // summary:
11913         //              Builds the regular needed to parse a localized date
11914
11915         return dojo.date.locale._parseInfo(options).regexp; // String
11916 };
11917
11918 dojo.date.locale._parseInfo = function(/*dojo.date.locale.__FormatOptions?*/options){
11919         options = options || {};
11920         var locale = dojo.i18n.normalizeLocale(options.locale);
11921         var bundle = dojo.date.locale._getGregorianBundle(locale);
11922         var formatLength = options.formatLength || 'short';
11923         var datePattern = options.datePattern || bundle["dateFormat-" + formatLength];
11924         var timePattern = options.timePattern || bundle["timeFormat-" + formatLength];
11925         var pattern;
11926         if(options.selector == 'date'){
11927                 pattern = datePattern;
11928         }else if(options.selector == 'time'){
11929                 pattern = timePattern;
11930         }else{
11931                 pattern = datePattern + ' ' + timePattern; //TODO: use locale-specific pattern to assemble date + time
11932         }
11933
11934         var tokens = [];
11935         var re = _processPattern(pattern, dojo.hitch(this, _buildDateTimeRE, tokens, bundle, options));
11936         return {regexp: re, tokens: tokens, bundle: bundle};
11937 };
11938
11939 dojo.date.locale.parse = function(/*String*/value, /*dojo.date.locale.__FormatOptions?*/options){
11940         // summary:
11941         //              Convert a properly formatted string to a primitive Date object,
11942         //              using locale-specific settings.
11943         //
11944         // description:
11945         //              Create a Date object from a string using a known localized pattern.
11946         //              By default, this method parses looking for both date and time in the string.
11947         //              Formatting patterns are chosen appropriate to the locale.  Different
11948         //              formatting lengths may be chosen, with "full" used by default.
11949         //              Custom patterns may be used or registered with translations using
11950         //              the dojo.date.locale.addCustomFormats method.
11951         //      
11952         //              Formatting patterns are implemented using [the syntax described at
11953         //              unicode.org](http://www.unicode.org/reports/tr35/tr35-4.html#Date_Format_Patterns)
11954         //              When two digit years are used, a century is chosen according to a sliding 
11955         //              window of 80 years before and 20 years after present year, for both `yy` and `yyyy` patterns.
11956         //              year < 100CE requires strict mode.
11957         //
11958         // value:
11959         //              A string representation of a date
11960
11961         var info = dojo.date.locale._parseInfo(options);
11962         var tokens = info.tokens, bundle = info.bundle;
11963         var re = new RegExp("^" + info.regexp + "$");
11964         var match = re.exec(value);
11965         if(!match){ return null; } // null
11966
11967         var widthList = ['abbr', 'wide', 'narrow'];
11968         var result = [1970,0,1,0,0,0,0]; // will get converted to a Date at the end
11969         var amPm = "";
11970         var valid = dojo.every(match, function(v, i){
11971                 if(!i){return true;}
11972                 var token=tokens[i-1];
11973                 var l=token.length;
11974                 switch(token.charAt(0)){
11975                         case 'y':
11976                                 if(l != 2 && options.strict){
11977                                         //interpret year literally, so '5' would be 5 A.D.
11978                                         result[0] = v;
11979                                 }else{
11980                                         if(v<100){
11981                                                 v = Number(v);
11982                                                 //choose century to apply, according to a sliding window
11983                                                 //of 80 years before and 20 years after present year
11984                                                 var year = '' + new Date().getFullYear();
11985                                                 var century = year.substring(0, 2) * 100;
11986                                                 var cutoff = Math.min(Number(year.substring(2, 4)) + 20, 99);
11987                                                 var num = (v < cutoff) ? century + v : century - 100 + v;
11988                                                 result[0] = num;
11989                                         }else{
11990                                                 //we expected 2 digits and got more...
11991                                                 if(options.strict){
11992                                                         return false;
11993                                                 }
11994                                                 //interpret literally, so '150' would be 150 A.D.
11995                                                 //also tolerate '1950', if 'yyyy' input passed to 'yy' format
11996                                                 result[0] = v;
11997                                         }
11998                                 }
11999                                 break;
12000                         case 'M':
12001                                 if(l>2){
12002                                         var months = bundle['months-format-' + widthList[l-3]].concat();
12003                                         if(!options.strict){
12004                                                 //Tolerate abbreviating period in month part
12005                                                 //Case-insensitive comparison
12006                                                 v = v.replace(".","").toLowerCase();
12007                                                 months = dojo.map(months, function(s){ return s.replace(".","").toLowerCase(); } );
12008                                         }
12009                                         v = dojo.indexOf(months, v);
12010                                         if(v == -1){
12011 //                                              console.debug("dojo.date.locale.parse: Could not parse month name: '" + v + "'.");
12012                                                 return false;
12013                                         }
12014                                 }else{
12015                                         v--;
12016                                 }
12017                                 result[1] = v;
12018                                 break;
12019                         case 'E':
12020                         case 'e':
12021                                 var days = bundle['days-format-' + widthList[l-3]].concat();
12022                                 if(!options.strict){
12023                                         //Case-insensitive comparison
12024                                         v = v.toLowerCase();
12025                                         days = dojo.map(days, function(d){return d.toLowerCase();});
12026                                 }
12027                                 v = dojo.indexOf(days, v);
12028                                 if(v == -1){
12029 //                                      console.debug("dojo.date.locale.parse: Could not parse weekday name: '" + v + "'.");
12030                                         return false;
12031                                 }
12032
12033                                 //TODO: not sure what to actually do with this input,
12034                                 //in terms of setting something on the Date obj...?
12035                                 //without more context, can't affect the actual date
12036                                 //TODO: just validate?
12037                                 break;
12038                         case 'D':
12039                                 result[1] = 0;
12040                                 // fallthrough...
12041                         case 'd':
12042                                 result[2] = v;
12043                                 break;
12044                         case 'a': //am/pm
12045                                 var am = options.am || bundle.am;
12046                                 var pm = options.pm || bundle.pm;
12047                                 if(!options.strict){
12048                                         var period = /\./g;
12049                                         v = v.replace(period,'').toLowerCase();
12050                                         am = am.replace(period,'').toLowerCase();
12051                                         pm = pm.replace(period,'').toLowerCase();
12052                                 }
12053                                 if(options.strict && v != am && v != pm){
12054 //                                      console.debug("dojo.date.locale.parse: Could not parse am/pm part.");
12055                                         return false;
12056                                 }
12057
12058                                 // we might not have seen the hours field yet, so store the state and apply hour change later
12059                                 amPm = (v == pm) ? 'p' : (v == am) ? 'a' : '';
12060                                 break;
12061                         case 'K': //hour (1-24)
12062                                 if(v == 24){ v = 0; }
12063                                 // fallthrough...
12064                         case 'h': //hour (1-12)
12065                         case 'H': //hour (0-23)
12066                         case 'k': //hour (0-11)
12067                                 //TODO: strict bounds checking, padding
12068                                 if(v > 23){
12069 //                                      console.debug("dojo.date.locale.parse: Illegal hours value");
12070                                         return false;
12071                                 }
12072
12073                                 //in the 12-hour case, adjusting for am/pm requires the 'a' part
12074                                 //which could come before or after the hour, so we will adjust later
12075                                 result[3] = v;
12076                                 break;
12077                         case 'm': //minutes
12078                                 result[4] = v;
12079                                 break;
12080                         case 's': //seconds
12081                                 result[5] = v;
12082                                 break;
12083                         case 'S': //milliseconds
12084                                 result[6] = v;
12085 //                              break;
12086 //                      case 'w':
12087 //TODO                          var firstDay = 0;
12088 //                      default:
12089 //TODO: throw?
12090 //                              console.debug("dojo.date.locale.parse: unsupported pattern char=" + token.charAt(0));
12091                 }
12092                 return true;
12093         });
12094
12095         var hours = +result[3];
12096         if(amPm === 'p' && hours < 12){
12097                 result[3] = hours + 12; //e.g., 3pm -> 15
12098         }else if(amPm === 'a' && hours == 12){
12099                 result[3] = 0; //12am -> 0
12100         }
12101
12102         //TODO: implement a getWeekday() method in order to test 
12103         //validity of input strings containing 'EEE' or 'EEEE'...
12104
12105         var dateObject = new Date(result[0], result[1], result[2], result[3], result[4], result[5], result[6]); // Date
12106         if(options.strict){
12107                 dateObject.setFullYear(result[0]);
12108         }
12109
12110         // Check for overflow.  The Date() constructor normalizes things like April 32nd...
12111         //TODO: why isn't this done for times as well?
12112         var allTokens = tokens.join("");
12113         if(!valid ||
12114                 (allTokens.indexOf('M') != -1 && dateObject.getMonth() != result[1]) ||
12115                 (allTokens.indexOf('d') != -1 && dateObject.getDate() != result[2])){
12116                 return null;
12117         }
12118
12119         return dateObject; // Date
12120 };
12121
12122 function _processPattern(pattern, applyPattern, applyLiteral, applyAll){
12123         //summary: Process a pattern with literals in it
12124
12125         // Break up on single quotes, treat every other one as a literal, except '' which becomes '
12126         var identity = function(x){return x;};
12127         applyPattern = applyPattern || identity;
12128         applyLiteral = applyLiteral || identity;
12129         applyAll = applyAll || identity;
12130
12131         //split on single quotes (which escape literals in date format strings) 
12132         //but preserve escaped single quotes (e.g., o''clock)
12133         var chunks = pattern.match(/(''|[^'])+/g); 
12134         var literal = false;
12135
12136         dojo.forEach(chunks, function(chunk, i){
12137                 if(!chunk){
12138                         chunks[i]='';
12139                 }else{
12140                         chunks[i]=(literal ? applyLiteral : applyPattern)(chunk);
12141                         literal = !literal;
12142                 }
12143         });
12144         return applyAll(chunks.join(''));
12145 }
12146
12147 function _buildDateTimeRE(tokens, bundle, options, pattern){
12148         pattern = dojo.regexp.escapeString(pattern);
12149         if(!options.strict){ pattern = pattern.replace(" a", " ?a"); } // kludge to tolerate no space before am/pm
12150         return pattern.replace(/([a-z])\1*/ig, function(match){
12151                 // Build a simple regexp.  Avoid captures, which would ruin the tokens list
12152                 var s;
12153                 var c = match.charAt(0);
12154                 var l = match.length;
12155                 var p2 = '', p3 = '';
12156                 if(options.strict){
12157                         if(l > 1){ p2 = '0' + '{'+(l-1)+'}'; }
12158                         if(l > 2){ p3 = '0' + '{'+(l-2)+'}'; }
12159                 }else{
12160                         p2 = '0?'; p3 = '0{0,2}';
12161                 }
12162                 switch(c){
12163                         case 'y':
12164                                 s = '\\d{2,4}';
12165                                 break;
12166                         case 'M':
12167                                 s = (l>2) ? '\\S+' : p2+'[1-9]|1[0-2]';
12168                                 break;
12169                         case 'D':
12170                                 s = p2+'[1-9]|'+p3+'[1-9][0-9]|[12][0-9][0-9]|3[0-5][0-9]|36[0-6]';
12171                                 break;
12172                         case 'd':
12173                                 s = p2+'[1-9]|[12]\\d|3[01]';
12174                                 break;
12175                         case 'w':
12176                                 s = p2+'[1-9]|[1-4][0-9]|5[0-3]';
12177                                 break;
12178                     case 'E':
12179                                 s = '\\S+';
12180                                 break;
12181                         case 'h': //hour (1-12)
12182                                 s = p2+'[1-9]|1[0-2]';
12183                                 break;
12184                         case 'k': //hour (0-11)
12185                                 s = p2+'\\d|1[01]';
12186                                 break;
12187                         case 'H': //hour (0-23)
12188                                 s = p2+'\\d|1\\d|2[0-3]';
12189                                 break;
12190                         case 'K': //hour (1-24)
12191                                 s = p2+'[1-9]|1\\d|2[0-4]';
12192                                 break;
12193                         case 'm':
12194                         case 's':
12195                                 s = '[0-5]\\d';
12196                                 break;
12197                         case 'S':
12198                                 s = '\\d{'+l+'}';
12199                                 break;
12200                         case 'a':
12201                                 var am = options.am || bundle.am || 'AM';
12202                                 var pm = options.pm || bundle.pm || 'PM';
12203                                 if(options.strict){
12204                                         s = am + '|' + pm;
12205                                 }else{
12206                                         s = am + '|' + pm;
12207                                         if(am != am.toLowerCase()){ s += '|' + am.toLowerCase(); }
12208                                         if(pm != pm.toLowerCase()){ s += '|' + pm.toLowerCase(); }
12209                                 }
12210                                 break;
12211                         default:
12212                         // case 'v':
12213                         // case 'z':
12214                         // case 'Z':
12215                                 s = ".*";
12216 //                              console.debug("parse of date format, pattern=" + pattern);
12217                 }
12218
12219                 if(tokens){ tokens.push(match); }
12220
12221                 return "(" + s + ")"; // add capture
12222         }).replace(/[\xa0 ]/g, "[\\s\\xa0]"); // normalize whitespace.  Need explicit handling of \xa0 for IE.
12223 }
12224 })();
12225
12226 (function(){
12227 var _customFormats = [];
12228 dojo.date.locale.addCustomFormats = function(/*String*/packageName, /*String*/bundleName){
12229         // summary:
12230         //              Add a reference to a bundle containing localized custom formats to be
12231         //              used by date/time formatting and parsing routines.
12232         //
12233         // description:
12234         //              The user may add custom localized formats where the bundle has properties following the
12235         //              same naming convention used by dojo.cldr: `dateFormat-xxxx` / `timeFormat-xxxx`
12236         //              The pattern string should match the format used by the CLDR.
12237         //              See dojo.date.locale.format() for details.
12238         //              The resources must be loaded by dojo.requireLocalization() prior to use
12239
12240         _customFormats.push({pkg:packageName,name:bundleName});
12241 };
12242
12243 dojo.date.locale._getGregorianBundle = function(/*String*/locale){
12244         var gregorian = {};
12245         dojo.forEach(_customFormats, function(desc){
12246                 var bundle = dojo.i18n.getLocalization(desc.pkg, desc.name, locale);
12247                 gregorian = dojo.mixin(gregorian, bundle);
12248         }, this);
12249         return gregorian; /*Object*/
12250 };
12251 })();
12252
12253 dojo.date.locale.addCustomFormats("dojo.cldr","gregorian");
12254
12255 dojo.date.locale.getNames = function(/*String*/item, /*String*/type, /*String?*/use, /*String?*/locale){
12256         // summary:
12257         //              Used to get localized strings from dojo.cldr for day or month names.
12258         //
12259         // item:
12260         //      'months' || 'days'
12261         // type:
12262         //      'wide' || 'narrow' || 'abbr' (e.g. "Monday", "Mon", or "M" respectively, in English)
12263         // use:
12264         //      'standAlone' || 'format' (default)
12265         // locale:
12266         //      override locale used to find the names
12267
12268         var label;
12269         var lookup = dojo.date.locale._getGregorianBundle(locale);
12270         var props = [item, use, type];
12271         if(use == 'standAlone'){
12272                 label = lookup[props.join('-')];
12273         }
12274         props[1] = 'format';
12275
12276         // return by copy so changes won't be made accidentally to the in-memory model
12277         return (label || lookup[props.join('-')]).concat(); /*Array*/
12278 };
12279
12280 dojo.date.locale.isWeekend = function(/*Date?*/dateObject, /*String?*/locale){
12281         // summary:
12282         //      Determines if the date falls on a weekend, according to local custom.
12283
12284         var weekend = dojo.cldr.supplemental.getWeekend(locale);
12285         var day = (dateObject || new Date()).getDay();
12286         if(weekend.end < weekend.start){
12287                 weekend.end += 7;
12288                 if(day < weekend.start){ day += 7; }
12289         }
12290         return day >= weekend.start && day <= weekend.end; // Boolean
12291 };
12292
12293 // These are used only by format and strftime.  Do they need to be public?  Which module should they go in?
12294
12295 dojo.date.locale._getDayOfYear = function(/*Date*/dateObject){
12296         // summary: gets the day of the year as represented by dateObject
12297         return dojo.date.difference(new Date(dateObject.getFullYear(), 0, 1), dateObject) + 1; // Number
12298 };
12299
12300 dojo.date.locale._getWeekOfYear = function(/*Date*/dateObject, /*Number*/firstDayOfWeek){
12301         if(arguments.length == 1){ firstDayOfWeek = 0; } // Sunday
12302
12303         var firstDayOfYear = new Date(dateObject.getFullYear(), 0, 1).getDay();
12304         var adj = (firstDayOfYear - firstDayOfWeek + 7) % 7;
12305         var week = Math.floor((dojo.date.locale._getDayOfYear(dateObject) + adj - 1) / 7);
12306
12307         // if year starts on the specified day, start counting weeks at 1
12308         if(firstDayOfYear == firstDayOfWeek){ week++; }
12309
12310         return week; // Number
12311 };
12312
12313 }
12314
12315 if(!dojo._hasResource["dijit._Calendar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
12316 dojo._hasResource["dijit._Calendar"] = true;
12317 dojo.provide("dijit._Calendar");
12318
12319
12320
12321
12322
12323
12324
12325
12326 dojo.declare(
12327         "dijit._Calendar",
12328         [dijit._Widget, dijit._Templated],
12329         {
12330         //      
12331         //      summary:
12332         //              A simple GUI for choosing a date in the context of a monthly calendar.
12333         //
12334         //      description:
12335         //              A simple GUI for choosing a date in the context of a monthly calendar.
12336         //              This widget is used internally by other widgets and is not accessible
12337         //              as a standalone widget.
12338         //              This widget can't be used in a form because it doesn't serialize the date to an
12339         //              `<input>` field.  For a form element, use dijit.form.DateTextBox instead.
12340         //
12341         //              Note that the parser takes all dates attributes passed in the
12342         //              [RFC 3339 format](http://www.faqs.org/rfcs/rfc3339.html), e.g. `2005-06-30T08:05:00-07:00`
12343         //              so that they are serializable and locale-independent.
12344         //
12345         //      example:
12346         //      |       var calendar = new dijit._Calendar({}, dojo.byId("calendarNode"));
12347         //
12348         //      example:
12349         //      |       <div dojoType="dijit._Calendar"></div>
12350         //      
12351                 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<div class=\"dijitInline dijitCalendarIncrementControl dijitCalendarDecrease\"><span dojoAttachPoint=\"decreaseArrowNode\" class=\"dijitA11ySideArrow dijitCalendarIncrementControl dijitCalendarDecreaseInner\">-</span></div>\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=\"dijitCalendarMonthLabel\"></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",
12352
12353                 // value: Date
12354                 //      the currently selected Date
12355                 value: new Date(),
12356
12357                 // dayWidth: String
12358                 //      How to represent the days of the week in the calendar header. See dojo.date.locale
12359                 dayWidth: "narrow",
12360
12361                 setValue: function(/*Date*/ value){
12362                         // summary: set the current date and update the UI.  If the date is disabled, the selection will
12363                         //      not change, but the display will change to the corresponding month.
12364                         if(!this.value || dojo.date.compare(value, this.value)){
12365                                 value = new Date(value);
12366                                 this.displayMonth = new Date(value);
12367                                 if(!this.isDisabledDate(value, this.lang)){
12368                                         this.value = value;
12369                                         this.value.setHours(0,0,0,0);
12370                                         this.onChange(this.value);
12371                                 }
12372                                 this._populateGrid();
12373                         }
12374                 },
12375
12376                 _setText: function(node, text){
12377                         while(node.firstChild){
12378                                 node.removeChild(node.firstChild);
12379                         }
12380                         node.appendChild(dojo.doc.createTextNode(text));
12381                 },
12382
12383                 _populateGrid: function(){
12384                         var month = this.displayMonth;
12385                         month.setDate(1);
12386                         var firstDay = month.getDay();
12387                         var daysInMonth = dojo.date.getDaysInMonth(month);
12388                         var daysInPreviousMonth = dojo.date.getDaysInMonth(dojo.date.add(month, "month", -1));
12389                         var today = new Date();
12390                         var selected = this.value;
12391
12392                         var dayOffset = dojo.cldr.supplemental.getFirstDayOfWeek(this.lang);
12393                         if(dayOffset > firstDay){ dayOffset -= 7; }
12394
12395                         // Iterate through dates in the calendar and fill in date numbers and style info
12396                         dojo.query(".dijitCalendarDateTemplate", this.domNode).forEach(function(template, i){
12397                                 i += dayOffset;
12398                                 var date = new Date(month);
12399                                 var number, clazz = "dijitCalendar", adj = 0;
12400
12401                                 if(i < firstDay){
12402                                         number = daysInPreviousMonth - firstDay + i + 1;
12403                                         adj = -1;
12404                                         clazz += "Previous";
12405                                 }else if(i >= (firstDay + daysInMonth)){
12406                                         number = i - firstDay - daysInMonth + 1;
12407                                         adj = 1;
12408                                         clazz += "Next";
12409                                 }else{
12410                                         number = i - firstDay + 1;
12411                                         clazz += "Current";
12412                                 }
12413
12414                                 if(adj){
12415                                         date = dojo.date.add(date, "month", adj);
12416                                 }
12417                                 date.setDate(number);
12418
12419                                 if(!dojo.date.compare(date, today, "date")){
12420                                         clazz = "dijitCalendarCurrentDate " + clazz;
12421                                 }
12422
12423                                 if(!dojo.date.compare(date, selected, "date")){
12424                                         clazz = "dijitCalendarSelectedDate " + clazz;
12425                                 }
12426
12427                                 if(this.isDisabledDate(date, this.lang)){
12428                                         clazz = "dijitCalendarDisabledDate " + clazz;
12429                                 }
12430
12431                                 var clazz2 = this.getClassForDate(date, this.lang);
12432                                 if(clazz2){
12433                                         clazz += clazz2 + " " + clazz;
12434                                 }
12435
12436                                 template.className =  clazz + "Month dijitCalendarDateTemplate";
12437                                 template.dijitDateValue = date.valueOf();
12438                                 var label = dojo.query(".dijitCalendarDateLabel", template)[0];
12439                                 this._setText(label, date.getDate());
12440                         }, this);
12441
12442                         // Fill in localized month name
12443                         var monthNames = dojo.date.locale.getNames('months', 'wide', 'standAlone', this.lang);
12444                         this._setText(this.monthLabelNode, monthNames[month.getMonth()]);
12445
12446                         // Fill in localized prev/current/next years
12447                         var y = month.getFullYear() - 1;
12448                         var d = new Date();
12449                         dojo.forEach(["previous", "current", "next"], function(name){
12450                                 d.setFullYear(y++);
12451                                 this._setText(this[name+"YearLabelNode"],
12452                                         dojo.date.locale.format(d, {selector:'year', locale:this.lang}));
12453                         }, this);
12454
12455                         // Set up repeating mouse behavior
12456                         var _this = this;
12457                         var typematic = function(nodeProp, dateProp, adj){
12458                                 dijit.typematic.addMouseListener(_this[nodeProp], _this, function(count){
12459                                         if(count >= 0){ _this._adjustDisplay(dateProp, adj); }
12460                                 }, 0.8, 500);
12461                         };
12462                         typematic("incrementMonth", "month", 1);
12463                         typematic("decrementMonth", "month", -1);
12464                         typematic("nextYearLabelNode", "year", 1);
12465                         typematic("previousYearLabelNode", "year", -1);
12466                 },
12467
12468                 goToToday: function(){
12469                         this.setValue(new Date());
12470                 },
12471
12472                 postCreate: function(){
12473                         this.inherited(arguments);
12474                         
12475                         var cloneClass = dojo.hitch(this, function(clazz, n){
12476                                 var template = dojo.query(clazz, this.domNode)[0];
12477                                 for(var i=0; i<n; i++){
12478                                         template.parentNode.appendChild(template.cloneNode(true));
12479                                 }
12480                         });
12481
12482                         // clone the day label and calendar day templates 6 times to make 7 columns
12483                         cloneClass(".dijitCalendarDayLabelTemplate", 6);
12484                         cloneClass(".dijitCalendarDateTemplate", 6);
12485
12486                         // now make 6 week rows
12487                         cloneClass(".dijitCalendarWeekTemplate", 5);
12488
12489                         // insert localized day names in the header
12490                         var dayNames = dojo.date.locale.getNames('days', this.dayWidth, 'standAlone', this.lang);
12491                         var dayOffset = dojo.cldr.supplemental.getFirstDayOfWeek(this.lang);
12492                         dojo.query(".dijitCalendarDayLabel", this.domNode).forEach(function(label, i){
12493                                 this._setText(label, dayNames[(i + dayOffset) % 7]);
12494                         }, this);
12495
12496                         // Fill in spacer element with all the month names (invisible) so that the maximum width will affect layout
12497                         var monthNames = dojo.date.locale.getNames('months', 'wide', 'standAlone', this.lang);
12498                         dojo.forEach(monthNames, function(name){
12499                                 var monthSpacer = dojo.doc.createElement("div");
12500                                 this._setText(monthSpacer, name);
12501                                 this.monthLabelSpacer.appendChild(monthSpacer);
12502                         }, this);
12503
12504                         this.value = null;
12505                         this.setValue(new Date());
12506                 },
12507
12508                 _adjustDisplay: function(/*String*/part, /*int*/amount){
12509                         this.displayMonth = dojo.date.add(this.displayMonth, part, amount);
12510                         this._populateGrid();
12511                 },
12512
12513                 _onDayClick: function(/*Event*/evt){
12514                         var node = evt.target;
12515                         dojo.stopEvent(evt);
12516                         while(!node.dijitDateValue){
12517                                 node = node.parentNode;
12518                         }
12519                         if(!dojo.hasClass(node, "dijitCalendarDisabledDate")){
12520                                 this.setValue(node.dijitDateValue);
12521                                 this.onValueSelected(this.value);
12522                         }
12523                 },
12524
12525                 onValueSelected: function(/*Date*/date){
12526                         // summary: a date cell was selected.  It may be the same as the previous value.
12527                 },
12528
12529                 onChange: function(/*Date*/date){
12530                         // summary: called only when the selected date has changed
12531                 },
12532
12533                 isDisabledDate: function(/*Date*/dateObject, /*String?*/locale){
12534                         // summary:
12535                         //      May be overridden to disable certain dates in the calendar e.g. `isDisabledDate=dojo.date.locale.isWeekend`
12536 /*=====
12537                         return false; // Boolean
12538 =====*/
12539                 },
12540
12541                 getClassForDate: function(/*Date*/dateObject, /*String?*/locale){
12542                         // summary:
12543                         //  May be overridden to return CSS classes to associate with the date entry for the given dateObject,
12544                         //  for example to indicate a holiday in specified locale.
12545
12546 /*=====
12547                         return ""; // String
12548 =====*/
12549                 }
12550         }
12551 );
12552
12553 }
12554
12555 if(!dojo._hasResource["dijit.form._DateTimeTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
12556 dojo._hasResource["dijit.form._DateTimeTextBox"] = true;
12557 dojo.provide("dijit.form._DateTimeTextBox");
12558
12559
12560
12561
12562
12563
12564 /*=====
12565 dojo.declare(
12566         "dijit.form._DateTimeTextBox.__Constraints",
12567         [dijit.form.RangeBoundTextBox.__Constraints, dojo.date.locale.__FormatOptions]
12568 );
12569 =====*/
12570
12571 dojo.declare(
12572         "dijit.form._DateTimeTextBox",
12573         dijit.form.RangeBoundTextBox,
12574         {
12575                 // summary:
12576                 //              A validating, serializable, range-bound date or time text box.
12577                 //
12578                 // constraints: dijit.form._DateTimeTextBox.__Constraints 
12579
12580                 /*=====
12581                 constraints: {},
12582                 ======*/
12583                 regExpGen: dojo.date.locale.regexp,
12584                 compare: dojo.date.compare,
12585                 format: function(/*Date*/ value, /*dojo.date.locale.__FormatOptions*/ constraints){
12586                         //      summary: formats the value as a Date, according to constraints
12587                         if(!value){ return ''; }
12588                         return dojo.date.locale.format(value, constraints);
12589                 },
12590                 parse: function(/*String*/ value, /*dojo.date.locale.__FormatOptions*/ constraints){
12591                         //      summary: parses the value as a Date, according to constraints
12592                         return dojo.date.locale.parse(value, constraints) || undefined; /* can't return null to getValue since that's special */
12593                 },
12594
12595                 serialize: dojo.date.stamp.toISOString,
12596
12597                 //      value: Date
12598                 //              The value of this widget as a JavaScript Date object.  Use `getValue`/`setValue` to manipulate.
12599                 //              When passed to the parser in markup, must be specified according to `dojo.date.stamp.fromISOString`
12600                 value: new Date(""),    // value.toString()="NaN"
12601
12602                 //      popupClass: String
12603                 //              Name of the popup widget class used to select a date/time
12604                 popupClass: "", // default is no popup = text only
12605                 _selector: "",
12606
12607                 postMixInProperties: function(){
12608                         //dijit.form.RangeBoundTextBox.prototype.postMixInProperties.apply(this, arguments);
12609                         this.inherited(arguments);
12610                         if(!this.value || this.value.toString() == dijit.form._DateTimeTextBox.prototype.value.toString()){
12611                                 this.value = undefined;
12612                         }
12613                         var constraints = this.constraints;
12614                         constraints.selector = this._selector;
12615                         constraints.fullYear = true; // see #5465 - always format with 4-digit years
12616                         var fromISO = dojo.date.stamp.fromISOString;
12617                         if(typeof constraints.min == "string"){ constraints.min = fromISO(constraints.min); }
12618                         if(typeof constraints.max == "string"){ constraints.max = fromISO(constraints.max); }
12619                 },
12620
12621                 _onFocus: function(/*Event*/ evt){
12622                         // summary: open the TimePicker popup
12623                         this._open();
12624                 },
12625
12626                 setValue: function(/*Date*/ value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
12627                         // summary:
12628                         //      Sets the date on this textbox.  Note that `value` must be a Javascript Date object.
12629                         this.inherited(arguments);
12630                         if(this._picker){
12631                                 // #3948: fix blank date on popup only
12632                                 if(!value){value=new Date();}
12633                                 this._picker.setValue(value);
12634                         }
12635                 },
12636
12637                 _open: function(){
12638                         // summary:
12639                         //      opens the TimePicker, and sets the onValueSelected value
12640
12641                         if(this.disabled || this.readOnly || !this.popupClass){return;}
12642
12643                         var textBox = this;
12644
12645                         if(!this._picker){
12646                                 var PopupProto=dojo.getObject(this.popupClass, false);
12647                                 this._picker = new PopupProto({
12648                                         onValueSelected: function(value){
12649
12650                                                 textBox.focus(); // focus the textbox before the popup closes to avoid reopening the popup
12651                                                 setTimeout(dojo.hitch(textBox, "_close"), 1); // allow focus time to take
12652
12653                                                 // this will cause InlineEditBox and other handlers to do stuff so make sure it's last
12654                                                 dijit.form._DateTimeTextBox.superclass.setValue.call(textBox, value, true);
12655                                         },
12656                                         lang: textBox.lang,
12657                                         constraints: textBox.constraints,
12658                                         isDisabledDate: function(/*Date*/ date){
12659                                                 // summary:
12660                                                 //      disables dates outside of the min/max of the _DateTimeTextBox
12661                                                 var compare = dojo.date.compare;
12662                                                 var constraints = textBox.constraints;
12663                                                 return constraints && (constraints.min && (compare(constraints.min, date, "date") > 0) || 
12664                                                         (constraints.max && compare(constraints.max, date, "date") < 0));
12665                                         }
12666                                 });
12667                                 this._picker.setValue(this.getValue() || new Date());
12668                         }
12669                         if(!this._opened){
12670                                 dijit.popup.open({
12671                                         parent: this,
12672                                         popup: this._picker,
12673                                         around: this.domNode,
12674                                         onCancel: dojo.hitch(this, this._close),
12675                                         onClose: function(){ textBox._opened=false; }
12676                                 });
12677                                 this._opened=true;
12678                         }
12679                         
12680                         dojo.marginBox(this._picker.domNode,{ w:this.domNode.offsetWidth });
12681                 },
12682
12683                 _close: function(){
12684                         if(this._opened){
12685                                 dijit.popup.close(this._picker);
12686                                 this._opened=false;
12687                         }                       
12688                 },
12689
12690                 _onBlur: function(){
12691                         // summary: called magically when focus has shifted away from this widget and it's dropdown
12692                         this._close();
12693                         if(this._picker){
12694                                 // teardown so that constraints will be rebuilt next time (redundant reference: #6002)
12695                                 this._picker.destroy();
12696                                 delete this._picker;
12697                         }
12698                         this.inherited(arguments);
12699                         // don't focus on <input>.  the user has explicitly focused on something else.
12700                 },
12701
12702                 getDisplayedValue:function(){
12703                         return this.textbox.value;
12704                 },
12705
12706                 setDisplayedValue:function(/*String*/ value, /*Boolean?*/ priorityChange){
12707                         this.setValue(this.parse(value, this.constraints), priorityChange, value);
12708                 },
12709
12710                 destroy: function(){
12711                         if(this._picker){
12712                                 this._picker.destroy();
12713                                 delete this._picker;
12714                         }
12715                         this.inherited(arguments);
12716                 },
12717
12718                 _onKeyPress: function(/*Event*/e){
12719                         if(dijit.form._DateTimeTextBox.superclass._onKeyPress.apply(this, arguments)){
12720                                 if(this._opened && e.keyCode == dojo.keys.ESCAPE && !e.shiftKey && !e.ctrlKey && !e.altKey){
12721                                         this._close();
12722                                         dojo.stopEvent(e);
12723                                 }
12724                         }
12725                 }
12726         }
12727 );
12728
12729 }
12730
12731 if(!dojo._hasResource["dijit.form.DateTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
12732 dojo._hasResource["dijit.form.DateTextBox"] = true;
12733 dojo.provide("dijit.form.DateTextBox");
12734
12735
12736
12737
12738 dojo.declare(
12739         "dijit.form.DateTextBox",
12740         dijit.form._DateTimeTextBox,
12741         {
12742                 // summary:
12743                 //              A validating, serializable, range-bound date text box with a popup calendar
12744
12745                 popupClass: "dijit._Calendar",
12746                 _selector: "date"
12747         }
12748 );
12749
12750 }
12751
12752 if(!dojo._hasResource["dijit.form.FilteringSelect"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
12753 dojo._hasResource["dijit.form.FilteringSelect"] = true;
12754 dojo.provide("dijit.form.FilteringSelect");
12755
12756
12757
12758 dojo.declare(
12759         "dijit.form.FilteringSelect",
12760         [dijit.form.MappedTextBox, dijit.form.ComboBoxMixin],
12761         {
12762                 // summary
12763                 // An enhanced version of the HTML SELECT tag, populated dynamically
12764                 //
12765                 // description
12766                 // An enhanced version of the HTML SELECT tag, populated dynamically. It works
12767                 // very nicely with very large data sets because it can load and page data as needed.
12768                 // It also resembles ComboBox, but does not allow values outside of the provided ones.
12769                 //  
12770                 // Similar features:
12771                 //  - There is a drop down list of possible values.
12772                 //      - You can only enter a value from the drop down list.  (You can't
12773                 //        enter an arbitrary value.)
12774                 //      - The value submitted with the form is the hidden value (ex: CA),
12775                 //        not the displayed value a.k.a. label (ex: California)
12776                 // 
12777                 //      Enhancements over plain HTML version:
12778                 //      - If you type in some text then it will filter down the list of
12779                 //        possible values in the drop down list.
12780                 //      - List can be specified either as a static list or via a javascript
12781                 //        function (that can get the list from a server)
12782                 //
12783                 // searchAttr: String
12784                 //              Searches pattern match against this field
12785                 //
12786                 // labelAttr: String
12787                 //              Optional.  The text that actually appears in the drop down.
12788                 //              If not specified, the searchAttr text is used instead.
12789                 labelAttr: "",
12790
12791                 // labelType: String
12792                 //              "html" or "text"
12793                 labelType: "text",
12794
12795                 _isvalid:true,
12796
12797                 _lastDisplayedValue: "",
12798
12799                 isValid:function(){
12800                         return this._isvalid;
12801                 },
12802
12803                 _callbackSetLabel: function(    /*Array*/ result, 
12804                                                 /*Object*/ dataObject, 
12805                                                 /*Boolean?*/ priorityChange){
12806                         // summary:
12807                         //              Callback function that dynamically sets the label of the
12808                         //              ComboBox
12809
12810                         // setValue does a synchronous lookup,
12811                         // so it calls _callbackSetLabel directly,
12812                         // and so does not pass dataObject
12813                         // dataObject==null means do not test the lastQuery, just continue
12814                         if(dataObject && dataObject.query[this.searchAttr] != this._lastQuery){
12815                                 return;
12816                         }
12817                         if(!result.length){
12818                                 //#3268: do nothing on bad input
12819                                 //this._setValue("", "");
12820                                 //#3285: change CSS to indicate error
12821                                 if(!this._focused){ this.valueNode.value=""; }
12822                                 dijit.form.TextBox.superclass.setValue.call(this, undefined, !this._focused);
12823                                 this._isvalid=false;
12824                                 this.validate(this._focused);
12825                         }else{
12826                                 this._setValueFromItem(result[0], priorityChange);
12827                         }
12828                 },
12829
12830                 _openResultList: function(/*Object*/ results, /*Object*/ dataObject){
12831                         // #3285: tap into search callback to see if user's query resembles a match
12832                         if(dataObject.query[this.searchAttr] != this._lastQuery){
12833                                 return;
12834                         }
12835                         this._isvalid = results.length != 0; // FIXME: should this be greater-than?
12836                         this.validate(true);
12837                         dijit.form.ComboBoxMixin.prototype._openResultList.apply(this, arguments);
12838                 },
12839
12840                 getValue:function(){
12841                         // don't get the textbox value but rather the previously set hidden value
12842                         return this.valueNode.value;
12843                 },
12844
12845                 _getValueField:function(){
12846                         // used for option tag selects
12847                         return "value";
12848                 },
12849
12850                 _setValue:function(     /*String*/ value, 
12851                                         /*String*/ displayedValue, 
12852                                         /*Boolean?*/ priorityChange){
12853                         this.valueNode.value = value;
12854                         dijit.form.FilteringSelect.superclass.setValue.call(this, value, priorityChange, displayedValue);
12855                         this._lastDisplayedValue = displayedValue;
12856                 },
12857
12858                 setValue: function(/*String*/ value, /*Boolean?*/ priorityChange){
12859                         // summary
12860                         //      Sets the value of the select.
12861                         //      Also sets the label to the corresponding value by reverse lookup.
12862
12863                         //#3347: fetchItemByIdentity if no keyAttr specified
12864                         var self=this;
12865                         var handleFetchByIdentity = function(item, priorityChange){
12866                                 if(item){
12867                                         if(self.store.isItemLoaded(item)){
12868                                                 self._callbackSetLabel([item], undefined, priorityChange);
12869                                         }else{
12870                                                 self.store.loadItem({
12871                                                         item: item, 
12872                                                         onItem: function(result, dataObject){
12873                                                                 self._callbackSetLabel(result, dataObject, priorityChange);
12874                                                         }
12875                                                 });
12876                                         }
12877                                 }else{
12878                                         self._isvalid=false;
12879                                         // prevent errors from Tooltip not being created yet
12880                                         self.validate(false);
12881                                 }
12882                         }
12883                         this.store.fetchItemByIdentity({
12884                                 identity: value, 
12885                                 onItem: function(item){
12886                                         handleFetchByIdentity(item, priorityChange);
12887                                 }
12888                         });
12889                 },
12890
12891                 _setValueFromItem: function(/*item*/ item, /*Boolean?*/ priorityChange){
12892                         //      summary:
12893                         //              Set the displayed valued in the input box, based on a
12894                         //              selected item.
12895                         //      description:
12896                         //              Users shouldn't call this function; they should be calling
12897                         //              setDisplayedValue() instead
12898                         this._isvalid=true;
12899                         this._setValue( this.store.getIdentity(item), 
12900                                                         this.labelFunc(item, this.store), 
12901                                                         priorityChange);
12902                 },
12903
12904                 labelFunc: function(/*item*/ item, /*dojo.data.store*/ store){
12905                         // summary: Event handler called when the label changes
12906                         // return: the label that the ComboBox should display
12907                         return store.getValue(item, this.searchAttr);
12908                 },
12909
12910                 _doSelect: function(/*Event*/ tgt){
12911                         // summary:
12912                         //              ComboBox's menu callback function
12913                         //      description:
12914                         //              FilteringSelect overrides this to set both the visible and
12915                         //              hidden value from the information stored in the menu
12916                         this.item = tgt.item;
12917                         this._setValueFromItem(tgt.item, true);
12918                 },
12919
12920                 setDisplayedValue:function(/*String*/ label, /*Boolean?*/ priorityChange){
12921                         // summary:
12922                         //              Set textbox to display label. Also performs reverse lookup
12923                         //              to set the hidden value. Used in InlineEditBox
12924
12925                         if(this.store){
12926                                 var query = dojo.clone(this.query); // #6196: populate query with user-specifics
12927                                 this._lastQuery = query[this.searchAttr] = label;
12928                                 // if the label is not valid, the callback will never set it,
12929                                 // so the last valid value will get the warning textbox set the
12930                                 // textbox value now so that the impending warning will make
12931                                 // sense to the user
12932                                 this.textbox.value = label;
12933                                 this._lastDisplayedValue = label;
12934                                 var _this = this;
12935                                 this.store.fetch({
12936                                         query: query, 
12937                                         queryOptions: {
12938                                                 ignoreCase: this.ignoreCase, 
12939                                                 deep: true
12940                                         }, 
12941                                         onComplete: function(result, dataObject){
12942                                                         dojo.hitch(_this, "_callbackSetLabel")(result, dataObject, priorityChange);
12943                                         },
12944                                         onError: function(errText){
12945                                                 console.error('dijit.form.FilteringSelect: ' + errText);
12946                                                 dojo.hitch(_this, "_setValue")(undefined, label, false);
12947                                         }
12948                                 });
12949                         }
12950                 },
12951
12952                 _getMenuLabelFromItem:function(/*Item*/ item){
12953                         // internal function to help ComboBoxMenu figure out what to display
12954                         if(this.labelAttr){
12955                                 return {
12956                                         html: this.labelType=="html", 
12957                                         label: this.store.getValue(item, this.labelAttr)
12958                                 };
12959                         }else{
12960                                 // because this function is called by ComboBoxMenu,
12961                                 // this.inherited tries to find the superclass of ComboBoxMenu
12962                                 return dijit.form.ComboBoxMixin.prototype._getMenuLabelFromItem.apply(this, arguments);
12963                         }
12964                 },
12965
12966                 postMixInProperties: function(){
12967                         // FIXME: shouldn't this just be a call to inherited?
12968                         dijit.form.ComboBoxMixin.prototype.postMixInProperties.apply(this, arguments);
12969                         dijit.form.MappedTextBox.prototype.postMixInProperties.apply(this, arguments);
12970                 },
12971
12972                 postCreate: function(){
12973                         dijit.form.ComboBoxMixin.prototype._postCreate.apply(this, arguments);
12974                         dijit.form.MappedTextBox.prototype.postCreate.apply(this, arguments);
12975                 },
12976                 
12977                 setAttribute: function(/*String*/ attr, /*anything*/ value){
12978                         dijit.form.MappedTextBox.prototype.setAttribute.apply(this, arguments);
12979                         dijit.form.ComboBoxMixin.prototype._setAttribute.apply(this, arguments);
12980                 },
12981
12982                 undo: function(){
12983                         this.setDisplayedValue(this._lastDisplayedValue);
12984                 },
12985
12986                 _valueChanged: function(){
12987                         return this.getDisplayedValue()!=this._lastDisplayedValue;
12988                 }
12989         }
12990 );
12991
12992 }
12993
12994 if(!dojo._hasResource["dijit.form._Spinner"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
12995 dojo._hasResource["dijit.form._Spinner"] = true;
12996 dojo.provide("dijit.form._Spinner");
12997
12998
12999
13000 dojo.declare(
13001         "dijit.form._Spinner",
13002         dijit.form.RangeBoundTextBox,
13003         {
13004
13005                 // summary: Mixin for validation widgets with a spinner
13006                 // description: This class basically (conceptually) extends dijit.form.ValidationTextBox.
13007                 //      It modifies the template to have up/down arrows, and provides related handling code.
13008
13009                 // defaultTimeout: Number
13010                 //        number of milliseconds before a held key or button becomes typematic
13011                 defaultTimeout: 500,
13012
13013                 // timeoutChangeRate: Number
13014                 //        fraction of time used to change the typematic timer between events
13015                 //        1.0 means that each typematic event fires at defaultTimeout intervals
13016                 //        < 1.0 means that each typematic event fires at an increasing faster rate
13017                 timeoutChangeRate: 0.90,
13018
13019                 // smallDelta: Number
13020                 //        adjust the value by this much when spinning using the arrow keys/buttons
13021                 smallDelta: 1,
13022                 // largeDelta: Number
13023                 //        adjust the value by this much when spinning using the PgUp/Dn keys
13024                 largeDelta: 10,
13025
13026                 templateString:"<div class=\"dijit dijitReset dijitInlineTable dijitLeft\"\n\tid=\"widget_${id}\"\n\tdojoAttachEvent=\"onmouseenter:_onMouse,onmouseleave:_onMouse,onmousedown:_onMouse\" waiRole=\"presentation\"\n\t><div class=\"dijitInputLayoutContainer\"\n\t\t><div class=\"dijitReset dijitSpinnerButtonContainer\"\n\t\t\t>&nbsp;<div class=\"dijitReset dijitLeft dijitButtonNode dijitArrowButton dijitUpArrowButton\"\n\t\t\t\tdojoAttachPoint=\"upArrowNode\"\n\t\t\t\tdojoAttachEvent=\"onmouseenter:_onMouse,onmouseleave:_onMouse\"\n\t\t\t\tstateModifier=\"UpArrow\"\n\t\t\t\t><div class=\"dijitArrowButtonInner\">&thinsp;</div\n\t\t\t\t><div class=\"dijitArrowButtonChar\">&#9650;</div\n\t\t\t></div\n\t\t\t><div class=\"dijitReset dijitLeft dijitButtonNode dijitArrowButton dijitDownArrowButton\"\n\t\t\t\tdojoAttachPoint=\"downArrowNode\"\n\t\t\t\tdojoAttachEvent=\"onmouseenter:_onMouse,onmouseleave:_onMouse\"\n\t\t\t\tstateModifier=\"DownArrow\"\n\t\t\t\t><div class=\"dijitArrowButtonInner\">&thinsp;</div\n\t\t\t\t><div class=\"dijitArrowButtonChar\">&#9660;</div\n\t\t\t></div\n\t\t></div\n\t\t><div class=\"dijitReset dijitValidationIcon\"><br></div\n\t\t><div class=\"dijitReset dijitValidationIconText\">&Chi;</div\n\t\t><div class=\"dijitReset dijitInputField\"\n\t\t\t><input class='dijitReset' dojoAttachPoint=\"textbox,focusNode\" type=\"${type}\" dojoAttachEvent=\"onfocus:_update,onkeyup:_onkeyup,onkeypress:_onKeyPress\"\n\t\t\t\twaiRole=\"spinbutton\" autocomplete=\"off\" name=\"${name}\"\n\t\t/></div\n\t></div\n></div>\n",
13027                 baseClass: "dijitSpinner",
13028
13029                 adjust: function(/* Object */ val, /*Number*/ delta){
13030                         // summary: user replaceable function used to adjust a primitive value(Number/Date/...) by the delta amount specified
13031                         // the val is adjusted in a way that makes sense to the object type
13032                         return val;
13033                 },
13034
13035                 _arrowState: function(/*Node*/ node, /*Boolean*/ pressed){
13036                         this._active = pressed;
13037                         this.stateModifier = node.getAttribute("stateModifier") || "";
13038                         this._setStateClass();
13039                 },
13040
13041                 _arrowPressed: function(/*Node*/ nodePressed, /*Number*/ direction){
13042                         if(this.disabled || this.readOnly){ return; }
13043                         this._arrowState(nodePressed, true);
13044                         this.setValue(this.adjust(this.getValue(), direction*this.smallDelta), false);
13045                         dijit.selectInputText(this.textbox, this.textbox.value.length);
13046                 },
13047
13048                 _arrowReleased: function(/*Node*/ node){
13049                         this._wheelTimer = null;
13050                         if(this.disabled || this.readOnly){ return; }
13051                         this._arrowState(node, false);
13052                 },
13053
13054                 _typematicCallback: function(/*Number*/ count, /*DOMNode*/ node, /*Event*/ evt){
13055                         if(node == this.textbox){ node = (evt.keyCode == dojo.keys.UP_ARROW) ? this.upArrowNode : this.downArrowNode; }
13056                         if(count == -1){ this._arrowReleased(node); }
13057                         else{ this._arrowPressed(node, (node == this.upArrowNode) ? 1 : -1); }
13058                 },
13059
13060                 _wheelTimer: null,
13061                 _mouseWheeled: function(/*Event*/ evt){
13062                         dojo.stopEvent(evt);
13063                         var scrollAmount = 0;
13064                         if(typeof evt.wheelDelta == 'number'){ // IE
13065                                 scrollAmount = evt.wheelDelta;
13066                         }else if(typeof evt.detail == 'number'){ // Mozilla+Firefox
13067                                 scrollAmount = -evt.detail;
13068                         }
13069                         var node, dir;
13070                         if(scrollAmount > 0){
13071                                 node = this.upArrowNode;
13072                                 dir = +1;
13073                         }else if(scrollAmount < 0){
13074                                 node = this.downArrowNode;
13075                                 dir = -1;
13076                         }else{ return; }
13077                         this._arrowPressed(node, dir);
13078                         if(this._wheelTimer != null){
13079                                 clearTimeout(this._wheelTimer);
13080                         }
13081                         var _this = this;
13082                         this._wheelTimer = setTimeout(function(){_this._arrowReleased(node);}, 50);
13083                 },
13084
13085                 postCreate: function(){
13086                         this.inherited('postCreate', arguments);
13087
13088                         // extra listeners
13089                         this.connect(this.textbox, dojo.isIE ? "onmousewheel" : 'DOMMouseScroll', "_mouseWheeled");
13090                         this._connects.push(dijit.typematic.addListener(this.upArrowNode, this.textbox, {keyCode:dojo.keys.UP_ARROW,ctrlKey:false,altKey:false,shiftKey:false}, this, "_typematicCallback", this.timeoutChangeRate, this.defaultTimeout));
13091                         this._connects.push(dijit.typematic.addListener(this.downArrowNode, this.textbox, {keyCode:dojo.keys.DOWN_ARROW,ctrlKey:false,altKey:false,shiftKey:false}, this, "_typematicCallback", this.timeoutChangeRate, this.defaultTimeout));
13092                         if(dojo.isIE){
13093                                 // When spinner is moved from hidden to visible, call _setStateClass to remind IE to render it. (#6123)
13094                                 var _this = this;
13095                                 this.connect(this.domNode, "onresize", 
13096                                         function(){ setTimeout(dojo.hitch(_this,
13097                                                 function(){
13098                                                         // cause the IE expressions to rerun
13099                                                         this.upArrowNode.style.behavior = '';
13100                                                         this.downArrowNode.style.behavior = '';
13101                                                         // cause IE to rerender
13102                                                         this._setStateClass();
13103                                                 }), 0);
13104                                         }
13105                                 );
13106                         }
13107                 }
13108 });
13109
13110 }
13111
13112 if(!dojo._hasResource["dijit.form.NumberSpinner"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
13113 dojo._hasResource["dijit.form.NumberSpinner"] = true;
13114 dojo.provide("dijit.form.NumberSpinner");
13115
13116
13117
13118
13119 dojo.declare(
13120 "dijit.form.NumberSpinner",
13121 [dijit.form._Spinner, dijit.form.NumberTextBoxMixin],
13122 {
13123         // summary:
13124         // extends NumberTextBox to add up/down arrows for incremental change to the value
13125
13126         required: true,
13127
13128         adjust: function(/* Object */ val, /*Number*/ delta){
13129                 // summary: change Number val by the given amount
13130                 var newval = val+delta;
13131                 if(isNaN(val) || isNaN(newval)){ return val; }
13132                 if((typeof this.constraints.max == "number") && (newval > this.constraints.max)){
13133                         newval = this.constraints.max;
13134                 }
13135                 if((typeof this.constraints.min == "number") && (newval < this.constraints.min)){
13136                         newval = this.constraints.min;
13137                 }
13138                 return newval;
13139         }
13140 });
13141
13142 }
13143
13144 if(!dojo._hasResource["dojo.dnd.move"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
13145 dojo._hasResource["dojo.dnd.move"] = true;
13146 dojo.provide("dojo.dnd.move");
13147
13148
13149
13150
13151 dojo.declare("dojo.dnd.move.constrainedMoveable", dojo.dnd.Moveable, {
13152         // object attributes (for markup)
13153         constraints: function(){},
13154         within: false,
13155         
13156         // markup methods
13157         markupFactory: function(params, node){
13158                 return new dojo.dnd.move.constrainedMoveable(node, params);
13159         },
13160
13161         constructor: function(node, params){
13162                 // summary: an object, which makes a node moveable
13163                 // node: Node: a node (or node's id) to be moved
13164                 // params: Object: an optional object with additional parameters;
13165                 //      following parameters are recognized:
13166                 //              constraints: Function: a function, which calculates a constraint box,
13167                 //                      it is called in a context of the moveable object.
13168                 //              within: Boolean: restrict move within boundaries.
13169                 //      the rest is passed to the base class
13170                 if(!params){ params = {}; }
13171                 this.constraints = params.constraints;
13172                 this.within = params.within;
13173         },
13174         onFirstMove: function(/* dojo.dnd.Mover */ mover){
13175                 // summary: called during the very first move notification,
13176                 //      can be used to initialize coordinates, can be overwritten.
13177                 var c = this.constraintBox = this.constraints.call(this, mover);
13178                 c.r = c.l + c.w;
13179                 c.b = c.t + c.h;
13180                 if(this.within){
13181                         var mb = dojo.marginBox(mover.node);
13182                         c.r -= mb.w;
13183                         c.b -= mb.h;
13184                 }
13185         },
13186         onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
13187                 // summary: called during every move notification,
13188                 //      should actually move the node, can be overwritten.
13189                 var c = this.constraintBox, s = mover.node.style;
13190                 s.left = (leftTop.l < c.l ? c.l : c.r < leftTop.l ? c.r : leftTop.l) + "px";
13191                 s.top  = (leftTop.t < c.t ? c.t : c.b < leftTop.t ? c.b : leftTop.t) + "px";
13192         }
13193 });
13194
13195 dojo.declare("dojo.dnd.move.boxConstrainedMoveable", dojo.dnd.move.constrainedMoveable, {
13196         // object attributes (for markup)
13197         box: {},
13198         
13199         // markup methods
13200         markupFactory: function(params, node){
13201                 return new dojo.dnd.move.boxConstrainedMoveable(node, params);
13202         },
13203
13204         constructor: function(node, params){
13205                 // summary: an object, which makes a node moveable
13206                 // node: Node: a node (or node's id) to be moved
13207                 // params: Object: an optional object with additional parameters;
13208                 //      following parameters are recognized:
13209                 //              box: Object: a constraint box
13210                 //      the rest is passed to the base class
13211                 var box = params && params.box;
13212                 this.constraints = function(){ return box; };
13213         }
13214 });
13215
13216 dojo.declare("dojo.dnd.move.parentConstrainedMoveable", dojo.dnd.move.constrainedMoveable, {
13217         // object attributes (for markup)
13218         area: "content",
13219
13220         // markup methods
13221         markupFactory: function(params, node){
13222                 return new dojo.dnd.move.parentConstrainedMoveable(node, params);
13223         },
13224
13225         constructor: function(node, params){
13226                 // summary: an object, which makes a node moveable
13227                 // node: Node: a node (or node's id) to be moved
13228                 // params: Object: an optional object with additional parameters;
13229                 //      following parameters are recognized:
13230                 //              area: String: a parent's area to restrict the move,
13231                 //                      can be "margin", "border", "padding", or "content".
13232                 //      the rest is passed to the base class
13233                 var area = params && params.area;
13234                 this.constraints = function(){
13235                         var n = this.node.parentNode, 
13236                                 s = dojo.getComputedStyle(n), 
13237                                 mb = dojo._getMarginBox(n, s);
13238                         if(area == "margin"){
13239                                 return mb;      // Object
13240                         }
13241                         var t = dojo._getMarginExtents(n, s);
13242                         mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
13243                         if(area == "border"){
13244                                 return mb;      // Object
13245                         }
13246                         t = dojo._getBorderExtents(n, s);
13247                         mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
13248                         if(area == "padding"){
13249                                 return mb;      // Object
13250                         }
13251                         t = dojo._getPadExtents(n, s);
13252                         mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
13253                         return mb;      // Object
13254                 };
13255         }
13256 });
13257
13258 // WARNING: below are obsolete objects, instead of custom movers use custom moveables (above)
13259
13260 dojo.dnd.move.constrainedMover = function(fun, within){
13261         // summary: returns a constrained version of dojo.dnd.Mover
13262         // description: this function produces n object, which will put a constraint on 
13263         //      the margin box of dragged object in absolute coordinates
13264         // fun: Function: called on drag, and returns a constraint box
13265         // within: Boolean: if true, constraints the whole dragged object withtin the rectangle, 
13266         //      otherwise the constraint is applied to the left-top corner
13267         dojo.deprecated("dojo.dnd.move.constrainedMover, use dojo.dnd.move.constrainedMoveable instead");
13268         var mover = function(node, e, notifier){
13269                 dojo.dnd.Mover.call(this, node, e, notifier);
13270         };
13271         dojo.extend(mover, dojo.dnd.Mover.prototype);
13272         dojo.extend(mover, {
13273                 onMouseMove: function(e){
13274                         // summary: event processor for onmousemove
13275                         // e: Event: mouse event
13276                         dojo.dnd.autoScroll(e);
13277                         var m = this.marginBox, c = this.constraintBox,
13278                                 l = m.l + e.pageX, t = m.t + e.pageY;
13279                         l = l < c.l ? c.l : c.r < l ? c.r : l;
13280                         t = t < c.t ? c.t : c.b < t ? c.b : t;
13281                         this.host.onMove(this, {l: l, t: t});
13282                 },
13283                 onFirstMove: function(){
13284                         // summary: called once to initialize things; it is meant to be called only once
13285                         dojo.dnd.Mover.prototype.onFirstMove.call(this);
13286                         var c = this.constraintBox = fun.call(this);
13287                         c.r = c.l + c.w;
13288                         c.b = c.t + c.h;
13289                         if(within){
13290                                 var mb = dojo.marginBox(this.node);
13291                                 c.r -= mb.w;
13292                                 c.b -= mb.h;
13293                         }
13294                 }
13295         });
13296         return mover;   // Object
13297 };
13298
13299 dojo.dnd.move.boxConstrainedMover = function(box, within){
13300         // summary: a specialization of dojo.dnd.constrainedMover, which constrains to the specified box
13301         // box: Object: a constraint box (l, t, w, h)
13302         // within: Boolean: if true, constraints the whole dragged object withtin the rectangle, 
13303         //      otherwise the constraint is applied to the left-top corner
13304         dojo.deprecated("dojo.dnd.move.boxConstrainedMover, use dojo.dnd.move.boxConstrainedMoveable instead");
13305         return dojo.dnd.move.constrainedMover(function(){ return box; }, within);       // Object
13306 };
13307
13308 dojo.dnd.move.parentConstrainedMover = function(area, within){
13309         // summary: a specialization of dojo.dnd.constrainedMover, which constrains to the parent node
13310         // area: String: "margin" to constrain within the parent's margin box, "border" for the border box,
13311         //      "padding" for the padding box, and "content" for the content box; "content" is the default value.
13312         // within: Boolean: if true, constraints the whole dragged object withtin the rectangle, 
13313         //      otherwise the constraint is applied to the left-top corner
13314         dojo.deprecated("dojo.dnd.move.parentConstrainedMover, use dojo.dnd.move.parentConstrainedMoveable instead");
13315         var fun = function(){
13316                 var n = this.node.parentNode, 
13317                         s = dojo.getComputedStyle(n), 
13318                         mb = dojo._getMarginBox(n, s);
13319                 if(area == "margin"){
13320                         return mb;      // Object
13321                 }
13322                 var t = dojo._getMarginExtents(n, s);
13323                 mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
13324                 if(area == "border"){
13325                         return mb;      // Object
13326                 }
13327                 t = dojo._getBorderExtents(n, s);
13328                 mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
13329                 if(area == "padding"){
13330                         return mb;      // Object
13331                 }
13332                 t = dojo._getPadExtents(n, s);
13333                 mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
13334                 return mb;      // Object
13335         };
13336         return dojo.dnd.move.constrainedMover(fun, within);     // Object
13337 };
13338
13339 // patching functions one level up for compatibility
13340
13341 dojo.dnd.constrainedMover = dojo.dnd.move.constrainedMover;
13342 dojo.dnd.boxConstrainedMover = dojo.dnd.move.boxConstrainedMover;
13343 dojo.dnd.parentConstrainedMover = dojo.dnd.move.parentConstrainedMover;
13344
13345 }
13346
13347 if(!dojo._hasResource["dijit.form.Slider"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
13348 dojo._hasResource["dijit.form.Slider"] = true;
13349 dojo.provide("dijit.form.Slider");
13350
13351
13352
13353
13354
13355
13356
13357
13358 dojo.declare(
13359         "dijit.form.HorizontalSlider",
13360         [dijit.form._FormValueWidget, dijit._Container],
13361 {
13362         // summary
13363         //      A form widget that allows one to select a value with a horizontally draggable image
13364
13365         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 dijitSliderButtonContainerH\"\n\t\t\t><div class=\"dijitSliderDecrementIconH\" 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 dijitSliderBumperH dijitSliderLeftBumper dijitSliderLeftBumper\" dojoAttachEvent=\"onclick:_onClkDecBumper\"></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 waiRole=\"presentation\" style=\"position:relative;\" dojoAttachPoint=\"sliderBarContainer\"\n\t\t\t\t><div waiRole=\"presentation\" dojoAttachPoint=\"progressBar\" class=\"dijitSliderBar dijitSliderBarH dijitSliderProgressBar dijitSliderProgressBarH\" dojoAttachEvent=\"onclick:_onBarClick\"\n\t\t\t\t\t><div dojoAttachPoint=\"sliderHandle,focusNode\" class=\"dijitSliderMoveable dijitSliderMoveableH\" dojoAttachEvent=\"onkeypress:_onKeyPress,onmousedown:_onHandleClick\" waiRole=\"slider\" valuemin=\"${minimum}\" valuemax=\"${maximum}\"\n\t\t\t\t\t\t><div class=\"dijitSliderImageHandle dijitSliderImageHandleH\"></div\n\t\t\t\t\t></div\n\t\t\t\t></div\n\t\t\t\t><div waiRole=\"presentation\" dojoAttachPoint=\"remainingBar\" class=\"dijitSliderBar dijitSliderBarH dijitSliderRemainingBar dijitSliderRemainingBarH\" 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 dijitSliderBumperH dijitSliderRightBumper dijitSliderRightBumper\" dojoAttachEvent=\"onclick:_onClkIncBumper\"></div\n\t\t></td\n\t\t><td class=\"dijitReset dijitSliderButtonContainer dijitSliderButtonContainerH\" style=\"right:0px;\"\n\t\t\t><div class=\"dijitSliderIncrementIconH\" 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",
13366         value: 0,
13367
13368         // showButtons: boolean
13369         //      Show increment/decrement buttons at the ends of the slider?
13370         showButtons: true,
13371
13372         // minimum:: integer
13373         //      The minimum value allowed.
13374         minimum: 0,
13375
13376         // maximum: integer
13377         //      The maximum allowed value.
13378         maximum: 100,
13379
13380         // discreteValues: integer
13381         //      The maximum allowed values dispersed evenly between minimum and maximum (inclusive).
13382         discreteValues: Infinity,
13383
13384         // pageIncrement: integer
13385         //      The amount of change with shift+arrow
13386         pageIncrement: 2,
13387
13388         // clickSelect: boolean
13389         //      If clicking the progress bar changes the value or not
13390         clickSelect: true,
13391
13392         // slideDuration: Number
13393         //      The time in ms to take to animate the slider handle from 0% to 100%
13394         slideDuration: 1000,
13395
13396         widgetsInTemplate: true,
13397
13398         attributeMap: dojo.mixin(dojo.clone(dijit.form._FormWidget.prototype.attributeMap),
13399                 {id:"", name:"valueNode"}),
13400
13401         baseClass: "dijitSlider",
13402
13403         _mousePixelCoord: "pageX",
13404         _pixelCount: "w",
13405         _startingPixelCoord: "x",
13406         _startingPixelCount: "l",
13407         _handleOffsetCoord: "left",
13408         _progressPixelSize: "width",
13409
13410         _onKeyPress: function(/*Event*/ e){
13411                 if(this.disabled || this.readOnly || e.altKey || e.ctrlKey){ return; }
13412                 switch(e.keyCode){
13413                         case dojo.keys.HOME:
13414                                 this.setValue(this.minimum, true);
13415                                 break;
13416                         case dojo.keys.END:
13417                                 this.setValue(this.maximum, true);
13418                                 break;
13419                         // this._descending === false: if ascending vertical (min on top)
13420                         // (this._descending || this.isLeftToRight()): if left-to-right horizontal or descending vertical
13421                         case ((this._descending || this.isLeftToRight()) ? dojo.keys.RIGHT_ARROW : dojo.keys.LEFT_ARROW):
13422                         case (this._descending === false ? dojo.keys.DOWN_ARROW : dojo.keys.UP_ARROW):
13423                         case (this._descending === false ? dojo.keys.PAGE_DOWN : dojo.keys.PAGE_UP):
13424                                 this.increment(e);
13425                                 break;
13426                         case ((this._descending || this.isLeftToRight()) ? dojo.keys.LEFT_ARROW : dojo.keys.RIGHT_ARROW):
13427                         case (this._descending === false ? dojo.keys.UP_ARROW : dojo.keys.DOWN_ARROW):
13428                         case (this._descending === false ? dojo.keys.PAGE_UP : dojo.keys.PAGE_DOWN):
13429                                 this.decrement(e);
13430                                 break;
13431                         default:
13432                                 this.inherited(arguments);
13433                                 return;
13434                 }
13435                 dojo.stopEvent(e);
13436         },
13437
13438         _onHandleClick: function(e){
13439                 if(this.disabled || this.readOnly){ return; }
13440                 if(!dojo.isIE){
13441                         // make sure you get focus when dragging the handle
13442                         // (but don't do on IE because it causes a flicker on mouse up (due to blur then focus)
13443                         dijit.focus(this.sliderHandle);
13444                 }
13445                 dojo.stopEvent(e);
13446         },
13447         
13448         _isReversed: function(){
13449                 return !this.isLeftToRight();
13450         },
13451
13452         _onBarClick: function(e){
13453                 if(this.disabled || this.readOnly || !this.clickSelect){ return; }
13454                 dijit.focus(this.sliderHandle);
13455                 dojo.stopEvent(e);
13456                 var abspos = dojo.coords(this.sliderBarContainer, true);
13457                 var pixelValue = e[this._mousePixelCoord] - abspos[this._startingPixelCoord];
13458                 this._setPixelValue(this._isReversed() ? (abspos[this._pixelCount] - pixelValue) : pixelValue, abspos[this._pixelCount], true);
13459         },
13460
13461         _setPixelValue: function(/*Number*/ pixelValue, /*Number*/ maxPixels, /*Boolean, optional*/ priorityChange){
13462                 if(this.disabled || this.readOnly){ return; }
13463                 pixelValue = pixelValue < 0 ? 0 : maxPixels < pixelValue ? maxPixels : pixelValue;
13464                 var count = this.discreteValues;
13465                 if(count <= 1 || count == Infinity){ count = maxPixels; }
13466                 count--;
13467                 var pixelsPerValue = maxPixels / count;
13468                 var wholeIncrements = Math.round(pixelValue / pixelsPerValue);
13469                 this.setValue((this.maximum-this.minimum)*wholeIncrements/count + this.minimum, priorityChange);
13470         },
13471
13472         setValue: function(/*Number*/ value, /*Boolean, optional*/ priorityChange){
13473                 this.valueNode.value = this.value = value;
13474                 dijit.setWaiState(this.focusNode, "valuenow", value);
13475                 this.inherited(arguments);
13476                 var percent = (value - this.minimum) / (this.maximum - this.minimum);
13477                 var progressBar = (this._descending === false) ? this.remainingBar : this.progressBar;
13478                 var remainingBar = (this._descending === false) ? this.progressBar : this.remainingBar;
13479                 if(priorityChange && this.slideDuration > 0 && progressBar.style[this._progressPixelSize]){
13480                         // animate the slider
13481                         var _this = this;
13482                         var props = {};
13483                         var start = parseFloat(progressBar.style[this._progressPixelSize]);
13484                         var duration = this.slideDuration * (percent-start/100);
13485                         if(duration == 0){ return; }
13486                         if(duration < 0){ duration = 0 - duration; }
13487                         props[this._progressPixelSize] = { start: start, end: percent*100, units:"%" };
13488                         dojo.animateProperty({ node: progressBar, duration: duration, 
13489                                 onAnimate: function(v){ remainingBar.style[_this._progressPixelSize] = (100-parseFloat(v[_this._progressPixelSize])) + "%"; },
13490                                 properties: props
13491                         }).play();
13492                 }
13493                 else{
13494                         progressBar.style[this._progressPixelSize] = (percent*100) + "%";
13495                         remainingBar.style[this._progressPixelSize] = ((1-percent)*100) + "%";
13496                 }
13497         },
13498
13499         _bumpValue: function(signedChange){
13500                 if(this.disabled || this.readOnly){ return; }
13501                 var s = dojo.getComputedStyle(this.sliderBarContainer);
13502                 var c = dojo._getContentBox(this.sliderBarContainer, s);
13503                 var count = this.discreteValues;
13504                 if(count <= 1 || count == Infinity){ count = c[this._pixelCount]; }
13505                 count--;
13506                 var value = (this.value - this.minimum) * count / (this.maximum - this.minimum) + signedChange;
13507                 if(value < 0){ value = 0; }
13508                 if(value > count){ value = count; }
13509                 value = value * (this.maximum - this.minimum) / count + this.minimum;
13510                 this.setValue(value, true);
13511         },
13512
13513         _onClkIncBumper: function(){
13514                 this.setValue(this._descending === false ? this.minimum : this.maximum, true);
13515         },
13516
13517         _onClkDecBumper: function(){
13518                 this.setValue(this._descending === false ? this.maximum : this.minimum, true);
13519         },
13520
13521         decrement: function(e){
13522                 // summary
13523                 //      decrement slider by 1 unit
13524                 this._bumpValue(e.keyCode == dojo.keys.PAGE_DOWN?-this.pageIncrement:-1);
13525         },
13526
13527         increment: function(e){
13528                 // summary
13529                 //      increment slider by 1 unit
13530                 this._bumpValue(e.keyCode == dojo.keys.PAGE_UP?this.pageIncrement:1);
13531         },
13532
13533         _mouseWheeled: function(/*Event*/ evt){
13534                 dojo.stopEvent(evt);
13535                 var scrollAmount = 0;
13536                 if(typeof evt.wheelDelta == 'number'){ // IE
13537                         scrollAmount = evt.wheelDelta;
13538                 }else if(typeof evt.detail == 'number'){ // Mozilla+Firefox
13539                         scrollAmount = -evt.detail;
13540                 }
13541                 if(scrollAmount > 0){
13542                         this.increment(evt);
13543                 }else if(scrollAmount < 0){
13544                         this.decrement(evt);
13545                 }
13546         },
13547
13548         startup: function(){
13549                 dojo.forEach(this.getChildren(), function(child){
13550                         if(this[child.container] != this.containerNode){
13551                                 this[child.container].appendChild(child.domNode);
13552                         }
13553                 }, this);
13554         },
13555
13556         postCreate: function(){
13557                 if(this.showButtons){
13558                         this.incrementButton.style.display="";
13559                         this.decrementButton.style.display="";
13560                 }
13561                 this.connect(this.domNode, dojo.isIE ? "onmousewheel" : 'DOMMouseScroll', "_mouseWheeled");
13562
13563                 // define a custom constructor for a SliderMover that points back to me
13564                 var _self = this;
13565                 var mover = function(){
13566                         dijit.form._SliderMover.apply(this, arguments);
13567                         this.widget = _self;
13568                 };
13569                 dojo.extend(mover, dijit.form._SliderMover.prototype);
13570
13571                 this._movable = new dojo.dnd.Moveable(this.sliderHandle, {mover: mover});
13572                 dijit.setWaiState(this.focusNode, "valuemin", this.minimum);
13573                 dijit.setWaiState(this.focusNode, "valuemax", this.maximum);
13574
13575                 this.inherited(arguments);
13576         },
13577
13578         destroy: function(){
13579                 this._movable.destroy();
13580                 this.inherited(arguments);      
13581         }
13582 });
13583
13584 dojo.declare(
13585         "dijit.form.VerticalSlider",
13586         dijit.form.HorizontalSlider,
13587 {
13588         // summary
13589         //      A form widget that allows one to select a value with a vertically draggable image
13590
13591         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 dijitSliderButtonContainerV\"\n\t\t\t><div class=\"dijitSliderIncrementIconV\" tabIndex=\"-1\" style=\"display:none\" dojoAttachPoint=\"incrementButton\" dojoAttachEvent=\"onclick:_topButtonClicked\"><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 dijitSliderBumperV dijitSliderTopBumper dijitSliderTopBumper\" dojoAttachEvent=\"onclick:_onClkIncBumper\"></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 waiRole=\"presentation\" style=\"position:relative;height:100%;\" dojoAttachPoint=\"sliderBarContainer\"\n\t\t\t\t><div waiRole=\"presentation\" dojoAttachPoint=\"remainingBar\" class=\"dijitSliderBar dijitSliderBarV dijitSliderRemainingBar dijitSliderRemainingBarV\" dojoAttachEvent=\"onclick:_onBarClick\"><!--#5629--></div\n\t\t\t\t><div waiRole=\"presentation\" dojoAttachPoint=\"progressBar\" class=\"dijitSliderBar dijitSliderBarV dijitSliderProgressBar dijitSliderProgressBarV\" dojoAttachEvent=\"onclick:_onBarClick\"\n\t\t\t\t\t><div dojoAttachPoint=\"sliderHandle,focusNode\" class=\"dijitSliderMoveable\" dojoAttachEvent=\"onkeypress:_onKeyPress,onmousedown:_onHandleClick\" style=\"vertical-align:top;\" waiRole=\"slider\" valuemin=\"${minimum}\" valuemax=\"${maximum}\"\n\t\t\t\t\t\t><div class=\"dijitSliderImageHandle dijitSliderImageHandleV\"></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 dijitSliderBumperV dijitSliderBottomBumper dijitSliderBottomBumper\" dojoAttachEvent=\"onclick:_onClkDecBumper\"></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 dijitSliderButtonContainerV\"\n\t\t\t><div class=\"dijitSliderDecrementIconV\" tabIndex=\"-1\" style=\"display:none\" dojoAttachPoint=\"decrementButton\" dojoAttachEvent=\"onclick:_bottomButtonClicked\"><span class=\"dijitSliderButtonInner\">-</span></div\n\t\t></td\n\t\t><td class=\"dijitReset\"></td\n\t></tr\n></tbody></table>\n",
13592         _mousePixelCoord: "pageY",
13593         _pixelCount: "h",
13594         _startingPixelCoord: "y",
13595         _startingPixelCount: "t",
13596         _handleOffsetCoord: "top",
13597         _progressPixelSize: "height",
13598
13599         // _descending: boolean
13600         //      Specifies if the slider values go from high-on-top (true), or low-on-top (false)
13601         //      TODO: expose this in 1.2 - the css progress/remaining bar classes need to be reversed
13602         _descending: true,
13603
13604         startup: function(){
13605                 if(this._started){ return; }
13606
13607                 if(!this.isLeftToRight() && dojo.isMoz){
13608                         if(this.leftDecoration){this._rtlRectify(this.leftDecoration);}
13609                         if(this.rightDecoration){this._rtlRectify(this.rightDecoration);}
13610                 }
13611
13612                 this.inherited(arguments);
13613         },
13614                 
13615         _isReversed: function(){
13616                 return this._descending;
13617         },
13618
13619         _topButtonClicked: function(e){
13620                 if(this._descending){
13621                         this.increment(e);
13622                 }else{
13623                         this.decrement(e);
13624                 }
13625         },
13626
13627         _bottomButtonClicked: function(e){
13628                 if(this._descending){
13629                         this.decrement(e);
13630                 }else{
13631                         this.increment(e);
13632                 }
13633         },
13634
13635         _rtlRectify: function(decorationNode/*NodeList*/){
13636                 // summary:
13637                 //      Rectify children nodes for left/right decoration in rtl case.
13638                 //              Simply switch the rule and label child for each decoration node.
13639                 var childNodes = [];
13640                 while(decorationNode.firstChild){
13641                                 childNodes.push(decorationNode.firstChild);
13642                                 decorationNode.removeChild(decorationNode.firstChild);
13643                 }
13644                 for(var i = childNodes.length-1; i >=0; i--){
13645                         if(childNodes[i]){
13646                                 decorationNode.appendChild(childNodes[i]);
13647                         }
13648                 }
13649         }
13650 });
13651
13652 dojo.declare("dijit.form._SliderMover",
13653         dojo.dnd.Mover,
13654 {
13655         onMouseMove: function(e){
13656                 var widget = this.widget;
13657                 var abspos = widget._abspos;
13658                 if(!abspos){
13659                         abspos = widget._abspos = dojo.coords(widget.sliderBarContainer, true);
13660                         widget._setPixelValue_ = dojo.hitch(widget, "_setPixelValue");
13661                         widget._isReversed_ = widget._isReversed();
13662                 }
13663                 var pixelValue = e[widget._mousePixelCoord] - abspos[widget._startingPixelCoord];
13664                 widget._setPixelValue_(widget._isReversed_ ? (abspos[widget._pixelCount]-pixelValue) : pixelValue, abspos[widget._pixelCount], false);
13665         },
13666         
13667         destroy: function(e){
13668                 dojo.dnd.Mover.prototype.destroy.apply(this, arguments);
13669                 var widget = this.widget;
13670                 widget.setValue(widget.value, true);
13671         }
13672 });
13673
13674
13675 dojo.declare("dijit.form.HorizontalRule", [dijit._Widget, dijit._Templated],
13676 {
13677         //      Summary:
13678         //              Create hash marks for the Horizontal slider
13679         templateString: '<div class="dijitRuleContainer dijitRuleContainerH"></div>',
13680
13681         // count: Integer
13682         //              Number of hash marks to generate
13683         count: 3,
13684
13685         // container: Node
13686         //              If this is a child widget, connect it to this parent node
13687         container: "containerNode",
13688
13689         // ruleStyle: String
13690         //              CSS style to apply to individual hash marks
13691         ruleStyle: "",
13692
13693         _positionPrefix: '<div class="dijitRuleMark dijitRuleMarkH" style="left:',
13694         _positionSuffix: '%;',
13695         _suffix: '"></div>',
13696
13697         _genHTML: function(pos, ndx){
13698                 return this._positionPrefix + pos + this._positionSuffix + this.ruleStyle + this._suffix;
13699         },
13700         
13701         _isHorizontal: true,
13702
13703         postCreate: function(){
13704                 var innerHTML;
13705                 if(this.count==1){
13706                         innerHTML = this._genHTML(50, 0);
13707                 }else{
13708                         var i;
13709                         var interval = 100 / (this.count-1);
13710                         if(!this._isHorizontal || this.isLeftToRight()){
13711                                 innerHTML = this._genHTML(0, 0);
13712                                 for(i=1; i < this.count-1; i++){
13713                                         innerHTML += this._genHTML(interval*i, i);
13714                                 }
13715                                 innerHTML += this._genHTML(100, this.count-1);
13716                         }else{
13717                                 innerHTML = this._genHTML(100, 0);
13718                                 for(i=1; i < this.count-1; i++){
13719                                         innerHTML += this._genHTML(100-interval*i, i);
13720                                 }
13721                                 innerHTML += this._genHTML(0, this.count-1);
13722                         }
13723                 }
13724                 this.domNode.innerHTML = innerHTML;
13725         }
13726 });
13727
13728 dojo.declare("dijit.form.VerticalRule", dijit.form.HorizontalRule,
13729 {
13730         //      Summary:
13731         //              Create hash marks for the Vertical slider
13732         templateString: '<div class="dijitRuleContainer dijitRuleContainerV"></div>',
13733         _positionPrefix: '<div class="dijitRuleMark dijitRuleMarkV" style="top:',
13734         
13735         _isHorizontal: false
13736 });
13737
13738 dojo.declare("dijit.form.HorizontalRuleLabels", dijit.form.HorizontalRule,
13739 {
13740         //      Summary:
13741         //              Create labels for the Horizontal slider
13742         templateString: '<div class="dijitRuleContainer dijitRuleContainerH"></div>',
13743
13744         // labelStyle: String
13745         //              CSS style to apply to individual text labels
13746         labelStyle: "",
13747
13748         // labels: Array
13749         //      Array of text labels to render - evenly spaced from left-to-right or bottom-to-top
13750         labels: [],
13751
13752         // numericMargin: Integer
13753         //      Number of generated numeric labels that should be rendered as '' on the ends when labels[] are not specified
13754         numericMargin: 0,
13755
13756         // numericMinimum: Integer
13757         //      Leftmost label value for generated numeric labels when labels[] are not specified
13758         minimum: 0,
13759
13760         // numericMaximum: Integer
13761         //      Rightmost label value for generated numeric labels when labels[] are not specified
13762         maximum: 1,
13763
13764         // constraints: object
13765         //      pattern, places, lang, et al (see dojo.number) for generated numeric labels when labels[] are not specified
13766         constraints: {pattern:"#%"},
13767
13768         _positionPrefix: '<div class="dijitRuleLabelContainer dijitRuleLabelContainerH" style="left:',
13769         _labelPrefix: '"><span class="dijitRuleLabel dijitRuleLabelH">',
13770         _suffix: '</span></div>',
13771
13772         _calcPosition: function(pos){
13773                 return pos;
13774         },
13775
13776         _genHTML: function(pos, ndx){
13777                 return this._positionPrefix + this._calcPosition(pos) + this._positionSuffix + this.labelStyle + this._labelPrefix + this.labels[ndx] + this._suffix;
13778         },
13779
13780         getLabels: function(){
13781                 // summary: user replaceable function to return the labels array
13782
13783                 // if the labels array was not specified directly, then see if <li> children were
13784                 var labels = this.labels;
13785                 if(!labels.length){
13786                         // for markup creation, labels are specified as child elements
13787                         labels = dojo.query("> li", this.srcNodeRef).map(function(node){
13788                                 return String(node.innerHTML);
13789                         });
13790                 }
13791                 this.srcNodeRef.innerHTML = '';
13792                 // if the labels were not specified directly and not as <li> children, then calculate numeric labels
13793                 if(!labels.length && this.count > 1){
13794                         var start = this.minimum;
13795                         var inc = (this.maximum - start) / (this.count-1);
13796                         for (var i=0; i < this.count; i++){
13797                                 labels.push((i<this.numericMargin||i>=(this.count-this.numericMargin))? '' : dojo.number.format(start, this.constraints));
13798                                 start += inc;
13799                         }
13800                 }
13801                 return labels;
13802         },
13803
13804         postMixInProperties: function(){
13805                 this.inherited(arguments);
13806                 this.labels = this.getLabels();
13807                 this.count = this.labels.length;
13808         }
13809 });
13810
13811 dojo.declare("dijit.form.VerticalRuleLabels", dijit.form.HorizontalRuleLabels,
13812 {
13813         //      Summary:
13814         //              Create labels for the Vertical slider
13815         templateString: '<div class="dijitRuleContainer dijitRuleContainerV"></div>',
13816
13817         _positionPrefix: '<div class="dijitRuleLabelContainer dijitRuleLabelContainerV" style="top:',
13818         _labelPrefix: '"><span class="dijitRuleLabel dijitRuleLabelV">',
13819
13820         _calcPosition: function(pos){
13821                 return 100-pos;
13822         },
13823         
13824         _isHorizontal: false
13825 });
13826
13827 }
13828
13829 if(!dojo._hasResource["dijit.form.Textarea"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
13830 dojo._hasResource["dijit.form.Textarea"] = true;
13831 dojo.provide("dijit.form.Textarea");
13832
13833
13834
13835
13836
13837 dojo.declare(
13838         "dijit.form.Textarea",
13839         dijit.form._FormValueWidget,
13840         {
13841         // summary: A resizing textarea widget
13842         //
13843         // description:
13844         //      A textarea that resizes vertically to contain the data.
13845         //      Takes nearly all the parameters (name, value, etc.) that a vanilla textarea takes.
13846         //      Cols is not supported and the width should be specified with style width.
13847         //      Rows is not supported since this widget adjusts the height.
13848         //
13849         // example:
13850         // |    <textarea dojoType="dijit.form.TextArea">...</textarea>
13851         //
13852
13853         attributeMap: dojo.mixin(dojo.clone(dijit.form._FormValueWidget.prototype.attributeMap),
13854                 {style:"styleNode", 'class':"styleNode"}),
13855
13856         templateString: (dojo.isIE || dojo.isSafari || dojo.isFF) ?
13857                                 ((dojo.isIE || dojo.isSafari || dojo.isFF >= 3) ? '<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;display:block;overflow:auto;" contentEditable="true"></div>'
13858                                         : '<span id="${id}" class="dijitReset">'+
13859                                         '<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>"'+
13860                                                         ' dojoAttachPoint="iframe,styleNode" dojoAttachEvent="onblur:_onIframeBlur" class="dijitInline dijitInputField dijitTextArea"></iframe>')
13861                                 + '<textarea name="${name}" value="${value}" dojoAttachPoint="formValueNode" style="display:none;"></textarea>'
13862                                 + ((dojo.isIE || dojo.isSafari || dojo.isFF >= 3) ? '</fieldset>':'</span>')
13863                         : '<textarea id="${id}" name="${name}" value="${value}" dojoAttachPoint="formValueNode,editNode,focusNode,styleNode" class="dijitInputField dijitTextArea">'+dojo.isFF+'</textarea>',
13864
13865         setAttribute: function(/*String*/ attr, /*anything*/ value){
13866                 this.inherited(arguments);
13867                 switch(attr){
13868                         case "disabled":
13869                                 this.formValueNode.disabled = this.disabled;
13870                         case "readOnly":
13871                                 if(dojo.isIE || dojo.isSafari || dojo.isFF >= 3){
13872                                         this.editNode.contentEditable = (!this.disabled && !this.readOnly);
13873                                 }else if(dojo.isFF){
13874                                         this.iframe.contentDocument.designMode = (this.disabled || this.readOnly)? "off" : "on";
13875                                 }
13876                 }
13877         },
13878
13879         focus: function(){
13880                 // summary: Received focus, needed for the InlineEditBox widget
13881                 if(!this.disabled && !this.readOnly){
13882                         this._changing(); // set initial height
13883                 }
13884                 dijit.focus(this.iframe || this.focusNode);
13885         },
13886
13887         setValue: function(/*String*/ value, /*Boolean, optional*/ priorityChange){
13888                 var editNode = this.editNode;
13889                 if(typeof value == "string"){
13890                         editNode.innerHTML = ""; // wipe out old nodes
13891                         if(value.split){
13892                                 var _this=this;
13893                                 var isFirst = true;
13894                                 dojo.forEach(value.split("\n"), function(line){
13895                                         if(isFirst){ isFirst = false; }
13896                                         else{
13897                                                 editNode.appendChild(dojo.doc.createElement("BR")); // preserve line breaks
13898                                         }
13899                                         if(line){
13900                                                 editNode.appendChild(dojo.doc.createTextNode(line)); // use text nodes so that imbedded tags can be edited
13901                                         }
13902                                 });
13903                         }else if(value){
13904                                 editNode.appendChild(dojo.doc.createTextNode(value));
13905                         }
13906                         if(!dojo.isIE){
13907                                 editNode.appendChild(dojo.doc.createElement("BR")); // so that you see a cursor
13908                         }
13909                 }else{
13910                         // blah<BR>blah --> blah\nblah
13911                         // <P>blah</P><P>blah</P> --> blah\nblah
13912                         // <DIV>blah</DIV><DIV>blah</DIV> --> blah\nblah
13913                         // &amp;&lt;&gt; -->&< >
13914                         value = editNode.innerHTML;
13915                         if(this.iframe){ // strip sizeNode
13916                                 value = value.replace(/<div><\/div>\r?\n?$/i,"");
13917                         }
13918                         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[^>]*>|<\/div>\s*<div[^>]*>/gi,"\n").replace(/<[^>]*>/g,"").replace(/&amp;/gi,"\&").replace(/&lt;/gi,"<").replace(/&gt;/gi,">");
13919                         if(!dojo.isIE){
13920                                 value = value.replace(/\n$/,""); // remove added <br>
13921                         }
13922                 }
13923                 this.value = this.formValueNode.value = value;
13924                 if(this.iframe){
13925                         var sizeNode = dojo.doc.createElement('div');
13926                         editNode.appendChild(sizeNode);
13927                         var newHeight = sizeNode.offsetTop;
13928                         if(editNode.scrollWidth > editNode.clientWidth){ newHeight+=16; } // scrollbar space needed?
13929                         if(this.lastHeight != newHeight){ // cache size so that we don't get a resize event because of a resize event
13930                                 if(newHeight == 0){ newHeight = 16; } // height = 0 causes the browser to not set scrollHeight
13931                                 dojo.contentBox(this.iframe, {h: newHeight});
13932                                 this.lastHeight = newHeight;
13933                         }
13934                         editNode.removeChild(sizeNode);
13935                 }
13936                 dijit.form.Textarea.superclass.setValue.call(this, this.getValue(), priorityChange);
13937         },
13938
13939         getValue: function(){
13940                 return this.value.replace(/\r/g,"");
13941         },
13942
13943         postMixInProperties: function(){
13944                 this.inherited(arguments);
13945                 // don't let the source text be converted to a DOM structure since we just want raw text
13946                 if(this.srcNodeRef && this.srcNodeRef.innerHTML != ""){
13947                         this.value = this.srcNodeRef.innerHTML;
13948                         this.srcNodeRef.innerHTML = "";
13949                 }
13950                 if((!this.value || this.value == "") && this.srcNodeRef && this.srcNodeRef.value){
13951                         this.value = this.srcNodeRef.value;
13952                 }
13953                 if(!this.value){ this.value = ""; }
13954                 this.value = this.value.replace(/\r\n/g,"\n").replace(/&gt;/g,">").replace(/&lt;/g,"<").replace(/&amp;/g,"&");
13955                 if(dojo.isFF == 2){
13956                         // In the case of Firefox an iframe is used and when the text gets focus,
13957                         // focus is fired from the document object.  There isn't a way to put a
13958                         // waiRole on the document object and as a result screen readers don't
13959                         // announce the role.  As a result screen reader users are lost.
13960                         //
13961                         // An additional problem is that the browser gives the document object a
13962                         // very cryptic accessible name, e.g.
13963                         // wysiwyg://13/http://archive.dojotoolkit.org/nightly/dojotoolkit/dijit/tests/form/test_InlineEditBox.html
13964                         // When focus is fired from the document object, the screen reader speaks
13965                         // the accessible name.  The cyptic accessile name is confusing.
13966                         //
13967                         // A workaround for both of these problems is to give the iframe's
13968                         // document a title, the name of which is similar to a role name, i.e.
13969                         // "edit area".  This will be used as the accessible name which will replace
13970                         // the cryptic name and will also convey the role information to the user.
13971                         // Because it is read directly to the user, the string must be localized.
13972                         // In addition, since a <label> element can not be associated with an iframe, if 
13973                         // this control has a label, insert the label text into the title as well.
13974                         var _nlsResources = dojo.i18n.getLocalization("dijit.form", "Textarea");
13975                         this._iframeEditTitle = _nlsResources.iframeEditTitle;
13976                         this._iframeFocusTitle = _nlsResources.iframeFocusTitle;
13977                         var label=dojo.query('label[for="'+this.id+'"]');
13978                         if(label.length){
13979                                 this._iframeEditTitle = label[0].innerHTML + " " + this._iframeEditTitle;
13980                         }
13981                         var body = this.focusNode = this.editNode = dojo.doc.createElement('BODY');
13982                         body.style.margin="0px";
13983                         body.style.padding="0px";
13984                         body.style.border="0px";
13985                 }
13986         },
13987
13988         postCreate: function(){
13989                 if(dojo.isIE || dojo.isSafari || dojo.isFF >= 3){
13990                         this.domNode.style.overflowY = 'hidden';
13991                 }else if(dojo.isFF){
13992                         var w = this.iframe.contentWindow;
13993                         var title = '';
13994                         try { // #4715: peeking at the title can throw a security exception during iframe setup
13995                                 title = this.iframe.contentDocument.title;
13996                         } catch(e) {}
13997                         if(!w || !title){
13998                                 this.iframe.postCreate = dojo.hitch(this, this.postCreate);
13999                                 return;
14000                         }
14001                         var d = w.document;
14002                         d.getElementsByTagName('HTML')[0].replaceChild(this.editNode, d.getElementsByTagName('BODY')[0]);
14003                         if(!this.isLeftToRight()){
14004                                 d.getElementsByTagName('HTML')[0].dir = "rtl";
14005                         }                       
14006                         this.iframe.style.overflowY = 'hidden';
14007                         this.eventNode = d;
14008                         // this.connect won't destroy this handler cleanly since its on the iframe's window object
14009                         // resize is a method of window, not document
14010                         w.addEventListener("resize", dojo.hitch(this, this._changed), false); // resize is only on the window object
14011                 }else{
14012                         this.focusNode = this.domNode;
14013                 }
14014                 if(this.eventNode){
14015                         this.connect(this.eventNode, "keypress", this._onKeyPress);
14016                         this.connect(this.eventNode, "mousemove", this._changed);
14017                         this.connect(this.eventNode, "focus", this._focused);
14018                         this.connect(this.eventNode, "blur", this._blurred);
14019                 }
14020                 if(this.editNode){
14021                         this.connect(this.editNode, "change", this._changed); // needed for mouse paste events per #3479
14022                 }
14023                 this.inherited('postCreate', arguments);
14024         },
14025
14026         // event handlers, you can over-ride these in your own subclasses
14027         _focused: function(e){
14028                 dojo.addClass(this.iframe||this.domNode, "dijitInputFieldFocused");
14029                 this._changed(e);
14030         },
14031
14032         _blurred: function(e){
14033                 dojo.removeClass(this.iframe||this.domNode, "dijitInputFieldFocused");
14034                 this._changed(e, true);
14035         },
14036
14037         _onIframeBlur: function(){
14038                 // Reset the title back to "edit area".
14039                 this.iframe.contentDocument.title = this._iframeEditTitle;
14040         },
14041
14042         _onKeyPress: function(e){
14043                 if(e.keyCode == dojo.keys.TAB && !e.shiftKey && !e.ctrlKey && !e.altKey && this.iframe){
14044                         // Pressing the tab key in the iframe (with designMode on) will cause the
14045                         // entry of a tab character so we have to trap that here.  Since we don't
14046                         // know the next focusable object we put focus on the iframe and then the
14047                         // user has to press tab again (which then does the expected thing).
14048                         // A problem with that is that the screen reader user hears "edit area"
14049                         // announced twice which causes confusion.  By setting the
14050                         // contentDocument's title to "edit area frame" the confusion should be
14051                         // eliminated.
14052                         this.iframe.contentDocument.title = this._iframeFocusTitle;
14053                         // Place focus on the iframe. A subsequent tab or shift tab will put focus
14054                         // on the correct control.
14055                         // Note: Can't use this.focus() because that results in a call to
14056                         // dijit.focus and if that receives an iframe target it will set focus
14057                         // on the iframe's contentWindow.
14058                         this.iframe.focus();  // this.focus(); won't work
14059                         dojo.stopEvent(e);
14060                 }else if(e.keyCode == dojo.keys.ENTER){
14061                         e.stopPropagation();
14062                 }else if(this.inherited("_onKeyPress", arguments) && this.iframe){
14063                         // #3752:
14064                         // The key press will not make it past the iframe.
14065                         // If a widget is listening outside of the iframe, (like InlineEditBox)
14066                         // it will not hear anything.
14067                         // Create an equivalent event so everyone else knows what is going on.
14068                         var te = dojo.doc.createEvent("KeyEvents");
14069                         te.initKeyEvent("keypress", true, true, null, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.keyCode, e.charCode);
14070                         this.iframe.dispatchEvent(te);
14071                 }
14072                 this._changing();
14073         },
14074
14075         _changing: function(e){
14076                 // summary: event handler for when a change is imminent
14077                 setTimeout(dojo.hitch(this, "_changed", e, false), 1);
14078         },
14079
14080         _changed: function(e, priorityChange){
14081                 // summary: event handler for when a change has already happened
14082                 if(this.iframe && this.iframe.contentDocument.designMode != "on" && !this.disabled && !this.readOnly){
14083                         this.iframe.contentDocument.designMode="on"; // in case this failed on init due to being hidden
14084                 }
14085                 this.setValue(null, priorityChange || false);
14086         }
14087 });
14088
14089 }
14090
14091 if(!dojo._hasResource["dijit.layout.StackContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
14092 dojo._hasResource["dijit.layout.StackContainer"] = true;
14093 dojo.provide("dijit.layout.StackContainer");
14094
14095
14096
14097
14098
14099
14100
14101 dojo.declare(
14102         "dijit.layout.StackContainer",
14103         dijit.layout._LayoutWidget,
14104         {
14105         // summary: 
14106         //      A container that has multiple children, but shows only
14107         //      one child at a time
14108         //
14109         // description:
14110         //      A container for widgets (ContentPanes, for example) That displays
14111         //      only one Widget at a time.
14112         //      
14113         //      Publishes topics [widgetId]-addChild, [widgetId]-removeChild, and [widgetId]-selectChild
14114         //
14115         //      Can be base class for container, Wizard, Show, etc.
14116         // 
14117         //
14118         // doLayout: Boolean
14119         //  if true, change the size of my currently displayed child to match my size
14120         doLayout: true,
14121
14122         _started: false,
14123 /*=====
14124         // selectedChildWidget: Widget
14125         //      References the currently selected child widget, if any
14126         //
14127         selectedChildWidget: null,
14128 =====*/
14129         postCreate: function(){
14130                 dijit.setWaiRole((this.containerNode || this.domNode), "tabpanel");
14131                 this.connect(this.domNode, "onkeypress", this._onKeyPress);
14132         },
14133         
14134         startup: function(){
14135                 if(this._started){ return; }
14136
14137                 var children = this.getChildren();
14138
14139                 // Setup each page panel
14140                 dojo.forEach(children, this._setupChild, this);
14141
14142                 // Figure out which child to initially display
14143                 dojo.some(children, function(child){
14144                         if(child.selected){
14145                                 this.selectedChildWidget = child;
14146                         }
14147                         return child.selected;
14148                 }, this);
14149
14150                 var selected = this.selectedChildWidget;
14151
14152                 // Default to the first child
14153                 if(!selected && children[0]){
14154                         selected = this.selectedChildWidget = children[0];
14155                         selected.selected = true;
14156                 }
14157                 if(selected){
14158                         this._showChild(selected);
14159                 }
14160
14161                 // Now publish information about myself so any StackControllers can initialize..
14162                 dojo.publish(this.id+"-startup", [{children: children, selected: selected}]);
14163
14164                 this.inherited(arguments);
14165         },
14166
14167         _setupChild: function(/*Widget*/ page){
14168                 // Summary: prepare the given child
14169
14170                 page.domNode.style.display = "none";
14171
14172                 // since we are setting the width/height of the child elements, they need
14173                 // to be position:relative, or IE has problems (See bug #2033)
14174                 page.domNode.style.position = "relative";
14175
14176                 return page; // dijit._Widget
14177         },
14178
14179         addChild: function(/*Widget*/ child, /*Integer?*/ insertIndex){
14180                 // summary: Adds a widget to the stack
14181                  
14182                 dijit._Container.prototype.addChild.apply(this, arguments);
14183                 child = this._setupChild(child);
14184
14185                 if(this._started){
14186                         // in case the tab titles have overflowed from one line to two lines
14187                         this.layout();
14188
14189                         dojo.publish(this.id+"-addChild", [child, insertIndex]);
14190
14191                         // if this is the first child, then select it
14192                         if(!this.selectedChildWidget){
14193                                 this.selectChild(child);
14194                         }
14195                 }
14196         },
14197
14198         removeChild: function(/*Widget*/ page){
14199                 // summary: Removes the pane from the stack
14200
14201                 dijit._Container.prototype.removeChild.apply(this, arguments);
14202
14203                 // If we are being destroyed than don't run the code below (to select another page), because we are deleting
14204                 // every page one by one
14205                 if(this._beingDestroyed){ return; }
14206
14207                 if(this._started){
14208                         // this will notify any tablists to remove a button; do this first because it may affect sizing
14209                         dojo.publish(this.id+"-removeChild", [page]);
14210
14211                         // in case the tab titles now take up one line instead of two lines
14212                         this.layout();
14213                 }
14214
14215                 if(this.selectedChildWidget === page){
14216                         this.selectedChildWidget = undefined;
14217                         if(this._started){
14218                                 var children = this.getChildren();
14219                                 if(children.length){
14220                                         this.selectChild(children[0]);
14221                                 }
14222                         }
14223                 }
14224         },
14225
14226         selectChild: function(/*Widget*/ page){
14227                 // summary:
14228                 //      Show the given widget (which must be one of my children)
14229
14230                 page = dijit.byId(page);
14231
14232                 if(this.selectedChildWidget != page){
14233                         // Deselect old page and select new one
14234                         this._transition(page, this.selectedChildWidget);
14235                         this.selectedChildWidget = page;
14236                         dojo.publish(this.id+"-selectChild", [page]);
14237                 }
14238         },
14239
14240         _transition: function(/*Widget*/newWidget, /*Widget*/oldWidget){
14241                 if(oldWidget){
14242                         this._hideChild(oldWidget);
14243                 }
14244                 this._showChild(newWidget);
14245
14246                 // Size the new widget, in case this is the first time it's being shown,
14247                 // or I have been resized since the last time it was shown.
14248                 // page must be visible for resizing to work
14249                 if(this.doLayout && newWidget.resize){
14250                         newWidget.resize(this._containerContentBox || this._contentBox);
14251                 }
14252         },
14253
14254         _adjacent: function(/*Boolean*/ forward){
14255                 // summary: Gets the next/previous child widget in this container from the current selection
14256                 var children = this.getChildren();
14257                 var index = dojo.indexOf(children, this.selectedChildWidget);
14258                 index += forward ? 1 : children.length - 1;
14259                 return children[ index % children.length ]; // dijit._Widget
14260         },
14261
14262         forward: function(){
14263                 // Summary: advance to next page
14264                 this.selectChild(this._adjacent(true));
14265         },
14266
14267         back: function(){
14268                 // Summary: go back to previous page
14269                 this.selectChild(this._adjacent(false));
14270         },
14271
14272         _onKeyPress: function(e){
14273                 dojo.publish(this.id+"-containerKeyPress", [{ e: e, page: this}]);
14274         },
14275
14276         layout: function(){
14277                 if(this.doLayout && this.selectedChildWidget && this.selectedChildWidget.resize){
14278                         this.selectedChildWidget.resize(this._contentBox);
14279                 }
14280         },
14281
14282         _showChild: function(/*Widget*/ page){
14283                 var children = this.getChildren();
14284                 page.isFirstChild = (page == children[0]);
14285                 page.isLastChild = (page == children[children.length-1]);
14286                 page.selected = true;
14287
14288                 page.domNode.style.display="";
14289                 if(page._loadCheck){
14290                         page._loadCheck(); // trigger load in ContentPane
14291                 }
14292                 if(page.onShow){
14293                         page.onShow();
14294                 }
14295         },
14296
14297         _hideChild: function(/*Widget*/ page){
14298                 page.selected=false;
14299                 page.domNode.style.display="none";
14300                 if(page.onHide){
14301                         page.onHide();
14302                 }
14303         },
14304
14305         closeChild: function(/*Widget*/ page){
14306                 // summary:
14307                 //      callback when user clicks the [X] to remove a page
14308                 //      if onClose() returns true then remove and destroy the child
14309                 var remove = page.onClose(this, page);
14310                 if(remove){
14311                         this.removeChild(page);
14312                         // makes sure we can clean up executeScripts in ContentPane onUnLoad
14313                         page.destroyRecursive();
14314                 }
14315         },
14316
14317         destroy: function(){
14318                 this._beingDestroyed = true;
14319                 this.inherited(arguments);
14320         }
14321 });
14322
14323 dojo.declare(
14324         "dijit.layout.StackController",
14325         [dijit._Widget, dijit._Templated, dijit._Container],
14326         {
14327         // summary:
14328         //      Set of buttons to select a page in a page list.
14329         //      Monitors the specified StackContainer, and whenever a page is
14330         //      added, deleted, or selected, updates itself accordingly.
14331
14332                 templateString: "<span wairole='tablist' dojoAttachEvent='onkeypress' class='dijitStackController'></span>",
14333
14334                 // containerId: String
14335                 //      the id of the page container that I point to
14336                 containerId: "",
14337
14338                 // buttonWidget: String
14339                 //      the name of the button widget to create to correspond to each page
14340                 buttonWidget: "dijit.layout._StackButton",
14341
14342                 postCreate: function(){
14343                         dijit.setWaiRole(this.domNode, "tablist");
14344
14345                         // TODO: change key from object to id, to get more separation from StackContainer
14346                         this.pane2button = {};          // mapping from panes to buttons
14347                         this.pane2menu = {};            // mapping from panes to close menu
14348
14349                         this._subscriptions=[
14350                                 dojo.subscribe(this.containerId+"-startup", this, "onStartup"),
14351                                 dojo.subscribe(this.containerId+"-addChild", this, "onAddChild"),
14352                                 dojo.subscribe(this.containerId+"-removeChild", this, "onRemoveChild"),
14353                                 dojo.subscribe(this.containerId+"-selectChild", this, "onSelectChild"),
14354                                 dojo.subscribe(this.containerId+"-containerKeyPress", this, "onContainerKeyPress")
14355                         ];
14356                 },
14357
14358                 onStartup: function(/*Object*/ info){
14359                         // summary: called after StackContainer has finished initializing
14360                         dojo.forEach(info.children, this.onAddChild, this);
14361                         this.onSelectChild(info.selected);
14362                 },
14363
14364                 destroy: function(){
14365                         for(var pane in this.pane2button){
14366                                 this.onRemoveChild(pane);
14367                         }
14368                         dojo.forEach(this._subscriptions, dojo.unsubscribe);
14369                         this.inherited(arguments);
14370                 },
14371
14372                 onAddChild: function(/*Widget*/ page, /*Integer?*/ insertIndex){
14373                         // summary:
14374                         //   Called whenever a page is added to the container.
14375                         //   Create button corresponding to the page.
14376
14377                         // add a node that will be promoted to the button widget
14378                         var refNode = dojo.doc.createElement("span");
14379                         this.domNode.appendChild(refNode);
14380                         // create an instance of the button widget
14381                         var cls = dojo.getObject(this.buttonWidget);
14382                         var button = new cls({label: page.title, closeButton: page.closable}, refNode);
14383                         this.addChild(button, insertIndex);
14384                         this.pane2button[page] = button;
14385                         page.controlButton = button;    // this value might be overwritten if two tabs point to same container
14386                         
14387                         dojo.connect(button, "onClick", dojo.hitch(this,"onButtonClick",page));
14388                         if(page.closable){
14389                                 dojo.connect(button, "onClickCloseButton", dojo.hitch(this,"onCloseButtonClick",page));
14390                                 // add context menu onto title button
14391                                 var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
14392                                 var closeMenu = new dijit.Menu({targetNodeIds:[button.id], id:button.id+"_Menu"});
14393                                 var mItem = new dijit.MenuItem({label:_nlsResources.itemClose});
14394                 dojo.connect(mItem, "onClick", dojo.hitch(this, "onCloseButtonClick", page));
14395                         closeMenu.addChild(mItem);
14396                         this.pane2menu[page] = closeMenu;
14397                         }
14398                         if(!this._currentChild){ // put the first child into the tab order
14399                                 button.focusNode.setAttribute("tabIndex", "0");
14400                                 this._currentChild = page;
14401                         }
14402                         //make sure all tabs have the same length
14403                         if(!this.isLeftToRight() && dojo.isIE && this._rectifyRtlTabList){
14404                                 this._rectifyRtlTabList();
14405                         }
14406                 },
14407
14408                 onRemoveChild: function(/*Widget*/ page){
14409                         // summary:
14410                         //   Called whenever a page is removed from the container.
14411                         //   Remove the button corresponding to the page.
14412                         if(this._currentChild === page){ this._currentChild = null; }
14413                         var button = this.pane2button[page];
14414                         var menu = this.pane2menu[page];
14415                         if (menu){
14416                                 menu.destroy();
14417                         }
14418                         if(button){
14419                                 // TODO? if current child { reassign }
14420                                 button.destroy();
14421                         }
14422                         this.pane2button[page] = null;
14423                 },
14424
14425                 onSelectChild: function(/*Widget*/ page){
14426                         // summary:
14427                         //      Called when a page has been selected in the StackContainer, either by me or by another StackController
14428
14429                         if(!page){ return; }
14430
14431                         if(this._currentChild){
14432                                 var oldButton=this.pane2button[this._currentChild];
14433                                 oldButton.setAttribute('checked', false);
14434                                 oldButton.focusNode.setAttribute("tabIndex", "-1");
14435                         }
14436
14437                         var newButton=this.pane2button[page];
14438                         newButton.setAttribute('checked', true);
14439                         this._currentChild = page;
14440                         newButton.focusNode.setAttribute("tabIndex", "0");
14441                         var container = dijit.byId(this.containerId);
14442                         dijit.setWaiState(container.containerNode || container.domNode, "labelledby", newButton.id);
14443                 },
14444
14445                 onButtonClick: function(/*Widget*/ page){
14446                         // summary:
14447                         //   Called whenever one of my child buttons is pressed in an attempt to select a page
14448                         var container = dijit.byId(this.containerId);   // TODO: do this via topics?
14449                         container.selectChild(page); 
14450                 },
14451
14452                 onCloseButtonClick: function(/*Widget*/ page){
14453                         // summary:
14454                         //   Called whenever one of my child buttons [X] is pressed in an attempt to close a page
14455                         var container = dijit.byId(this.containerId);
14456                         container.closeChild(page);
14457                         var b = this.pane2button[this._currentChild];
14458                         if(b){
14459                                 dijit.focus(b.focusNode || b.domNode);
14460                         }
14461                 },
14462                 
14463                 // TODO: this is a bit redundant with forward, back api in StackContainer
14464                 adjacent: function(/*Boolean*/ forward){
14465                         if(!this.isLeftToRight() && (!this.tabPosition || /top|bottom/.test(this.tabPosition))){ forward = !forward; }
14466                         // find currently focused button in children array
14467                         var children = this.getChildren();
14468                         var current = dojo.indexOf(children, this.pane2button[this._currentChild]);
14469                         // pick next button to focus on
14470                         var offset = forward ? 1 : children.length - 1;
14471                         return children[ (current + offset) % children.length ]; // dijit._Widget
14472                 },
14473
14474                 onkeypress: function(/*Event*/ e){
14475                         // summary:
14476                         //   Handle keystrokes on the page list, for advancing to next/previous button
14477                         //   and closing the current page if the page is closable.
14478
14479                         if(this.disabled || e.altKey ){ return; }
14480                         var forward = null;
14481                         if(e.ctrlKey || !e._djpage){
14482                                 var k = dojo.keys;
14483                                 switch(e.keyCode){
14484                                         case k.LEFT_ARROW:
14485                                         case k.UP_ARROW:
14486                                                 if(!e._djpage){ forward = false; }
14487                                                 break;
14488                                         case k.PAGE_UP:
14489                                                 if(e.ctrlKey){ forward = false; }
14490                                                 break;
14491                                         case k.RIGHT_ARROW:
14492                                         case k.DOWN_ARROW:
14493                                                 if(!e._djpage){ forward = true; }
14494                                                 break;
14495                                         case k.PAGE_DOWN:
14496                                                 if(e.ctrlKey){ forward = true; }
14497                                                 break;
14498                                         case k.DELETE:
14499                                                 if(this._currentChild.closable){
14500                                                         this.onCloseButtonClick(this._currentChild);
14501                                                 }
14502                                                 dojo.stopEvent(e);
14503                                                 break;
14504                                         default:
14505                                                 if(e.ctrlKey){
14506                                                         if(e.keyCode == k.TAB){
14507                                                                 this.adjacent(!e.shiftKey).onClick();
14508                                                                 dojo.stopEvent(e);
14509                                                         }else if(e.keyChar == "w"){
14510                                                                 if(this._currentChild.closable){
14511                                                                         this.onCloseButtonClick(this._currentChild);
14512                                                                 }
14513                                                                 dojo.stopEvent(e); // avoid browser tab closing.
14514                                                         }
14515                                                 }
14516                                 }
14517                                 // handle page navigation
14518                                 if(forward !== null){
14519                                         this.adjacent(forward).onClick();
14520                                         dojo.stopEvent(e);
14521                                 }
14522                         }
14523                 },
14524
14525                 onContainerKeyPress: function(/*Object*/ info){
14526                         info.e._djpage = info.page;
14527                         this.onkeypress(info.e);
14528                 }
14529 });
14530
14531 dojo.declare("dijit.layout._StackButton",
14532         dijit.form.ToggleButton,
14533         {
14534         // summary
14535         //      Internal widget used by StackContainer.
14536         //      The button-like or tab-like object you click to select or delete a page
14537         
14538         tabIndex: "-1", // StackContainer buttons are not in the tab order by default
14539         
14540         postCreate: function(/*Event*/ evt){
14541                 dijit.setWaiRole((this.focusNode || this.domNode), "tab");
14542                 this.inherited(arguments);
14543         },
14544         
14545         onClick: function(/*Event*/ evt){
14546                 // summary: This is for TabContainer where the tabs are <span> rather than button,
14547                 //      so need to set focus explicitly (on some browsers)
14548                 dijit.focus(this.focusNode);
14549
14550                 // ... now let StackController catch the event and tell me what to do
14551         },
14552
14553         onClickCloseButton: function(/*Event*/ evt){
14554                 // summary
14555                 //      StackContainer connects to this function; if your widget contains a close button
14556                 //      then clicking it should call this function.
14557                 evt.stopPropagation();
14558         }
14559 });
14560
14561 // These arguments can be specified for the children of a StackContainer.
14562 // Since any widget can be specified as a StackContainer child, mix them
14563 // into the base widget class.  (This is a hack, but it's effective.)
14564 dojo.extend(dijit._Widget, {
14565         // title: String
14566         //              Title of this widget.  Used by TabContainer to the name the tab, etc.
14567         title: "",
14568
14569         // selected: Boolean
14570         //              Is this child currently selected?
14571         selected: false,
14572
14573         // closable: Boolean
14574         //              True if user can close (destroy) this child, such as (for example) clicking the X on the tab.
14575         closable: false,        // true if user can close this tab pane
14576
14577         onClose: function(){
14578                 // summary: Callback if someone tries to close the child, child will be closed if func returns true
14579                 return true;
14580         }
14581 });
14582
14583 }
14584
14585 if(!dojo._hasResource["dijit.layout.AccordionContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
14586 dojo._hasResource["dijit.layout.AccordionContainer"] = true;
14587 dojo.provide("dijit.layout.AccordionContainer");
14588
14589
14590
14591
14592
14593
14594
14595
14596 dojo.declare(
14597         "dijit.layout.AccordionContainer",
14598         dijit.layout.StackContainer,
14599         {
14600                 // summary:
14601                 //              Holds a set of panes where every pane's title is visible, but only one pane's content is visible at a time,
14602                 //              and switching between panes is visualized by sliding the other panes up/down.
14603                 // example:
14604                 // |    <div dojoType="dijit.layout.AccordionContainer">
14605                 // |            <div dojoType="dijit.layout.AccordionPane" title="pane 1">
14606                 // |                    <div dojoType="dijit.layout.ContentPane">...</div>
14607                 // |    </div>
14608                 // |            <div dojoType="dijit.layout.AccordionPane" title="pane 2">
14609                 // |                    <p>This is some text</p>
14610                 // ||           ...
14611                 // |    </div>
14612                 //
14613                 // duration: Integer
14614                 //              Amount of time (in ms) it takes to slide panes
14615                 duration: 250,
14616
14617                 _verticalSpace: 0,
14618
14619                 postCreate: function(){
14620                         this.domNode.style.overflow="hidden";
14621                         this.inherited("postCreate",arguments); 
14622                         dijit.setWaiRole(this.domNode, "tablist");
14623                         dojo.addClass(this.domNode,"dijitAccordionContainer");
14624                 },
14625
14626                 startup: function(){
14627                         if(this._started){ return; }
14628                         this.inherited("startup",arguments);    
14629                         if(this.selectedChildWidget){
14630                                 var style = this.selectedChildWidget.containerNode.style;
14631                                 style.display = "";
14632                                 style.overflow = "auto";
14633                                 this.selectedChildWidget._setSelectedState(true);
14634                         }
14635                 },
14636
14637                 layout: function(){
14638                         // summary: 
14639                         //              Set the height of the open pane based on what room remains
14640
14641                         // get cumulative height of all the title bars, and figure out which pane is open
14642                         var totalCollapsedHeight = 0;
14643                         var openPane = this.selectedChildWidget;
14644                         dojo.forEach(this.getChildren(), function(child){
14645                                 totalCollapsedHeight += child.getTitleHeight();
14646                         });
14647                         var mySize = this._contentBox;
14648                         this._verticalSpace = (mySize.h - totalCollapsedHeight);
14649                         if(openPane){
14650                                 openPane.containerNode.style.height = this._verticalSpace + "px";
14651 /***
14652 TODO: this is wrong.  probably you wanted to call resize on the SplitContainer
14653 inside the AccordionPane??
14654                                 if(openPane.resize){
14655                                         openPane.resize({h: this._verticalSpace});
14656                                 }
14657 ***/
14658                         }
14659                 },
14660
14661                 _setupChild: function(/*Widget*/ page){
14662                         // Summary: prepare the given child
14663                         return page;
14664                 },
14665
14666                 _transition: function(/*Widget?*/newWidget, /*Widget?*/oldWidget){
14667 //TODO: should be able to replace this with calls to slideIn/slideOut
14668                         if(this._inTransition){ return; }
14669                         this._inTransition = true;
14670                         var animations = [];
14671                         var paneHeight = this._verticalSpace;
14672                         if(newWidget){
14673                                 newWidget.setSelected(true);
14674                                 var newContents = newWidget.containerNode;
14675                                 newContents.style.display = "";
14676
14677                                 animations.push(dojo.animateProperty({
14678                                         node: newContents,
14679                                         duration: this.duration,
14680                                         properties: {
14681                                                 height: { start: "1", end: paneHeight }
14682                                         },
14683                                         onEnd: function(){
14684                                                 newContents.style.overflow = "auto";
14685                                         }
14686                                 }));
14687                         }
14688                         if(oldWidget){
14689                                 oldWidget.setSelected(false);
14690                                 var oldContents = oldWidget.containerNode;
14691                                 oldContents.style.overflow = "hidden";
14692                                 animations.push(dojo.animateProperty({
14693                                         node: oldContents,
14694                                         duration: this.duration,
14695                                         properties: {
14696                                                 height: { start: paneHeight, end: "1" }
14697                                         },
14698                                         onEnd: function(){
14699                                                 oldContents.style.display = "none";
14700                                         }
14701                                 }));
14702                         }
14703
14704                         this._inTransition = false;
14705
14706                         dojo.fx.combine(animations).play();
14707                 },
14708
14709                 // note: we are treating the container as controller here
14710                 _onKeyPress: function(/*Event*/ e){
14711                         if(this.disabled || e.altKey || !(e._dijitWidget || e.ctrlKey)){ return; }
14712                         var k = dojo.keys;
14713                         var fromTitle = e._dijitWidget;
14714                         switch(e.keyCode){
14715                                 case k.LEFT_ARROW:
14716                                 case k.UP_ARROW:
14717                                         if (fromTitle){
14718                                                 this._adjacent(false)._onTitleClick();
14719                                                 dojo.stopEvent(e);
14720                                         }
14721                                         break;
14722                                 case k.PAGE_UP:
14723                                         if (e.ctrlKey){
14724                                                 this._adjacent(false)._onTitleClick();
14725                                                 dojo.stopEvent(e);
14726                                         }
14727                                         break;
14728                                 case k.RIGHT_ARROW:
14729                                 case k.DOWN_ARROW:
14730                                         if (fromTitle){
14731                                                 this._adjacent(true)._onTitleClick();
14732                                                 dojo.stopEvent(e);
14733                                         }
14734                                         break;
14735                                 case k.PAGE_DOWN:
14736                                         if (e.ctrlKey){
14737                                                 this._adjacent(true)._onTitleClick();
14738                                                 dojo.stopEvent(e);
14739                                         }
14740                                         break;
14741                                 default:
14742                                         if(e.ctrlKey && e.keyCode == k.TAB){
14743                                                 this._adjacent(e._dijitWidget, !e.shiftKey)._onTitleClick();
14744                                                 dojo.stopEvent(e);
14745                                         }
14746                                 
14747                         }
14748                 }
14749         }
14750 );
14751
14752 dojo.declare("dijit.layout.AccordionPane",
14753         [dijit.layout.ContentPane, dijit._Templated, dijit._Contained],
14754         {
14755         // summary:
14756         //              AccordionPane is a ContentPane with a title that may contain another widget.
14757         //              Nested layout widgets, such as SplitContainer, are not supported at this time.
14758         // example: 
14759         // | see dijit.layout.AccordionContainer
14760
14761         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' waiRole=\"presentation\"></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 waiRole=\"presentation\" 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",
14762
14763         postCreate: function(){
14764                 this.inherited("postCreate",arguments)
14765                 dojo.setSelectable(this.titleNode, false);
14766                 this.setSelected(this.selected);
14767         },
14768
14769         getTitleHeight: function(){
14770                 // summary: returns the height of the title dom node
14771                 return dojo.marginBox(this.titleNode).h;        // Integer
14772         },
14773
14774         _onTitleClick: function(){
14775                 // summary: callback when someone clicks my title
14776                 var parent = this.getParent();
14777                 if(!parent._inTransition){
14778                         parent.selectChild(this);
14779                         dijit.focus(this.focusNode);
14780                 }
14781         },
14782
14783         _onTitleKeyPress: function(/*Event*/ evt){
14784                 evt._dijitWidget = this;
14785                 return this.getParent()._onKeyPress(evt);
14786         },
14787
14788         _setSelectedState: function(/*Boolean*/ isSelected){
14789                 this.selected = isSelected;
14790                 dojo[(isSelected ? "addClass" : "removeClass")](this.titleNode,"dijitAccordionTitle-selected");
14791                 this.focusNode.setAttribute("tabIndex", isSelected ? "0" : "-1");
14792         },
14793
14794         _handleFocus: function(/*Event*/e){
14795                 // summary: handle the blur and focus state of this widget
14796                 dojo[(e.type=="focus" ? "addClass" : "removeClass")](this.focusNode,"dijitAccordionFocused");           
14797         },
14798
14799         setSelected: function(/*Boolean*/ isSelected){
14800                 // summary: change the selected state on this pane
14801                 this._setSelectedState(isSelected);
14802                 if(isSelected){
14803                         this.onSelected();
14804                         this._loadCheck(true); // if href specified, trigger load
14805                 }
14806         },
14807
14808         onSelected: function(){
14809                 // summary: called when this pane is selected
14810         }
14811 });
14812
14813 }
14814
14815 if(!dojo._hasResource["dijit.layout.BorderContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
14816 dojo._hasResource["dijit.layout.BorderContainer"] = true;
14817 dojo.provide("dijit.layout.BorderContainer");
14818
14819
14820
14821
14822 dojo.declare(
14823         "dijit.layout.BorderContainer",
14824 //      [dijit._Widget, dijit._Container, dijit._Contained],
14825         dijit.layout._LayoutWidget,
14826 {
14827         // summary:
14828         //      Provides layout in 5 regions, a center and borders along its 4 sides.
14829         //
14830         // description:
14831         //      A BorderContainer is a box with a specified size (like style="width: 500px; height: 500px;"),
14832         //      that contains a child widget marked region="center" and optionally children widgets marked
14833         //      region equal to "top", "bottom", "leading", "trailing", "left" or "right".
14834         //      Children along the edges will be laid out according to width or height dimensions.  The remaining
14835         //      space is designated for the center region.
14836         //      The outer size must be specified on the BorderContainer node.  Width must be specified for the sides
14837         //  and height for the top and bottom, respectively.  No dimensions should be specified on the center;
14838         //      it will fill the remaining space.  Regions named "leading" and "trailing" may be used just like
14839         //      "left" and "right" except that they will be reversed in right-to-left environments.
14840         //  Optional splitters may be specified on the edge widgets only to make them resizable by the user.
14841         //
14842         // example:
14843         // |    <style>
14844         // |            html, body { height: 100%; width: 100%; }
14845         // |    </style>
14846         // |    <div dojoType="BorderContainer" design="sidebar" style="width: 100%; height: 100%">
14847         // |            <div dojoType="ContentPane" region="top">header text</div>
14848         // |            <div dojoType="ContentPane" region="right" style="width: 200px;">table of contents</div>
14849         // |            <div dojoType="ContentPane" region="center">client area</div>
14850         // |    </div>
14851         //
14852         // design: String
14853         //  choose which design is used for the layout: "headline" (default) where the top and bottom extend
14854         //  the full width of the container, or "sidebar" where the left and right sides extend from top to bottom.
14855         design: "headline",
14856
14857         // liveSplitters: Boolean
14858         //  specifies whether splitters resize as you drag (true) or only upon mouseup (false)
14859         liveSplitters: true,
14860
14861         // persist: Boolean
14862         //              Save splitter positions in a cookie.
14863         persist: false, // Boolean
14864
14865         // _splitterClass: String
14866         //              Optional hook to override the default Splitter widget used by BorderContainer
14867         _splitterClass: "dijit.layout._Splitter",
14868
14869         postCreate: function(){
14870                 this.inherited(arguments);
14871
14872                 this._splitters = {};
14873                 this._splitterThickness = {};
14874                 dojo.addClass(this.domNode, "dijitBorderContainer");
14875         },
14876
14877         startup: function(){
14878                 if(this._started){ return; }
14879                 dojo.forEach(this.getChildren(), this._setupChild, this);
14880                 this.inherited(arguments);
14881         },
14882
14883         _setupChild: function(/*Widget*/child){
14884                 var region = child.region;
14885                 if(region){
14886 //                      dojo.addClass(child.domNode, "dijitBorderContainerPane");
14887                         child.domNode.style.position = "absolute"; // bill says not to set this in CSS, since we can't keep others
14888                                 // from destroying the class list
14889
14890                         var ltr = this.isLeftToRight();
14891                         if(region == "leading"){ region = ltr ? "left" : "right"; }
14892                         if(region == "trailing"){ region = ltr ? "right" : "left"; }
14893
14894                         this["_"+region] = child.domNode;
14895                         this["_"+region+"Widget"] = child;
14896
14897                         if(child.splitter){
14898                                 var _Splitter = dojo.getObject(this._splitterClass);
14899                                 var flip = {left:'right', right:'left', top:'bottom', bottom:'top', leading:'trailing', trailing:'leading'};
14900                                 var oppNodeList = dojo.query('[region=' + flip[child.region] + ']', this.domNode);
14901                                 var splitter = new _Splitter({ container: this, child: child, region: region,
14902                                         oppNode: oppNodeList[0], live: this.liveSplitters });
14903                                 this._splitters[region] = splitter.domNode;
14904                                 dojo.place(splitter.domNode, child.domNode, "after");
14905                                 this._computeSplitterThickness(region);
14906                         }
14907                         child.region = region;
14908                 }
14909         },
14910
14911         _computeSplitterThickness: function(region){
14912                 var re = new RegExp("top|bottom");
14913                 this._splitterThickness[region] =
14914                         dojo.marginBox(this._splitters[region])[(re.test(region) ? 'h' : 'w')];
14915         },
14916
14917         layout: function(){
14918                 this._layoutChildren();
14919         },
14920
14921         addChild: function(/*Widget*/ child, /*Integer?*/ insertIndex){
14922                 this.inherited(arguments);
14923                 this._setupChild(child);
14924                 if(this._started){
14925                         this._layoutChildren(); //OPT
14926                 }
14927         },
14928
14929         removeChild: function(/*Widget*/ child){
14930                 var region = child.region;
14931                 var splitter = this._splitters[region];
14932                 if(splitter){
14933                         dijit.byNode(splitter).destroy();
14934                         delete this._splitters[region];
14935                         delete this._splitterThickness[region];
14936                 }
14937                 this.inherited(arguments);
14938                 delete this["_"+region];
14939                 delete this["_" +region+"Widget"];
14940                 if(this._started){
14941                         this._layoutChildren(child.region);
14942                 }
14943         },
14944
14945         _layoutChildren: function(/*String?*/changedRegion){
14946                 var sidebarLayout = (this.design == "sidebar");
14947                 var topHeight = 0, bottomHeight = 0, leftWidth = 0, rightWidth = 0;
14948                 var topStyle = {}, leftStyle = {}, rightStyle = {}, bottomStyle = {},
14949                         centerStyle = (this._center && this._center.style) || {};
14950
14951                 var changedSide = /left|right/.test(changedRegion);
14952
14953                 var layoutSides = !changedRegion || (!changedSide && !sidebarLayout);
14954                 var layoutTopBottom = !changedRegion || (changedSide && sidebarLayout);
14955                 if(this._top){
14956                         topStyle = layoutTopBottom && this._top.style;
14957                         topHeight = dojo.marginBox(this._top).h;
14958                 }
14959                 if(this._left){
14960                         leftStyle = layoutSides && this._left.style;
14961                         leftWidth = dojo.marginBox(this._left).w;
14962                 }
14963                 if(this._right){
14964                         rightStyle = layoutSides && this._right.style;
14965                         rightWidth = dojo.marginBox(this._right).w;
14966                 }
14967                 if(this._bottom){
14968                         bottomStyle = layoutTopBottom && this._bottom.style;
14969                         bottomHeight = dojo.marginBox(this._bottom).h;
14970                 }
14971
14972                 var splitters = this._splitters;
14973                 var topSplitter = splitters.top;
14974                 var bottomSplitter = splitters.bottom;
14975                 var leftSplitter = splitters.left;
14976                 var rightSplitter = splitters.right;
14977                 var splitterThickness = this._splitterThickness;
14978                 var topSplitterThickness = splitterThickness.top || 0;
14979                 var leftSplitterThickness = splitterThickness.left || 0;
14980                 var rightSplitterThickness = splitterThickness.right || 0;
14981                 var bottomSplitterThickness = splitterThickness.bottom || 0;
14982
14983                 // Check for race condition where CSS hasn't finished loading, so
14984                 // the splitter width == the viewport width (#5824)
14985                 if(leftSplitterThickness > 50 || rightSplitterThickness > 50){
14986                         setTimeout(dojo.hitch(this, function(){
14987                                 for(var region in this._splitters){
14988                                         this._computeSplitterThickness(region);
14989                                 }
14990                                 this._layoutChildren();
14991                         }), 50);
14992                         return false;
14993                 }
14994
14995                 var splitterBounds = {
14996                         left: (sidebarLayout ? leftWidth + leftSplitterThickness: "0") + "px",
14997                         right: (sidebarLayout ? rightWidth + rightSplitterThickness: "0") + "px"
14998                 };
14999
15000                 if(topSplitter){
15001                         dojo.mixin(topSplitter.style, splitterBounds);
15002                         topSplitter.style.top = topHeight + "px";
15003                 }
15004
15005                 if(bottomSplitter){
15006                         dojo.mixin(bottomSplitter.style, splitterBounds);
15007                         bottomSplitter.style.bottom = bottomHeight + "px";
15008                 }
15009
15010                 splitterBounds = {
15011                         top: (sidebarLayout ? "0" : topHeight + topSplitterThickness) + "px",
15012                         bottom: (sidebarLayout ? "0" : bottomHeight + bottomSplitterThickness) + "px"
15013                 };
15014
15015                 if(leftSplitter){
15016                         dojo.mixin(leftSplitter.style, splitterBounds);
15017                         leftSplitter.style.left = leftWidth + "px";
15018                 }
15019
15020                 if(rightSplitter){
15021                         dojo.mixin(rightSplitter.style, splitterBounds);
15022                         rightSplitter.style.right = rightWidth + "px";
15023                 }
15024
15025                 dojo.mixin(centerStyle, {
15026                         top: topHeight + topSplitterThickness + "px",
15027                         left: leftWidth + leftSplitterThickness + "px",
15028                         right:  rightWidth + rightSplitterThickness + "px",
15029                         bottom: bottomHeight + bottomSplitterThickness + "px"
15030                 });
15031
15032                 var bounds = {
15033                         top: sidebarLayout ? "0" : centerStyle.top,
15034                         bottom: sidebarLayout ? "0" : centerStyle.bottom
15035                 };
15036                 dojo.mixin(leftStyle, bounds);
15037                 dojo.mixin(rightStyle, bounds);
15038                 leftStyle.left = rightStyle.right = topStyle.top = bottomStyle.bottom = "0";
15039                 if(sidebarLayout){
15040                         topStyle.left = bottomStyle.left = leftWidth + (this.isLeftToRight() ? leftSplitterThickness : 0) + "px";
15041                         topStyle.right = bottomStyle.right = rightWidth + (this.isLeftToRight() ? 0 : rightSplitterThickness) + "px";
15042                 }else{
15043                         topStyle.left = topStyle.right = bottomStyle.left = bottomStyle.right = "0";
15044                 }
15045
15046                 // Nodes in IE respond to t/l/b/r, and TEXTAREA doesn't respond in any browser
15047                 var janky = dojo.isIE || dojo.some(this.getChildren(), function(child){
15048                         return child.domNode.tagName == "TEXTAREA";
15049                 });
15050                 if(janky){
15051                         // Set the size of the children the old fashioned way, by calling
15052                         // childNode.resize({h: int, w: int}) for each child node)
15053
15054                         var borderBox = function(n, b){
15055                                 n=dojo.byId(n);
15056                                 var s = dojo.getComputedStyle(n);
15057                                 if(!b){ return dojo._getBorderBox(n, s); }
15058                                 var me = dojo._getMarginExtents(n, s);
15059                                 dojo._setMarginBox(n, b.l, b.t, b.w + me.w, b.h + me.h, s);
15060                                 return null;
15061                         };
15062
15063                         var resizeWidget = function(widget, dim){
15064                                 if(widget){
15065                                         widget.resize ? widget.resize(dim) : dojo.marginBox(widget.domNode, dim);
15066                                 }
15067                         };
15068
15069                         // TODO: use dim passed in to resize() (see _LayoutWidget.js resize())
15070                         // Then can make borderBox setBorderBox(), since no longer need to ever get the borderBox() size
15071                         var thisBorderBox = borderBox(this.domNode);
15072
15073                         var containerHeight = thisBorderBox.h;
15074                         var middleHeight = containerHeight;
15075                         if(this._top){ middleHeight -= topHeight; }
15076                         if(this._bottom){ middleHeight -= bottomHeight; }
15077                         if(topSplitter){ middleHeight -= topSplitterThickness; }
15078                         if(bottomSplitter){ middleHeight -= bottomSplitterThickness; }
15079                         var centerDim = { h: middleHeight };
15080
15081                         var sidebarHeight = sidebarLayout ? containerHeight : middleHeight;
15082                         if(leftSplitter){ leftSplitter.style.height = sidebarHeight; }
15083                         if(rightSplitter){ rightSplitter.style.height = sidebarHeight; }
15084                         resizeWidget(this._leftWidget, {h: sidebarHeight});
15085                         resizeWidget(this._rightWidget, {h: sidebarHeight});
15086
15087                         var containerWidth = thisBorderBox.w;
15088                         var middleWidth = containerWidth;
15089                         if(this._left){ middleWidth -= leftWidth; }
15090                         if(this._right){ middleWidth -= rightWidth; }
15091                         if(leftSplitter){ middleWidth -= leftSplitterThickness; }
15092                         if(rightSplitter){ middleWidth -= rightSplitterThickness; }
15093                         centerDim.w = middleWidth;
15094
15095                         var sidebarWidth = sidebarLayout ? middleWidth : containerWidth;
15096                         if(topSplitter){ topSplitter.style.width = sidebarWidth; }
15097                         if(bottomSplitter){ bottomSplitter.style.width = sidebarWidth; }
15098                         resizeWidget(this._topWidget, {w: sidebarWidth});
15099                         resizeWidget(this._bottomWidget, {w: sidebarWidth});
15100
15101                         resizeWidget(this._centerWidget, centerDim);
15102                 }else{
15103
15104                         // We've already sized the children by setting style.top/bottom/left/right...
15105                         // Now just need to call resize() on those children so they can re-layout themselves
15106
15107                         // TODO: calling child.resize() without an argument is bad, because it forces
15108                         // the child to query it's own size (even though this function already knows
15109                         // the size), plus which querying the size of a node right after setting it
15110                         // is known to cause problems (incorrect answer or an exception).
15111                         // This is a setback from older layout widgets, which
15112                         // don't do that.  See #3399, #2678, #3624 and #2955, #1988
15113
15114                         var resizeList = {};
15115                         if(changedRegion){
15116                                 resizeList[changedRegion] = resizeList.center = true;
15117                                 if(/top|bottom/.test(changedRegion) && this.design != "sidebar"){
15118                                         resizeList.left = resizeList.right = true;
15119                                 }else if(/left|right/.test(changedRegion) && this.design == "sidebar"){
15120                                         resizeList.top = resizeList.bottom = true;
15121                                 }
15122                         }
15123
15124                         dojo.forEach(this.getChildren(), function(child){
15125                                 if(child.resize && (!changedRegion || child.region in resizeList)){
15126         //                              console.log(this.id, ": resizing child id=" + child.id + " (region=" + child.region + "), style before resize is " +
15127         //                                                                       "{ t: " + child.domNode.style.top +
15128         //                                                                      ", b: " + child.domNode.style.bottom +
15129         //                                                                      ", l: " + child.domNode.style.left +
15130         //                                                                       ", r: " + child.domNode.style.right +
15131         //                                                                       ", w: " + child.domNode.style.width +
15132         //                                                                       ", h: " + child.domNode.style.height +
15133         //                                                                      "}"
15134         //                                              );
15135                                         child.resize();
15136         //                              console.log(this.id, ": after resize of child id=" + child.id + " (region=" + child.region + ") " +
15137         //                                                                       "{ t: " + child.domNode.style.top +
15138         //                                                                      ", b: " + child.domNode.style.bottom +
15139         //                                                                      ", l: " + child.domNode.style.left +
15140         //                                                                       ", r: " + child.domNode.style.right +
15141         //                                                                       ", w: " + child.domNode.style.width +
15142         //                                                                       ", h: " + child.domNode.style.height +
15143         //                                                                      "}"
15144         //                                              );
15145                                 }
15146                         }, this);
15147                 }
15148         }
15149 });
15150
15151 // This argument can be specified for the children of a BorderContainer.
15152 // Since any widget can be specified as a LayoutContainer child, mix it
15153 // into the base widget class.  (This is a hack, but it's effective.)
15154 dojo.extend(dijit._Widget, {
15155         // region: String
15156         //              "top", "bottom", "leading", "trailing", "left", "right", "center".
15157         //              See the BorderContainer description for details on this parameter.
15158         region: '',
15159
15160         // splitter: Boolean
15161         splitter: false,
15162
15163         // minSize: Number
15164         minSize: 0,
15165
15166         // maxSize: Number
15167         maxSize: Infinity
15168 });
15169
15170
15171
15172 dojo.declare("dijit.layout._Splitter", [ dijit._Widget, dijit._Templated ],
15173 {
15174 /*=====
15175         container: null,
15176         child: null,
15177         region: null,
15178 =====*/
15179
15180         // live: Boolean
15181         //              If true, the child's size changes and the child widget is redrawn as you drag the splitter;
15182         //              otherwise, the size doesn't change until you drop the splitter (by mouse-up)
15183         live: true,
15184
15185         // summary: A draggable spacer between two items in a BorderContainer
15186         templateString: '<div class="dijitSplitter" dojoAttachEvent="onkeypress:_onKeyPress,onmousedown:_startDrag" tabIndex="0" waiRole="separator"><div class="dijitSplitterThumb"></div></div>',
15187
15188         postCreate: function(){
15189                 this.inherited(arguments);
15190                 this.horizontal = /top|bottom/.test(this.region);
15191                 dojo.addClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V"));
15192 //              dojo.addClass(this.child.domNode, "dijitSplitterPane");
15193 //              dojo.setSelectable(this.domNode, false); //TODO is this necessary?
15194
15195                 this._factor = /top|left/.test(this.region) ? 1 : -1;
15196                 this._minSize = this.child.minSize;
15197
15198                 this._computeMaxSize();
15199                 //TODO: might be more accurate to recompute constraints on resize?
15200                 this.connect(this.container, "layout", dojo.hitch(this, this._computeMaxSize));
15201
15202                 this._cookieName = this.container.id + "_" + this.region;
15203                 if(this.container.persist){
15204                         // restore old size
15205                         var persistSize = dojo.cookie(this._cookieName);
15206                         if(persistSize){
15207                                 this.child.domNode.style[this.horizontal ? "height" : "width"] = persistSize;
15208                         }
15209                 }
15210         },
15211
15212         _computeMaxSize: function(){
15213                 var dim = this.horizontal ? 'h' : 'w';
15214                 var available = dojo.contentBox(this.container.domNode)[dim] - (this.oppNode ? dojo.marginBox(this.oppNode)[dim] : 0);
15215                 this._maxSize = Math.min(this.child.maxSize, available);
15216         },
15217
15218         _startDrag: function(e){
15219                 if(!this.cover){
15220                         this.cover = dojo.doc.createElement('div');
15221                         dojo.addClass(this.cover, "dijitSplitterCover");
15222                         dojo.place(this.cover, this.child.domNode, "after");
15223                 }else{
15224                         this.cover.style.zIndex = 1;
15225                 }
15226
15227                 // Safeguard in case the stop event was missed.  Shouldn't be necessary if we always get the mouse up. 
15228                 if(this.fake){ dojo._destroyElement(this.fake); }
15229                 if(!(this._resize = this.live)){ //TODO: disable live for IE6?
15230                         // create fake splitter to display at old position while we drag
15231                         (this.fake = this.domNode.cloneNode(true)).removeAttribute("id");
15232                         dojo.addClass(this.domNode, "dijitSplitterShadow");
15233                         dojo.place(this.fake, this.domNode, "after");
15234                 }
15235                 dojo.addClass(this.domNode, "dijitSplitterActive");
15236
15237                 //Performance: load data info local vars for onmousevent function closure
15238                 var factor = this._factor,
15239                         max = this._maxSize,
15240                         min = this._minSize || 10;
15241                 var axis = this.horizontal ? "pageY" : "pageX";
15242                 var pageStart = e[axis];
15243                 var splitterStyle = this.domNode.style;
15244                 var dim = this.horizontal ? 'h' : 'w';
15245                 var childStart = dojo.marginBox(this.child.domNode)[dim];
15246                 var splitterStart = parseInt(this.domNode.style[this.region]);
15247                 var resize = this._resize;
15248                 var region = this.region;
15249                 var mb = {};
15250                 var childNode = this.child.domNode;
15251                 var layoutFunc = dojo.hitch(this.container, this.container._layoutChildren);
15252
15253                 var de = dojo.doc.body;
15254                 this._handlers = (this._handlers || []).concat([
15255                         dojo.connect(de, "onmousemove", this._drag = function(e, forceResize){
15256                                 var delta = e[axis] - pageStart,
15257                                         childSize = factor * delta + childStart,
15258                                         boundChildSize = Math.max(Math.min(childSize, max), min);
15259
15260                                 if(resize || forceResize){
15261                                         mb[dim] = boundChildSize;
15262                                         // TODO: inefficient; we set the marginBox here and then immediately layoutFunc() needs to query it
15263                                         dojo.marginBox(childNode, mb);
15264                                         layoutFunc(region);
15265                                 }
15266                                 splitterStyle[region] = factor * delta + splitterStart + (boundChildSize - childSize) + "px";
15267                         }),
15268                         dojo.connect(de, "onmouseup", this, "_stopDrag")
15269                 ]);
15270                 dojo.stopEvent(e);
15271         },
15272
15273         _stopDrag: function(e){
15274                 try{
15275                         if(this.cover){ this.cover.style.zIndex = -1; }
15276                         if(this.fake){ dojo._destroyElement(this.fake); }
15277                         dojo.removeClass(this.domNode, "dijitSplitterActive");
15278                         dojo.removeClass(this.domNode, "dijitSplitterShadow");
15279                         this._drag(e); //TODO: redundant with onmousemove?
15280                         this._drag(e, true);
15281                 }finally{
15282                         this._cleanupHandlers();
15283                         delete this._drag;
15284                 }
15285
15286                 if(this.container.persist){
15287                         dojo.cookie(this._cookieName, this.child.domNode.style[this.horizontal ? "height" : "width"]);
15288                 }
15289         },
15290
15291         _cleanupHandlers: function(){
15292                 dojo.forEach(this._handlers, dojo.disconnect);
15293                 delete this._handlers;
15294         },
15295
15296         _onKeyPress: function(/*Event*/ e){
15297                 // should we apply typematic to this?
15298                 this._resize = true;
15299                 var horizontal = this.horizontal;
15300                 var tick = 1;
15301                 var dk = dojo.keys;
15302                 switch(e.keyCode){
15303                         case horizontal ? dk.UP_ARROW : dk.LEFT_ARROW:
15304                                 tick *= -1;
15305                                 break;
15306                         case horizontal ? dk.DOWN_ARROW : dk.RIGHT_ARROW:
15307                                 break;
15308                         default:
15309 //                              this.inherited(arguments);
15310                                 return;
15311                 }
15312                 var childSize = dojo.marginBox(this.child.domNode)[ horizontal ? 'h' : 'w' ] + this._factor * tick;
15313                 var mb = {};
15314                 mb[ this.horizontal ? "h" : "w"] = Math.max(Math.min(childSize, this._maxSize), this._minSize);
15315                 dojo.marginBox(this.child.domNode, mb);
15316                 this.container._layoutChildren(this.region);
15317                 dojo.stopEvent(e);
15318         },
15319
15320         destroy: function(){
15321                 this._cleanupHandlers();
15322                 delete this.child;
15323                 delete this.container;
15324                 delete this.fake;
15325                 this.inherited(arguments);
15326         }
15327 });
15328
15329 }
15330
15331 if(!dojo._hasResource["dijit.layout.LayoutContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
15332 dojo._hasResource["dijit.layout.LayoutContainer"] = true;
15333 dojo.provide("dijit.layout.LayoutContainer");
15334
15335
15336
15337 dojo.declare("dijit.layout.LayoutContainer",
15338         dijit.layout._LayoutWidget,
15339         {
15340         // summary:
15341         //      Provides Delphi-style panel layout semantics.
15342         //
15343         // description:
15344         //      A LayoutContainer is a box with a specified size (like style="width: 500px; height: 500px;"),
15345         //      that contains children widgets marked with "layoutAlign" of "left", "right", "bottom", "top", and "client".
15346         //      It takes it's children marked as left/top/bottom/right, and lays them out along the edges of the box,
15347         //      and then it takes the child marked "client" and puts it into the remaining space in the middle.
15348         //
15349         //  Left/right positioning is similar to CSS's "float: left" and "float: right",
15350         //      and top/bottom positioning would be similar to "float: top" and "float: bottom", if there were such
15351         //      CSS.
15352         //
15353         //      Note that there can only be one client element, but there can be multiple left, right, top,
15354         //      or bottom elements.
15355         //
15356         // example:
15357         // |    <style>
15358         // |            html, body{ height: 100%; width: 100%; }
15359         // |    </style>
15360         // |    <div dojoType="dijit.layout.LayoutContainer" style="width: 100%; height: 100%">
15361         // |            <div dojoType="dijit.layout.ContentPane" layoutAlign="top">header text</div>
15362         // |            <div dojoType="dijit.layout.ContentPane" layoutAlign="left" style="width: 200px;">table of contents</div>
15363         // |            <div dojoType="dijit.layout.ContentPane" layoutAlign="client">client area</div>
15364         // |    </div>
15365         // |
15366         // |    Lays out each child in the natural order the children occur in.
15367         // |    Basically each child is laid out into the "remaining space", where "remaining space" is initially
15368         // |    the content area of this widget, but is reduced to a smaller rectangle each time a child is added.
15369         //      
15370
15371         constructor: function(){
15372                 dojo.deprecated("dijit.layout.LayoutContainer is deprecated", "use BorderContainer instead", 2.0);
15373         },
15374
15375         layout: function(){
15376                 dijit.layout.layoutChildren(this.domNode, this._contentBox, this.getChildren());
15377         },
15378
15379         addChild: function(/*Widget*/ child, /*Integer?*/ insertIndex){
15380                 dijit._Container.prototype.addChild.apply(this, arguments);
15381                 if(this._started){
15382                         dijit.layout.layoutChildren(this.domNode, this._contentBox, this.getChildren());
15383                 }
15384         },
15385
15386         removeChild: function(/*Widget*/ widget){
15387                 dijit._Container.prototype.removeChild.apply(this, arguments);
15388                 if(this._started){
15389                         dijit.layout.layoutChildren(this.domNode, this._contentBox, this.getChildren());
15390                 }
15391         }
15392 });
15393
15394 // This argument can be specified for the children of a LayoutContainer.
15395 // Since any widget can be specified as a LayoutContainer child, mix it
15396 // into the base widget class.  (This is a hack, but it's effective.)
15397 dojo.extend(dijit._Widget, {
15398         // layoutAlign: String
15399         //              "none", "left", "right", "bottom", "top", and "client".
15400         //              See the LayoutContainer description for details on this parameter.
15401         layoutAlign: 'none'
15402 });
15403
15404 }
15405
15406 if(!dojo._hasResource["dijit.layout.LinkPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
15407 dojo._hasResource["dijit.layout.LinkPane"] = true;
15408 dojo.provide("dijit.layout.LinkPane");
15409
15410
15411
15412
15413 dojo.declare("dijit.layout.LinkPane",
15414         [dijit.layout.ContentPane, dijit._Templated],
15415         {
15416         // summary: 
15417         //      A ContentPane that loads data remotely
15418         // description:
15419         //      LinkPane is just a ContentPane that loads data remotely (via the href attribute),
15420         //      and has markup similar to an anchor.  The anchor's body (the words between `<a>` and `</a>`)
15421         //      become the title of the widget (used for TabContainer, AccordionContainer, etc.)
15422         // example:
15423         //      <a href="foo.html">my title</a>
15424
15425         // I'm using a template because the user may specify the input as
15426         // <a href="foo.html">title</a>, in which case we need to get rid of the
15427         // <a> because we don't want a link.
15428         templateString: '<div class="dijitLinkPane"></div>',
15429
15430         postCreate: function(){
15431
15432                 // If user has specified node contents, they become the title
15433                 // (the link must be plain text)
15434                 if(this.srcNodeRef){
15435                         this.title += this.srcNodeRef.innerHTML;
15436                 }
15437                 this.inherited("postCreate",arguments);
15438         }
15439 });
15440
15441 }
15442
15443 if(!dojo._hasResource["dijit.layout.SplitContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
15444 dojo._hasResource["dijit.layout.SplitContainer"] = true;
15445 dojo.provide("dijit.layout.SplitContainer");
15446
15447 //
15448 // FIXME: make it prettier
15449 // FIXME: active dragging upwards doesn't always shift other bars (direction calculation is wrong in this case)
15450 //
15451
15452
15453
15454
15455 dojo.declare("dijit.layout.SplitContainer",
15456         dijit.layout._LayoutWidget,
15457         {
15458         // summary: 
15459         //      A Container widget with sizing handles in-between each child
15460         // description:
15461         //              Contains multiple children widgets, all of which are displayed side by side
15462         //              (either horizontally or vertically); there's a bar between each of the children,
15463         //              and you can adjust the relative size of each child by dragging the bars.
15464         //
15465         //              You must specify a size (width and height) for the SplitContainer.
15466
15467         constructor: function(){
15468                 dojo.deprecated("dijit.layout.SplitContainer is deprecated", "use BorderContainer with splitter instead", 2.0);
15469         },
15470
15471         // activeSizing: Boolean
15472         //              If true, the children's size changes as you drag the bar;
15473         //              otherwise, the sizes don't change until you drop the bar (by mouse-up)
15474         activeSizing: false,
15475
15476         // sizerWidth: Integer
15477         //              Size in pixels of the bar between each child
15478         sizerWidth: 7, // FIXME: this should be a CSS attribute (at 7 because css wants it to be 7 until we fix to css)
15479
15480         // orientation: String
15481         //              either 'horizontal' or vertical; indicates whether the children are
15482         //              arranged side-by-side or up/down.
15483         orientation: 'horizontal',
15484
15485         // persist: Boolean
15486         //              Save splitter positions in a cookie
15487         persist: true,
15488
15489         postMixInProperties: function(){
15490                 this.inherited("postMixInProperties",arguments);
15491                 this.isHorizontal = (this.orientation == 'horizontal');
15492         },
15493
15494         postCreate: function(){
15495                 this.inherited("postCreate",arguments);
15496                 this.sizers = [];
15497                 dojo.addClass(this.domNode, "dijitSplitContainer");
15498                 // overflow has to be explicitly hidden for splitContainers using gekko (trac #1435)
15499                 // to keep other combined css classes from inadvertantly making the overflow visible
15500                 if(dojo.isMozilla){
15501                         this.domNode.style.overflow = '-moz-scrollbars-none'; // hidden doesn't work
15502                 }
15503
15504                 // create the fake dragger
15505                 if(typeof this.sizerWidth == "object"){
15506                         try{ //FIXME: do this without a try/catch
15507                                 this.sizerWidth = parseInt(this.sizerWidth.toString());
15508                         }catch(e){ this.sizerWidth = 7; }
15509                 }
15510                 var sizer = this.virtualSizer = dojo.doc.createElement('div');
15511                 sizer.style.position = 'relative';
15512
15513                 // #1681: work around the dreaded 'quirky percentages in IE' layout bug
15514                 // If the splitcontainer's dimensions are specified in percentages, it
15515                 // will be resized when the virtualsizer is displayed in _showSizingLine
15516                 // (typically expanding its bounds unnecessarily). This happens because
15517                 // we use position: relative for .dijitSplitContainer.
15518                 // The workaround: instead of changing the display style attribute,
15519                 // switch to changing the zIndex (bring to front/move to back)
15520
15521                 sizer.style.zIndex = 10;
15522                 sizer.className = this.isHorizontal ? 'dijitSplitContainerVirtualSizerH' : 'dijitSplitContainerVirtualSizerV';
15523                 this.domNode.appendChild(sizer);
15524                 dojo.setSelectable(sizer, false);
15525         },
15526
15527         destroy: function(){
15528                 delete this.virtualSizer;
15529                 dojo.forEach(this._ownconnects, dojo.disconnect);
15530                 this.inherited(arguments);
15531         },
15532         startup: function(){
15533                 if(this._started){ return; }
15534
15535                 dojo.forEach(this.getChildren(), function(child, i, children){
15536                         // attach the children and create the draggers
15537                         this._injectChild(child);
15538
15539                         if(i < children.length-1){
15540                                 this._addSizer();
15541                         }
15542                 }, this);
15543
15544                 if(this.persist){
15545                         this._restoreState();
15546                 }
15547
15548                 this.inherited(arguments); 
15549         },
15550
15551         _injectChild: function(child){
15552                 child.domNode.style.position = "absolute";
15553                 dojo.addClass(child.domNode, "dijitSplitPane");
15554         },
15555
15556         _addSizer: function(){
15557                 var i = this.sizers.length;
15558
15559                 // TODO: use a template for this!!!
15560                 var sizer = this.sizers[i] = dojo.doc.createElement('div');
15561                 this.domNode.appendChild(sizer);
15562
15563                 sizer.className = this.isHorizontal ? 'dijitSplitContainerSizerH' : 'dijitSplitContainerSizerV';
15564
15565                 // add the thumb div
15566                 var thumb = dojo.doc.createElement('div');
15567                 thumb.className = 'thumb';
15568                 sizer.appendChild(thumb);
15569
15570                 // FIXME: are you serious? why aren't we using mover start/stop combo?
15571                 var self = this;
15572                 var handler = (function(){ var sizer_i = i; return function(e){ self.beginSizing(e, sizer_i); } })();
15573                 this.connect(sizer, "onmousedown", handler);
15574                 
15575                 dojo.setSelectable(sizer, false);
15576         },
15577
15578         removeChild: function(widget){
15579                 // summary: Remove sizer, but only if widget is really our child and
15580                 // we have at least one sizer to throw away
15581                 if(this.sizers.length){
15582                         var i=dojo.indexOf(this.getChildren(), widget)
15583                         if(i != -1){
15584                                 if(i==this.sizers.length){
15585                                         i--;
15586                                 }
15587                                 dojo._destroyElement(this.sizers[i]);
15588                                 this.sizers.splice(i,1);
15589                         }
15590                 }
15591
15592                 // Remove widget and repaint
15593                 this.inherited(arguments); 
15594                 if(this._started){
15595                         this.layout();
15596                 }
15597         },
15598
15599         addChild: function(/*Widget*/ child, /*Integer?*/ insertIndex){
15600                 // summary: Add a child widget to the container
15601                 // child: a widget to add
15602                 // insertIndex: postion in the "stack" to add the child widget
15603                 
15604                 this.inherited("addChild",arguments); 
15605
15606                 if(this._started){
15607                         // Do the stuff that startup() does for each widget
15608                         this._injectChild(child);
15609                         var children = this.getChildren();
15610                         if(children.length > 1){
15611                                 this._addSizer();
15612                         }
15613
15614                         // and then reposition (ie, shrink) every pane to make room for the new guy
15615                         this.layout();
15616                 }
15617         },
15618
15619         layout: function(){
15620                 // summary:
15621                 //              Do layout of panels
15622
15623                 // base class defines this._contentBox on initial creation and also
15624                 // on resize
15625                 this.paneWidth = this._contentBox.w;
15626                 this.paneHeight = this._contentBox.h;
15627
15628                 var children = this.getChildren();
15629                 if(!children.length){ return; }
15630
15631                 //
15632                 // calculate space
15633                 //
15634
15635                 var space = this.isHorizontal ? this.paneWidth : this.paneHeight;
15636                 if(children.length > 1){
15637                         space -= this.sizerWidth * (children.length - 1);
15638                 }
15639
15640                 //
15641                 // calculate total of SizeShare values
15642                 //
15643                 var outOf = 0;
15644                 dojo.forEach(children, function(child){
15645                         outOf += child.sizeShare;
15646                 });
15647
15648                 //
15649                 // work out actual pixels per sizeshare unit
15650                 //
15651                 var pixPerUnit = space / outOf;
15652
15653                 //
15654                 // set the SizeActual member of each pane
15655                 //
15656                 var totalSize = 0;
15657                 dojo.forEach(children.slice(0, children.length - 1), function(child){
15658                         var size = Math.round(pixPerUnit * child.sizeShare);
15659                         child.sizeActual = size;
15660                         totalSize += size;
15661                 });
15662
15663                 children[children.length-1].sizeActual = space - totalSize;
15664
15665                 //
15666                 // make sure the sizes are ok
15667                 //
15668                 this._checkSizes();
15669
15670                 //
15671                 // now loop, positioning each pane and letting children resize themselves
15672                 //
15673
15674                 var pos = 0;
15675                 var size = children[0].sizeActual;
15676                 this._movePanel(children[0], pos, size);
15677                 children[0].position = pos;
15678                 pos += size;
15679
15680                 // if we don't have any sizers, our layout method hasn't been called yet
15681                 // so bail until we are called..TODO: REVISIT: need to change the startup
15682                 // algorithm to guaranteed the ordering of calls to layout method
15683                 if(!this.sizers){
15684                         return;
15685                 }
15686
15687                 dojo.some(children.slice(1), function(child, i){
15688                         // error-checking
15689                         if(!this.sizers[i]){
15690                                 return true;
15691                         }
15692                         // first we position the sizing handle before this pane
15693                         this._moveSlider(this.sizers[i], pos, this.sizerWidth);
15694                         this.sizers[i].position = pos;
15695                         pos += this.sizerWidth;
15696
15697                         size = child.sizeActual;
15698                         this._movePanel(child, pos, size);
15699                         child.position = pos;
15700                         pos += size;
15701                 }, this);
15702         },
15703
15704         _movePanel: function(panel, pos, size){
15705                 if(this.isHorizontal){
15706                         panel.domNode.style.left = pos + 'px';  // TODO: resize() takes l and t parameters too, don't need to set manually
15707                         panel.domNode.style.top = 0;
15708                         var box = {w: size, h: this.paneHeight};
15709                         if(panel.resize){
15710                                 panel.resize(box);
15711                         }else{
15712                                 dojo.marginBox(panel.domNode, box);
15713                         }
15714                 }else{
15715                         panel.domNode.style.left = 0;   // TODO: resize() takes l and t parameters too, don't need to set manually
15716                         panel.domNode.style.top = pos + 'px';
15717                         var box = {w: this.paneWidth, h: size};
15718                         if(panel.resize){
15719                                 panel.resize(box);
15720                         }else{
15721                                 dojo.marginBox(panel.domNode, box);
15722                         }
15723                 }
15724         },
15725
15726         _moveSlider: function(slider, pos, size){
15727                 if(this.isHorizontal){
15728                         slider.style.left = pos + 'px';
15729                         slider.style.top = 0;
15730                         dojo.marginBox(slider, { w: size, h: this.paneHeight });
15731                 }else{
15732                         slider.style.left = 0;
15733                         slider.style.top = pos + 'px';
15734                         dojo.marginBox(slider, { w: this.paneWidth, h: size });
15735                 }
15736         },
15737
15738         _growPane: function(growth, pane){
15739                 if(growth > 0){
15740                         if(pane.sizeActual > pane.sizeMin){
15741                                 if((pane.sizeActual - pane.sizeMin) > growth){
15742
15743                                         // stick all the growth in this pane
15744                                         pane.sizeActual = pane.sizeActual - growth;
15745                                         growth = 0;
15746                                 }else{
15747                                         // put as much growth in here as we can
15748                                         growth -= pane.sizeActual - pane.sizeMin;
15749                                         pane.sizeActual = pane.sizeMin;
15750                                 }
15751                         }
15752                 }
15753                 return growth;
15754         },
15755
15756         _checkSizes: function(){
15757
15758                 var totalMinSize = 0;
15759                 var totalSize = 0;
15760                 var children = this.getChildren();
15761
15762                 dojo.forEach(children, function(child){
15763                         totalSize += child.sizeActual;
15764                         totalMinSize += child.sizeMin;
15765                 });
15766
15767                 // only make adjustments if we have enough space for all the minimums
15768
15769                 if(totalMinSize <= totalSize){
15770
15771                         var growth = 0;
15772
15773                         dojo.forEach(children, function(child){
15774                                 if(child.sizeActual < child.sizeMin){
15775                                         growth += child.sizeMin - child.sizeActual;
15776                                         child.sizeActual = child.sizeMin;
15777                                 }
15778                         });
15779
15780                         if(growth > 0){
15781                                 var list = this.isDraggingLeft ? children.reverse() : children;
15782                                 dojo.forEach(list, function(child){
15783                                         growth = this._growPane(growth, child);
15784                                 }, this);
15785                         }
15786                 }else{
15787                         dojo.forEach(children, function(child){
15788                                 child.sizeActual = Math.round(totalSize * (child.sizeMin / totalMinSize));
15789                         });
15790                 }
15791         },
15792
15793         beginSizing: function(e, i){
15794                 var children = this.getChildren();
15795                 this.paneBefore = children[i];
15796                 this.paneAfter = children[i+1];
15797
15798                 this.isSizing = true;
15799                 this.sizingSplitter = this.sizers[i];
15800
15801                 if(!this.cover){
15802                         this.cover = dojo.doc.createElement('div');
15803                         this.domNode.appendChild(this.cover);
15804                         var s = this.cover.style;
15805                         s.position = 'absolute';
15806                         s.zIndex = 1;
15807                         s.top = 0;
15808                         s.left = 0;
15809                         s.width = "100%";
15810                         s.height = "100%";
15811                 }else{
15812                         this.cover.style.zIndex = 1;
15813                 }
15814                 this.sizingSplitter.style.zIndex = 2;
15815
15816                 // 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.)
15817                 this.originPos = dojo.coords(children[0].domNode, true);
15818                 if(this.isHorizontal){
15819                         var client = (e.layerX ? e.layerX : e.offsetX);
15820                         var screen = e.pageX;
15821                         this.originPos = this.originPos.x;
15822                 }else{
15823                         var client = (e.layerY ? e.layerY : e.offsetY);
15824                         var screen = e.pageY;
15825                         this.originPos = this.originPos.y;
15826                 }
15827                 this.startPoint = this.lastPoint = screen;
15828                 this.screenToClientOffset = screen - client;
15829                 this.dragOffset = this.lastPoint - this.paneBefore.sizeActual - this.originPos - this.paneBefore.position;
15830
15831                 if(!this.activeSizing){
15832                         this._showSizingLine();
15833                 }
15834
15835                 //                                      
15836                 // attach mouse events
15837                 //
15838                 this._ownconnects = [];
15839                 this._ownconnects.push(dojo.connect(dojo.doc.documentElement, "onmousemove", this, "changeSizing"));
15840                 this._ownconnects.push(dojo.connect(dojo.doc.documentElement, "onmouseup", this, "endSizing"));
15841
15842                 dojo.stopEvent(e);
15843         },
15844
15845         changeSizing: function(e){
15846                 if(!this.isSizing){ return; }
15847                 this.lastPoint = this.isHorizontal ? e.pageX : e.pageY;
15848                 this.movePoint();
15849                 if(this.activeSizing){
15850                         this._updateSize();
15851                 }else{
15852                         this._moveSizingLine();
15853                 }
15854                 dojo.stopEvent(e);
15855         },
15856
15857         endSizing: function(e){
15858                 if(!this.isSizing){ return; }
15859                 if(this.cover){
15860                         this.cover.style.zIndex = -1;
15861                 }
15862                 if(!this.activeSizing){
15863                         this._hideSizingLine();
15864                 }
15865
15866                 this._updateSize();
15867
15868                 this.isSizing = false;
15869
15870                 if(this.persist){
15871                         this._saveState(this);
15872                 }
15873
15874                 dojo.forEach(this._ownconnects,dojo.disconnect); 
15875         },
15876
15877         movePoint: function(){
15878
15879                 // make sure lastPoint is a legal point to drag to
15880                 var p = this.lastPoint - this.screenToClientOffset;
15881
15882                 var a = p - this.dragOffset;
15883                 a = this.legaliseSplitPoint(a);
15884                 p = a + this.dragOffset;
15885
15886                 this.lastPoint = p + this.screenToClientOffset;
15887         },
15888
15889         legaliseSplitPoint: function(a){
15890
15891                 a += this.sizingSplitter.position;
15892
15893                 this.isDraggingLeft = !!(a > 0);
15894
15895                 if(!this.activeSizing){
15896                         var min = this.paneBefore.position + this.paneBefore.sizeMin;
15897                         if(a < min){
15898                                 a = min;
15899                         }
15900
15901                         var max = this.paneAfter.position + (this.paneAfter.sizeActual - (this.sizerWidth + this.paneAfter.sizeMin));
15902                         if(a > max){
15903                                 a = max;
15904                         }
15905                 }
15906
15907                 a -= this.sizingSplitter.position;
15908
15909                 this._checkSizes();
15910
15911                 return a;
15912         },
15913
15914         _updateSize: function(){
15915         //FIXME: sometimes this.lastPoint is NaN
15916                 var pos = this.lastPoint - this.dragOffset - this.originPos;
15917
15918                 var start_region = this.paneBefore.position;
15919                 var end_region   = this.paneAfter.position + this.paneAfter.sizeActual;
15920
15921                 this.paneBefore.sizeActual = pos - start_region;
15922                 this.paneAfter.position = pos + this.sizerWidth;
15923                 this.paneAfter.sizeActual  = end_region - this.paneAfter.position;
15924
15925                 dojo.forEach(this.getChildren(), function(child){
15926                         child.sizeShare = child.sizeActual;
15927                 });
15928
15929                 if(this._started){
15930                         this.layout();
15931                 }
15932         },
15933
15934         _showSizingLine: function(){
15935
15936                 this._moveSizingLine();
15937
15938                 dojo.marginBox(this.virtualSizer,
15939                         this.isHorizontal ? { w: this.sizerWidth, h: this.paneHeight } : { w: this.paneWidth, h: this.sizerWidth });
15940
15941                 this.virtualSizer.style.display = 'block';
15942         },
15943
15944         _hideSizingLine: function(){
15945                 this.virtualSizer.style.display = 'none';
15946         },
15947
15948         _moveSizingLine: function(){
15949                 var pos = (this.lastPoint - this.startPoint) + this.sizingSplitter.position;
15950                 dojo.style(this.virtualSizer,(this.isHorizontal ? "left" : "top"),pos+"px");
15951                 // this.virtualSizer.style[ this.isHorizontal ? "left" : "top" ] = pos + 'px'; // FIXME: remove this line if the previous is better
15952         },
15953
15954         _getCookieName: function(i){
15955                 return this.id + "_" + i;
15956         },
15957
15958         _restoreState: function(){
15959                 dojo.forEach(this.getChildren(), function(child, i){
15960                         var cookieName = this._getCookieName(i);
15961                         var cookieValue = dojo.cookie(cookieName);
15962                         if(cookieValue){
15963                                 var pos = parseInt(cookieValue);
15964                                 if(typeof pos == "number"){
15965                                         child.sizeShare = pos;
15966                                 }
15967                         }
15968                 }, this);
15969         },
15970
15971         _saveState: function(){
15972                 dojo.forEach(this.getChildren(), function(child, i){
15973                         dojo.cookie(this._getCookieName(i), child.sizeShare);
15974                 }, this);
15975         }
15976 });
15977
15978 // These arguments can be specified for the children of a SplitContainer.
15979 // Since any widget can be specified as a SplitContainer child, mix them
15980 // into the base widget class.  (This is a hack, but it's effective.)
15981 dojo.extend(dijit._Widget, {
15982         // sizeMin: Integer
15983         //      Minimum size (width or height) of a child of a SplitContainer.
15984         //      The value is relative to other children's sizeShare properties.
15985         sizeMin: 10,
15986
15987         // sizeShare: Integer
15988         //      Size (width or height) of a child of a SplitContainer.
15989         //      The value is relative to other children's sizeShare properties.
15990         //      For example, if there are two children and each has sizeShare=10, then
15991         //      each takes up 50% of the available space.
15992         sizeShare: 10
15993 });
15994
15995 }
15996
15997 if(!dojo._hasResource["dijit.layout.TabContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
15998 dojo._hasResource["dijit.layout.TabContainer"] = true;
15999 dojo.provide("dijit.layout.TabContainer");
16000
16001
16002
16003
16004 dojo.declare("dijit.layout.TabContainer",
16005         [dijit.layout.StackContainer, dijit._Templated],
16006         {       
16007         // summary: 
16008         //      A Container with Title Tabs, each one pointing at a pane in the container.
16009         // description:
16010         //      A TabContainer is a container that has multiple panes, but shows only
16011         //      one pane at a time.  There are a set of tabs corresponding to each pane,
16012         //      where each tab has the title (aka title) of the pane, and optionally a close button.
16013         //
16014         //      Publishes topics [widgetId]-addChild, [widgetId]-removeChild, and [widgetId]-selectChild
16015         //      (where [widgetId] is the id of the TabContainer itself.
16016         //
16017         // tabPosition: String
16018         //   Defines where tabs go relative to tab content.
16019         //   "top", "bottom", "left-h", "right-h"
16020         tabPosition: "top",
16021
16022         templateString: null,   // override setting in StackContainer
16023         templateString:"<div class=\"dijitTabContainer\">\n\t<div dojoAttachPoint=\"tablistNode\"></div>\n\t<div class=\"dijitTabPaneWrapper\" dojoAttachPoint=\"containerNode\"></div>\n</div>\n",
16024
16025         // _controllerWidget: String
16026         //              An optional parameter to overrider the default TabContainer controller used.
16027         _controllerWidget: "dijit.layout.TabController",
16028
16029         postCreate: function(){ 
16030                 this.inherited(arguments);
16031                 // create the tab list that will have a tab (a.k.a. tab button) for each tab panel
16032                 var TabController = dojo.getObject(this._controllerWidget);
16033                 this.tablist = new TabController({
16034                         id: this.id + "_tablist",
16035                         tabPosition: this.tabPosition,
16036                         doLayout: this.doLayout,
16037                         containerId: this.id
16038                 }, this.tablistNode);           
16039         },
16040
16041         _setupChild: function(/* Widget */tab){
16042                 dojo.addClass(tab.domNode, "dijitTabPane");
16043                 this.inherited(arguments);
16044                 return tab; // Widget
16045         },
16046
16047         startup: function(){
16048                 if(this._started){ return; }
16049
16050                 // wire up the tablist and its tabs
16051                 this.tablist.startup();
16052                 this.inherited(arguments);
16053
16054                 if(dojo.isSafari){
16055                         // sometimes safari 3.0.3 miscalculates the height of the tab labels, see #4058
16056                         setTimeout(dojo.hitch(this, "layout"), 0);
16057                 }
16058
16059                 if(dojo.isIE && !this.isLeftToRight() && this.tabPosition == "right-h" &&
16060                    this.tablist && this.tablist.pane2button){
16061                         //need rectify non-closable tab in IE, only for "right-h" mode
16062                         for(var pane in this.tablist.pane2button){
16063                                 var tabButton = this.tablist.pane2button[pane];
16064                                 if(!tabButton.closeButton){ continue; }
16065                                 tabButtonStyle = tabButton.closeButtonNode.style;
16066                                 tabButtonStyle.position ="absolute";
16067                                 if(dojo.isIE < 7){
16068                                         tabButtonStyle.left = tabButton.domNode.offsetWidth + "px";
16069                                 }else{
16070                                         tabButtonStyle.padding = "0px";
16071                                 }
16072                         }
16073                 }
16074         },
16075
16076         layout: function(){
16077                 // Summary: Configure the content pane to take up all the space except for where the tabs are
16078                 if(!this.doLayout){ return; }
16079
16080                 // position and size the titles and the container node
16081                 var titleAlign = this.tabPosition.replace(/-h/,"");
16082                 var children = [
16083                         { domNode: this.tablist.domNode, layoutAlign: titleAlign },
16084                         { domNode: this.containerNode, layoutAlign: "client" }
16085                 ];
16086                 dijit.layout.layoutChildren(this.domNode, this._contentBox, children);
16087
16088                 // Compute size to make each of my children.
16089                 // children[1] is the margin-box size of this.containerNode, set by layoutChildren() call above
16090                 this._containerContentBox = dijit.layout.marginBox2contentBox(this.containerNode, children[1]);
16091
16092                 if(this.selectedChildWidget){
16093                         this._showChild(this.selectedChildWidget);
16094                         if(this.doLayout && this.selectedChildWidget.resize){
16095                                 this.selectedChildWidget.resize(this._containerContentBox);
16096                         }
16097                 }
16098         },
16099
16100         destroy: function(){
16101                 if(this.tablist){
16102                         this.tablist.destroy();
16103                 }
16104                 this.inherited(arguments);
16105         }
16106 });
16107
16108 //TODO: make private?
16109 dojo.declare("dijit.layout.TabController",
16110         dijit.layout.StackController,
16111         {
16112         // summary:
16113         //      Set of tabs (the things with titles and a close button, that you click to show a tab panel).
16114         // description:
16115         //      Lets the user select the currently shown pane in a TabContainer or StackContainer.
16116         //      TabController also monitors the TabContainer, and whenever a pane is
16117         //      added or deleted updates itself accordingly.
16118
16119         templateString: "<div wairole='tablist' dojoAttachEvent='onkeypress:onkeypress'></div>",
16120
16121         // tabPosition: String
16122         //   Defines where tabs go relative to the content.
16123         //   "top", "bottom", "left-h", "right-h"
16124         tabPosition: "top",
16125
16126         // doLayout: Boolean
16127         //      TODOC: deprecate doLayout? not sure.
16128         doLayout: true,
16129
16130         // buttonWidget: String
16131         //      The name of the tab widget to create to correspond to each page
16132         buttonWidget: "dijit.layout._TabButton",
16133
16134         postMixInProperties: function(){
16135                 this["class"] = "dijitTabLabels-" + this.tabPosition + (this.doLayout ? "" : " dijitTabNoLayout");
16136                 this.inherited(arguments);
16137         },
16138
16139 //TODO: can this be accomplished in CSS?
16140         _rectifyRtlTabList: function(){
16141                 //Summary: Rectify the length of all tabs in rtl, otherwise the tab lengths are different in IE
16142                 if(0 >= this.tabPosition.indexOf('-h')){ return; }
16143                 if(!this.pane2button){ return; }
16144
16145                 var maxLen = 0;
16146                 for(var pane in this.pane2button){
16147                         maxLen = Math.max(maxLen, dojo.marginBox(this.pane2button[pane].innerDiv).w);
16148                 }
16149                 //unify the length of all the tabs
16150                 for(pane in this.pane2button){
16151                         this.pane2button[pane].innerDiv.style.width = maxLen + 'px';
16152                 }       
16153         }
16154 });
16155
16156 dojo.declare("dijit.layout._TabButton",
16157         dijit.layout._StackButton,
16158         {
16159         // summary:
16160         //      A tab (the thing you click to select a pane).
16161         // description:
16162         //      Contains the title of the pane, and optionally a close-button to destroy the pane.
16163         //      This is an internal widget and should not be instantiated directly.
16164
16165         baseClass: "dijitTab",
16166
16167         templateString:"<div waiRole=\"presentation\" dojoAttachEvent='onclick:onClick,onmouseenter:_onMouse,onmouseleave:_onMouse'>\n    <div waiRole=\"presentation\" class='dijitTabInnerDiv' dojoAttachPoint='innerDiv'>\n        <div waiRole=\"presentation\" class='dijitTabContent' dojoAttachPoint='tabContent'>\n\t        <span dojoAttachPoint='containerNode,focusNode' class='tabLabel'>${!label}</span>\n\t        <span dojoAttachPoint='closeButtonNode' class='closeImage' dojoAttachEvent='onmouseenter:_onMouse, onmouseleave:_onMouse, onclick:onClickCloseButton' stateModifier='CloseButton'>\n\t            <span dojoAttachPoint='closeText' class='closeText'>x</span>\n\t        </span>\n        </div>\n    </div>\n</div>\n",
16168
16169         postCreate: function(){
16170                 if(this.closeButton){
16171                         dojo.addClass(this.innerDiv, "dijitClosable");
16172                 }else{
16173                         this.closeButtonNode.style.display="none";
16174                 }
16175                 this.inherited(arguments); 
16176                 dojo.setSelectable(this.containerNode, false);
16177         }
16178 });
16179
16180 }
16181
16182 if(!dojo._hasResource["dijit.dijit-all"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
16183 dojo._hasResource["dijit.dijit-all"] = true;
16184 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");
16185 dojo.provide("dijit.dijit-all");
16186
16187 /*=====
16188 dijit["dijit-all"] = { 
16189         // summary: A rollup that includes every dijit. You probably don't need this.
16190 };
16191 =====*/
16192
16193
16194
16195
16196
16197
16198
16199
16200
16201
16202
16203
16204
16205
16206
16207
16208
16209
16210
16211
16212
16213
16214
16215
16216
16217
16218
16219
16220
16221
16222
16223
16224
16225  //deprecated
16226
16227  //deprecated
16228
16229
16230
16231
16232 }
16233
16234
16235 dojo.i18n._preloadLocalizations("dijit.nls.dijit-all", ["he","nl","tr","no","ko","el","en","en-gb","ROOT","zh-cn","hu","es","fi-fi","pt-br","fi","he-il","xx","ru","it","fr","cs","de-de","fr-fr","it-it","es-es","ja","da","pl","de","sv","pt","zh-tw","pt-pt","nl-nl","ko-kr","ar","en-us","zh","ja-jp"]);